blob: 51cdb295cffccbaf6cb5f251f3e5d4af661275bc [file] [log] [blame]
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001"""IDLE Configuration Dialog: support user customization of IDLE by GUI
2
3Customize font faces, sizes, and colorization attributes. Set indentation
4defaults. Customize keybindings. Colorization and keybindings can be
5saved as user defined sets. Select startup options including shell/editor
6and default window size. Define additional help sources.
7
8Note that tab width in IDLE is currently fixed at eight due to Tk issues.
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00009Refer to comments in EditorWindow autoindent code for details.
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +000010
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000011"""
Georg Brandl6634bf22008-05-20 07:13:37 +000012from Tkinter import *
13import tkMessageBox, tkColorChooser, tkFont
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000014
Florent Xiclunad630c042010-04-02 07:24:52 +000015from idlelib.configHandler import idleConf
16from idlelib.dynOptionMenuWidget import DynOptionMenu
Florent Xiclunad630c042010-04-02 07:24:52 +000017from idlelib.keybindingDialog import GetKeysDialog
18from idlelib.configSectionNameDialog import GetCfgSectionNameDialog
19from idlelib.configHelpSourceEdit import GetHelpSourceDialog
Terry Jan Reedy7a162072014-10-22 20:15:12 -040020from idlelib.tabbedpages import TabbedPageSet
Terry Jan Reedyc0a053e2015-10-11 22:07:25 -040021from idlelib.textView import view_text
Florent Xiclunad630c042010-04-02 07:24:52 +000022from idlelib import macosxSupport
Terry Jan Reedyc0a053e2015-10-11 22:07:25 -040023
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000024class ConfigDialog(Toplevel):
Kurt B. Kaiseracdef852005-01-31 03:34:26 +000025
Terry Jan Reedycf834762014-10-17 01:31:29 -040026 def __init__(self, parent, title='', _htest=False, _utest=False):
Terry Jan Reedy76916e82014-05-29 01:46:16 -040027 """
28 _htest - bool, change box location when running htest
Terry Jan Reedyaf0dce92014-07-14 23:07:21 -040029 _utest - bool, don't wait_window when running unittest
Terry Jan Reedy76916e82014-05-29 01:46:16 -040030 """
Steven M. Gavad721c482001-07-31 10:46:53 +000031 Toplevel.__init__(self, parent)
Terry Jan Reedy19b7a752014-07-30 19:24:26 -040032 self.parent = parent
Terry Jan Reedyae410862014-08-03 23:02:53 -040033 if _htest:
34 parent.instance_dict = {}
Kurt B. Kaisere3fde8f2007-10-04 03:11:12 +000035 self.wm_withdraw()
36
Steven M. Gavad721c482001-07-31 10:46:53 +000037 self.configure(borderwidth=5)
Terry Jan Reedycf834762014-10-17 01:31:29 -040038 self.title(title or 'IDLE Preferences')
Terry Jan Reedyae410862014-08-03 23:02:53 -040039 self.geometry(
40 "+%d+%d" % (parent.winfo_rootx() + 20,
41 parent.winfo_rooty() + (30 if not _htest else 150)))
Georg Brandl7eb4b7d2005-07-22 21:49:32 +000042 #Theme Elements. Each theme element key is its display name.
Steven M. Gava9dd16b32001-11-03 14:54:25 +000043 #The first value of the tuple is the sample area tag name.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000044 #The second value is the display name list sort index.
Terry Jan Reedyae410862014-08-03 23:02:53 -040045 self.themeElements={
Terry Jan Reedyc30475e2015-10-02 22:12:09 -040046 'Normal Text': ('normal', '00'),
47 'Python Keywords': ('keyword', '01'),
48 'Python Definitions': ('definition', '02'),
49 'Python Builtins': ('builtin', '03'),
50 'Python Comments': ('comment', '04'),
51 'Python Strings': ('string', '05'),
52 'Selected Text': ('hilite', '06'),
53 'Found Text': ('hit', '07'),
54 'Cursor': ('cursor', '08'),
55 'Editor Breakpoint': ('break', '09'),
56 'Shell Normal Text': ('console', '10'),
57 'Shell Error Text': ('error', '11'),
58 'Shell Stdout Text': ('stdout', '12'),
59 'Shell Stderr Text': ('stderr', '13'),
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000060 }
Steven M. Gavaa498af22002-02-01 01:33:36 +000061 self.ResetChangedItems() #load initial values in changed items dict
Steven M. Gavad721c482001-07-31 10:46:53 +000062 self.CreateWidgets()
Terry Jan Reedyae410862014-08-03 23:02:53 -040063 self.resizable(height=FALSE, width=FALSE)
Steven M. Gavad721c482001-07-31 10:46:53 +000064 self.transient(parent)
65 self.grab_set()
66 self.protocol("WM_DELETE_WINDOW", self.Cancel)
Steven M. Gava2d4e03b2001-12-05 07:54:07 +000067 self.tabPages.focus_set()
Steven M. Gavad721c482001-07-31 10:46:53 +000068 #key bindings for this dialog
Terry Jan Reedyae410862014-08-03 23:02:53 -040069 #self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
70 #self.bind('<Alt-a>', self.Apply) #apply changes, save
71 #self.bind('<F1>', self.Help) #context help
Steven M. Gava429a86a2001-10-23 10:42:12 +000072 self.LoadConfigs()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000073 self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
Kurt B. Kaisere3fde8f2007-10-04 03:11:12 +000074
Terry Jan Reedyaf0dce92014-07-14 23:07:21 -040075 if not _utest:
76 self.wm_deiconify()
77 self.wait_window()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000078
Steven M. Gavad721c482001-07-31 10:46:53 +000079 def CreateWidgets(self):
Kurt B. Kaiser20172f92007-10-30 02:38:54 +000080 self.tabPages = TabbedPageSet(self,
Terry Jan Reedyae410862014-08-03 23:02:53 -040081 page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General'])
Terry Jan Reedy23030db2014-10-08 20:29:05 -040082 self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
83 self.CreatePageFontTab()
84 self.CreatePageHighlight()
85 self.CreatePageKeys()
86 self.CreatePageGeneral()
87 self.create_action_buttons().pack(side=BOTTOM)
Terry Jan Reedyc0a053e2015-10-11 22:07:25 -040088
Terry Jan Reedy23030db2014-10-08 20:29:05 -040089 def create_action_buttons(self):
Ned Deily57847df2014-03-27 20:47:04 -070090 if macosxSupport.isAquaTk():
Ronald Oussorena97063a2009-03-04 21:35:05 +000091 # Changing the default padding on OSX results in unreadable
92 # text in the buttons
Terry Jan Reedyae410862014-08-03 23:02:53 -040093 paddingArgs = {}
Ronald Oussorena97063a2009-03-04 21:35:05 +000094 else:
Terry Jan Reedyae410862014-08-03 23:02:53 -040095 paddingArgs = {'padx':6, 'pady':3}
Terry Jan Reedy7a162072014-10-22 20:15:12 -040096 outer = Frame(self, pady=2)
97 buttons = Frame(outer, pady=2)
Terry Jan Reedyc0a053e2015-10-11 22:07:25 -040098 for txt, cmd in (
99 ('Ok', self.Ok),
100 ('Apply', self.Apply),
101 ('Cancel', self.Cancel),
102 ('Help', self.Help)):
103 Button(buttons, text=txt, command=cmd, takefocus=FALSE,
104 **paddingArgs).pack(side=LEFT, padx=5)
Terry Jan Reedy7a162072014-10-22 20:15:12 -0400105 # add space above buttons
106 Frame(outer, height=2, borderwidth=0).pack(side=TOP)
107 buttons.pack(side=BOTTOM)
108 return outer
Terry Jan Reedyc0a053e2015-10-11 22:07:25 -0400109
Steven M. Gava60fc7072001-08-04 13:58:22 +0000110 def CreatePageFontTab(self):
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400111 parent = self.parent
112 self.fontSize = StringVar(parent)
113 self.fontBold = BooleanVar(parent)
114 self.fontName = StringVar(parent)
115 self.spaceNum = IntVar(parent)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400116 self.editFont = tkFont.Font(parent, ('courier', 10, 'normal'))
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400117
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000118 ##widget creation
119 #body frame
Terry Jan Reedyae410862014-08-03 23:02:53 -0400120 frame = self.tabPages.pages['Fonts/Tabs'].frame
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000121 #body section frames
Terry Jan Reedyae410862014-08-03 23:02:53 -0400122 frameFont = LabelFrame(
123 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
124 frameIndent = LabelFrame(
125 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000126 #frameFont
Terry Jan Reedyae410862014-08-03 23:02:53 -0400127 frameFontName = Frame(frameFont)
128 frameFontParam = Frame(frameFont)
129 labelFontNameTitle = Label(
130 frameFontName, justify=LEFT, text='Font Face :')
131 self.listFontName = Listbox(
132 frameFontName, height=5, takefocus=FALSE, exportselection=FALSE)
133 self.listFontName.bind(
134 '<ButtonRelease-1>', self.OnListFontButtonRelease)
135 scrollFont = Scrollbar(frameFontName)
Steven M. Gavac01e30f2001-08-11 15:48:13 +0000136 scrollFont.config(command=self.listFontName.yview)
137 self.listFontName.config(yscrollcommand=scrollFont.set)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400138 labelFontSizeTitle = Label(frameFontParam, text='Size :')
139 self.optMenuFontSize = DynOptionMenu(
140 frameFontParam, self.fontSize, None, command=self.SetFontSample)
141 checkFontBold = Checkbutton(
142 frameFontParam, variable=self.fontBold, onvalue=1,
143 offvalue=0, text='Bold', command=self.SetFontSample)
144 frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1)
145 self.labelFontSample = Label(
146 frameFontSample, justify=LEFT, font=self.editFont,
147 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000148 #frameIndent
Terry Jan Reedyae410862014-08-03 23:02:53 -0400149 frameIndentSize = Frame(frameIndent)
150 labelSpaceNumTitle = Label(
151 frameIndentSize, justify=LEFT,
152 text='Python Standard: 4 Spaces!')
153 self.scaleSpaceNum = Scale(
154 frameIndentSize, variable=self.spaceNum,
155 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400156
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000157 #widget packing
158 #body
Terry Jan Reedyae410862014-08-03 23:02:53 -0400159 frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
160 frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y)
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000161 #frameFont
Terry Jan Reedyae410862014-08-03 23:02:53 -0400162 frameFontName.pack(side=TOP, padx=5, pady=5, fill=X)
163 frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X)
164 labelFontNameTitle.pack(side=TOP, anchor=W)
165 self.listFontName.pack(side=LEFT, expand=TRUE, fill=X)
166 scrollFont.pack(side=LEFT, fill=Y)
167 labelFontSizeTitle.pack(side=LEFT, anchor=W)
168 self.optMenuFontSize.pack(side=LEFT, anchor=W)
169 checkFontBold.pack(side=LEFT, anchor=W, padx=20)
170 frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
171 self.labelFontSample.pack(expand=TRUE, fill=BOTH)
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000172 #frameIndent
Terry Jan Reedyae410862014-08-03 23:02:53 -0400173 frameIndentSize.pack(side=TOP, fill=X)
174 labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5)
175 self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000176 return frame
177
178 def CreatePageHighlight(self):
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400179 parent = self.parent
180 self.builtinTheme = StringVar(parent)
181 self.customTheme = StringVar(parent)
182 self.fgHilite = BooleanVar(parent)
183 self.colour = StringVar(parent)
184 self.fontName = StringVar(parent)
185 self.themeIsBuiltin = BooleanVar(parent)
186 self.highlightTarget = StringVar(parent)
187
Steven M. Gava952d0a52001-08-03 04:43:44 +0000188 ##widget creation
189 #body frame
Terry Jan Reedyae410862014-08-03 23:02:53 -0400190 frame = self.tabPages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000191 #body section frames
Terry Jan Reedyae410862014-08-03 23:02:53 -0400192 frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
193 text=' Custom Highlighting ')
194 frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
195 text=' Highlighting Theme ')
Steven M. Gava952d0a52001-08-03 04:43:44 +0000196 #frameCustom
Terry Jan Reedyae410862014-08-03 23:02:53 -0400197 self.textHighlightSample=Text(
198 frameCustom, relief=SOLID, borderwidth=1,
199 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
200 takefocus=FALSE, highlightthickness=0, wrap=NONE)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000201 text=self.textHighlightSample
Terry Jan Reedyae410862014-08-03 23:02:53 -0400202 text.bind('<Double-Button-1>', lambda e: 'break')
203 text.bind('<B1-Motion>', lambda e: 'break')
204 textAndTags=(
205 ('#you can click here', 'comment'), ('\n', 'normal'),
206 ('#to choose items', 'comment'), ('\n', 'normal'),
207 ('def', 'keyword'), (' ', 'normal'),
208 ('func', 'definition'), ('(param):\n ', 'normal'),
209 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
210 ("'string'", 'string'), ('\n var1 = ', 'normal'),
211 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
212 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
213 ('list', 'builtin'), ('(', 'normal'),
Terry Jan Reedyc30475e2015-10-02 22:12:09 -0400214 ('None', 'builtin'), (')\n', 'normal'),
215 (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
Terry Jan Reedyae410862014-08-03 23:02:53 -0400216 (' error ', 'error'), (' ', 'normal'),
217 ('cursor |', 'cursor'), ('\n ', 'normal'),
218 ('shell', 'console'), (' ', 'normal'),
219 ('stdout', 'stdout'), (' ', 'normal'),
220 ('stderr', 'stderr'), ('\n', 'normal'))
Steven M. Gava9dd16b32001-11-03 14:54:25 +0000221 for txTa in textAndTags:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400222 text.insert(END, txTa[0], txTa[1])
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400223 for element in self.themeElements:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400224 def tem(event, elem=element):
225 event.widget.winfo_toplevel().highlightTarget.set(elem)
226 text.tag_bind(
227 self.themeElements[element][0], '<ButtonPress-1>', tem)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000228 text.config(state=DISABLED)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400229 self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
230 frameFgBg = Frame(frameCustom)
231 buttonSetColour = Button(
232 self.frameColourSet, text='Choose Colour for :',
233 command=self.GetColour, highlightthickness=0)
234 self.optMenuHighlightTarget = DynOptionMenu(
235 self.frameColourSet, self.highlightTarget, None,
236 highlightthickness=0) #, command=self.SetHighlightTargetBinding
237 self.radioFg = Radiobutton(
238 frameFgBg, variable=self.fgHilite, value=1,
239 text='Foreground', command=self.SetColourSampleBinding)
240 self.radioBg=Radiobutton(
241 frameFgBg, variable=self.fgHilite, value=0,
242 text='Background', command=self.SetColourSampleBinding)
Steven M. Gava7c017862001-10-29 11:19:46 +0000243 self.fgHilite.set(1)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400244 buttonSaveCustomTheme = Button(
245 frameCustom, text='Save as New Custom Theme',
246 command=self.SaveAsNewTheme)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000247 #frameTheme
Terry Jan Reedyae410862014-08-03 23:02:53 -0400248 labelTypeTitle = Label(frameTheme, text='Select : ')
249 self.radioThemeBuiltin = Radiobutton(
250 frameTheme, variable=self.themeIsBuiltin, value=1,
251 command=self.SetThemeType, text='a Built-in Theme')
252 self.radioThemeCustom = Radiobutton(
253 frameTheme, variable=self.themeIsBuiltin, value=0,
254 command=self.SetThemeType, text='a Custom Theme')
255 self.optMenuThemeBuiltin = DynOptionMenu(
256 frameTheme, self.builtinTheme, None, command=None)
257 self.optMenuThemeCustom=DynOptionMenu(
258 frameTheme, self.customTheme, None, command=None)
259 self.buttonDeleteCustomTheme=Button(
260 frameTheme, text='Delete Custom Theme',
Steven M. Gava49745752002-02-18 01:43:11 +0000261 command=self.DeleteCustomTheme)
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400262
Steven M. Gava952d0a52001-08-03 04:43:44 +0000263 ##widget packing
264 #body
Terry Jan Reedyae410862014-08-03 23:02:53 -0400265 frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
266 frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000267 #frameCustom
Terry Jan Reedyae410862014-08-03 23:02:53 -0400268 self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
269 frameFgBg.pack(side=TOP, padx=5, pady=0)
270 self.textHighlightSample.pack(
271 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
272 buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
273 self.optMenuHighlightTarget.pack(
274 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
275 self.radioFg.pack(side=LEFT, anchor=E)
276 self.radioBg.pack(side=RIGHT, anchor=W)
277 buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000278 #frameTheme
Terry Jan Reedyae410862014-08-03 23:02:53 -0400279 labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
280 self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
281 self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
282 self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
283 self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
284 self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000285 return frame
286
287 def CreatePageKeys(self):
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400288 parent = self.parent
289 self.bindingTarget = StringVar(parent)
290 self.builtinKeys = StringVar(parent)
291 self.customKeys = StringVar(parent)
292 self.keysAreBuiltin = BooleanVar(parent)
293 self.keyBinding = StringVar(parent)
294
Steven M. Gava60fc7072001-08-04 13:58:22 +0000295 ##widget creation
296 #body frame
Terry Jan Reedyae410862014-08-03 23:02:53 -0400297 frame = self.tabPages.pages['Keys'].frame
Steven M. Gava60fc7072001-08-04 13:58:22 +0000298 #body section frames
Terry Jan Reedyae410862014-08-03 23:02:53 -0400299 frameCustom = LabelFrame(
300 frame, borderwidth=2, relief=GROOVE,
301 text=' Custom Key Bindings ')
302 frameKeySets = LabelFrame(
303 frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
Steven M. Gava60fc7072001-08-04 13:58:22 +0000304 #frameCustom
Terry Jan Reedyae410862014-08-03 23:02:53 -0400305 frameTarget = Frame(frameCustom)
306 labelTargetTitle = Label(frameTarget, text='Action - Key(s)')
307 scrollTargetY = Scrollbar(frameTarget)
308 scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL)
309 self.listBindings = Listbox(
310 frameTarget, takefocus=FALSE, exportselection=FALSE)
311 self.listBindings.bind('<ButtonRelease-1>', self.KeyBindingSelected)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000312 scrollTargetY.config(command=self.listBindings.yview)
313 scrollTargetX.config(command=self.listBindings.xview)
314 self.listBindings.config(yscrollcommand=scrollTargetY.set)
315 self.listBindings.config(xscrollcommand=scrollTargetX.set)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400316 self.buttonNewKeys = Button(
317 frameCustom, text='Get New Keys for Selection',
318 command=self.GetNewKeys, state=DISABLED)
Steven M. Gava60fc7072001-08-04 13:58:22 +0000319 #frameKeySets
Kurt B. Kaiser28c7bcf2007-12-28 04:18:01 +0000320 frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0)
321 for i in range(2)]
Terry Jan Reedyae410862014-08-03 23:02:53 -0400322 self.radioKeysBuiltin = Radiobutton(
323 frames[0], variable=self.keysAreBuiltin, value=1,
324 command=self.SetKeysType, text='Use a Built-in Key Set')
325 self.radioKeysCustom = Radiobutton(
326 frames[0], variable=self.keysAreBuiltin, value=0,
327 command=self.SetKeysType, text='Use a Custom Key Set')
328 self.optMenuKeysBuiltin = DynOptionMenu(
329 frames[0], self.builtinKeys, None, command=None)
330 self.optMenuKeysCustom = DynOptionMenu(
331 frames[0], self.customKeys, None, command=None)
332 self.buttonDeleteCustomKeys = Button(
333 frames[1], text='Delete Custom Key Set',
Steven M. Gava49745752002-02-18 01:43:11 +0000334 command=self.DeleteCustomKeys)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400335 buttonSaveCustomKeys = Button(
336 frames[1], text='Save as New Custom Key Set',
337 command=self.SaveAsNewKeySet)
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400338
Steven M. Gava60fc7072001-08-04 13:58:22 +0000339 ##widget packing
340 #body
Terry Jan Reedyae410862014-08-03 23:02:53 -0400341 frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
342 frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
Steven M. Gava60fc7072001-08-04 13:58:22 +0000343 #frameCustom
Terry Jan Reedyae410862014-08-03 23:02:53 -0400344 self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
345 frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000346 #frame target
Terry Jan Reedyae410862014-08-03 23:02:53 -0400347 frameTarget.columnconfigure(0, weight=1)
348 frameTarget.rowconfigure(1, weight=1)
349 labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W)
350 self.listBindings.grid(row=1, column=0, sticky=NSEW)
351 scrollTargetY.grid(row=1, column=1, sticky=NS)
352 scrollTargetX.grid(row=2, column=0, sticky=EW)
Steven M. Gava60fc7072001-08-04 13:58:22 +0000353 #frameKeySets
Kurt B. Kaiser28c7bcf2007-12-28 04:18:01 +0000354 self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS)
355 self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS)
356 self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
357 self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400358 self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
359 buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
Kurt B. Kaiser28c7bcf2007-12-28 04:18:01 +0000360 frames[0].pack(side=TOP, fill=BOTH, expand=True)
361 frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000362 return frame
363
364 def CreatePageGeneral(self):
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400365 parent = self.parent
366 self.winWidth = StringVar(parent)
367 self.winHeight = StringVar(parent)
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400368 self.startupEdit = IntVar(parent)
369 self.autoSave = IntVar(parent)
370 self.encoding = StringVar(parent)
371 self.userHelpBrowser = BooleanVar(parent)
372 self.helpBrowser = StringVar(parent)
373
Steven M. Gava230e5782001-08-07 03:28:25 +0000374 #widget creation
375 #body
Terry Jan Reedyae410862014-08-03 23:02:53 -0400376 frame = self.tabPages.pages['General'].frame
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000377 #body section frames
Terry Jan Reedyae410862014-08-03 23:02:53 -0400378 frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE,
379 text=' Startup Preferences ')
380 frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE,
381 text=' Autosave Preferences ')
382 frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400383 frameEncoding = Frame(frame, borderwidth=2, relief=GROOVE)
384 frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE,
385 text=' Additional Help Sources ')
Steven M. Gava230e5782001-08-07 03:28:25 +0000386 #frameRun
Terry Jan Reedyae410862014-08-03 23:02:53 -0400387 labelRunChoiceTitle = Label(frameRun, text='At Startup')
388 radioStartupEdit = Radiobutton(
389 frameRun, variable=self.startupEdit, value=1,
390 command=self.SetKeysType, text="Open Edit Window")
391 radioStartupShell = Radiobutton(
392 frameRun, variable=self.startupEdit, value=0,
393 command=self.SetKeysType, text='Open Shell Window')
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000394 #frameSave
Terry Jan Reedyae410862014-08-03 23:02:53 -0400395 labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ')
396 radioSaveAsk = Radiobutton(
397 frameSave, variable=self.autoSave, value=0,
398 command=self.SetKeysType, text="Prompt to Save")
399 radioSaveAuto = Radiobutton(
400 frameSave, variable=self.autoSave, value=1,
401 command=self.SetKeysType, text='No Prompt')
Steven M. Gava230e5782001-08-07 03:28:25 +0000402 #frameWinSize
Terry Jan Reedyae410862014-08-03 23:02:53 -0400403 labelWinSizeTitle = Label(
404 frameWinSize, text='Initial Window Size (in characters)')
405 labelWinWidthTitle = Label(frameWinSize, text='Width')
406 entryWinWidth = Entry(
407 frameWinSize, textvariable=self.winWidth, width=3)
408 labelWinHeightTitle = Label(frameWinSize, text='Height')
409 entryWinHeight = Entry(
410 frameWinSize, textvariable=self.winHeight, width=3)
Kurt B. Kaisera053f332003-05-10 00:49:56 +0000411 #frameEncoding
Terry Jan Reedyae410862014-08-03 23:02:53 -0400412 labelEncodingTitle = Label(
413 frameEncoding, text="Default Source Encoding")
414 radioEncLocale = Radiobutton(
415 frameEncoding, variable=self.encoding,
416 value="locale", text="Locale-defined")
417 radioEncUTF8 = Radiobutton(
418 frameEncoding, variable=self.encoding,
419 value="utf-8", text="UTF-8")
420 radioEncNone = Radiobutton(
421 frameEncoding, variable=self.encoding,
422 value="none", text="None")
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000423 #frameHelp
Terry Jan Reedyae410862014-08-03 23:02:53 -0400424 frameHelpList = Frame(frameHelp)
425 frameHelpListButtons = Frame(frameHelpList)
426 scrollHelpList = Scrollbar(frameHelpList)
427 self.listHelp = Listbox(
428 frameHelpList, height=5, takefocus=FALSE,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000429 exportselection=FALSE)
430 scrollHelpList.config(command=self.listHelp.yview)
431 self.listHelp.config(yscrollcommand=scrollHelpList.set)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400432 self.listHelp.bind('<ButtonRelease-1>', self.HelpSourceSelected)
433 self.buttonHelpListEdit = Button(
434 frameHelpListButtons, text='Edit', state=DISABLED,
435 width=8, command=self.HelpListItemEdit)
436 self.buttonHelpListAdd = Button(
437 frameHelpListButtons, text='Add',
438 width=8, command=self.HelpListItemAdd)
439 self.buttonHelpListRemove = Button(
440 frameHelpListButtons, text='Remove', state=DISABLED,
441 width=8, command=self.HelpListItemRemove)
Terry Jan Reedy19b7a752014-07-30 19:24:26 -0400442
Steven M. Gava230e5782001-08-07 03:28:25 +0000443 #widget packing
444 #body
Terry Jan Reedyae410862014-08-03 23:02:53 -0400445 frameRun.pack(side=TOP, padx=5, pady=5, fill=X)
446 frameSave.pack(side=TOP, padx=5, pady=5, fill=X)
447 frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400448 frameEncoding.pack(side=TOP, padx=5, pady=5, fill=X)
449 frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gava230e5782001-08-07 03:28:25 +0000450 #frameRun
Terry Jan Reedyae410862014-08-03 23:02:53 -0400451 labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
452 radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
453 radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000454 #frameSave
Terry Jan Reedyae410862014-08-03 23:02:53 -0400455 labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
456 radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
457 radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
Steven M. Gava230e5782001-08-07 03:28:25 +0000458 #frameWinSize
Terry Jan Reedyae410862014-08-03 23:02:53 -0400459 labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
460 entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
461 labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5)
462 entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
463 labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5)
Kurt B. Kaisera053f332003-05-10 00:49:56 +0000464 #frameEncoding
Terry Jan Reedyae410862014-08-03 23:02:53 -0400465 labelEncodingTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
466 radioEncNone.pack(side=RIGHT, anchor=E, pady=5)
467 radioEncUTF8.pack(side=RIGHT, anchor=E, pady=5)
468 radioEncLocale.pack(side=RIGHT, anchor=E, pady=5)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000469 #frameHelp
Terry Jan Reedyae410862014-08-03 23:02:53 -0400470 frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
471 frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
472 scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y)
473 self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
474 self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5)
475 self.buttonHelpListAdd.pack(side=TOP, anchor=W)
476 self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000477 return frame
478
Steven M. Gavac112cd82002-01-22 05:56:40 +0000479 def AttachVarCallbacks(self):
Terry Jan Reedy12352142015-08-01 18:57:27 -0400480 self.fontSize.trace_variable('w', self.VarChanged_font)
481 self.fontName.trace_variable('w', self.VarChanged_font)
482 self.fontBold.trace_variable('w', self.VarChanged_font)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400483 self.spaceNum.trace_variable('w', self.VarChanged_spaceNum)
484 self.colour.trace_variable('w', self.VarChanged_colour)
485 self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme)
486 self.customTheme.trace_variable('w', self.VarChanged_customTheme)
487 self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin)
488 self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget)
489 self.keyBinding.trace_variable('w', self.VarChanged_keyBinding)
490 self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys)
491 self.customKeys.trace_variable('w', self.VarChanged_customKeys)
492 self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin)
493 self.winWidth.trace_variable('w', self.VarChanged_winWidth)
494 self.winHeight.trace_variable('w', self.VarChanged_winHeight)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400495 self.startupEdit.trace_variable('w', self.VarChanged_startupEdit)
496 self.autoSave.trace_variable('w', self.VarChanged_autoSave)
497 self.encoding.trace_variable('w', self.VarChanged_encoding)
Steven M. Gava052937f2002-02-11 02:20:53 +0000498
Terry Jan Reedy12352142015-08-01 18:57:27 -0400499 def VarChanged_font(self, *params):
500 '''When one font attribute changes, save them all, as they are
501 not independent from each other. In particular, when we are
502 overriding the default font, we need to write out everything.
503 '''
Terry Jan Reedyae410862014-08-03 23:02:53 -0400504 value = self.fontName.get()
505 self.AddChangedItem('main', 'EditorWindow', 'font', value)
Terry Jan Reedy12352142015-08-01 18:57:27 -0400506 value = self.fontSize.get()
507 self.AddChangedItem('main', 'EditorWindow', 'font-size', value)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400508 value = self.fontBold.get()
509 self.AddChangedItem('main', 'EditorWindow', 'font-bold', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000510
Terry Jan Reedyae410862014-08-03 23:02:53 -0400511 def VarChanged_spaceNum(self, *params):
512 value = self.spaceNum.get()
513 self.AddChangedItem('main', 'Indent', 'num-spaces', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000514
Terry Jan Reedyae410862014-08-03 23:02:53 -0400515 def VarChanged_colour(self, *params):
Steven M. Gava052937f2002-02-11 02:20:53 +0000516 self.OnNewColourSet()
517
Terry Jan Reedyae410862014-08-03 23:02:53 -0400518 def VarChanged_builtinTheme(self, *params):
519 value = self.builtinTheme.get()
Terry Jan Reedy9f37eae2015-10-04 00:30:59 -0400520 if value == 'IDLE Dark':
521 tkMessageBox.showwarning(
522 title="The 'IDLE Dark' Text Color Theme",
523 message="IDLE Dark is new in October, 2015. Trying to "
524 "run earlier versions of IDLE with it selected "
525 "will disable colorizing, or worse.\n\n"
526 "If you might ever run an earlier release of IDLE, "
527 "then before exiting this version, "
528 "either switch to another theme or "
529 "hit the 'Save as New Custom Theme' button. "
530 "The latter requires a new name, such as "
531 "'Custom Dark', but the custom theme will work "
532 "with any IDLE release, and can be modified.",
533 parent=self)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400534 self.AddChangedItem('main', 'Theme', 'name', value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000535 self.PaintThemeSample()
536
Terry Jan Reedyae410862014-08-03 23:02:53 -0400537 def VarChanged_customTheme(self, *params):
538 value = self.customTheme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000539 if value != '- no custom themes -':
Terry Jan Reedyae410862014-08-03 23:02:53 -0400540 self.AddChangedItem('main', 'Theme', 'name', value)
Steven M. Gava49745752002-02-18 01:43:11 +0000541 self.PaintThemeSample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000542
Terry Jan Reedyae410862014-08-03 23:02:53 -0400543 def VarChanged_themeIsBuiltin(self, *params):
544 value = self.themeIsBuiltin.get()
545 self.AddChangedItem('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000546 if value:
547 self.VarChanged_builtinTheme()
548 else:
549 self.VarChanged_customTheme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000550
Terry Jan Reedyae410862014-08-03 23:02:53 -0400551 def VarChanged_highlightTarget(self, *params):
Steven M. Gava052937f2002-02-11 02:20:53 +0000552 self.SetHighlightTarget()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000553
Terry Jan Reedyae410862014-08-03 23:02:53 -0400554 def VarChanged_keyBinding(self, *params):
555 value = self.keyBinding.get()
556 keySet = self.customKeys.get()
557 event = self.listBindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000558 if idleConf.IsCoreBinding(event):
559 #this is a core keybinding
Terry Jan Reedyae410862014-08-03 23:02:53 -0400560 self.AddChangedItem('keys', keySet, event, value)
Steven M. Gavaa498af22002-02-01 01:33:36 +0000561 else: #this is an extension key binding
Terry Jan Reedyae410862014-08-03 23:02:53 -0400562 extName = idleConf.GetExtnNameForEvent(event)
563 extKeybindSection = extName + '_cfgBindings'
564 self.AddChangedItem('extensions', extKeybindSection, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000565
Terry Jan Reedyae410862014-08-03 23:02:53 -0400566 def VarChanged_builtinKeys(self, *params):
567 value = self.builtinKeys.get()
568 self.AddChangedItem('main', 'Keys', 'name', value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000569 self.LoadKeysList(value)
570
Terry Jan Reedyae410862014-08-03 23:02:53 -0400571 def VarChanged_customKeys(self, *params):
572 value = self.customKeys.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000573 if value != '- no custom keys -':
Terry Jan Reedyae410862014-08-03 23:02:53 -0400574 self.AddChangedItem('main', 'Keys', 'name', value)
Steven M. Gava49745752002-02-18 01:43:11 +0000575 self.LoadKeysList(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000576
Terry Jan Reedyae410862014-08-03 23:02:53 -0400577 def VarChanged_keysAreBuiltin(self, *params):
578 value = self.keysAreBuiltin.get()
579 self.AddChangedItem('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000580 if value:
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000581 self.VarChanged_builtinKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000582 else:
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000583 self.VarChanged_customKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000584
Terry Jan Reedyae410862014-08-03 23:02:53 -0400585 def VarChanged_winWidth(self, *params):
586 value = self.winWidth.get()
587 self.AddChangedItem('main', 'EditorWindow', 'width', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000588
Terry Jan Reedyae410862014-08-03 23:02:53 -0400589 def VarChanged_winHeight(self, *params):
590 value = self.winHeight.get()
591 self.AddChangedItem('main', 'EditorWindow', 'height', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000592
Terry Jan Reedyae410862014-08-03 23:02:53 -0400593 def VarChanged_startupEdit(self, *params):
594 value = self.startupEdit.get()
595 self.AddChangedItem('main', 'General', 'editor-on-startup', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000596
Terry Jan Reedyae410862014-08-03 23:02:53 -0400597 def VarChanged_autoSave(self, *params):
598 value = self.autoSave.get()
599 self.AddChangedItem('main', 'General', 'autosave', value)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000600
Terry Jan Reedyae410862014-08-03 23:02:53 -0400601 def VarChanged_encoding(self, *params):
602 value = self.encoding.get()
603 self.AddChangedItem('main', 'EditorWindow', 'encoding', value)
Kurt B. Kaisera053f332003-05-10 00:49:56 +0000604
Steven M. Gavaa498af22002-02-01 01:33:36 +0000605 def ResetChangedItems(self):
Steven M. Gavab77d3432002-03-02 07:16:21 +0000606 #When any config item is changed in this dialog, an entry
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000607 #should be made in the relevant section (config type) of this
608 #dictionary. The key should be the config file section name and the
Steven M. Gavaa498af22002-02-01 01:33:36 +0000609 #value a dictionary, whose key:value pairs are item=value pairs for
610 #that config file section.
Terry Jan Reedyae410862014-08-03 23:02:53 -0400611 self.changedItems = {'main':{}, 'highlight':{}, 'keys':{},
612 'extensions':{}}
Steven M. Gavaa498af22002-02-01 01:33:36 +0000613
Terry Jan Reedyae410862014-08-03 23:02:53 -0400614 def AddChangedItem(self, typ, section, item, value):
615 value = str(value) #make sure we use a string
616 if section not in self.changedItems[typ]:
617 self.changedItems[typ][section] = {}
618 self.changedItems[typ][section][item] = value
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000619
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000620 def GetDefaultItems(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400621 dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400622 for configType in dItems:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400623 sections = idleConf.GetSectionList('default', configType)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000624 for section in sections:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400625 dItems[configType][section] = {}
626 options = idleConf.defaultCfg[configType].GetOptionList(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000627 for option in options:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400628 dItems[configType][section][option] = (
629 idleConf.defaultCfg[configType].Get(section, option))
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000630 return dItems
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000631
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000632 def SetThemeType(self):
633 if self.themeIsBuiltin.get():
634 self.optMenuThemeBuiltin.config(state=NORMAL)
635 self.optMenuThemeCustom.config(state=DISABLED)
636 self.buttonDeleteCustomTheme.config(state=DISABLED)
637 else:
638 self.optMenuThemeBuiltin.config(state=DISABLED)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000639 self.radioThemeCustom.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000640 self.optMenuThemeCustom.config(state=NORMAL)
641 self.buttonDeleteCustomTheme.config(state=NORMAL)
642
643 def SetKeysType(self):
Steven M. Gava052937f2002-02-11 02:20:53 +0000644 if self.keysAreBuiltin.get():
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000645 self.optMenuKeysBuiltin.config(state=NORMAL)
646 self.optMenuKeysCustom.config(state=DISABLED)
647 self.buttonDeleteCustomKeys.config(state=DISABLED)
648 else:
649 self.optMenuKeysBuiltin.config(state=DISABLED)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000650 self.radioKeysCustom.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000651 self.optMenuKeysCustom.config(state=NORMAL)
652 self.buttonDeleteCustomKeys.config(state=NORMAL)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000653
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000654 def GetNewKeys(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400655 listIndex = self.listBindings.index(ANCHOR)
656 binding = self.listBindings.get(listIndex)
657 bindName = binding.split()[0] #first part, up to first space
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000658 if self.keysAreBuiltin.get():
Terry Jan Reedyae410862014-08-03 23:02:53 -0400659 currentKeySetName = self.builtinKeys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000660 else:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400661 currentKeySetName = self.customKeys.get()
662 currentBindings = idleConf.GetCurrentKeySet()
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400663 if currentKeySetName in self.changedItems['keys']: #unsaved changes
Terry Jan Reedyae410862014-08-03 23:02:53 -0400664 keySetChanges = self.changedItems['keys'][currentKeySetName]
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400665 for event in keySetChanges:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400666 currentBindings[event] = keySetChanges[event].split()
667 currentKeySequences = currentBindings.values()
668 newKeys = GetKeysDialog(self, 'Get New Keys', bindName,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000669 currentKeySequences).result
670 if newKeys: #new keys were specified
Steven M. Gava052937f2002-02-11 02:20:53 +0000671 if self.keysAreBuiltin.get(): #current key set is a built-in
Terry Jan Reedyae410862014-08-03 23:02:53 -0400672 message = ('Your changes will be saved as a new Custom Key Set.'
673 ' Enter a name for your new Custom Key Set below.')
674 newKeySet = self.GetNewKeysName(message)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000675 if not newKeySet: #user cancelled custom key set creation
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000676 self.listBindings.select_set(listIndex)
677 self.listBindings.select_anchor(listIndex)
678 return
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000679 else: #create new custom key set based on previously active key set
680 self.CreateNewKeySet(newKeySet)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000681 self.listBindings.delete(listIndex)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400682 self.listBindings.insert(listIndex, bindName+' - '+newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000683 self.listBindings.select_set(listIndex)
684 self.listBindings.select_anchor(listIndex)
Steven M. Gava052937f2002-02-11 02:20:53 +0000685 self.keyBinding.set(newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000686 else:
687 self.listBindings.select_set(listIndex)
688 self.listBindings.select_anchor(listIndex)
689
Terry Jan Reedyae410862014-08-03 23:02:53 -0400690 def GetNewKeysName(self, message):
691 usedNames = (idleConf.GetSectionList('user', 'keys') +
692 idleConf.GetSectionList('default', 'keys'))
693 newKeySet = GetCfgSectionNameDialog(
694 self, 'New Custom Key Set', message, usedNames).result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000695 return newKeySet
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000696
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000697 def SaveAsNewKeySet(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400698 newKeysName = self.GetNewKeysName('New Key Set Name:')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000699 if newKeysName:
700 self.CreateNewKeySet(newKeysName)
701
Terry Jan Reedyae410862014-08-03 23:02:53 -0400702 def KeyBindingSelected(self, event):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000703 self.buttonNewKeys.config(state=NORMAL)
704
Terry Jan Reedyae410862014-08-03 23:02:53 -0400705 def CreateNewKeySet(self, newKeySetName):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000706 #creates new custom key set based on the previously active key set,
707 #and makes the new key set active
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000708 if self.keysAreBuiltin.get():
Terry Jan Reedyae410862014-08-03 23:02:53 -0400709 prevKeySetName = self.builtinKeys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000710 else:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400711 prevKeySetName = self.customKeys.get()
712 prevKeys = idleConf.GetCoreKeys(prevKeySetName)
713 newKeys = {}
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400714 for event in prevKeys: #add key set to changed items
Terry Jan Reedyae410862014-08-03 23:02:53 -0400715 eventName = event[2:-2] #trim off the angle brackets
716 binding = ' '.join(prevKeys[event])
717 newKeys[eventName] = binding
Steven M. Gava052937f2002-02-11 02:20:53 +0000718 #handle any unsaved changes to prev key set
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400719 if prevKeySetName in self.changedItems['keys']:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400720 keySetChanges = self.changedItems['keys'][prevKeySetName]
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400721 for event in keySetChanges:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400722 newKeys[event] = keySetChanges[event]
Steven M. Gava052937f2002-02-11 02:20:53 +0000723 #save the new theme
Terry Jan Reedyae410862014-08-03 23:02:53 -0400724 self.SaveNewKeySet(newKeySetName, newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000725 #change gui over to the new key set
Terry Jan Reedyae410862014-08-03 23:02:53 -0400726 customKeyList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000727 customKeyList.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -0400728 self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName)
Steven M. Gava052937f2002-02-11 02:20:53 +0000729 self.keysAreBuiltin.set(0)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000730 self.SetKeysType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000731
Terry Jan Reedyae410862014-08-03 23:02:53 -0400732 def LoadKeysList(self, keySetName):
733 reselect = 0
734 newKeySet = 0
Steven M. Gava052937f2002-02-11 02:20:53 +0000735 if self.listBindings.curselection():
Terry Jan Reedyae410862014-08-03 23:02:53 -0400736 reselect = 1
737 listIndex = self.listBindings.index(ANCHOR)
738 keySet = idleConf.GetKeySet(keySetName)
739 bindNames = keySet.keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000740 bindNames.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -0400741 self.listBindings.delete(0, END)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000742 for bindName in bindNames:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400743 key = ' '.join(keySet[bindName]) #make key(s) into a string
744 bindName = bindName[2:-2] #trim off the angle brackets
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400745 if keySetName in self.changedItems['keys']:
Steven M. Gava052937f2002-02-11 02:20:53 +0000746 #handle any unsaved changes to this key set
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400747 if bindName in self.changedItems['keys'][keySetName]:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400748 key = self.changedItems['keys'][keySetName][bindName]
Steven M. Gava052937f2002-02-11 02:20:53 +0000749 self.listBindings.insert(END, bindName+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +0000750 if reselect:
751 self.listBindings.see(listIndex)
752 self.listBindings.select_set(listIndex)
753 self.listBindings.select_anchor(listIndex)
754
Steven M. Gava49745752002-02-18 01:43:11 +0000755 def DeleteCustomKeys(self):
756 keySetName=self.customKeys.get()
Terry Jan Reedyae410862014-08-03 23:02:53 -0400757 delmsg = 'Are you sure you wish to delete the key set %r ?'
758 if not tkMessageBox.askyesno(
759 'Delete Key Set', delmsg % keySetName, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +0000760 return
761 #remove key set from config
762 idleConf.userCfg['keys'].remove_section(keySetName)
Benjamin Peterson6e3dbbd2009-10-09 22:15:50 +0000763 if keySetName in self.changedItems['keys']:
Steven M. Gava49745752002-02-18 01:43:11 +0000764 del(self.changedItems['keys'][keySetName])
765 #write changes
766 idleConf.userCfg['keys'].Save()
767 #reload user key set list
Terry Jan Reedyae410862014-08-03 23:02:53 -0400768 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gava49745752002-02-18 01:43:11 +0000769 itemList.sort()
770 if not itemList:
771 self.radioKeysCustom.config(state=DISABLED)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400772 self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +0000773 else:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400774 self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
Steven M. Gava49745752002-02-18 01:43:11 +0000775 #revert to default key set
Terry Jan Reedyae410862014-08-03 23:02:53 -0400776 self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default'))
777 self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))
Steven M. Gava49745752002-02-18 01:43:11 +0000778 #user can't back out of these changes, they must be applied now
779 self.Apply()
780 self.SetKeysType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000781
Steven M. Gava49745752002-02-18 01:43:11 +0000782 def DeleteCustomTheme(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400783 themeName = self.customTheme.get()
784 delmsg = 'Are you sure you wish to delete the theme %r ?'
785 if not tkMessageBox.askyesno(
786 'Delete Theme', delmsg % themeName, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +0000787 return
788 #remove theme from config
789 idleConf.userCfg['highlight'].remove_section(themeName)
Benjamin Peterson6e3dbbd2009-10-09 22:15:50 +0000790 if themeName in self.changedItems['highlight']:
Steven M. Gava49745752002-02-18 01:43:11 +0000791 del(self.changedItems['highlight'][themeName])
792 #write changes
793 idleConf.userCfg['highlight'].Save()
794 #reload user theme list
Terry Jan Reedyae410862014-08-03 23:02:53 -0400795 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gava49745752002-02-18 01:43:11 +0000796 itemList.sort()
797 if not itemList:
798 self.radioThemeCustom.config(state=DISABLED)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400799 self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +0000800 else:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400801 self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
Steven M. Gava49745752002-02-18 01:43:11 +0000802 #revert to default theme
Terry Jan Reedyae410862014-08-03 23:02:53 -0400803 self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
804 self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
Steven M. Gava49745752002-02-18 01:43:11 +0000805 #user can't back out of these changes, they must be applied now
806 self.Apply()
807 self.SetThemeType()
808
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000809 def GetColour(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400810 target = self.highlightTarget.get()
811 prevColour = self.frameColourSet.cget('bg')
812 rgbTuplet, colourString = tkColorChooser.askcolor(
813 parent=self, title='Pick new colour for : '+target,
814 initialcolor=prevColour)
815 if colourString and (colourString != prevColour):
Steven M. Gava052937f2002-02-11 02:20:53 +0000816 #user didn't cancel, and they chose a new colour
Terry Jan Reedyae410862014-08-03 23:02:53 -0400817 if self.themeIsBuiltin.get(): #current theme is a built-in
818 message = ('Your changes will be saved as a new Custom Theme. '
819 'Enter a name for your new Custom Theme below.')
820 newTheme = self.GetNewThemeName(message)
821 if not newTheme: #user cancelled custom theme creation
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000822 return
Terry Jan Reedyae410862014-08-03 23:02:53 -0400823 else: #create new custom theme based on previously active theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000824 self.CreateNewTheme(newTheme)
Steven M. Gava052937f2002-02-11 02:20:53 +0000825 self.colour.set(colourString)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400826 else: #current theme is user defined
Steven M. Gava052937f2002-02-11 02:20:53 +0000827 self.colour.set(colourString)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000828
Steven M. Gava052937f2002-02-11 02:20:53 +0000829 def OnNewColourSet(self):
830 newColour=self.colour.get()
Terry Jan Reedyae410862014-08-03 23:02:53 -0400831 self.frameColourSet.config(bg=newColour) #set sample
832 plane ='foreground' if self.fgHilite.get() else 'background'
833 sampleElement = self.themeElements[self.highlightTarget.get()][0]
Raymond Hettinger931237e2003-07-09 18:48:24 +0000834 self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
Terry Jan Reedyae410862014-08-03 23:02:53 -0400835 theme = self.customTheme.get()
836 themeElement = sampleElement + '-' + plane
837 self.AddChangedItem('highlight', theme, themeElement, newColour)
Steven M. Gava052937f2002-02-11 02:20:53 +0000838
Terry Jan Reedyae410862014-08-03 23:02:53 -0400839 def GetNewThemeName(self, message):
840 usedNames = (idleConf.GetSectionList('user', 'highlight') +
841 idleConf.GetSectionList('default', 'highlight'))
842 newTheme = GetCfgSectionNameDialog(
843 self, 'New Custom Theme', message, usedNames).result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000844 return newTheme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000845
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000846 def SaveAsNewTheme(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400847 newThemeName = self.GetNewThemeName('New Theme Name:')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000848 if newThemeName:
849 self.CreateNewTheme(newThemeName)
850
Terry Jan Reedyae410862014-08-03 23:02:53 -0400851 def CreateNewTheme(self, newThemeName):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000852 #creates new custom theme based on the previously active theme,
853 #and makes the new theme active
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000854 if self.themeIsBuiltin.get():
Terry Jan Reedyae410862014-08-03 23:02:53 -0400855 themeType = 'default'
856 themeName = self.builtinTheme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000857 else:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400858 themeType = 'user'
859 themeName = self.customTheme.get()
860 newTheme = idleConf.GetThemeDict(themeType, themeName)
Steven M. Gava052937f2002-02-11 02:20:53 +0000861 #apply any of the old theme's unsaved changes to the new theme
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400862 if themeName in self.changedItems['highlight']:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400863 themeChanges = self.changedItems['highlight'][themeName]
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400864 for element in themeChanges:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400865 newTheme[element] = themeChanges[element]
Steven M. Gava052937f2002-02-11 02:20:53 +0000866 #save the new theme
Terry Jan Reedyae410862014-08-03 23:02:53 -0400867 self.SaveNewTheme(newThemeName, newTheme)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000868 #change gui over to the new theme
Terry Jan Reedyae410862014-08-03 23:02:53 -0400869 customThemeList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000870 customThemeList.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -0400871 self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000872 self.themeIsBuiltin.set(0)
873 self.SetThemeType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000874
Terry Jan Reedyae410862014-08-03 23:02:53 -0400875 def OnListFontButtonRelease(self, event):
Kurt B. Kaiser05391692003-05-26 20:35:53 +0000876 font = self.listFontName.get(ANCHOR)
877 self.fontName.set(font.lower())
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000878 self.SetFontSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000879
Terry Jan Reedyae410862014-08-03 23:02:53 -0400880 def SetFontSample(self, event=None):
881 fontName = self.fontName.get()
882 fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL
Ned Deilye3d47122012-10-22 15:13:01 -0700883 newFont = (fontName, self.fontSize.get(), fontWeight)
884 self.labelFontSample.config(font=newFont)
885 self.textHighlightSample.configure(font=newFont)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000886
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000887 def SetHighlightTarget(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400888 if self.highlightTarget.get() == 'Cursor': #bg not possible
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000889 self.radioFg.config(state=DISABLED)
890 self.radioBg.config(state=DISABLED)
891 self.fgHilite.set(1)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400892 else: #both fg and bg can be set
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000893 self.radioFg.config(state=NORMAL)
894 self.radioBg.config(state=NORMAL)
895 self.fgHilite.set(1)
896 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000897
Terry Jan Reedyae410862014-08-03 23:02:53 -0400898 def SetColourSampleBinding(self, *args):
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000899 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000900
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000901 def SetColourSample(self):
902 #set the colour smaple area
Terry Jan Reedyae410862014-08-03 23:02:53 -0400903 tag = self.themeElements[self.highlightTarget.get()][0]
904 plane = 'foreground' if self.fgHilite.get() else 'background'
905 colour = self.textHighlightSample.tag_cget(tag, plane)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000906 self.frameColourSet.config(bg=colour)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000907
Steven M. Gava9dd16b32001-11-03 14:54:25 +0000908 def PaintThemeSample(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400909 if self.themeIsBuiltin.get(): #a default theme
910 theme = self.builtinTheme.get()
911 else: #a user theme
912 theme = self.customTheme.get()
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400913 for elementTitle in self.themeElements:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400914 element = self.themeElements[elementTitle][0]
915 colours = idleConf.GetHighlight(theme, element)
916 if element == 'cursor': #cursor sample needs special painting
917 colours['background'] = idleConf.GetHighlight(
918 theme, 'normal', fgBg='bg')
Steven M. Gava052937f2002-02-11 02:20:53 +0000919 #handle any unsaved changes to this theme
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400920 if theme in self.changedItems['highlight']:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400921 themeDict = self.changedItems['highlight'][theme]
922 if element + '-foreground' in themeDict:
923 colours['foreground'] = themeDict[element + '-foreground']
924 if element + '-background' in themeDict:
925 colours['background'] = themeDict[element + '-background']
Raymond Hettinger931237e2003-07-09 18:48:24 +0000926 self.textHighlightSample.tag_config(element, **colours)
Steven M. Gava052937f2002-02-11 02:20:53 +0000927 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000928
Terry Jan Reedyae410862014-08-03 23:02:53 -0400929 def HelpSourceSelected(self, event):
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000930 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000931
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000932 def SetHelpListButtonStates(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400933 if self.listHelp.size() < 1: #no entries in list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000934 self.buttonHelpListEdit.config(state=DISABLED)
935 self.buttonHelpListRemove.config(state=DISABLED)
936 else: #there are some entries
Terry Jan Reedyae410862014-08-03 23:02:53 -0400937 if self.listHelp.curselection(): #there currently is a selection
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000938 self.buttonHelpListEdit.config(state=NORMAL)
939 self.buttonHelpListRemove.config(state=NORMAL)
940 else: #there currently is not a selection
941 self.buttonHelpListEdit.config(state=DISABLED)
942 self.buttonHelpListRemove.config(state=DISABLED)
943
944 def HelpListItemAdd(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400945 helpSource = GetHelpSourceDialog(self, 'New Help Source').result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000946 if helpSource:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400947 self.userHelpList.append((helpSource[0], helpSource[1]))
948 self.listHelp.insert(END, helpSource[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000949 self.UpdateUserHelpChangedItems()
950 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000951
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000952 def HelpListItemEdit(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400953 itemIndex = self.listHelp.index(ANCHOR)
954 helpSource = self.userHelpList[itemIndex]
955 newHelpSource = GetHelpSourceDialog(
956 self, 'Edit Help Source', menuItem=helpSource[0],
957 filePath=helpSource[1]).result
958 if (not newHelpSource) or (newHelpSource == helpSource):
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000959 return #no changes
Terry Jan Reedyae410862014-08-03 23:02:53 -0400960 self.userHelpList[itemIndex] = newHelpSource
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000961 self.listHelp.delete(itemIndex)
Terry Jan Reedyae410862014-08-03 23:02:53 -0400962 self.listHelp.insert(itemIndex, newHelpSource[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000963 self.UpdateUserHelpChangedItems()
964 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000965
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000966 def HelpListItemRemove(self):
Terry Jan Reedyae410862014-08-03 23:02:53 -0400967 itemIndex = self.listHelp.index(ANCHOR)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000968 del(self.userHelpList[itemIndex])
969 self.listHelp.delete(itemIndex)
970 self.UpdateUserHelpChangedItems()
971 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000972
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000973 def UpdateUserHelpChangedItems(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000974 "Clear and rebuild the HelpFiles section in self.changedItems"
975 self.changedItems['main']['HelpFiles'] = {}
Terry Jan Reedyae410862014-08-03 23:02:53 -0400976 for num in range(1, len(self.userHelpList) + 1):
977 self.AddChangedItem(
978 'main', 'HelpFiles', str(num),
Terry Jan Reedy110796f2014-07-27 04:07:18 -0400979 ';'.join(self.userHelpList[num-1][:2]))
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000980
Steven M. Gava429a86a2001-10-23 10:42:12 +0000981 def LoadFontCfg(self):
982 ##base editor font selection list
Terry Jan Reedyae410862014-08-03 23:02:53 -0400983 fonts = list(tkFont.families(self))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000984 fonts.sort()
985 for font in fonts:
Terry Jan Reedyae410862014-08-03 23:02:53 -0400986 self.listFontName.insert(END, font)
Terry Jan Reedy12352142015-08-01 18:57:27 -0400987 configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow')
988 fontName = configuredFont[0].lower()
989 fontSize = configuredFont[1]
990 fontBold = configuredFont[2]=='bold'
991 self.fontName.set(fontName)
Kurt B. Kaiser05391692003-05-26 20:35:53 +0000992 lc_fonts = [s.lower() for s in fonts]
Terry Jan Reedy12352142015-08-01 18:57:27 -0400993 try:
994 currentFontIndex = lc_fonts.index(fontName)
Steven M. Gava429a86a2001-10-23 10:42:12 +0000995 self.listFontName.see(currentFontIndex)
996 self.listFontName.select_set(currentFontIndex)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000997 self.listFontName.select_anchor(currentFontIndex)
Terry Jan Reedy12352142015-08-01 18:57:27 -0400998 except ValueError:
999 pass
Steven M. Gava429a86a2001-10-23 10:42:12 +00001000 ##font size dropdown
Terry Jan Reedyae410862014-08-03 23:02:53 -04001001 self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13',
1002 '14', '16', '18', '20', '22'), fontSize )
Steven M. Gavac112cd82002-01-22 05:56:40 +00001003 ##fontWeight
Terry Jan Reedy12352142015-08-01 18:57:27 -04001004 self.fontBold.set(fontBold)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001005 ##font sample
Steven M. Gava429a86a2001-10-23 10:42:12 +00001006 self.SetFontSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001007
Steven M. Gava429a86a2001-10-23 10:42:12 +00001008 def LoadTabCfg(self):
Steven M. Gava429a86a2001-10-23 10:42:12 +00001009 ##indent sizes
Terry Jan Reedyae410862014-08-03 23:02:53 -04001010 spaceNum = idleConf.GetOption(
1011 'main', 'Indent', 'num-spaces', default=4, type='int')
Steven M. Gava429a86a2001-10-23 10:42:12 +00001012 self.spaceNum.set(spaceNum)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001013
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001014 def LoadThemeCfg(self):
Steven M. Gava41a85322001-10-29 08:05:34 +00001015 ##current theme type radiobutton
Terry Jan Reedyae410862014-08-03 23:02:53 -04001016 self.themeIsBuiltin.set(idleConf.GetOption(
1017 'main', 'Theme', 'default', type='bool', default=1))
Steven M. Gava41a85322001-10-29 08:05:34 +00001018 ##currently set theme
Terry Jan Reedyae410862014-08-03 23:02:53 -04001019 currentOption = idleConf.CurrentTheme()
Steven M. Gava7c017862001-10-29 11:19:46 +00001020 ##load available theme option menus
Steven M. Gavad0342cd2001-11-04 11:53:10 +00001021 if self.themeIsBuiltin.get(): #default theme selected
Terry Jan Reedyae410862014-08-03 23:02:53 -04001022 itemList = idleConf.GetSectionList('default', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001023 itemList.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -04001024 self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
1025 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001026 itemList.sort()
Steven M. Gava41a85322001-10-29 08:05:34 +00001027 if not itemList:
1028 self.radioThemeCustom.config(state=DISABLED)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001029 self.customTheme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001030 else:
Terry Jan Reedyae410862014-08-03 23:02:53 -04001031 self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001032 else: #user theme selected
Terry Jan Reedyae410862014-08-03 23:02:53 -04001033 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001034 itemList.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -04001035 self.optMenuThemeCustom.SetMenu(itemList, currentOption)
1036 itemList = idleConf.GetSectionList('default', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001037 itemList.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -04001038 self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
Steven M. Gava7c017862001-10-29 11:19:46 +00001039 self.SetThemeType()
1040 ##load theme element option menu
Terry Jan Reedyae410862014-08-03 23:02:53 -04001041 themeNames = self.themeElements.keys()
Florent Xiclunaa7f242f2010-04-02 08:15:26 +00001042 themeNames.sort(key=lambda x: self.themeElements[x][1])
Terry Jan Reedyae410862014-08-03 23:02:53 -04001043 self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001044 self.PaintThemeSample()
Steven M. Gava0cae01c2002-01-04 07:53:06 +00001045 self.SetHighlightTarget()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001046
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001047 def LoadKeyCfg(self):
Steven M. Gava41a85322001-10-29 08:05:34 +00001048 ##current keys type radiobutton
Terry Jan Reedyae410862014-08-03 23:02:53 -04001049 self.keysAreBuiltin.set(idleConf.GetOption(
1050 'main', 'Keys', 'default', type='bool', default=1))
Steven M. Gava41a85322001-10-29 08:05:34 +00001051 ##currently set keys
Terry Jan Reedyae410862014-08-03 23:02:53 -04001052 currentOption = idleConf.CurrentKeys()
Steven M. Gava7c017862001-10-29 11:19:46 +00001053 ##load available keyset option menus
Steven M. Gava052937f2002-02-11 02:20:53 +00001054 if self.keysAreBuiltin.get(): #default theme selected
Terry Jan Reedyae410862014-08-03 23:02:53 -04001055 itemList = idleConf.GetSectionList('default', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001056 itemList.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -04001057 self.optMenuKeysBuiltin.SetMenu(itemList, currentOption)
1058 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001059 itemList.sort()
Steven M. Gava41a85322001-10-29 08:05:34 +00001060 if not itemList:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001061 self.radioKeysCustom.config(state=DISABLED)
1062 self.customKeys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001063 else:
Terry Jan Reedyae410862014-08-03 23:02:53 -04001064 self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
Steven M. Gavaa498af22002-02-01 01:33:36 +00001065 else: #user key set selected
Terry Jan Reedyae410862014-08-03 23:02:53 -04001066 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001067 itemList.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -04001068 self.optMenuKeysCustom.SetMenu(itemList, currentOption)
1069 itemList = idleConf.GetSectionList('default', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001070 itemList.sort()
Terry Jan Reedyae410862014-08-03 23:02:53 -04001071 self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001072 self.SetKeysType()
Steven M. Gavafacfc092002-01-19 00:29:54 +00001073 ##load keyset element list
Terry Jan Reedyae410862014-08-03 23:02:53 -04001074 keySetName = idleConf.CurrentKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +00001075 self.LoadKeysList(keySetName)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001076
Steven M. Gavafacfc092002-01-19 00:29:54 +00001077 def LoadGeneralCfg(self):
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001078 #startup state
Terry Jan Reedyae410862014-08-03 23:02:53 -04001079 self.startupEdit.set(idleConf.GetOption(
1080 'main', 'General', 'editor-on-startup', default=1, type='bool'))
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +00001081 #autosave state
Terry Jan Reedyae410862014-08-03 23:02:53 -04001082 self.autoSave.set(idleConf.GetOption(
1083 'main', 'General', 'autosave', default=0, type='bool'))
Steven M. Gavafacfc092002-01-19 00:29:54 +00001084 #initial window size
Terry Jan Reedyae410862014-08-03 23:02:53 -04001085 self.winWidth.set(idleConf.GetOption(
1086 'main', 'EditorWindow', 'width', type='int'))
1087 self.winHeight.set(idleConf.GetOption(
1088 'main', 'EditorWindow', 'height', type='int'))
Kurt B. Kaisera053f332003-05-10 00:49:56 +00001089 # default source encoding
Terry Jan Reedyae410862014-08-03 23:02:53 -04001090 self.encoding.set(idleConf.GetOption(
1091 'main', 'EditorWindow', 'encoding', default='none'))
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001092 # additional help sources
1093 self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001094 for helpItem in self.userHelpList:
Terry Jan Reedyae410862014-08-03 23:02:53 -04001095 self.listHelp.insert(END, helpItem[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001096 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001097
Steven M. Gava429a86a2001-10-23 10:42:12 +00001098 def LoadConfigs(self):
1099 """
1100 load configuration from default and user config files and populate
1101 the widgets on the config dialog pages.
1102 """
1103 ### fonts / tabs page
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001104 self.LoadFontCfg()
1105 self.LoadTabCfg()
Steven M. Gava429a86a2001-10-23 10:42:12 +00001106 ### highlighting page
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001107 self.LoadThemeCfg()
Steven M. Gava429a86a2001-10-23 10:42:12 +00001108 ### keys page
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001109 self.LoadKeyCfg()
Steven M. Gava429a86a2001-10-23 10:42:12 +00001110 ### general page
Steven M. Gavafacfc092002-01-19 00:29:54 +00001111 self.LoadGeneralCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001112
Terry Jan Reedyae410862014-08-03 23:02:53 -04001113 def SaveNewKeySet(self, keySetName, keySet):
Steven M. Gava052937f2002-02-11 02:20:53 +00001114 """
1115 save a newly created core key set.
1116 keySetName - string, the name of the new key set
1117 keySet - dictionary containing the new key set
1118 """
1119 if not idleConf.userCfg['keys'].has_section(keySetName):
1120 idleConf.userCfg['keys'].add_section(keySetName)
Terry Jan Reedy110796f2014-07-27 04:07:18 -04001121 for event in keySet:
Terry Jan Reedyae410862014-08-03 23:02:53 -04001122 value = keySet[event]
1123 idleConf.userCfg['keys'].SetOption(keySetName, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001124
Terry Jan Reedyae410862014-08-03 23:02:53 -04001125 def SaveNewTheme(self, themeName, theme):
Steven M. Gava052937f2002-02-11 02:20:53 +00001126 """
1127 save a newly created theme.
1128 themeName - string, the name of the new theme
1129 theme - dictionary containing the new theme
1130 """
1131 if not idleConf.userCfg['highlight'].has_section(themeName):
1132 idleConf.userCfg['highlight'].add_section(themeName)
Terry Jan Reedy110796f2014-07-27 04:07:18 -04001133 for element in theme:
Terry Jan Reedyae410862014-08-03 23:02:53 -04001134 value = theme[element]
1135 idleConf.userCfg['highlight'].SetOption(themeName, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001136
Terry Jan Reedyae410862014-08-03 23:02:53 -04001137 def SetUserValue(self, configType, section, item, value):
1138 if idleConf.defaultCfg[configType].has_option(section, item):
1139 if idleConf.defaultCfg[configType].Get(section, item) == value:
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +00001140 #the setting equals a default setting, remove it from user cfg
Terry Jan Reedyae410862014-08-03 23:02:53 -04001141 return idleConf.userCfg[configType].RemoveOption(section, item)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +00001142 #if we got here set the option
Terry Jan Reedyae410862014-08-03 23:02:53 -04001143 return idleConf.userCfg[configType].SetOption(section, item, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001144
Steven M. Gava052937f2002-02-11 02:20:53 +00001145 def SaveAllChangedConfigs(self):
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001146 "Save configuration changes to the user config file."
Steven M. Gava0c5bc8c2002-03-27 02:25:44 +00001147 idleConf.userCfg['main'].Save()
Terry Jan Reedy110796f2014-07-27 04:07:18 -04001148 for configType in self.changedItems:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001149 cfgTypeHasChanges = False
Terry Jan Reedy110796f2014-07-27 04:07:18 -04001150 for section in self.changedItems[configType]:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001151 if section == 'HelpFiles':
1152 #this section gets completely replaced
1153 idleConf.userCfg['main'].remove_section('HelpFiles')
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001154 cfgTypeHasChanges = True
Terry Jan Reedy110796f2014-07-27 04:07:18 -04001155 for item in self.changedItems[configType][section]:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001156 value = self.changedItems[configType][section][item]
Terry Jan Reedyae410862014-08-03 23:02:53 -04001157 if self.SetUserValue(configType, section, item, value):
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001158 cfgTypeHasChanges = True
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001159 if cfgTypeHasChanges:
1160 idleConf.userCfg[configType].Save()
Kurt B. Kaiser5acdf932004-11-16 21:28:36 +00001161 for configType in ['keys', 'highlight']:
1162 # save these even if unchanged!
1163 idleConf.userCfg[configType].Save()
Steven M. Gavaa498af22002-02-01 01:33:36 +00001164 self.ResetChangedItems() #clear the changed items dict
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001165
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001166 def DeactivateCurrentConfig(self):
1167 #Before a config is saved, some cleanup of current
1168 #config must be done - remove the previous keybindings
Terry Jan Reedyae410862014-08-03 23:02:53 -04001169 winInstances = self.parent.instance_dict
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001170 for instance in winInstances:
1171 instance.RemoveKeybindings()
1172
Steven M. Gava49745752002-02-18 01:43:11 +00001173 def ActivateConfigChanges(self):
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001174 "Dynamically apply configuration changes"
Terry Jan Reedy110796f2014-07-27 04:07:18 -04001175 winInstances = self.parent.instance_dict.keys()
Steven M. Gavab77d3432002-03-02 07:16:21 +00001176 for instance in winInstances:
1177 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001178 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001179 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001180 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001181 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001182
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001183 def Cancel(self):
1184 self.destroy()
1185
1186 def Ok(self):
1187 self.Apply()
1188 self.destroy()
1189
1190 def Apply(self):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001191 self.DeactivateCurrentConfig()
Steven M. Gava052937f2002-02-11 02:20:53 +00001192 self.SaveAllChangedConfigs()
Steven M. Gava49745752002-02-18 01:43:11 +00001193 self.ActivateConfigChanges()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001194
1195 def Help(self):
Terry Jan Reedyc0a053e2015-10-11 22:07:25 -04001196 page = self.tabPages._current_page
1197 view_text(self, title='Help for IDLE preferences',
1198 text=help_common+help_pages.get(page, ''))
1199
1200help_common = '''\
1201When you click either the Apply or Ok buttons, settings in this
1202dialog that are different from IDLE's default are saved in
1203a .idlerc directory in your home directory. Except as noted,
1204hese changes apply to all versions of IDLE installed on this
1205machine. Some do not take affect until IDLE is restarted.
1206[Cancel] only cancels changes made since the last save.
1207'''
1208help_pages = {
1209 'Highlighting':'''
1210Highlighting:
1211The IDLE Dark color theme is new in Octover 2015. It can only
1212be used with older IDLE releases if it is saved as a custom
1213theme, with a different name.
1214'''
1215}
1216
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001217
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001218class VerticalScrolledFrame(Frame):
1219 """A pure Tkinter vertically scrollable frame.
1220
1221 * Use the 'interior' attribute to place widgets inside the scrollable frame
1222 * Construct and pack/place/grid normally
1223 * This frame only allows vertical scrolling
1224 """
1225 def __init__(self, parent, *args, **kw):
1226 Frame.__init__(self, parent, *args, **kw)
1227
1228 # create a canvas object and a vertical scrollbar for scrolling it
1229 vscrollbar = Scrollbar(self, orient=VERTICAL)
1230 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1231 canvas = Canvas(self, bd=0, highlightthickness=0,
1232 yscrollcommand=vscrollbar.set)
1233 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1234 vscrollbar.config(command=canvas.yview)
1235
1236 # reset the view
1237 canvas.xview_moveto(0)
1238 canvas.yview_moveto(0)
1239
1240 # create a frame inside the canvas which will be scrolled with it
1241 self.interior = interior = Frame(canvas)
1242 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1243
1244 # track changes to the canvas and frame width and sync them,
1245 # also updating the scrollbar
1246 def _configure_interior(event):
1247 # update the scrollbars to match the size of the inner frame
1248 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1249 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001250 interior.bind('<Configure>', _configure_interior)
1251
1252 def _configure_canvas(event):
1253 if interior.winfo_reqwidth() != canvas.winfo_width():
1254 # update the inner frame's width to fill the canvas
1255 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1256 canvas.bind('<Configure>', _configure_canvas)
1257
1258 return
1259
1260def is_int(s):
1261 "Return 's is blank or represents an int'"
1262 if not s:
1263 return True
1264 try:
1265 int(s)
1266 return True
1267 except ValueError:
1268 return False
1269
1270# TODO:
1271# * Revert to default(s)? Per option or per extension?
1272# * List options in their original order (possible??)
1273class ConfigExtensionsDialog(Toplevel):
1274 """A dialog for configuring IDLE extensions.
1275
1276 This dialog is generic - it works for any and all IDLE extensions.
1277
1278 IDLE extensions save their configuration options using idleConf.
1279 ConfigExtensionsDialog reads the current configuration using idleConf,
1280 supplies a GUI interface to change the configuration values, and saves the
1281 changes using idleConf.
1282
1283 Not all changes take effect immediately - some may require restarting IDLE.
1284 This depends on each extension's implementation.
1285
1286 All values are treated as text, and it is up to the user to supply
1287 reasonable values. The only exception to this are the 'enable*' options,
1288 which are boolean, and can be toggled with an True/False button.
1289 """
1290 def __init__(self, parent, title=None, _htest=False):
1291 Toplevel.__init__(self, parent)
1292 self.wm_withdraw()
1293
1294 self.configure(borderwidth=5)
1295 self.geometry(
1296 "+%d+%d" % (parent.winfo_rootx() + 20,
1297 parent.winfo_rooty() + (30 if not _htest else 150)))
1298 self.wm_title(title or 'IDLE Extensions Configuration')
1299
1300 self.defaultCfg = idleConf.defaultCfg['extensions']
1301 self.userCfg = idleConf.userCfg['extensions']
1302 self.is_int = self.register(is_int)
1303 self.load_extensions()
1304 self.create_widgets()
1305
1306 self.resizable(height=FALSE, width=FALSE) # don't allow resizing yet
1307 self.transient(parent)
1308 self.protocol("WM_DELETE_WINDOW", self.Cancel)
1309 self.tabbed_page_set.focus_set()
1310 # wait for window to be generated
1311 self.update()
1312 # set current width as the minimum width
1313 self.wm_minsize(self.winfo_width(), 1)
1314 # now allow resizing
1315 self.resizable(height=TRUE, width=TRUE)
1316
1317 self.wm_deiconify()
1318 if not _htest:
1319 self.grab_set()
1320 self.wait_window()
1321
1322 def load_extensions(self):
1323 "Fill self.extensions with data from the default and user configs."
1324 self.extensions = {}
1325 for ext_name in idleConf.GetExtensions(active_only=False):
1326 self.extensions[ext_name] = []
1327
1328 for ext_name in self.extensions:
1329 opt_list = sorted(self.defaultCfg.GetOptionList(ext_name))
1330
1331 # bring 'enable' options to the beginning of the list
1332 enables = [opt_name for opt_name in opt_list
1333 if opt_name.startswith('enable')]
1334 for opt_name in enables:
1335 opt_list.remove(opt_name)
1336 opt_list = enables + opt_list
1337
1338 for opt_name in opt_list:
1339 def_str = self.defaultCfg.Get(
1340 ext_name, opt_name, raw=True)
1341 try:
1342 def_obj = {'True':True, 'False':False}[def_str]
1343 opt_type = 'bool'
1344 except KeyError:
1345 try:
1346 def_obj = int(def_str)
1347 opt_type = 'int'
1348 except ValueError:
1349 def_obj = def_str
1350 opt_type = None
1351 try:
1352 value = self.userCfg.Get(
1353 ext_name, opt_name, type=opt_type, raw=True,
1354 default=def_obj)
1355 except ValueError: # Need this until .Get fixed
1356 value = def_obj # bad values overwritten by entry
1357 var = StringVar(self)
1358 var.set(str(value))
1359
1360 self.extensions[ext_name].append({'name': opt_name,
1361 'type': opt_type,
1362 'default': def_str,
1363 'value': value,
1364 'var': var,
1365 })
1366
1367 def create_widgets(self):
1368 """Create the dialog's widgets."""
Terry Jan Reedy936a6f12015-08-26 23:13:16 -04001369 self.extension_names = StringVar(self)
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001370 self.rowconfigure(0, weight=1)
Terry Jan Reedy936a6f12015-08-26 23:13:16 -04001371 self.columnconfigure(2, weight=1)
1372 self.extension_list = Listbox(self, listvariable=self.extension_names,
1373 selectmode='browse')
1374 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1375 scroll = Scrollbar(self, command=self.extension_list.yview)
1376 self.extension_list.yscrollcommand=scroll.set
1377 self.details_frame = LabelFrame(self, width=250, height=250)
1378 self.extension_list.grid(column=0, row=0, sticky='nws')
1379 scroll.grid(column=1, row=0, sticky='ns')
1380 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1381 self.configure(padx=10, pady=10)
1382 self.config_frame = {}
1383 self.current_extension = None
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001384
Terry Jan Reedy936a6f12015-08-26 23:13:16 -04001385 self.outerframe = self # TEMPORARY
1386 self.tabbed_page_set = self.extension_list # TEMPORARY
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001387
Terry Jan Reedy936a6f12015-08-26 23:13:16 -04001388 # create the individual pages
1389 ext_names = ''
1390 for ext_name in sorted(self.extensions):
1391 self.create_extension_frame(ext_name)
1392 ext_names = ext_names + '{' + ext_name + '} '
1393 self.extension_names.set(ext_names)
1394 self.extension_list.selection_set(0)
1395 self.extension_selected(None)
1396 self.create_action_buttons().grid(row=1, columnspan=3)
1397
1398 def extension_selected(self, event):
1399 newsel = self.extension_list.curselection()
1400 if newsel:
1401 newsel = self.extension_list.get(newsel)
1402 if newsel is None or newsel != self.current_extension:
1403 if self.current_extension:
1404 self.details_frame.config(text='')
1405 self.config_frame[self.current_extension].grid_forget()
1406 self.current_extension = None
1407 if newsel:
1408 self.details_frame.config(text=newsel)
1409 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1410 self.current_extension = newsel
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001411
1412 create_action_buttons = ConfigDialog.create_action_buttons.im_func
1413
Terry Jan Reedy936a6f12015-08-26 23:13:16 -04001414 def create_extension_frame(self, ext_name):
1415 """Create a frame holding the widgets to configure one extension"""
1416 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1417 self.config_frame[ext_name] = f
1418 entry_area = f.interior
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001419 # create an entry for each configuration option
1420 for row, opt in enumerate(self.extensions[ext_name]):
1421 # create a row with a label and entry/checkbutton
1422 label = Label(entry_area, text=opt['name'])
1423 label.grid(row=row, column=0, sticky=NW)
1424 var = opt['var']
1425 if opt['type'] == 'bool':
1426 Checkbutton(entry_area, textvariable=var, variable=var,
1427 onvalue='True', offvalue='False',
1428 indicatoron=FALSE, selectcolor='', width=8
Terry Jan Reedy936a6f12015-08-26 23:13:16 -04001429 ).grid(row=row, column=1, sticky=W, padx=7)
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001430 elif opt['type'] == 'int':
1431 Entry(entry_area, textvariable=var, validate='key',
Terry Jan Reedy936a6f12015-08-26 23:13:16 -04001432 validatecommand=(self.is_int, '%P')
1433 ).grid(row=row, column=1, sticky=NSEW, padx=7)
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001434
1435 else:
1436 Entry(entry_area, textvariable=var
Terry Jan Reedy936a6f12015-08-26 23:13:16 -04001437 ).grid(row=row, column=1, sticky=NSEW, padx=7)
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001438 return
1439
1440
1441 Ok = ConfigDialog.Ok.im_func
1442
1443 def Apply(self):
1444 self.save_all_changed_configs()
1445 pass
1446
1447 Cancel = ConfigDialog.Cancel.im_func
1448
1449 def Help(self):
1450 pass
1451
1452 def set_user_value(self, section, opt):
1453 name = opt['name']
1454 default = opt['default']
1455 value = opt['var'].get().strip() or default
1456 opt['var'].set(value)
1457 # if self.defaultCfg.has_section(section):
1458 # Currently, always true; if not, indent to return
1459 if (value == default):
1460 return self.userCfg.RemoveOption(section, name)
1461 # set the option
1462 return self.userCfg.SetOption(section, name, value)
1463
1464 def save_all_changed_configs(self):
1465 """Save configuration changes to the user config file."""
1466 has_changes = False
1467 for ext_name in self.extensions:
1468 options = self.extensions[ext_name]
1469 for opt in options:
1470 if self.set_user_value(ext_name, opt):
1471 has_changes = True
1472 if has_changes:
1473 self.userCfg.Save()
1474
1475
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00001476if __name__ == '__main__':
Terry Jan Reedyaf0dce92014-07-14 23:07:21 -04001477 import unittest
1478 unittest.main('idlelib.idle_test.test_configdialog',
1479 verbosity=2, exit=False)
Terry Jan Reedy76916e82014-05-29 01:46:16 -04001480 from idlelib.idle_test.htest import run
Terry Jan Reedy7a162072014-10-22 20:15:12 -04001481 run(ConfigDialog, ConfigExtensionsDialog)