configDialog.py 51.3 KB
Newer Older
1 2 3 4
"""
configuration dialog
"""
from Tkinter import *
Steven M. Gava's avatar
Steven M. Gava committed
5
import tkMessageBox, tkColorChooser, tkFont
6
import string, copy
7

8
from configHandler import idleConf
9
from dynOptionMenuWidget import DynOptionMenu
10
from tabpage import TabPageSet
11
from keybindingDialog import GetKeysDialog
12
from configSectionNameDialog import GetCfgSectionNameDialog
13
from configHelpSourceEdit import GetHelpSourceDialog
14 15
class ConfigDialog(Toplevel):
    """
16 17
    configuration dialog for idle
    """ 
18
    def __init__(self,parent,title):
19 20 21 22
        Toplevel.__init__(self, parent)
        self.configure(borderwidth=5)
        self.geometry("+%d+%d" % (parent.winfo_rootx()+20,
                parent.winfo_rooty()+30))
23 24 25
        #Theme Elements. Each theme element key is it's display name.
        #The first value of the tuple is the sample area tag name.
        #The second value is the display name list sort index. 
26 27 28 29 30 31 32 33 34 35 36 37
        self.themeElements={'Normal Text':('normal','00'),
            'Python Keywords':('keyword','01'),
            'Python Definitions':('definition','02'),
            'Python Comments':('comment','03'),
            'Python Strings':('string','04'),
            'Selected Text':('hilite','05'),
            'Found Text':('hit','06'),
            'Cursor':('cursor','07'),
            'Error Text':('error','08'),
            'Shell Normal Text':('console','09'),
            'Shell Stdout Text':('stdout','10'),
            'Shell Stderr Text':('stderr','11')}
38
        self.ResetChangedItems() #load initial values in changed items dict
39 40 41 42 43 44
        self.CreateWidgets()
        self.resizable(height=FALSE,width=FALSE)
        self.transient(parent)
        self.grab_set()
        self.protocol("WM_DELETE_WINDOW", self.Cancel)
        self.parent = parent
45
        self.tabPages.focus_set()
46
        #key bindings for this dialog
47 48 49
        #self.bind('<Escape>',self.Cancel) #dismiss dialog, no save
        #self.bind('<Alt-a>',self.Apply) #apply changes, save
        #self.bind('<F1>',self.Help) #context help
50
        self.LoadConfigs()
51
        self.AttachVarCallbacks() #avoid callbacks during LoadConfigs 
52 53 54
        self.wait_window()
        
    def CreateWidgets(self):
55 56
        self.tabPages = TabPageSet(self,
                pageNames=['Fonts/Tabs','Highlighting','Keys','General'])
57
        self.tabPages.ChangePage()#activates default (first) page
58 59
        frameActionButtons = Frame(self)
        #action buttons
60 61
        self.buttonHelp = Button(frameActionButtons,text='Help',
                command=self.Help,takefocus=FALSE)
Steven M. Gava's avatar
Steven M. Gava committed
62 63 64
        self.buttonOk = Button(frameActionButtons,text='Ok',
                command=self.Ok,takefocus=FALSE)
        self.buttonApply = Button(frameActionButtons,text='Apply',
65
                command=self.Apply,takefocus=FALSE)
66 67
        self.buttonCancel = Button(frameActionButtons,text='Cancel',
                command=self.Cancel,takefocus=FALSE)
68 69 70 71
        self.CreatePageFontTab()
        self.CreatePageHighlight()
        self.CreatePageKeys()
        self.CreatePageGeneral()
Steven M. Gava's avatar
Steven M. Gava committed
72 73 74
        self.buttonHelp.pack(side=RIGHT,padx=5,pady=5)
        self.buttonOk.pack(side=LEFT,padx=5,pady=5)
        self.buttonApply.pack(side=LEFT,padx=5,pady=5)
75 76
        self.buttonCancel.pack(side=LEFT,padx=5,pady=5)
        frameActionButtons.pack(side=BOTTOM)
77
        self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH)
78
   
79
    def CreatePageFontTab(self):
80
        #tkVars
81
        self.fontSize=StringVar(self)
82 83
        self.fontBold=BooleanVar(self)
        self.fontName=StringVar(self)
84
        self.spaceNum=IntVar(self)
85
        #self.tabCols=IntVar(self)
86
        self.indentBySpaces=BooleanVar(self) 
87
        self.editFont=tkFont.Font(self,('courier',12,'normal'))
88 89
        ##widget creation
        #body frame
90
        frame=self.tabPages.pages['Fonts/Tabs']['page']
91 92 93 94 95 96
        #body section frames
        frameFont=Frame(frame,borderwidth=2,relief=GROOVE)
        frameIndent=Frame(frame,borderwidth=2,relief=GROOVE)
        #frameFont
        labelFontTitle=Label(frameFont,text='Set Base Editor Font')
        frameFontName=Frame(frameFont)
97
        frameFontParam=Frame(frameFont)
98
        labelFontNameTitle=Label(frameFontName,justify=LEFT,
Steven M. Gava's avatar
Steven M. Gava committed
99 100 101
                text='Font :')
        self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE,
                exportselection=FALSE)
102
        self.listFontName.bind('<ButtonRelease-1>',self.OnListFontButtonRelease)
Steven M. Gava's avatar
Steven M. Gava committed
103 104 105
        scrollFont=Scrollbar(frameFontName)
        scrollFont.config(command=self.listFontName.yview)
        self.listFontName.config(yscrollcommand=scrollFont.set)
106 107
        labelFontSizeTitle=Label(frameFontParam,text='Size :')
        self.optMenuFontSize=DynOptionMenu(frameFontParam,self.fontSize,None,
108
            command=self.SetFontSample)
109
        checkFontBold=Checkbutton(frameFontParam,variable=self.fontBold,
110
            onvalue=1,offvalue=0,text='Bold',command=self.SetFontSample)
111 112 113
        frameFontSample=Frame(frameFont,relief=SOLID,borderwidth=1)
        self.labelFontSample=Label(frameFontSample,
                text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]',
114
                justify=LEFT,font=self.editFont)
115 116 117 118 119 120
        #frameIndent
        labelIndentTitle=Label(frameIndent,text='Set Indentation Defaults')
        frameIndentType=Frame(frameIndent)
        frameIndentSize=Frame(frameIndent)
        labelIndentTypeTitle=Label(frameIndentType,
                text='Choose indentation type :')
121
        radioUseSpaces=Radiobutton(frameIndentType,variable=self.indentBySpaces,
122
            value=1,text='Tab key inserts spaces')
123
        radioUseTabs=Radiobutton(frameIndentType,variable=self.indentBySpaces,
124
            value=0,text='Tab key inserts tabs')
125 126 127
        labelIndentSizeTitle=Label(frameIndentSize,
                text='Choose indentation size :')
        labelSpaceNumTitle=Label(frameIndentSize,justify=LEFT,
128
                text='indent width')
129
        self.scaleSpaceNum=Scale(frameIndentSize,variable=self.spaceNum,
130
                orient='horizontal',tickinterval=2,from_=2,to=16)
131 132 133 134
        #labeltabColsTitle=Label(frameIndentSize,justify=LEFT,
        #        text='when tab key inserts tabs,\ncolumns per tab')
        #self.scaleTabCols=Scale(frameIndentSize,variable=self.tabCols,
        #        orient='horizontal',tickinterval=2,from_=2,to=8)
135 136
        #widget packing
        #body
Steven M. Gava's avatar
Steven M. Gava committed
137 138
        frameFont.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
        frameIndent.pack(side=LEFT,padx=5,pady=10,fill=Y)
139 140
        #frameFont
        labelFontTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
141 142
        frameFontName.pack(side=TOP,padx=5,pady=5,fill=X)
        frameFontParam.pack(side=TOP,padx=5,pady=5,fill=X)
143
        labelFontNameTitle.pack(side=TOP,anchor=W)
144
        self.listFontName.pack(side=LEFT,expand=TRUE,fill=X)
Steven M. Gava's avatar
Steven M. Gava committed
145
        scrollFont.pack(side=LEFT,fill=Y)
146 147 148
        labelFontSizeTitle.pack(side=LEFT,anchor=W)
        self.optMenuFontSize.pack(side=LEFT,anchor=W)
        checkFontBold.pack(side=LEFT,anchor=W,padx=20)
149
        frameFontSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
Steven M. Gava's avatar
Steven M. Gava committed
150
        self.labelFontSample.pack(expand=TRUE,fill=BOTH)
151 152 153 154 155 156 157 158 159 160
        #frameIndent
        labelIndentTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
        frameIndentType.pack(side=TOP,padx=5,fill=X)
        frameIndentSize.pack(side=TOP,padx=5,pady=5,fill=BOTH)
        labelIndentTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
        radioUseSpaces.pack(side=TOP,anchor=W,padx=5)
        radioUseTabs.pack(side=TOP,anchor=W,padx=5)
        labelIndentSizeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
        labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5)
        self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X)
161 162
        #labeltabColsTitle.pack(side=TOP,anchor=W,padx=5)
        #self.scaleTabCols.pack(side=TOP,padx=5,fill=X)
163 164 165
        return frame

    def CreatePageHighlight(self):
166 167
        self.builtinTheme=StringVar(self)
        self.customTheme=StringVar(self)
168
        self.fgHilite=BooleanVar(self)
169 170
        self.colour=StringVar(self)
        self.fontName=StringVar(self)
171
        self.themeIsBuiltin=BooleanVar(self) 
172
        self.highlightTarget=StringVar(self)
173 174
        ##widget creation
        #body frame
175
        frame=self.tabPages.pages['Highlighting']['page']
176 177 178 179
        #body section frames
        frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
        frameTheme=Frame(frame,borderwidth=2,relief=GROOVE)
        #frameCustom
180
        self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1,
181 182
            font=('courier',12,''),cursor='hand2',width=21,height=10,
            takefocus=FALSE,highlightthickness=0,wrap=NONE)
183 184 185
        text=self.textHighlightSample
        text.bind('<Double-Button-1>',lambda e: 'break')
        text.bind('<B1-Motion>',lambda e: 'break')
186
        textAndTags=(('#you can click here','comment'),('\n','normal'),
187 188 189
            ('#to choose items','comment'),('\n','normal'),('def','keyword'),
            (' ','normal'),('func','definition'),('(param):','normal'),
            ('\n  ','normal'),('"""string"""','string'),('\n  var0 = ','normal'),
190 191
            ("'string'",'string'),('\n  var1 = ','normal'),("'selected'",'hilite'),
            ('\n  var2 = ','normal'),("'found'",'hit'),('\n\n','normal'),
192
            (' error ','error'),(' ','normal'),('cursor |','cursor'),
193 194
            ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'),
            (' ','normal'),('stderr','stderr'),('\n','normal'))
195 196 197 198 199 200
        for txTa in textAndTags:
            text.insert(END,txTa[0],txTa[1])
        for element in self.themeElements.keys(): 
            text.tag_bind(self.themeElements[element][0],'<ButtonPress-1>',
                lambda event,elem=element: event.widget.winfo_toplevel()
                .highlightTarget.set(elem))
201 202
        text.config(state=DISABLED)
        self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1)
203
        frameFgBg=Frame(frameCustom)
204
        labelCustomTitle=Label(frameCustom,text='Set Custom Highlighting')
205
        buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :',
206
            command=self.GetColour,highlightthickness=0)
207
        self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet,
208
            self.highlightTarget,None,highlightthickness=0)#,command=self.SetHighlightTargetBinding
209
        self.radioFg=Radiobutton(frameFgBg,variable=self.fgHilite,
210
            value=1,text='Foreground',command=self.SetColourSampleBinding)
211
        self.radioBg=Radiobutton(frameFgBg,variable=self.fgHilite,
212
            value=0,text='Background',command=self.SetColourSampleBinding)
213
        self.fgHilite.set(1)
214
        buttonSaveCustomTheme=Button(frameCustom, 
215
            text='Save as New Custom Theme',command=self.SaveAsNewTheme)
216 217 218
        #frameTheme
        labelThemeTitle=Label(frameTheme,text='Select a Highlighting Theme')
        labelTypeTitle=Label(frameTheme,text='Select : ')
219 220 221 222
        self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
            value=1,command=self.SetThemeType,text='a Built-in Theme')
        self.radioThemeCustom=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
            value=0,command=self.SetThemeType,text='a Custom Theme')
223
        self.optMenuThemeBuiltin=DynOptionMenu(frameTheme,
224
            self.builtinTheme,None,command=None)
225
        self.optMenuThemeCustom=DynOptionMenu(frameTheme,
226
            self.customTheme,None,command=None)
227 228
        self.buttonDeleteCustomTheme=Button(frameTheme,text='Delete Custom Theme',
                command=self.DeleteCustomTheme)
229 230
        ##widget packing
        #body
231 232
        frameCustom.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
        frameTheme.pack(side=LEFT,padx=5,pady=10,fill=Y)
233 234
        #frameCustom
        labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
235
        self.frameColourSet.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=X)
236
        frameFgBg.pack(side=TOP,padx=5,pady=0)
237 238
        self.textHighlightSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,
            fill=BOTH)
239 240
        buttonSetColour.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=4)
        self.optMenuHighlightTarget.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=3)
241 242
        self.radioFg.pack(side=LEFT,anchor=E)
        self.radioBg.pack(side=RIGHT,anchor=W)
243
        buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5)        
244 245 246
        #frameTheme
        labelThemeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
        labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
247 248
        self.radioThemeBuiltin.pack(side=TOP,anchor=W,padx=5)
        self.radioThemeCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
249 250
        self.optMenuThemeBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
        self.optMenuThemeCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
251
        self.buttonDeleteCustomTheme.pack(side=TOP,fill=X,padx=5,pady=5)
252 253 254
        return frame

    def CreatePageKeys(self):
255
        #tkVars
256 257 258
        self.bindingTarget=StringVar(self)
        self.builtinKeys=StringVar(self)
        self.customKeys=StringVar(self)
259
        self.keysAreBuiltin=BooleanVar(self) 
260
        self.keyBinding=StringVar(self)
261 262
        ##widget creation
        #body frame
263
        frame=self.tabPages.pages['Keys']['page']
264 265 266 267 268 269
        #body section frames
        frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
        frameKeySets=Frame(frame,borderwidth=2,relief=GROOVE)
        #frameCustom
        frameTarget=Frame(frameCustom)
        labelCustomTitle=Label(frameCustom,text='Set Custom Key Bindings')
270 271 272
        labelTargetTitle=Label(frameTarget,text='Action - Key(s)')
        scrollTargetY=Scrollbar(frameTarget)
        scrollTargetX=Scrollbar(frameTarget,orient=HORIZONTAL)
273 274
        self.listBindings=Listbox(frameTarget,takefocus=FALSE,
                exportselection=FALSE)
275
        self.listBindings.bind('<ButtonRelease-1>',self.KeyBindingSelected)
276 277 278 279
        scrollTargetY.config(command=self.listBindings.yview)
        scrollTargetX.config(command=self.listBindings.xview)
        self.listBindings.config(yscrollcommand=scrollTargetY.set)
        self.listBindings.config(xscrollcommand=scrollTargetX.set)
280 281
        self.buttonNewKeys=Button(frameCustom,text='Get New Keys for Selection',
            command=self.GetNewKeys,state=DISABLED)
282 283
        buttonSaveCustomKeys=Button(frameCustom,
                text='Save as New Custom Key Set',command=self.SaveAsNewKeySet)
284
        #frameKeySets
285
        labelKeysTitle=Label(frameKeySets,text='Select a Key Set')
286
        labelTypeTitle=Label(frameKeySets,text='Select : ')
287
        self.radioKeysBuiltin=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
288
            value=1,command=self.SetKeysType,text='a Built-in Key Set')
289
        self.radioKeysCustom=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
290
            value=0,command=self.SetKeysType,text='a Custom Key Set')
291 292 293 294
        self.optMenuKeysBuiltin=DynOptionMenu(frameKeySets,
            self.builtinKeys,None,command=None)
        self.optMenuKeysCustom=DynOptionMenu(frameKeySets,
            self.customKeys,None,command=None)
295 296
        self.buttonDeleteCustomKeys=Button(frameKeySets,text='Delete Custom Key Set',
                command=self.DeleteCustomKeys)
297 298 299 300 301 302 303
        ##widget packing
        #body
        frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
        frameKeySets.pack(side=LEFT,padx=5,pady=5,fill=Y)
        #frameCustom
        labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
        buttonSaveCustomKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)        
304
        self.buttonNewKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)        
305 306 307 308 309 310 311 312
        frameTarget.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
        #frame target
        frameTarget.columnconfigure(0,weight=1)
        frameTarget.rowconfigure(1,weight=1)
        labelTargetTitle.grid(row=0,column=0,columnspan=2,sticky=W)
        self.listBindings.grid(row=1,column=0,sticky=NSEW)
        scrollTargetY.grid(row=1,column=1,sticky=NS)
        scrollTargetX.grid(row=2,column=0,sticky=EW)
313 314 315
        #frameKeySets
        labelKeysTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
        labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
316 317
        self.radioKeysBuiltin.pack(side=TOP,anchor=W,padx=5)
        self.radioKeysCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
318 319 320
        self.optMenuKeysBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
        self.optMenuKeysCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
        self.buttonDeleteCustomKeys.pack(side=TOP,fill=X,padx=5,pady=5)
321 322 323
        return frame

    def CreatePageGeneral(self):
324
        #tkVars        
325 326
        self.winWidth=StringVar(self)       
        self.winHeight=StringVar(self)
327
        self.startupEdit=IntVar(self)       
328 329
        self.userHelpBrowser=BooleanVar(self)
        self.helpBrowser=StringVar(self)
330 331
        #widget creation
        #body
332
        frame=self.tabPages.pages['General']['page']
333 334 335
        #body section frames        
        frameRun=Frame(frame,borderwidth=2,relief=GROOVE)
        frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE)
336
        frameHelp=Frame(frame,borderwidth=2,relief=GROOVE)
337
        #frameRun
338 339 340 341 342 343
        labelRunTitle=Label(frameRun,text='Startup Preferences')
        labelRunChoiceTitle=Label(frameRun,text='On startup : ')
        radioStartupEdit=Radiobutton(frameRun,variable=self.startupEdit,
            value=1,command=self.SetKeysType,text="open Edit Window")
        radioStartupShell=Radiobutton(frameRun,variable=self.startupEdit,
            value=0,command=self.SetKeysType,text='open Shell Window')
344
        #frameWinSize
345 346
        labelWinSizeTitle=Label(frameWinSize,text='Initial Window Size'+
                '  (in characters)')
347 348 349 350 351 352
        labelWinWidthTitle=Label(frameWinSize,text='Width')
        entryWinWidth=Entry(frameWinSize,textvariable=self.winWidth,
                width=3)
        labelWinHeightTitle=Label(frameWinSize,text='Height')
        entryWinHeight=Entry(frameWinSize,textvariable=self.winHeight,
                width=3)
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
        #frameHelp
        labelHelpTitle=Label(frameHelp,text='Help Options')
        frameHelpList=Frame(frameHelp)
        frameHelpListButtons=Frame(frameHelpList)
        labelHelpListTitle=Label(frameHelpList,text='Additional (html) Help Sources:')
        scrollHelpList=Scrollbar(frameHelpList)
        self.listHelp=Listbox(frameHelpList,height=5,takefocus=FALSE,
                exportselection=FALSE)
        scrollHelpList.config(command=self.listHelp.yview)
        self.listHelp.config(yscrollcommand=scrollHelpList.set)
        self.listHelp.bind('<ButtonRelease-1>',self.HelpSourceSelected)
        self.buttonHelpListEdit=Button(frameHelpListButtons,text='Edit',
                state=DISABLED,width=8,command=self.HelpListItemEdit)
        self.buttonHelpListAdd=Button(frameHelpListButtons,text='Add',
                width=8,command=self.HelpListItemAdd)
        self.buttonHelpListRemove=Button(frameHelpListButtons,text='Remove',
                state=DISABLED,width=8,command=self.HelpListItemRemove)
370 371 372 373 374 375 376
        # the following is better handled by the BROWSER environment
        # variable under unix/linux
        #checkHelpBrowser=Checkbutton(frameHelp,variable=self.userHelpBrowser,
        #    onvalue=1,offvalue=0,text='user specified (html) help browser:',
        #    command=self.OnCheckUserHelpBrowser)
        #self.entryHelpBrowser=Entry(frameHelp,textvariable=self.helpBrowser,
        #        width=40)
377 378 379 380
        #widget packing
        #body
        frameRun.pack(side=TOP,padx=5,pady=5,fill=X)
        frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X)
381
        frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
382 383 384
        #frameRun
        labelRunTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
        labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
385 386
        radioStartupEdit.pack(side=LEFT,anchor=W,padx=5,pady=5)
        radioStartupShell.pack(side=LEFT,anchor=W,padx=5,pady=5)     
387
        #frameWinSize
388 389 390 391 392
        labelWinSizeTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
        entryWinHeight.pack(side=RIGHT,anchor=E,padx=10,pady=5)
        labelWinHeightTitle.pack(side=RIGHT,anchor=E,pady=5)
        entryWinWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5)
        labelWinWidthTitle.pack(side=RIGHT,anchor=E,pady=5)
393 394 395 396 397 398 399 400 401 402
        #frameHelp
        labelHelpTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
        frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y)
        frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
        labelHelpListTitle.pack(side=TOP,anchor=W)
        scrollHelpList.pack(side=RIGHT,anchor=W,fill=Y)
        self.listHelp.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH)
        self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5)
        self.buttonHelpListAdd.pack(side=TOP,anchor=W)
        self.buttonHelpListRemove.pack(side=TOP,anchor=W,pady=5)
403 404
        #checkHelpBrowser.pack(side=TOP,anchor=W,padx=5)
        #self.entryHelpBrowser.pack(side=TOP,anchor=W,padx=5,pady=5)
405 406
        return frame

407 408 409 410 411
    def AttachVarCallbacks(self):
        self.fontSize.trace_variable('w',self.VarChanged_fontSize)
        self.fontName.trace_variable('w',self.VarChanged_fontName)
        self.fontBold.trace_variable('w',self.VarChanged_fontBold)
        self.spaceNum.trace_variable('w',self.VarChanged_spaceNum)
412
        #self.tabCols.trace_variable('w',self.VarChanged_tabCols)
413 414
        self.indentBySpaces.trace_variable('w',self.VarChanged_indentBySpaces)
        self.colour.trace_variable('w',self.VarChanged_colour)
415 416 417 418
        self.builtinTheme.trace_variable('w',self.VarChanged_builtinTheme)
        self.customTheme.trace_variable('w',self.VarChanged_customTheme)
        self.themeIsBuiltin.trace_variable('w',self.VarChanged_themeIsBuiltin) 
        self.highlightTarget.trace_variable('w',self.VarChanged_highlightTarget)
419
        self.keyBinding.trace_variable('w',self.VarChanged_keyBinding)
420 421 422
        self.builtinKeys.trace_variable('w',self.VarChanged_builtinKeys)
        self.customKeys.trace_variable('w',self.VarChanged_customKeys)
        self.keysAreBuiltin.trace_variable('w',self.VarChanged_keysAreBuiltin) 
423 424 425
        self.winWidth.trace_variable('w',self.VarChanged_winWidth)
        self.winHeight.trace_variable('w',self.VarChanged_winHeight)
        self.startupEdit.trace_variable('w',self.VarChanged_startupEdit)
426

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
    def VarChanged_fontSize(self,*params):
        value=self.fontSize.get()
        self.AddChangedItem('main','EditorWindow','font-size',value)
        
    def VarChanged_fontName(self,*params):
        value=self.fontName.get()
        self.AddChangedItem('main','EditorWindow','font',value)

    def VarChanged_fontBold(self,*params):
        value=self.fontBold.get()
        self.AddChangedItem('main','EditorWindow','font-bold',value)

    def VarChanged_indentBySpaces(self,*params):
        value=self.indentBySpaces.get()
        self.AddChangedItem('main','Indent','use-spaces',value)

    def VarChanged_spaceNum(self,*params):
        value=self.spaceNum.get()
        self.AddChangedItem('main','Indent','num-spaces',value)

447 448 449
    #def VarChanged_tabCols(self,*params):
    #    value=self.tabCols.get()
    #    self.AddChangedItem('main','Indent','tab-cols',value)
450 451

    def VarChanged_colour(self,*params):
452 453 454 455 456 457 458 459 460
        self.OnNewColourSet()

    def VarChanged_builtinTheme(self,*params):
        value=self.builtinTheme.get()
        self.AddChangedItem('main','Theme','name',value)
        self.PaintThemeSample()

    def VarChanged_customTheme(self,*params):
        value=self.customTheme.get()
461 462 463
        if value != '- no custom themes -':
            self.AddChangedItem('main','Theme','name',value)
            self.PaintThemeSample()
464 465 466 467

    def VarChanged_themeIsBuiltin(self,*params):
        value=self.themeIsBuiltin.get()
        self.AddChangedItem('main','Theme','default',value)
468 469 470 471
        if value:
            self.VarChanged_builtinTheme()
        else:
            self.VarChanged_customTheme()
472 473 474

    def VarChanged_highlightTarget(self,*params):
        self.SetHighlightTarget()
Steven M. Gava's avatar
Steven M. Gava committed
475
        
476
    def VarChanged_keyBinding(self,*params):
Steven M. Gava's avatar
Steven M. Gava committed
477 478 479
        value=self.keyBinding.get()
        keySet=self.customKeys.get()
        event=self.listBindings.get(ANCHOR).split()[0]
480 481 482 483 484 485 486 487
        if idleConf.IsCoreBinding(event):
            #this is a core keybinding
            self.AddChangedItem('keys',keySet,event,value)
        else: #this is an extension key binding
            extName=idleConf.GetExtnNameForEvent(event)
            extKeybindSection=extName+'_cfgBindings'
            self.AddChangedItem('extensions',extKeybindSection,event,value)
        
488 489 490 491 492 493 494
    def VarChanged_builtinKeys(self,*params):
        value=self.builtinKeys.get()
        self.AddChangedItem('main','Keys','name',value)
        self.LoadKeysList(value)

    def VarChanged_customKeys(self,*params):
        value=self.customKeys.get()
495 496 497
        if value != '- no custom keys -':
            self.AddChangedItem('main','Keys','name',value)
            self.LoadKeysList(value)
498 499 500 501 502

    def VarChanged_keysAreBuiltin(self,*params):
        value=self.keysAreBuiltin.get() 
        self.AddChangedItem('main','Keys','default',value)
        if value: 
503
            self.VarChanged_builtinKeys()
504
        else:
505
            self.VarChanged_customKeys()
506

507
    def VarChanged_winWidth(self,*params):
Steven M. Gava's avatar
Steven M. Gava committed
508 509
        value=self.winWidth.get()
        self.AddChangedItem('main','EditorWindow','width',value)
510 511

    def VarChanged_winHeight(self,*params):
Steven M. Gava's avatar
Steven M. Gava committed
512 513
        value=self.winHeight.get()
        self.AddChangedItem('main','EditorWindow','height',value)
514 515

    def VarChanged_startupEdit(self,*params):
Steven M. Gava's avatar
Steven M. Gava committed
516 517
        value=self.startupEdit.get()
        self.AddChangedItem('main','General','editor-on-startup',value)
518

519
    def ResetChangedItems(self):
520
        #When any config item is changed in this dialog, an entry
521 522 523 524 525 526
        #should be made in the relevant section (config type) of this 
        #dictionary. The key should be the config file section name and the 
        #value a dictionary, whose key:value pairs are item=value pairs for
        #that config file section.
        self.changedItems={'main':{},'highlight':{},'keys':{},'extensions':{}}

527
    def AddChangedItem(self,type,section,item,value):
528
        value=str(value) #make sure we use a string
529 530 531 532
        if not self.changedItems[type].has_key(section):
            self.changedItems[type][section]={}    
        self.changedItems[type][section][item]=value
    
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
    def GetDefaultItems(self):
        dItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
        for configType in dItems.keys():
            sections=idleConf.GetSectionList('default',configType)
            for section in sections:
                dItems[configType][section]={}
                options=idleConf.defaultCfg[configType].GetOptionList(section)
                for option in options:            
                    dItems[configType][section][option]=(
                            idleConf.defaultCfg[configType].Get(section,option))
        return dItems
            
    def SetThemeType(self):
        if self.themeIsBuiltin.get():
            self.optMenuThemeBuiltin.config(state=NORMAL)
            self.optMenuThemeCustom.config(state=DISABLED)
            self.buttonDeleteCustomTheme.config(state=DISABLED)
        else:
            self.optMenuThemeBuiltin.config(state=DISABLED)
552
            self.radioThemeCustom.config(state=NORMAL)
553 554 555 556
            self.optMenuThemeCustom.config(state=NORMAL)
            self.buttonDeleteCustomTheme.config(state=NORMAL)

    def SetKeysType(self):
557
        if self.keysAreBuiltin.get():
558 559 560 561 562
            self.optMenuKeysBuiltin.config(state=NORMAL)
            self.optMenuKeysCustom.config(state=DISABLED)
            self.buttonDeleteCustomKeys.config(state=DISABLED)
        else:
            self.optMenuKeysBuiltin.config(state=DISABLED)
563
            self.radioKeysCustom.config(state=NORMAL)
564 565 566
            self.optMenuKeysCustom.config(state=NORMAL)
            self.buttonDeleteCustomKeys.config(state=NORMAL)
    
567 568 569 570
    def GetNewKeys(self):
        listIndex=self.listBindings.index(ANCHOR)
        binding=self.listBindings.get(listIndex)
        bindName=binding.split()[0] #first part, up to first space
571 572 573 574 575 576 577 578 579 580
        if self.keysAreBuiltin.get(): 
            currentKeySetName=self.builtinKeys.get()
        else:  
            currentKeySetName=self.customKeys.get()
        currentBindings=idleConf.GetCurrentKeySet()
        if currentKeySetName in self.changedItems['keys'].keys(): #unsaved changes
            keySetChanges=self.changedItems['keys'][currentKeySetName]
            for event in keySetChanges.keys():
                currentBindings[event]=keySetChanges[event].split()
        currentKeySequences=currentBindings.values()
581 582 583
        newKeys=GetKeysDialog(self,'Get New Keys',bindName,
                currentKeySequences).result
        if newKeys: #new keys were specified
584
            if self.keysAreBuiltin.get(): #current key set is a built-in
585 586
                message=('Your changes will be saved as a new Custom Key Set. '+
                        'Enter a name for your new Custom Key Set below.')
587 588
                newKeySet=self.GetNewKeysName(message)
                if not newKeySet: #user cancelled custom key set creation
589 590 591 592
                    self.listBindings.select_set(listIndex)
                    self.listBindings.select_anchor(listIndex)
                    return
                else: #create new custom key set based on previously active key set 
593
                    self.CreateNewKeySet(newKeySet)    
594
            self.listBindings.delete(listIndex)
595
            self.listBindings.insert(listIndex,bindName+' - '+newKeys)
596 597
            self.listBindings.select_set(listIndex)
            self.listBindings.select_anchor(listIndex)
598
            self.keyBinding.set(newKeys)
599 600 601 602
        else:
            self.listBindings.select_set(listIndex)
            self.listBindings.select_anchor(listIndex)

603
    def GetNewKeysName(self,message):
604 605
        usedNames=(idleConf.GetSectionList('user','keys')+
                idleConf.GetSectionList('default','keys'))
606 607 608 609 610 611 612 613 614
        newKeySet=GetCfgSectionNameDialog(self,'New Custom Key Set',
                message,usedNames).result
        return newKeySet
    
    def SaveAsNewKeySet(self):
        newKeysName=self.GetNewKeysName('New Key Set Name:')
        if newKeysName:
            self.CreateNewKeySet(newKeysName)

615 616 617 618 619 620
    def KeyBindingSelected(self,event):
        self.buttonNewKeys.config(state=NORMAL)

    def CreateNewKeySet(self,newKeySetName):
        #creates new custom key set based on the previously active key set,
        #and makes the new key set active
621 622
        if self.keysAreBuiltin.get(): 
            prevKeySetName=self.builtinKeys.get()
623
        else:  
624 625 626 627
            prevKeySetName=self.customKeys.get()
        prevKeys=idleConf.GetCoreKeys(prevKeySetName)
        newKeys={}
        for event in prevKeys.keys(): #add key set to changed items
628
            eventName=event[2:-2] #trim off the angle brackets
629 630 631 632 633 634 635 636 637
            binding=string.join(prevKeys[event])
            newKeys[eventName]=binding
        #handle any unsaved changes to prev key set
        if prevKeySetName in self.changedItems['keys'].keys():
            keySetChanges=self.changedItems['keys'][prevKeySetName]
            for event in keySetChanges.keys():
                newKeys[event]=keySetChanges[event]
        #save the new theme
        self.SaveNewKeySet(newKeySetName,newKeys)
638 639 640 641
        #change gui over to the new key set
        customKeyList=idleConf.GetSectionList('user','keys')
        customKeyList.sort()
        self.optMenuKeysCustom.SetMenu(customKeyList,newKeySetName)
642
        self.keysAreBuiltin.set(0)
643 644
        self.SetKeysType()
    
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
    def LoadKeysList(self,keySetName):
        reselect=0
        newKeySet=0
        if self.listBindings.curselection():
            reselect=1
            listIndex=self.listBindings.index(ANCHOR)
        keySet=idleConf.GetKeySet(keySetName)
        bindNames=keySet.keys()
        bindNames.sort()
        self.listBindings.delete(0,END)
        for bindName in bindNames: 
            key=string.join(keySet[bindName]) #make key(s) into a string
            bindName=bindName[2:-2] #trim off the angle brackets
            if keySetName in self.changedItems['keys'].keys():
                #handle any unsaved changes to this key set
                if bindName in self.changedItems['keys'][keySetName].keys():
                    key=self.changedItems['keys'][keySetName][bindName]
            self.listBindings.insert(END, bindName+' - '+key)
        if reselect:
            self.listBindings.see(listIndex)
            self.listBindings.select_set(listIndex)
            self.listBindings.select_anchor(listIndex)

668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
    def DeleteCustomKeys(self):
        keySetName=self.customKeys.get()
        if not tkMessageBox.askyesno('Delete Key Set','Are you sure you wish '+
                'to delete the key set '+`keySetName`+' ?'):
            return
        #remove key set from config
        idleConf.userCfg['keys'].remove_section(keySetName)
        if self.changedItems['keys'].has_key(keySetName):
            del(self.changedItems['keys'][keySetName])
        #write changes
        idleConf.userCfg['keys'].Save()
        #reload user key set list
        itemList=idleConf.GetSectionList('user','keys')
        itemList.sort()
        if not itemList:
            self.radioKeysCustom.config(state=DISABLED)
            self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -')
        else:
            self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
        #revert to default key set
        self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default'))
        self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name'))
        #user can't back out of these changes, they must be applied now
        self.Apply()
        self.SetKeysType()
    
    def DeleteCustomTheme(self):
        themeName=self.customTheme.get()
        if not tkMessageBox.askyesno('Delete Theme','Are you sure you wish '+
                'to delete the theme '+`themeName`+' ?'):
            return
        #remove theme from config
        idleConf.userCfg['highlight'].remove_section(themeName)
        if self.changedItems['highlight'].has_key(themeName):
            del(self.changedItems['highlight'][themeName])
        #write changes
        idleConf.userCfg['highlight'].Save()
        #reload user theme list
        itemList=idleConf.GetSectionList('user','highlight')
        itemList.sort()
        if not itemList:
            self.radioThemeCustom.config(state=DISABLED)
            self.optMenuThemeCustom.SetMenu(itemList,'- no custom themes -')
        else:
            self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
        #revert to default theme
        self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme','default'))
        self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme','name'))
        #user can't back out of these changes, they must be applied now
        self.Apply()
        self.SetThemeType()

720 721
    def GetColour(self):
        target=self.highlightTarget.get()
722
        prevColour=self.frameColourSet.cget('bg')
723
        rgbTuplet, colourString = tkColorChooser.askcolor(parent=self,
724 725 726
            title='Pick new colour for : '+target,initialcolor=prevColour)
        if colourString and (colourString!=prevColour): 
            #user didn't cancel, and they chose a new colour
727 728 729
            if self.themeIsBuiltin.get(): #current theme is a built-in
                message=('Your changes will be saved as a new Custom Theme. '+
                        'Enter a name for your new Custom Theme below.')
730 731
                newTheme=self.GetNewThemeName(message)
                if not newTheme: #user cancelled custom theme creation
732 733
                    return
                else: #create new custom theme based on previously active theme 
734
                    self.CreateNewTheme(newTheme)    
735 736 737
                    self.colour.set(colourString)
            else: #current theme is user defined
                self.colour.set(colourString)
738
    
739 740 741 742 743 744 745 746 747 748 749 750
    def OnNewColourSet(self):
        newColour=self.colour.get()
        self.frameColourSet.config(bg=newColour)#set sample
        if self.fgHilite.get(): plane='foreground'
        else: plane='background'
        sampleElement=self.themeElements[self.highlightTarget.get()][0]
        apply(self.textHighlightSample.tag_config,
                (sampleElement,),{plane:newColour})
        theme=self.customTheme.get()
        themeElement=sampleElement+'-'+plane
        self.AddChangedItem('highlight',theme,themeElement,newColour)

751
    def GetNewThemeName(self,message):
752 753
        usedNames=(idleConf.GetSectionList('user','highlight')+
                idleConf.GetSectionList('default','highlight'))
754 755 756 757 758 759 760 761 762
        newTheme=GetCfgSectionNameDialog(self,'New Custom Theme',
                message,usedNames).result
        return newTheme
    
    def SaveAsNewTheme(self):
        newThemeName=self.GetNewThemeName('New Theme Name:')
        if newThemeName:
            self.CreateNewTheme(newThemeName)

763 764 765 766 767 768 769 770 771 772
    def CreateNewTheme(self,newThemeName):
        #creates new custom theme based on the previously active theme,
        #and makes the new theme active
        if self.themeIsBuiltin.get(): 
            themeType='default'
            themeName=self.builtinTheme.get()
        else:  
            themeType='user'
            themeName=self.customTheme.get()
        newTheme=idleConf.GetThemeDict(themeType,themeName)
773 774 775 776 777 778 779
        #apply any of the old theme's unsaved changes to the new theme
        if themeName in self.changedItems['highlight'].keys():
            themeChanges=self.changedItems['highlight'][themeName]
            for element in themeChanges.keys():
                newTheme[element]=themeChanges[element]
        #save the new theme
        self.SaveNewTheme(newThemeName,newTheme)
780 781 782 783 784 785 786
        #change gui over to the new theme
        customThemeList=idleConf.GetSectionList('user','highlight')
        customThemeList.sort()
        self.optMenuThemeCustom.SetMenu(customThemeList,newThemeName)
        self.themeIsBuiltin.set(0)
        self.SetThemeType()
    
787 788
    def OnListFontButtonRelease(self,event):
        self.fontName.set(self.listFontName.get(ANCHOR))
789 790
        self.SetFontSample()
        
791 792 793 794 795 796 797 798
    def SetFontSample(self,event=None):
        fontName=self.fontName.get()
        if self.fontBold.get(): 
            fontWeight=tkFont.BOLD
        else: 
            fontWeight=tkFont.NORMAL
        self.editFont.config(size=self.fontSize.get(),
                weight=fontWeight,family=fontName)
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821

    def SetHighlightTarget(self):
        if self.highlightTarget.get()=='Cursor': #bg not possible
            self.radioFg.config(state=DISABLED)
            self.radioBg.config(state=DISABLED)
            self.fgHilite.set(1)
        else: #both fg and bg can be set
            self.radioFg.config(state=NORMAL)
            self.radioBg.config(state=NORMAL)
            self.fgHilite.set(1)
        self.SetColourSample()
    
    def SetColourSampleBinding(self,*args):
        self.SetColourSample()
        
    def SetColourSample(self):
        #set the colour smaple area
        tag=self.themeElements[self.highlightTarget.get()][0]
        if self.fgHilite.get(): plane='foreground'
        else: plane='background'
        colour=self.textHighlightSample.tag_cget(tag,plane)
        self.frameColourSet.config(bg=colour)
    
822
    def PaintThemeSample(self):
823
        if self.themeIsBuiltin.get(): #a default theme
824 825 826
            theme=self.builtinTheme.get()
        else: #a user theme
            theme=self.customTheme.get()
827 828 829 830
        for elementTitle in self.themeElements.keys():
            element=self.themeElements[elementTitle][0]
            colours=idleConf.GetHighlight(theme,element)
            if element=='cursor': #cursor sample needs special painting
831
                colours['background']=idleConf.GetHighlight(theme, 
832
                        'normal', fgBg='bg')
833 834 835 836 837 838 839 840 841
            #handle any unsaved changes to this theme
            if theme in self.changedItems['highlight'].keys():
                themeDict=self.changedItems['highlight'][theme]
                if themeDict.has_key(element+'-foreground'):
                    colours['foreground']=themeDict[element+'-foreground']
                if themeDict.has_key(element+'-background'):
                    colours['background']=themeDict[element+'-background']
            apply(self.textHighlightSample.tag_config,(element,),colours)
        self.SetColourSample()
842
    
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
    def OnCheckUserHelpBrowser(self):
        if self.userHelpBrowser.get():
            self.entryHelpBrowser.config(state=NORMAL)
        else:
            self.entryHelpBrowser.config(state=DISABLED)
    
    def HelpSourceSelected(self,event):
        self.SetHelpListButtonStates()
    
    def SetHelpListButtonStates(self):
        if self.listHelp.size()<1: #no entries in list
            self.buttonHelpListEdit.config(state=DISABLED)
            self.buttonHelpListRemove.config(state=DISABLED)
        else: #there are some entries
            if self.listHelp.curselection(): #there currently is a selection
                self.buttonHelpListEdit.config(state=NORMAL)
                self.buttonHelpListRemove.config(state=NORMAL)
            else:  #there currently is not a selection
                self.buttonHelpListEdit.config(state=DISABLED)
                self.buttonHelpListRemove.config(state=DISABLED)

    def HelpListItemAdd(self):
        helpSource=GetHelpSourceDialog(self,'New Help Source').result
        if helpSource:
            self.userHelpList.append( (helpSource[0],helpSource[1]) )
            self.listHelp.insert(END,helpSource[0]+'  '+helpSource[1])
            self.UpdateUserHelpChangedItems()
        self.SetHelpListButtonStates()
    
    def HelpListItemEdit(self):
        itemIndex=self.listHelp.index(ANCHOR)
        helpSource=self.userHelpList[itemIndex]
875
        newHelpSource=GetHelpSourceDialog(self,'Edit Help Source',
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
                menuItem=helpSource[0],filePath=helpSource[1]).result
        if (not newHelpSource) or (newHelpSource==helpSource):
            return #no changes
        self.userHelpList[itemIndex]=newHelpSource
        self.listHelp.delete(itemIndex)
        self.listHelp.insert(itemIndex,newHelpSource[0]+'  '+newHelpSource[1])
        self.UpdateUserHelpChangedItems()
        self.SetHelpListButtonStates()
    
    def HelpListItemRemove(self):
        itemIndex=self.listHelp.index(ANCHOR)
        del(self.userHelpList[itemIndex])
        self.listHelp.delete(itemIndex)
        self.UpdateUserHelpChangedItems()
        self.SetHelpListButtonStates()
    
    def UpdateUserHelpChangedItems(self):
        #clear and rebuild the HelpFiles secion in self.changedItems
        if self.changedItems['main'].has_key('HelpFiles'):
            del(self.changedItems['main']['HelpFiles'])
        for num in range(1,len(self.userHelpList)+1):
            self.AddChangedItem('main','HelpFiles',str(num),
                    string.join(self.userHelpList[num-1],';'))
    
900 901
    def LoadFontCfg(self):
        ##base editor font selection list
902 903 904 905
        fonts=list(tkFont.families(self))
        fonts.sort()
        for font in fonts:
            self.listFontName.insert(END,font)
906
        configuredFont=idleConf.GetOption('main','EditorWindow','font',
907
                default='courier')
908
        self.fontName.set(configuredFont)
909 910 911 912
        if configuredFont in fonts:
            currentFontIndex=fonts.index(configuredFont)
            self.listFontName.see(currentFontIndex)
            self.listFontName.select_set(currentFontIndex)
913
            self.listFontName.select_anchor(currentFontIndex)
914
        ##font size dropdown
915 916
        fontSize=idleConf.GetOption('main','EditorWindow','font-size',
                default='12')
917
        self.optMenuFontSize.SetMenu(('7','8','9','10','11','12','13','14',
918
                '16','18','20','22'),fontSize )
919 920 921
        ##fontWeight
        self.fontBold.set(idleConf.GetOption('main','EditorWindow',
                'font-bold',default=0,type='bool'))
922 923 924 925
        ##font sample 
        self.SetFontSample()
    
    def LoadTabCfg(self):
926
        ##indent type radiobuttons
927
        spaceIndent=idleConf.GetOption('main','Indent','use-spaces',
928
                default=1,type='bool')
929
        self.indentBySpaces.set(spaceIndent)
930
        ##indent sizes
931
        spaceNum=idleConf.GetOption('main','Indent','num-spaces',
932
                default=4,type='int')
933 934
        #tabCols=idleConf.GetOption('main','Indent','tab-cols',
        #        default=4,type='int')
935
        self.spaceNum.set(spaceNum)
936
        #self.tabCols.set(tabCols)
937
    
938
    def LoadThemeCfg(self):
939
        ##current theme type radiobutton
940
        self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default',
941
            type='bool',default=1))
942
        ##currently set theme
943
        currentOption=idleConf.CurrentTheme()
944
        ##load available theme option menus
945
        if self.themeIsBuiltin.get(): #default theme selected
946
            itemList=idleConf.GetSectionList('default','highlight')
947
            itemList.sort()
948 949
            self.optMenuThemeBuiltin.SetMenu(itemList,currentOption)
            itemList=idleConf.GetSectionList('user','highlight')
950
            itemList.sort()
951 952 953 954 955
            if not itemList:
                self.radioThemeCustom.config(state=DISABLED)
                self.customTheme.set('- no custom themes -')    
            else:
                self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
956
        else: #user theme selected
957
            itemList=idleConf.GetSectionList('user','highlight')
958
            itemList.sort()
959 960
            self.optMenuThemeCustom.SetMenu(itemList,currentOption)
            itemList=idleConf.GetSectionList('default','highlight')
961
            itemList.sort()
962
            self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0])
963 964
        self.SetThemeType()
        ##load theme element option menu
965 966 967 968
        themeNames=self.themeElements.keys()
        themeNames.sort(self.__ThemeNameIndexCompare)
        self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0])   
        self.PaintThemeSample()
969
        self.SetHighlightTarget()
970 971 972 973 974
    
    def __ThemeNameIndexCompare(self,a,b):
        if self.themeElements[a][1]<self.themeElements[b][1]: return -1
        elif self.themeElements[a][1]==self.themeElements[b][1]: return 0
        else: return 1
975
    
976
    def LoadKeyCfg(self):
977
        ##current keys type radiobutton
978
        self.keysAreBuiltin.set(idleConf.GetOption('main','Keys','default',
979
            type='bool',default=1))
980
        ##currently set keys
981
        currentOption=idleConf.CurrentKeys()
982
        ##load available keyset option menus
983
        if self.keysAreBuiltin.get(): #default theme selected
984
            itemList=idleConf.GetSectionList('default','keys')
985
            itemList.sort()
986 987
            self.optMenuKeysBuiltin.SetMenu(itemList,currentOption)
            itemList=idleConf.GetSectionList('user','keys')
988
            itemList.sort()
989 990 991 992 993
            if not itemList:
                self.radioKeysCustom.config(state=DISABLED)    
                self.customKeys.set('- no custom keys -')    
            else:
                self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
994
        else: #user key set selected
995
            itemList=idleConf.GetSectionList('user','keys')
996
            itemList.sort()
997 998
            self.optMenuKeysCustom.SetMenu(itemList,currentOption)
            itemList=idleConf.GetSectionList('default','keys')
999
            itemList.sort()
1000 1001
            self.optMenuKeysBuiltin.SetMenu(itemList,itemList[0])
        self.SetKeysType()   
1002
        ##load keyset element list
1003 1004 1005
        keySetName=idleConf.CurrentKeys()
        self.LoadKeysList(keySetName)
    
1006
    def LoadGeneralCfg(self):
1007 1008 1009
        #startup state
        self.startupEdit.set(idleConf.GetOption('main','General',
                'editor-on-startup',default=1,type='bool'))
1010 1011 1012
        #initial window size
        self.winWidth.set(idleConf.GetOption('main','EditorWindow','width'))       
        self.winHeight.set(idleConf.GetOption('main','EditorWindow','height'))
1013 1014 1015 1016 1017
        #help browsing
        self.userHelpList=idleConf.GetExtraHelpSourceList('user')
        for helpItem in self.userHelpList:
            self.listHelp.insert(END,helpItem[0]+'  '+helpItem[1])
        self.SetHelpListButtonStates()
1018 1019 1020 1021 1022
        #self.userHelpBrowser.set(idleConf.GetOption('main','General',
        #        'user-help-browser',default=0,type='bool'))
        #self.helpBrowser.set(idleConf.GetOption('main','General',
        #        'user-help-browser-command',default=''))
        #self.OnCheckUserHelpBrowser()
1023
    
1024 1025 1026 1027 1028 1029 1030 1031 1032
    def LoadConfigs(self):
        """
        load configuration from default and user config files and populate
        the widgets on the config dialog pages.
        """
        ### fonts / tabs page
        self.LoadFontCfg()        
        self.LoadTabCfg()        
        ### highlighting page
1033
        self.LoadThemeCfg()
1034
        ### keys page
1035
        self.LoadKeyCfg()
1036
        ### general page
1037
        self.LoadGeneralCfg()
1038
        
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
    def SaveNewKeySet(self,keySetName,keySet):
        """
        save a newly created core key set.
        keySetName - string, the name of the new key set
        keySet - dictionary containing the new key set
        """
        if not idleConf.userCfg['keys'].has_section(keySetName):
            idleConf.userCfg['keys'].add_section(keySetName)
        for event in keySet.keys():
            value=keySet[event]
            idleConf.userCfg['keys'].SetOption(keySetName,event,value)
    
    def SaveNewTheme(self,themeName,theme):
        """
        save a newly created theme.
        themeName - string, the name of the new theme
        theme - dictionary containing the new theme
        """
        if not idleConf.userCfg['highlight'].has_section(themeName):
            idleConf.userCfg['highlight'].add_section(themeName)
        for element in theme.keys():
            value=theme[element]
            idleConf.userCfg['highlight'].SetOption(themeName,element,value)
    
1063 1064 1065 1066 1067 1068 1069 1070
    def SetUserValue(self,configType,section,item,value):
        if idleConf.defaultCfg[configType].has_option(section,item):
            if idleConf.defaultCfg[configType].Get(section,item)==value:
                #the setting equals a default setting, remove it from user cfg
                return idleConf.userCfg[configType].RemoveOption(section,item)
        #if we got here set the option
        return idleConf.userCfg[configType].SetOption(section,item,value)
            
1071
    def SaveAllChangedConfigs(self):
1072
        """
1073
        save all configuration changes to user config files.
1074
        """
1075 1076 1077
        #this section gets completely replaced
        idleConf.userCfg['main'].remove_section('HelpFiles')
        idleConf.userCfg['main'].Save()
1078
        for configType in self.changedItems.keys():
1079
            cfgTypeHasChanges=0
1080 1081 1082
            for section in self.changedItems[configType].keys():
                for item in self.changedItems[configType][section].keys():
                    value=self.changedItems[configType][section][item]
1083 1084 1085 1086
                    if self.SetUserValue(configType,section,item,value):
                        cfgTypeHasChanges=1
            if cfgTypeHasChanges: 
                idleConf.userCfg[configType].Save()                
1087 1088
        self.ResetChangedItems() #clear the changed items dict
         
1089 1090 1091 1092 1093 1094 1095 1096
    def ActivateConfigChanges(self):
        #things that need to be done to make 
        #applied config changes dynamic:
        #update editor/shell font and repaint
        #dynamically update indentation setttings
        #update theme and repaint
        #update keybindings and re-bind
        #update user help sources menu
1097 1098 1099
        winInstances=self.parent.instanceDict.keys()
        for instance in winInstances:
            instance.ResetColorizer()
1100
            instance.ResetFont()
1101
            instance.ResetKeybindings()
1102
            instance.ResetExtraHelpMenu()
1103
        
1104 1105 1106 1107 1108 1109 1110 1111
    def Cancel(self):
        self.destroy()

    def Ok(self):
        self.Apply()
        self.destroy()

    def Apply(self):
1112
        self.SaveAllChangedConfigs()
1113
        self.ActivateConfigChanges()
1114 1115

    def Help(self):
1116 1117
        pass

1118
if __name__ == '__main__':
1119 1120 1121
    #test the dialog
    root=Tk()
    Button(root,text='Dialog',
1122
            command=lambda:ConfigDialog(root,'Settings')).pack()
1123
    root.instanceDict={}
1124
    root.mainloop()