def CreateWidgets(self):
     self.tabPages = TabbedPageSet(self, page_names=['Fonts/Tabs',
      'Highlighting',
      'Keys',
      'General'])
     frameActionButtons = Frame(self, pady=2)
     if macosxSupport.isAquaTk():
         paddingArgs = {}
     else:
         paddingArgs = {'padx': 6,
          'pady': 3}
     self.buttonOk = Button(frameActionButtons, text='Ok', command=self.Ok, takefocus=FALSE, **paddingArgs)
     self.buttonApply = Button(frameActionButtons, text='Apply', command=self.Apply, takefocus=FALSE, **paddingArgs)
     self.buttonCancel = Button(frameActionButtons, text='Cancel', command=self.Cancel, takefocus=FALSE, **paddingArgs)
     self.CreatePageFontTab()
     self.CreatePageHighlight()
     self.CreatePageKeys()
     self.CreatePageGeneral()
     self.buttonOk.pack(side=LEFT, padx=5)
     self.buttonApply.pack(side=LEFT, padx=5)
     self.buttonCancel.pack(side=LEFT, padx=5)
     frameActionButtons.pack(side=BOTTOM)
     Frame(self, height=2, borderwidth=0).pack(side=BOTTOM)
     self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
Example #2
0
 def CreateWidgets(self):
     self.tabPages = TabbedPageSet(self, page_names=['Fonts/Tabs',
      'Highlighting',
      'Keys',
      'General'])
     frameActionButtons = Frame(self, pady=2)
     if macosxSupport.isAquaTk():
         paddingArgs = {}
     else:
         paddingArgs = {'padx': 6,
          'pady': 3}
     self.buttonOk = Button(frameActionButtons, text='Ok', command=self.Ok, takefocus=FALSE, **paddingArgs)
     self.buttonApply = Button(frameActionButtons, text='Apply', command=self.Apply, takefocus=FALSE, **paddingArgs)
     self.buttonCancel = Button(frameActionButtons, text='Cancel', command=self.Cancel, takefocus=FALSE, **paddingArgs)
     self.CreatePageFontTab()
     self.CreatePageHighlight()
     self.CreatePageKeys()
     self.CreatePageGeneral()
     self.buttonOk.pack(side=LEFT, padx=5)
     self.buttonApply.pack(side=LEFT, padx=5)
     self.buttonCancel.pack(side=LEFT, padx=5)
     frameActionButtons.pack(side=BOTTOM)
     Frame(self, height=2, borderwidth=0).pack(side=BOTTOM)
     self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
Example #3
0
class ConfigDialog(Toplevel):

    def __init__(self, parent, title):
        Toplevel.__init__(self, parent)
        self.wm_withdraw()
        self.configure(borderwidth=5)
        self.title('IDLE Preferences')
        self.geometry('+%d+%d' % (parent.winfo_rootx() + 20, parent.winfo_rooty() + 30))
        self.themeElements = {'Normal Text': ('normal', '00'),
         'Python Keywords': ('keyword', '01'),
         'Python Definitions': ('definition', '02'),
         'Python Builtins': ('builtin', '03'),
         'Python Comments': ('comment', '04'),
         'Python Strings': ('string', '05'),
         'Selected Text': ('hilite', '06'),
         'Found Text': ('hit', '07'),
         'Cursor': ('cursor', '08'),
         'Error Text': ('error', '09'),
         'Shell Normal Text': ('console', '10'),
         'Shell Stdout Text': ('stdout', '11'),
         'Shell Stderr Text': ('stderr', '12')}
        self.ResetChangedItems()
        self.CreateWidgets()
        self.resizable(height=FALSE, width=FALSE)
        self.transient(parent)
        self.grab_set()
        self.protocol('WM_DELETE_WINDOW', self.Cancel)
        self.parent = parent
        self.tabPages.focus_set()
        self.LoadConfigs()
        self.AttachVarCallbacks()
        self.wm_deiconify()
        self.wait_window()

    def CreateWidgets(self):
        self.tabPages = TabbedPageSet(self, page_names=['Fonts/Tabs',
         'Highlighting',
         'Keys',
         'General'])
        frameActionButtons = Frame(self, pady=2)
        if macosxSupport.isAquaTk():
            paddingArgs = {}
        else:
            paddingArgs = {'padx': 6,
             'pady': 3}
        self.buttonOk = Button(frameActionButtons, text='Ok', command=self.Ok, takefocus=FALSE, **paddingArgs)
        self.buttonApply = Button(frameActionButtons, text='Apply', command=self.Apply, takefocus=FALSE, **paddingArgs)
        self.buttonCancel = Button(frameActionButtons, text='Cancel', command=self.Cancel, takefocus=FALSE, **paddingArgs)
        self.CreatePageFontTab()
        self.CreatePageHighlight()
        self.CreatePageKeys()
        self.CreatePageGeneral()
        self.buttonOk.pack(side=LEFT, padx=5)
        self.buttonApply.pack(side=LEFT, padx=5)
        self.buttonCancel.pack(side=LEFT, padx=5)
        frameActionButtons.pack(side=BOTTOM)
        Frame(self, height=2, borderwidth=0).pack(side=BOTTOM)
        self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)

    def CreatePageFontTab(self):
        self.fontSize = StringVar(self)
        self.fontBold = BooleanVar(self)
        self.fontName = StringVar(self)
        self.spaceNum = IntVar(self)
        self.editFont = tkFont.Font(self, ('courier', 10, 'normal'))
        frame = self.tabPages.pages['Fonts/Tabs'].frame
        frameFont = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
        frameIndent = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
        frameFontName = Frame(frameFont)
        frameFontParam = Frame(frameFont)
        labelFontNameTitle = Label(frameFontName, justify=LEFT, text='Font Face :')
        self.listFontName = Listbox(frameFontName, height=5, takefocus=FALSE, exportselection=FALSE)
        self.listFontName.bind('<ButtonRelease-1>', self.OnListFontButtonRelease)
        scrollFont = Scrollbar(frameFontName)
        scrollFont.config(command=self.listFontName.yview)
        self.listFontName.config(yscrollcommand=scrollFont.set)
        labelFontSizeTitle = Label(frameFontParam, text='Size :')
        self.optMenuFontSize = DynOptionMenu(frameFontParam, self.fontSize, None, command=self.SetFontSample)
        checkFontBold = Checkbutton(frameFontParam, variable=self.fontBold, onvalue=1, offvalue=0, text='Bold', command=self.SetFontSample)
        frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1)
        self.labelFontSample = Label(frameFontSample, text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]', justify=LEFT, font=self.editFont)
        frameIndentSize = Frame(frameIndent)
        labelSpaceNumTitle = Label(frameIndentSize, justify=LEFT, text='Python Standard: 4 Spaces!')
        self.scaleSpaceNum = Scale(frameIndentSize, variable=self.spaceNum, orient='horizontal', tickinterval=2, from_=2, to=16)
        frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
        frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y)
        frameFontName.pack(side=TOP, padx=5, pady=5, fill=X)
        frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X)
        labelFontNameTitle.pack(side=TOP, anchor=W)
        self.listFontName.pack(side=LEFT, expand=TRUE, fill=X)
        scrollFont.pack(side=LEFT, fill=Y)
        labelFontSizeTitle.pack(side=LEFT, anchor=W)
        self.optMenuFontSize.pack(side=LEFT, anchor=W)
        checkFontBold.pack(side=LEFT, anchor=W, padx=20)
        frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
        self.labelFontSample.pack(expand=TRUE, fill=BOTH)
        frameIndentSize.pack(side=TOP, fill=X)
        labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5)
        self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
        return frame

    def CreatePageHighlight(self):
        self.builtinTheme = StringVar(self)
        self.customTheme = StringVar(self)
        self.fgHilite = BooleanVar(self)
        self.colour = StringVar(self)
        self.fontName = StringVar(self)
        self.themeIsBuiltin = BooleanVar(self)
        self.highlightTarget = StringVar(self)
        frame = self.tabPages.pages['Highlighting'].frame
        frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Custom Highlighting ')
        frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ')
        self.textHighlightSample = Text(frameCustom, relief=SOLID, borderwidth=1, font=('courier', 12, ''), cursor='hand2', width=21, height=11, takefocus=FALSE, highlightthickness=0, wrap=NONE)
        text = self.textHighlightSample
        text.bind('<Double-Button-1>', lambda e: 'break')
        text.bind('<B1-Motion>', lambda e: 'break')
        textAndTags = (('#you can click here', 'comment'),
         ('\n', 'normal'),
         ('#to choose items', 'comment'),
         ('\n', 'normal'),
         ('def', 'keyword'),
         (' ', 'normal'),
         ('func', 'definition'),
         ('(param):', 'normal'),
         ('\n  ', 'normal'),
         ('"""string"""', 'string'),
         ('\n  var0 = ', 'normal'),
         ("'string'", 'string'),
         ('\n  var1 = ', 'normal'),
         ("'selected'", 'hilite'),
         ('\n  var2 = ', 'normal'),
         ("'found'", 'hit'),
         ('\n  var3 = ', 'normal'),
         ('list', 'builtin'),
         ('(', 'normal'),
         ('None', 'builtin'),
         (')\n\n', 'normal'),
         (' error ', 'error'),
         (' ', 'normal'),
         ('cursor |', 'cursor'),
         ('\n ', 'normal'),
         ('shell', 'console'),
         (' ', 'normal'),
         ('stdout', 'stdout'),
         (' ', 'normal'),
         ('stderr', 'stderr'),
         ('\n', 'normal'))
        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))

        text.config(state=DISABLED)
        self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
        frameFgBg = Frame(frameCustom)
        buttonSetColour = Button(self.frameColourSet, text='Choose Colour for :', command=self.GetColour, highlightthickness=0)
        self.optMenuHighlightTarget = DynOptionMenu(self.frameColourSet, self.highlightTarget, None, highlightthickness=0)
        self.radioFg = Radiobutton(frameFgBg, variable=self.fgHilite, value=1, text='Foreground', command=self.SetColourSampleBinding)
        self.radioBg = Radiobutton(frameFgBg, variable=self.fgHilite, value=0, text='Background', command=self.SetColourSampleBinding)
        self.fgHilite.set(1)
        buttonSaveCustomTheme = Button(frameCustom, text='Save as New Custom Theme', command=self.SaveAsNewTheme)
        labelTypeTitle = Label(frameTheme, text='Select : ')
        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')
        self.optMenuThemeBuiltin = DynOptionMenu(frameTheme, self.builtinTheme, None, command=None)
        self.optMenuThemeCustom = DynOptionMenu(frameTheme, self.customTheme, None, command=None)
        self.buttonDeleteCustomTheme = Button(frameTheme, text='Delete Custom Theme', command=self.DeleteCustomTheme)
        frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
        frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
        self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
        frameFgBg.pack(side=TOP, padx=5, pady=0)
        self.textHighlightSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
        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)
        self.radioFg.pack(side=LEFT, anchor=E)
        self.radioBg.pack(side=RIGHT, anchor=W)
        buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
        labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
        self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
        self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
        self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
        self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
        self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5)
        return frame

    def CreatePageKeys(self):
        self.bindingTarget = StringVar(self)
        self.builtinKeys = StringVar(self)
        self.customKeys = StringVar(self)
        self.keysAreBuiltin = BooleanVar(self)
        self.keyBinding = StringVar(self)
        frame = self.tabPages.pages['Keys'].frame
        frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Custom Key Bindings ')
        frameKeySets = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
        frameTarget = Frame(frameCustom)
        labelTargetTitle = Label(frameTarget, text='Action - Key(s)')
        scrollTargetY = Scrollbar(frameTarget)
        scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL)
        self.listBindings = Listbox(frameTarget, takefocus=FALSE, exportselection=FALSE)
        self.listBindings.bind('<ButtonRelease-1>', self.KeyBindingSelected)
        scrollTargetY.config(command=self.listBindings.yview)
        scrollTargetX.config(command=self.listBindings.xview)
        self.listBindings.config(yscrollcommand=scrollTargetY.set)
        self.listBindings.config(xscrollcommand=scrollTargetX.set)
        self.buttonNewKeys = Button(frameCustom, text='Get New Keys for Selection', command=self.GetNewKeys, state=DISABLED)
        frames = [ Frame(frameKeySets, padx=2, pady=2, borderwidth=0) for i in range(2) ]
        self.radioKeysBuiltin = Radiobutton(frames[0], variable=self.keysAreBuiltin, value=1, command=self.SetKeysType, text='Use a Built-in Key Set')
        self.radioKeysCustom = Radiobutton(frames[0], variable=self.keysAreBuiltin, value=0, command=self.SetKeysType, text='Use a Custom Key Set')
        self.optMenuKeysBuiltin = DynOptionMenu(frames[0], self.builtinKeys, None, command=None)
        self.optMenuKeysCustom = DynOptionMenu(frames[0], self.customKeys, None, command=None)
        self.buttonDeleteCustomKeys = Button(frames[1], text='Delete Custom Key Set', command=self.DeleteCustomKeys)
        buttonSaveCustomKeys = Button(frames[1], text='Save as New Custom Key Set', command=self.SaveAsNewKeySet)
        frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
        frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
        self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
        frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
        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)
        self.radioKeysBuiltin.grid(row=0, column=0, sticky=W + NS)
        self.radioKeysCustom.grid(row=1, column=0, sticky=W + NS)
        self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
        self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
        self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
        buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
        frames[0].pack(side=TOP, fill=BOTH, expand=True)
        frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
        return frame

    def CreatePageGeneral(self):
        self.winWidth = StringVar(self)
        self.winHeight = StringVar(self)
        self.paraWidth = StringVar(self)
        self.startupEdit = IntVar(self)
        self.autoSave = IntVar(self)
        self.encoding = StringVar(self)
        self.userHelpBrowser = BooleanVar(self)
        self.helpBrowser = StringVar(self)
        frame = self.tabPages.pages['General'].frame
        frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Startup Preferences ')
        frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Autosave Preferences ')
        frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE)
        frameParaSize = Frame(frame, borderwidth=2, relief=GROOVE)
        frameEncoding = Frame(frame, borderwidth=2, relief=GROOVE)
        frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Additional Help Sources ')
        labelRunChoiceTitle = Label(frameRun, text='At 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')
        labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5)  ')
        radioSaveAsk = Radiobutton(frameSave, variable=self.autoSave, value=0, command=self.SetKeysType, text='Prompt to Save')
        radioSaveAuto = Radiobutton(frameSave, variable=self.autoSave, value=1, command=self.SetKeysType, text='No Prompt')
        labelWinSizeTitle = Label(frameWinSize, text='Initial Window Size' + '  (in characters)')
        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)
        labelParaWidthTitle = Label(frameParaSize, text='Paragraph reformat' + ' width (in characters)')
        entryParaWidth = Entry(frameParaSize, textvariable=self.paraWidth, width=3)
        labelEncodingTitle = Label(frameEncoding, text='Default Source Encoding')
        radioEncLocale = Radiobutton(frameEncoding, variable=self.encoding, value='locale', text='Locale-defined')
        radioEncUTF8 = Radiobutton(frameEncoding, variable=self.encoding, value='utf-8', text='UTF-8')
        radioEncNone = Radiobutton(frameEncoding, variable=self.encoding, value='none', text='None')
        frameHelpList = Frame(frameHelp)
        frameHelpListButtons = Frame(frameHelpList)
        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)
        frameRun.pack(side=TOP, padx=5, pady=5, fill=X)
        frameSave.pack(side=TOP, padx=5, pady=5, fill=X)
        frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X)
        frameParaSize.pack(side=TOP, padx=5, pady=5, fill=X)
        frameEncoding.pack(side=TOP, padx=5, pady=5, fill=X)
        frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
        labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
        radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
        radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
        labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
        radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
        radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
        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)
        labelParaWidthTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
        entryParaWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
        labelEncodingTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
        radioEncNone.pack(side=RIGHT, anchor=E, pady=5)
        radioEncUTF8.pack(side=RIGHT, anchor=E, pady=5)
        radioEncLocale.pack(side=RIGHT, anchor=E, pady=5)
        frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
        frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
        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)
        return frame

    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)
        self.colour.trace_variable('w', self.VarChanged_colour)
        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)
        self.keyBinding.trace_variable('w', self.VarChanged_keyBinding)
        self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys)
        self.customKeys.trace_variable('w', self.VarChanged_customKeys)
        self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin)
        self.winWidth.trace_variable('w', self.VarChanged_winWidth)
        self.winHeight.trace_variable('w', self.VarChanged_winHeight)
        self.paraWidth.trace_variable('w', self.VarChanged_paraWidth)
        self.startupEdit.trace_variable('w', self.VarChanged_startupEdit)
        self.autoSave.trace_variable('w', self.VarChanged_autoSave)
        self.encoding.trace_variable('w', self.VarChanged_encoding)

    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_spaceNum(self, *params):
        value = self.spaceNum.get()
        self.AddChangedItem('main', 'Indent', 'num-spaces', value)

    def VarChanged_colour(self, *params):
        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()
        if value != '- no custom themes -':
            self.AddChangedItem('main', 'Theme', 'name', value)
            self.PaintThemeSample()

    def VarChanged_themeIsBuiltin(self, *params):
        value = self.themeIsBuiltin.get()
        self.AddChangedItem('main', 'Theme', 'default', value)
        if value:
            self.VarChanged_builtinTheme()
        else:
            self.VarChanged_customTheme()

    def VarChanged_highlightTarget(self, *params):
        self.SetHighlightTarget()

    def VarChanged_keyBinding(self, *params):
        value = self.keyBinding.get()
        keySet = self.customKeys.get()
        event = self.listBindings.get(ANCHOR).split()[0]
        if idleConf.IsCoreBinding(event):
            self.AddChangedItem('keys', keySet, event, value)
        else:
            extName = idleConf.GetExtnNameForEvent(event)
            extKeybindSection = extName + '_cfgBindings'
            self.AddChangedItem('extensions', extKeybindSection, event, value)

    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()
        if value != '- no custom keys -':
            self.AddChangedItem('main', 'Keys', 'name', value)
            self.LoadKeysList(value)

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

    def VarChanged_winWidth(self, *params):
        value = self.winWidth.get()
        self.AddChangedItem('main', 'EditorWindow', 'width', value)

    def VarChanged_winHeight(self, *params):
        value = self.winHeight.get()
        self.AddChangedItem('main', 'EditorWindow', 'height', value)

    def VarChanged_paraWidth(self, *params):
        value = self.paraWidth.get()
        self.AddChangedItem('main', 'FormatParagraph', 'paragraph', value)

    def VarChanged_startupEdit(self, *params):
        value = self.startupEdit.get()
        self.AddChangedItem('main', 'General', 'editor-on-startup', value)

    def VarChanged_autoSave(self, *params):
        value = self.autoSave.get()
        self.AddChangedItem('main', 'General', 'autosave', value)

    def VarChanged_encoding(self, *params):
        value = self.encoding.get()
        self.AddChangedItem('main', 'EditorWindow', 'encoding', value)

    def ResetChangedItems(self):
        self.changedItems = {'main': {},
         'highlight': {},
         'keys': {},
         'extensions': {}}

    def AddChangedItem(self, type, section, item, value):
        value = str(value)
        if section not in self.changedItems[type]:
            self.changedItems[type][section] = {}
        self.changedItems[type][section][item] = value

    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)
            self.radioThemeCustom.config(state=NORMAL)
            self.optMenuThemeCustom.config(state=NORMAL)
            self.buttonDeleteCustomTheme.config(state=NORMAL)

    def SetKeysType(self):
        if self.keysAreBuiltin.get():
            self.optMenuKeysBuiltin.config(state=NORMAL)
            self.optMenuKeysCustom.config(state=DISABLED)
            self.buttonDeleteCustomKeys.config(state=DISABLED)
        else:
            self.optMenuKeysBuiltin.config(state=DISABLED)
            self.radioKeysCustom.config(state=NORMAL)
            self.optMenuKeysCustom.config(state=NORMAL)
            self.buttonDeleteCustomKeys.config(state=NORMAL)

    def GetNewKeys(self):
        listIndex = self.listBindings.index(ANCHOR)
        binding = self.listBindings.get(listIndex)
        bindName = binding.split()[0]
        if self.keysAreBuiltin.get():
            currentKeySetName = self.builtinKeys.get()
        else:
            currentKeySetName = self.customKeys.get()
        currentBindings = idleConf.GetCurrentKeySet()
        if currentKeySetName in self.changedItems['keys'].keys():
            keySetChanges = self.changedItems['keys'][currentKeySetName]
            for event in keySetChanges.keys():
                currentBindings[event] = keySetChanges[event].split()

        currentKeySequences = currentBindings.values()
        newKeys = GetKeysDialog(self, 'Get New Keys', bindName, currentKeySequences).result
        if newKeys:
            if self.keysAreBuiltin.get():
                message = 'Your changes will be saved as a new Custom Key Set. ' + 'Enter a name for your new Custom Key Set below.'
                newKeySet = self.GetNewKeysName(message)
                if not newKeySet:
                    self.listBindings.select_set(listIndex)
                    self.listBindings.select_anchor(listIndex)
                    return
                self.CreateNewKeySet(newKeySet)
            self.listBindings.delete(listIndex)
            self.listBindings.insert(listIndex, bindName + ' - ' + newKeys)
            self.listBindings.select_set(listIndex)
            self.listBindings.select_anchor(listIndex)
            self.keyBinding.set(newKeys)
        else:
            self.listBindings.select_set(listIndex)
            self.listBindings.select_anchor(listIndex)

    def GetNewKeysName(self, message):
        usedNames = idleConf.GetSectionList('user', 'keys') + idleConf.GetSectionList('default', 'keys')
        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)

    def KeyBindingSelected(self, event):
        self.buttonNewKeys.config(state=NORMAL)

    def CreateNewKeySet(self, newKeySetName):
        if self.keysAreBuiltin.get():
            prevKeySetName = self.builtinKeys.get()
        else:
            prevKeySetName = self.customKeys.get()
        prevKeys = idleConf.GetCoreKeys(prevKeySetName)
        newKeys = {}
        for event in prevKeys.keys():
            eventName = event[2:-2]
            binding = string.join(prevKeys[event])
            newKeys[eventName] = binding

        if prevKeySetName in self.changedItems['keys'].keys():
            keySetChanges = self.changedItems['keys'][prevKeySetName]
            for event in keySetChanges.keys():
                newKeys[event] = keySetChanges[event]

        self.SaveNewKeySet(newKeySetName, newKeys)
        customKeyList = idleConf.GetSectionList('user', 'keys')
        customKeyList.sort()
        self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName)
        self.keysAreBuiltin.set(0)
        self.SetKeysType()

    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])
            bindName = bindName[2:-2]
            if keySetName in self.changedItems['keys'].keys():
                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)

    def DeleteCustomKeys(self):
        keySetName = self.customKeys.get()
        if not tkMessageBox.askyesno('Delete Key Set', 'Are you sure you wish ' + 'to delete the key set %r ?' % keySetName, parent=self):
            return
        idleConf.userCfg['keys'].remove_section(keySetName)
        if keySetName in self.changedItems['keys']:
            del self.changedItems['keys'][keySetName]
        idleConf.userCfg['keys'].Save()
        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])
        self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default'))
        self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))
        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 %r ?' % (themeName,), parent=self):
            return
        idleConf.userCfg['highlight'].remove_section(themeName)
        if themeName in self.changedItems['highlight']:
            del self.changedItems['highlight'][themeName]
        idleConf.userCfg['highlight'].Save()
        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])
        self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
        self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
        self.Apply()
        self.SetThemeType()

    def GetColour(self):
        target = self.highlightTarget.get()
        prevColour = self.frameColourSet.cget('bg')
        rgbTuplet, colourString = tkColorChooser.askcolor(parent=self, title='Pick new colour for : ' + target, initialcolor=prevColour)
        if colourString and colourString != prevColour:
            if self.themeIsBuiltin.get():
                message = 'Your changes will be saved as a new Custom Theme. ' + 'Enter a name for your new Custom Theme below.'
                newTheme = self.GetNewThemeName(message)
                if not newTheme:
                    return
                self.CreateNewTheme(newTheme)
                self.colour.set(colourString)
            else:
                self.colour.set(colourString)

    def OnNewColourSet(self):
        newColour = self.colour.get()
        self.frameColourSet.config(bg=newColour)
        if self.fgHilite.get():
            plane = 'foreground'
        else:
            plane = 'background'
        sampleElement = self.themeElements[self.highlightTarget.get()][0]
        self.textHighlightSample.tag_config(sampleElement, **{plane: newColour})
        theme = self.customTheme.get()
        themeElement = sampleElement + '-' + plane
        self.AddChangedItem('highlight', theme, themeElement, newColour)

    def GetNewThemeName(self, message):
        usedNames = idleConf.GetSectionList('user', 'highlight') + idleConf.GetSectionList('default', 'highlight')
        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)

    def CreateNewTheme(self, newThemeName):
        if self.themeIsBuiltin.get():
            themeType = 'default'
            themeName = self.builtinTheme.get()
        else:
            themeType = 'user'
            themeName = self.customTheme.get()
        newTheme = idleConf.GetThemeDict(themeType, themeName)
        if themeName in self.changedItems['highlight'].keys():
            themeChanges = self.changedItems['highlight'][themeName]
            for element in themeChanges.keys():
                newTheme[element] = themeChanges[element]

        self.SaveNewTheme(newThemeName, newTheme)
        customThemeList = idleConf.GetSectionList('user', 'highlight')
        customThemeList.sort()
        self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName)
        self.themeIsBuiltin.set(0)
        self.SetThemeType()

    def OnListFontButtonRelease(self, event):
        font = self.listFontName.get(ANCHOR)
        self.fontName.set(font.lower())
        self.SetFontSample()

    def SetFontSample(self, event = None):
        fontName = self.fontName.get()
        if self.fontBold.get():
            fontWeight = tkFont.BOLD
        else:
            fontWeight = tkFont.NORMAL
        newFont = (fontName, self.fontSize.get(), fontWeight)
        self.labelFontSample.config(font=newFont)
        self.textHighlightSample.configure(font=newFont)

    def SetHighlightTarget(self):
        if self.highlightTarget.get() == 'Cursor':
            self.radioFg.config(state=DISABLED)
            self.radioBg.config(state=DISABLED)
            self.fgHilite.set(1)
        else:
            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):
        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)

    def PaintThemeSample(self):
        if self.themeIsBuiltin.get():
            theme = self.builtinTheme.get()
        else:
            theme = self.customTheme.get()
        for elementTitle in self.themeElements.keys():
            element = self.themeElements[elementTitle][0]
            colours = idleConf.GetHighlight(theme, element)
            if element == 'cursor':
                colours['background'] = idleConf.GetHighlight(theme, 'normal', fgBg='bg')
            if theme in self.changedItems['highlight'].keys():
                themeDict = self.changedItems['highlight'][theme]
                if element + '-foreground' in themeDict:
                    colours['foreground'] = themeDict[element + '-foreground']
                if element + '-background' in themeDict:
                    colours['background'] = themeDict[element + '-background']
            self.textHighlightSample.tag_config(element, **colours)

        self.SetColourSample()

    def HelpSourceSelected(self, event):
        self.SetHelpListButtonStates()

    def SetHelpListButtonStates(self):
        if self.listHelp.size() < 1:
            self.buttonHelpListEdit.config(state=DISABLED)
            self.buttonHelpListRemove.config(state=DISABLED)
        elif self.listHelp.curselection():
            self.buttonHelpListEdit.config(state=NORMAL)
            self.buttonHelpListRemove.config(state=NORMAL)
        else:
            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])
            self.UpdateUserHelpChangedItems()
        self.SetHelpListButtonStates()

    def HelpListItemEdit(self):
        itemIndex = self.listHelp.index(ANCHOR)
        helpSource = self.userHelpList[itemIndex]
        newHelpSource = GetHelpSourceDialog(self, 'Edit Help Source', menuItem=helpSource[0], filePath=helpSource[1]).result
        if not newHelpSource or newHelpSource == helpSource:
            return
        self.userHelpList[itemIndex] = newHelpSource
        self.listHelp.delete(itemIndex)
        self.listHelp.insert(itemIndex, newHelpSource[0])
        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 section in self.changedItems"""
        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][:2], ';'))

    def LoadFontCfg(self):
        fonts = list(tkFont.families(self))
        fonts.sort()
        for font in fonts:
            self.listFontName.insert(END, font)

        configuredFont = idleConf.GetOption('main', 'EditorWindow', 'font', default='courier')
        lc_configuredFont = configuredFont.lower()
        self.fontName.set(lc_configuredFont)
        lc_fonts = [ s.lower() for s in fonts ]
        if lc_configuredFont in lc_fonts:
            currentFontIndex = lc_fonts.index(lc_configuredFont)
            self.listFontName.see(currentFontIndex)
            self.listFontName.select_set(currentFontIndex)
            self.listFontName.select_anchor(currentFontIndex)
        fontSize = idleConf.GetOption('main', 'EditorWindow', 'font-size', type='int', default='10')
        self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14', '16', '18', '20', '22'), fontSize)
        self.fontBold.set(idleConf.GetOption('main', 'EditorWindow', 'font-bold', default=0, type='bool'))
        self.SetFontSample()

    def LoadTabCfg(self):
        spaceNum = idleConf.GetOption('main', 'Indent', 'num-spaces', default=4, type='int')
        self.spaceNum.set(spaceNum)

    def LoadThemeCfg(self):
        self.themeIsBuiltin.set(idleConf.GetOption('main', 'Theme', 'default', type='bool', default=1))
        currentOption = idleConf.CurrentTheme()
        if self.themeIsBuiltin.get():
            itemList = idleConf.GetSectionList('default', 'highlight')
            itemList.sort()
            self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
            itemList = idleConf.GetSectionList('user', 'highlight')
            itemList.sort()
            if not itemList:
                self.radioThemeCustom.config(state=DISABLED)
                self.customTheme.set('- no custom themes -')
            else:
                self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
        else:
            itemList = idleConf.GetSectionList('user', 'highlight')
            itemList.sort()
            self.optMenuThemeCustom.SetMenu(itemList, currentOption)
            itemList = idleConf.GetSectionList('default', 'highlight')
            itemList.sort()
            self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
        self.SetThemeType()
        themeNames = self.themeElements.keys()
        themeNames.sort(key=lambda x: self.themeElements[x][1])
        self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
        self.PaintThemeSample()
        self.SetHighlightTarget()

    def LoadKeyCfg(self):
        self.keysAreBuiltin.set(idleConf.GetOption('main', 'Keys', 'default', type='bool', default=1))
        currentOption = idleConf.CurrentKeys()
        if self.keysAreBuiltin.get():
            itemList = idleConf.GetSectionList('default', 'keys')
            itemList.sort()
            self.optMenuKeysBuiltin.SetMenu(itemList, currentOption)
            itemList = idleConf.GetSectionList('user', 'keys')
            itemList.sort()
            if not itemList:
                self.radioKeysCustom.config(state=DISABLED)
                self.customKeys.set('- no custom keys -')
            else:
                self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
        else:
            itemList = idleConf.GetSectionList('user', 'keys')
            itemList.sort()
            self.optMenuKeysCustom.SetMenu(itemList, currentOption)
            itemList = idleConf.GetSectionList('default', 'keys')
            itemList.sort()
            self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])
        self.SetKeysType()
        keySetName = idleConf.CurrentKeys()
        self.LoadKeysList(keySetName)

    def LoadGeneralCfg(self):
        self.startupEdit.set(idleConf.GetOption('main', 'General', 'editor-on-startup', default=1, type='bool'))
        self.autoSave.set(idleConf.GetOption('main', 'General', 'autosave', default=0, type='bool'))
        self.winWidth.set(idleConf.GetOption('main', 'EditorWindow', 'width', type='int'))
        self.winHeight.set(idleConf.GetOption('main', 'EditorWindow', 'height', type='int'))
        self.paraWidth.set(idleConf.GetOption('main', 'FormatParagraph', 'paragraph', type='int'))
        self.encoding.set(idleConf.GetOption('main', 'EditorWindow', 'encoding', default='none'))
        self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
        for helpItem in self.userHelpList:
            self.listHelp.insert(END, helpItem[0])

        self.SetHelpListButtonStates()

    def LoadConfigs(self):
        """
        load configuration from default and user config files and populate
        the widgets on the config dialog pages.
        """
        self.LoadFontCfg()
        self.LoadTabCfg()
        self.LoadThemeCfg()
        self.LoadKeyCfg()
        self.LoadGeneralCfg()

    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)

    def SetUserValue(self, configType, section, item, value):
        if idleConf.defaultCfg[configType].has_option(section, item):
            if idleConf.defaultCfg[configType].Get(section, item) == value:
                return idleConf.userCfg[configType].RemoveOption(section, item)
        return idleConf.userCfg[configType].SetOption(section, item, value)

    def SaveAllChangedConfigs(self):
        """Save configuration changes to the user config file."""
        idleConf.userCfg['main'].Save()
        for configType in self.changedItems.keys():
            cfgTypeHasChanges = False
            for section in self.changedItems[configType].keys():
                if section == 'HelpFiles':
                    idleConf.userCfg['main'].remove_section('HelpFiles')
                    cfgTypeHasChanges = True
                for item in self.changedItems[configType][section].keys():
                    value = self.changedItems[configType][section][item]
                    if self.SetUserValue(configType, section, item, value):
                        cfgTypeHasChanges = True

            if cfgTypeHasChanges:
                idleConf.userCfg[configType].Save()

        for configType in ['keys', 'highlight']:
            idleConf.userCfg[configType].Save()

        self.ResetChangedItems()

    def DeactivateCurrentConfig(self):
        winInstances = self.parent.instance_dict.keys()
        for instance in winInstances:
            instance.RemoveKeybindings()

    def ActivateConfigChanges(self):
        """Dynamically apply configuration changes"""
        winInstances = self.parent.instance_dict.keys()
        for instance in winInstances:
            instance.ResetColorizer()
            instance.ResetFont()
            instance.set_notabs_indentwidth()
            instance.ApplyKeybindings()
            instance.reset_help_menu_entries()

    def Cancel(self):
        self.destroy()

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

    def Apply(self):
        self.DeactivateCurrentConfig()
        self.SaveAllChangedConfigs()
        self.ActivateConfigChanges()

    def Help(self):
        pass
class ConfigDialog(Toplevel):

    def __init__(self, parent, title):
        Toplevel.__init__(self, parent)
        self.wm_withdraw()
        self.configure(borderwidth=5)
        self.title('IDLE Preferences')
        self.geometry('+%d+%d' % (parent.winfo_rootx() + 20, parent.winfo_rooty() + 30))
        self.themeElements = {'Normal Text': ('normal', '00'),
         'Python Keywords': ('keyword', '01'),
         'Python Definitions': ('definition', '02'),
         'Python Builtins': ('builtin', '03'),
         'Python Comments': ('comment', '04'),
         'Python Strings': ('string', '05'),
         'Selected Text': ('hilite', '06'),
         'Found Text': ('hit', '07'),
         'Cursor': ('cursor', '08'),
         'Error Text': ('error', '09'),
         'Shell Normal Text': ('console', '10'),
         'Shell Stdout Text': ('stdout', '11'),
         'Shell Stderr Text': ('stderr', '12')}
        self.ResetChangedItems()
        self.CreateWidgets()
        self.resizable(height=FALSE, width=FALSE)
        self.transient(parent)
        self.grab_set()
        self.protocol('WM_DELETE_WINDOW', self.Cancel)
        self.parent = parent
        self.tabPages.focus_set()
        self.LoadConfigs()
        self.AttachVarCallbacks()
        self.wm_deiconify()
        self.wait_window()

    def CreateWidgets(self):
        self.tabPages = TabbedPageSet(self, page_names=['Fonts/Tabs',
         'Highlighting',
         'Keys',
         'General'])
        frameActionButtons = Frame(self, pady=2)
        if macosxSupport.isAquaTk():
            paddingArgs = {}
        else:
            paddingArgs = {'padx': 6,
             'pady': 3}
        self.buttonOk = Button(frameActionButtons, text='Ok', command=self.Ok, takefocus=FALSE, **paddingArgs)
        self.buttonApply = Button(frameActionButtons, text='Apply', command=self.Apply, takefocus=FALSE, **paddingArgs)
        self.buttonCancel = Button(frameActionButtons, text='Cancel', command=self.Cancel, takefocus=FALSE, **paddingArgs)
        self.CreatePageFontTab()
        self.CreatePageHighlight()
        self.CreatePageKeys()
        self.CreatePageGeneral()
        self.buttonOk.pack(side=LEFT, padx=5)
        self.buttonApply.pack(side=LEFT, padx=5)
        self.buttonCancel.pack(side=LEFT, padx=5)
        frameActionButtons.pack(side=BOTTOM)
        Frame(self, height=2, borderwidth=0).pack(side=BOTTOM)
        self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)

    def CreatePageFontTab(self):
        self.fontSize = StringVar(self)
        self.fontBold = BooleanVar(self)
        self.fontName = StringVar(self)
        self.spaceNum = IntVar(self)
        self.editFont = tkFont.Font(self, ('courier', 10, 'normal'))
        frame = self.tabPages.pages['Fonts/Tabs'].frame
        frameFont = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
        frameIndent = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
        frameFontName = Frame(frameFont)
        frameFontParam = Frame(frameFont)
        labelFontNameTitle = Label(frameFontName, justify=LEFT, text='Font Face :')
        self.listFontName = Listbox(frameFontName, height=5, takefocus=FALSE, exportselection=FALSE)
        self.listFontName.bind('<ButtonRelease-1>', self.OnListFontButtonRelease)
        scrollFont = Scrollbar(frameFontName)
        scrollFont.config(command=self.listFontName.yview)
        self.listFontName.config(yscrollcommand=scrollFont.set)
        labelFontSizeTitle = Label(frameFontParam, text='Size :')
        self.optMenuFontSize = DynOptionMenu(frameFontParam, self.fontSize, None, command=self.SetFontSample)
        checkFontBold = Checkbutton(frameFontParam, variable=self.fontBold, onvalue=1, offvalue=0, text='Bold', command=self.SetFontSample)
        frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1)
        self.labelFontSample = Label(frameFontSample, text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]', justify=LEFT, font=self.editFont)
        frameIndentSize = Frame(frameIndent)
        labelSpaceNumTitle = Label(frameIndentSize, justify=LEFT, text='Python Standard: 4 Spaces!')
        self.scaleSpaceNum = Scale(frameIndentSize, variable=self.spaceNum, orient='horizontal', tickinterval=2, from_=2, to=16)
        frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
        frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y)
        frameFontName.pack(side=TOP, padx=5, pady=5, fill=X)
        frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X)
        labelFontNameTitle.pack(side=TOP, anchor=W)
        self.listFontName.pack(side=LEFT, expand=TRUE, fill=X)
        scrollFont.pack(side=LEFT, fill=Y)
        labelFontSizeTitle.pack(side=LEFT, anchor=W)
        self.optMenuFontSize.pack(side=LEFT, anchor=W)
        checkFontBold.pack(side=LEFT, anchor=W, padx=20)
        frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
        self.labelFontSample.pack(expand=TRUE, fill=BOTH)
        frameIndentSize.pack(side=TOP, fill=X)
        labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5)
        self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
        return frame

    def CreatePageHighlight(self):
        self.builtinTheme = StringVar(self)
        self.customTheme = StringVar(self)
        self.fgHilite = BooleanVar(self)
        self.colour = StringVar(self)
        self.fontName = StringVar(self)
        self.themeIsBuiltin = BooleanVar(self)
        self.highlightTarget = StringVar(self)
        frame = self.tabPages.pages['Highlighting'].frame
        frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Custom Highlighting ')
        frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ')
        self.textHighlightSample = Text(frameCustom, relief=SOLID, borderwidth=1, font=('courier', 12, ''), cursor='hand2', width=21, height=11, takefocus=FALSE, highlightthickness=0, wrap=NONE)
        text = self.textHighlightSample
        text.bind('<Double-Button-1>', lambda e: 'break')
        text.bind('<B1-Motion>', lambda e: 'break')
        textAndTags = (('#you can click here', 'comment'),
         ('\n', 'normal'),
         ('#to choose items', 'comment'),
         ('\n', 'normal'),
         ('def', 'keyword'),
         (' ', 'normal'),
         ('func', 'definition'),
         ('(param):', 'normal'),
         ('\n  ', 'normal'),
         ('"""string"""', 'string'),
         ('\n  var0 = ', 'normal'),
         ("'string'", 'string'),
         ('\n  var1 = ', 'normal'),
         ("'selected'", 'hilite'),
         ('\n  var2 = ', 'normal'),
         ("'found'", 'hit'),
         ('\n  var3 = ', 'normal'),
         ('list', 'builtin'),
         ('(', 'normal'),
         ('None', 'builtin'),
         (')\n\n', 'normal'),
         (' error ', 'error'),
         (' ', 'normal'),
         ('cursor |', 'cursor'),
         ('\n ', 'normal'),
         ('shell', 'console'),
         (' ', 'normal'),
         ('stdout', 'stdout'),
         (' ', 'normal'),
         ('stderr', 'stderr'),
         ('\n', 'normal'))
        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))

        text.config(state=DISABLED)
        self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
        frameFgBg = Frame(frameCustom)
        buttonSetColour = Button(self.frameColourSet, text='Choose Colour for :', command=self.GetColour, highlightthickness=0)
        self.optMenuHighlightTarget = DynOptionMenu(self.frameColourSet, self.highlightTarget, None, highlightthickness=0)
        self.radioFg = Radiobutton(frameFgBg, variable=self.fgHilite, value=1, text='Foreground', command=self.SetColourSampleBinding)
        self.radioBg = Radiobutton(frameFgBg, variable=self.fgHilite, value=0, text='Background', command=self.SetColourSampleBinding)
        self.fgHilite.set(1)
        buttonSaveCustomTheme = Button(frameCustom, text='Save as New Custom Theme', command=self.SaveAsNewTheme)
        labelTypeTitle = Label(frameTheme, text='Select : ')
        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')
        self.optMenuThemeBuiltin = DynOptionMenu(frameTheme, self.builtinTheme, None, command=None)
        self.optMenuThemeCustom = DynOptionMenu(frameTheme, self.customTheme, None, command=None)
        self.buttonDeleteCustomTheme = Button(frameTheme, text='Delete Custom Theme', command=self.DeleteCustomTheme)
        frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
        frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
        self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
        frameFgBg.pack(side=TOP, padx=5, pady=0)
        self.textHighlightSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
        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)
        self.radioFg.pack(side=LEFT, anchor=E)
        self.radioBg.pack(side=RIGHT, anchor=W)
        buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
        labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
        self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
        self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
        self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
        self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
        self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5)
        return frame

    def CreatePageKeys(self):
        self.bindingTarget = StringVar(self)
        self.builtinKeys = StringVar(self)
        self.customKeys = StringVar(self)
        self.keysAreBuiltin = BooleanVar(self)
        self.keyBinding = StringVar(self)
        frame = self.tabPages.pages['Keys'].frame
        frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Custom Key Bindings ')
        frameKeySets = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
        frameTarget = Frame(frameCustom)
        labelTargetTitle = Label(frameTarget, text='Action - Key(s)')
        scrollTargetY = Scrollbar(frameTarget)
        scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL)
        self.listBindings = Listbox(frameTarget, takefocus=FALSE, exportselection=FALSE)
        self.listBindings.bind('<ButtonRelease-1>', self.KeyBindingSelected)
        scrollTargetY.config(command=self.listBindings.yview)
        scrollTargetX.config(command=self.listBindings.xview)
        self.listBindings.config(yscrollcommand=scrollTargetY.set)
        self.listBindings.config(xscrollcommand=scrollTargetX.set)
        self.buttonNewKeys = Button(frameCustom, text='Get New Keys for Selection', command=self.GetNewKeys, state=DISABLED)
        frames = [ Frame(frameKeySets, padx=2, pady=2, borderwidth=0) for i in range(2) ]
        self.radioKeysBuiltin = Radiobutton(frames[0], variable=self.keysAreBuiltin, value=1, command=self.SetKeysType, text='Use a Built-in Key Set')
        self.radioKeysCustom = Radiobutton(frames[0], variable=self.keysAreBuiltin, value=0, command=self.SetKeysType, text='Use a Custom Key Set')
        self.optMenuKeysBuiltin = DynOptionMenu(frames[0], self.builtinKeys, None, command=None)
        self.optMenuKeysCustom = DynOptionMenu(frames[0], self.customKeys, None, command=None)
        self.buttonDeleteCustomKeys = Button(frames[1], text='Delete Custom Key Set', command=self.DeleteCustomKeys)
        buttonSaveCustomKeys = Button(frames[1], text='Save as New Custom Key Set', command=self.SaveAsNewKeySet)
        frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
        frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
        self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
        frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
        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)
        self.radioKeysBuiltin.grid(row=0, column=0, sticky=W + NS)
        self.radioKeysCustom.grid(row=1, column=0, sticky=W + NS)
        self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
        self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
        self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
        buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
        frames[0].pack(side=TOP, fill=BOTH, expand=True)
        frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
        return frame

    def CreatePageGeneral(self):
        self.winWidth = StringVar(self)
        self.winHeight = StringVar(self)
        self.paraWidth = StringVar(self)
        self.startupEdit = IntVar(self)
        self.autoSave = IntVar(self)
        self.encoding = StringVar(self)
        self.userHelpBrowser = BooleanVar(self)
        self.helpBrowser = StringVar(self)
        frame = self.tabPages.pages['General'].frame
        frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Startup Preferences ')
        frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Autosave Preferences ')
        frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE)
        frameParaSize = Frame(frame, borderwidth=2, relief=GROOVE)
        frameEncoding = Frame(frame, borderwidth=2, relief=GROOVE)
        frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Additional Help Sources ')
        labelRunChoiceTitle = Label(frameRun, text='At 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')
        labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5)  ')
        radioSaveAsk = Radiobutton(frameSave, variable=self.autoSave, value=0, command=self.SetKeysType, text='Prompt to Save')
        radioSaveAuto = Radiobutton(frameSave, variable=self.autoSave, value=1, command=self.SetKeysType, text='No Prompt')
        labelWinSizeTitle = Label(frameWinSize, text='Initial Window Size' + '  (in characters)')
        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)
        labelParaWidthTitle = Label(frameParaSize, text='Paragraph reformat' + ' width (in characters)')
        entryParaWidth = Entry(frameParaSize, textvariable=self.paraWidth, width=3)
        labelEncodingTitle = Label(frameEncoding, text='Default Source Encoding')
        radioEncLocale = Radiobutton(frameEncoding, variable=self.encoding, value='locale', text='Locale-defined')
        radioEncUTF8 = Radiobutton(frameEncoding, variable=self.encoding, value='utf-8', text='UTF-8')
        radioEncNone = Radiobutton(frameEncoding, variable=self.encoding, value='none', text='None')
        frameHelpList = Frame(frameHelp)
        frameHelpListButtons = Frame(frameHelpList)
        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)
        frameRun.pack(side=TOP, padx=5, pady=5, fill=X)
        frameSave.pack(side=TOP, padx=5, pady=5, fill=X)
        frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X)
        frameParaSize.pack(side=TOP, padx=5, pady=5, fill=X)
        frameEncoding.pack(side=TOP, padx=5, pady=5, fill=X)
        frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
        labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
        radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
        radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
        labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
        radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
        radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
        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)
        labelParaWidthTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
        entryParaWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
        labelEncodingTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
        radioEncNone.pack(side=RIGHT, anchor=E, pady=5)
        radioEncUTF8.pack(side=RIGHT, anchor=E, pady=5)
        radioEncLocale.pack(side=RIGHT, anchor=E, pady=5)
        frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
        frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
        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)
        return frame

    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)
        self.colour.trace_variable('w', self.VarChanged_colour)
        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)
        self.keyBinding.trace_variable('w', self.VarChanged_keyBinding)
        self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys)
        self.customKeys.trace_variable('w', self.VarChanged_customKeys)
        self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin)
        self.winWidth.trace_variable('w', self.VarChanged_winWidth)
        self.winHeight.trace_variable('w', self.VarChanged_winHeight)
        self.paraWidth.trace_variable('w', self.VarChanged_paraWidth)
        self.startupEdit.trace_variable('w', self.VarChanged_startupEdit)
        self.autoSave.trace_variable('w', self.VarChanged_autoSave)
        self.encoding.trace_variable('w', self.VarChanged_encoding)

    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_spaceNum(self, *params):
        value = self.spaceNum.get()
        self.AddChangedItem('main', 'Indent', 'num-spaces', value)

    def VarChanged_colour(self, *params):
        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()
        if value != '- no custom themes -':
            self.AddChangedItem('main', 'Theme', 'name', value)
            self.PaintThemeSample()

    def VarChanged_themeIsBuiltin(self, *params):
        value = self.themeIsBuiltin.get()
        self.AddChangedItem('main', 'Theme', 'default', value)
        if value:
            self.VarChanged_builtinTheme()
        else:
            self.VarChanged_customTheme()

    def VarChanged_highlightTarget(self, *params):
        self.SetHighlightTarget()

    def VarChanged_keyBinding(self, *params):
        value = self.keyBinding.get()
        keySet = self.customKeys.get()
        event = self.listBindings.get(ANCHOR).split()[0]
        if idleConf.IsCoreBinding(event):
            self.AddChangedItem('keys', keySet, event, value)
        else:
            extName = idleConf.GetExtnNameForEvent(event)
            extKeybindSection = extName + '_cfgBindings'
            self.AddChangedItem('extensions', extKeybindSection, event, value)

    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()
        if value != '- no custom keys -':
            self.AddChangedItem('main', 'Keys', 'name', value)
            self.LoadKeysList(value)

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

    def VarChanged_winWidth(self, *params):
        value = self.winWidth.get()
        self.AddChangedItem('main', 'EditorWindow', 'width', value)

    def VarChanged_winHeight(self, *params):
        value = self.winHeight.get()
        self.AddChangedItem('main', 'EditorWindow', 'height', value)

    def VarChanged_paraWidth(self, *params):
        value = self.paraWidth.get()
        self.AddChangedItem('main', 'FormatParagraph', 'paragraph', value)

    def VarChanged_startupEdit(self, *params):
        value = self.startupEdit.get()
        self.AddChangedItem('main', 'General', 'editor-on-startup', value)

    def VarChanged_autoSave(self, *params):
        value = self.autoSave.get()
        self.AddChangedItem('main', 'General', 'autosave', value)

    def VarChanged_encoding(self, *params):
        value = self.encoding.get()
        self.AddChangedItem('main', 'EditorWindow', 'encoding', value)

    def ResetChangedItems(self):
        self.changedItems = {'main': {},
         'highlight': {},
         'keys': {},
         'extensions': {}}

    def AddChangedItem(self, type, section, item, value):
        value = str(value)
        if section not in self.changedItems[type]:
            self.changedItems[type][section] = {}
        self.changedItems[type][section][item] = value

    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)
            self.radioThemeCustom.config(state=NORMAL)
            self.optMenuThemeCustom.config(state=NORMAL)
            self.buttonDeleteCustomTheme.config(state=NORMAL)

    def SetKeysType(self):
        if self.keysAreBuiltin.get():
            self.optMenuKeysBuiltin.config(state=NORMAL)
            self.optMenuKeysCustom.config(state=DISABLED)
            self.buttonDeleteCustomKeys.config(state=DISABLED)
        else:
            self.optMenuKeysBuiltin.config(state=DISABLED)
            self.radioKeysCustom.config(state=NORMAL)
            self.optMenuKeysCustom.config(state=NORMAL)
            self.buttonDeleteCustomKeys.config(state=NORMAL)

    def GetNewKeys(self):
        listIndex = self.listBindings.index(ANCHOR)
        binding = self.listBindings.get(listIndex)
        bindName = binding.split()[0]
        if self.keysAreBuiltin.get():
            currentKeySetName = self.builtinKeys.get()
        else:
            currentKeySetName = self.customKeys.get()
        currentBindings = idleConf.GetCurrentKeySet()
        if currentKeySetName in self.changedItems['keys'].keys():
            keySetChanges = self.changedItems['keys'][currentKeySetName]
            for event in keySetChanges.keys():
                currentBindings[event] = keySetChanges[event].split()

        currentKeySequences = currentBindings.values()
        newKeys = GetKeysDialog(self, 'Get New Keys', bindName, currentKeySequences).result
        if newKeys:
            if self.keysAreBuiltin.get():
                message = 'Your changes will be saved as a new Custom Key Set. ' + 'Enter a name for your new Custom Key Set below.'
                newKeySet = self.GetNewKeysName(message)
                if not newKeySet:
                    self.listBindings.select_set(listIndex)
                    self.listBindings.select_anchor(listIndex)
                    return
                self.CreateNewKeySet(newKeySet)
            self.listBindings.delete(listIndex)
            self.listBindings.insert(listIndex, bindName + ' - ' + newKeys)
            self.listBindings.select_set(listIndex)
            self.listBindings.select_anchor(listIndex)
            self.keyBinding.set(newKeys)
        else:
            self.listBindings.select_set(listIndex)
            self.listBindings.select_anchor(listIndex)

    def GetNewKeysName(self, message):
        usedNames = idleConf.GetSectionList('user', 'keys') + idleConf.GetSectionList('default', 'keys')
        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)

    def KeyBindingSelected(self, event):
        self.buttonNewKeys.config(state=NORMAL)

    def CreateNewKeySet(self, newKeySetName):
        if self.keysAreBuiltin.get():
            prevKeySetName = self.builtinKeys.get()
        else:
            prevKeySetName = self.customKeys.get()
        prevKeys = idleConf.GetCoreKeys(prevKeySetName)
        newKeys = {}
        for event in prevKeys.keys():
            eventName = event[2:-2]
            binding = string.join(prevKeys[event])
            newKeys[eventName] = binding

        if prevKeySetName in self.changedItems['keys'].keys():
            keySetChanges = self.changedItems['keys'][prevKeySetName]
            for event in keySetChanges.keys():
                newKeys[event] = keySetChanges[event]

        self.SaveNewKeySet(newKeySetName, newKeys)
        customKeyList = idleConf.GetSectionList('user', 'keys')
        customKeyList.sort()
        self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName)
        self.keysAreBuiltin.set(0)
        self.SetKeysType()

    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])
            bindName = bindName[2:-2]
            if keySetName in self.changedItems['keys'].keys():
                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)

    def DeleteCustomKeys(self):
        keySetName = self.customKeys.get()
        if not tkMessageBox.askyesno('Delete Key Set', 'Are you sure you wish ' + 'to delete the key set %r ?' % keySetName, parent=self):
            return
        idleConf.userCfg['keys'].remove_section(keySetName)
        if keySetName in self.changedItems['keys']:
            del self.changedItems['keys'][keySetName]
        idleConf.userCfg['keys'].Save()
        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])
        self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default'))
        self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))
        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 %r ?' % (themeName,), parent=self):
            return
        idleConf.userCfg['highlight'].remove_section(themeName)
        if themeName in self.changedItems['highlight']:
            del self.changedItems['highlight'][themeName]
        idleConf.userCfg['highlight'].Save()
        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])
        self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
        self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
        self.Apply()
        self.SetThemeType()

    def GetColour(self):
        target = self.highlightTarget.get()
        prevColour = self.frameColourSet.cget('bg')
        rgbTuplet, colourString = tkColorChooser.askcolor(parent=self, title='Pick new colour for : ' + target, initialcolor=prevColour)
        if colourString and colourString != prevColour:
            if self.themeIsBuiltin.get():
                message = 'Your changes will be saved as a new Custom Theme. ' + 'Enter a name for your new Custom Theme below.'
                newTheme = self.GetNewThemeName(message)
                if not newTheme:
                    return
                self.CreateNewTheme(newTheme)
                self.colour.set(colourString)
            else:
                self.colour.set(colourString)

    def OnNewColourSet(self):
        newColour = self.colour.get()
        self.frameColourSet.config(bg=newColour)
        if self.fgHilite.get():
            plane = 'foreground'
        else:
            plane = 'background'
        sampleElement = self.themeElements[self.highlightTarget.get()][0]
        self.textHighlightSample.tag_config(sampleElement, **{plane: newColour})
        theme = self.customTheme.get()
        themeElement = sampleElement + '-' + plane
        self.AddChangedItem('highlight', theme, themeElement, newColour)

    def GetNewThemeName(self, message):
        usedNames = idleConf.GetSectionList('user', 'highlight') + idleConf.GetSectionList('default', 'highlight')
        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)

    def CreateNewTheme(self, newThemeName):
        if self.themeIsBuiltin.get():
            themeType = 'default'
            themeName = self.builtinTheme.get()
        else:
            themeType = 'user'
            themeName = self.customTheme.get()
        newTheme = idleConf.GetThemeDict(themeType, themeName)
        if themeName in self.changedItems['highlight'].keys():
            themeChanges = self.changedItems['highlight'][themeName]
            for element in themeChanges.keys():
                newTheme[element] = themeChanges[element]

        self.SaveNewTheme(newThemeName, newTheme)
        customThemeList = idleConf.GetSectionList('user', 'highlight')
        customThemeList.sort()
        self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName)
        self.themeIsBuiltin.set(0)
        self.SetThemeType()

    def OnListFontButtonRelease(self, event):
        font = self.listFontName.get(ANCHOR)
        self.fontName.set(font.lower())
        self.SetFontSample()

    def SetFontSample(self, event = None):
        fontName = self.fontName.get()
        if self.fontBold.get():
            fontWeight = tkFont.BOLD
        else:
            fontWeight = tkFont.NORMAL
        newFont = (fontName, self.fontSize.get(), fontWeight)
        self.labelFontSample.config(font=newFont)
        self.textHighlightSample.configure(font=newFont)

    def SetHighlightTarget(self):
        if self.highlightTarget.get() == 'Cursor':
            self.radioFg.config(state=DISABLED)
            self.radioBg.config(state=DISABLED)
            self.fgHilite.set(1)
        else:
            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):
        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)

    def PaintThemeSample(self):
        if self.themeIsBuiltin.get():
            theme = self.builtinTheme.get()
        else:
            theme = self.customTheme.get()
        for elementTitle in self.themeElements.keys():
            element = self.themeElements[elementTitle][0]
            colours = idleConf.GetHighlight(theme, element)
            if element == 'cursor':
                colours['background'] = idleConf.GetHighlight(theme, 'normal', fgBg='bg')
            if theme in self.changedItems['highlight'].keys():
                themeDict = self.changedItems['highlight'][theme]
                if element + '-foreground' in themeDict:
                    colours['foreground'] = themeDict[element + '-foreground']
                if element + '-background' in themeDict:
                    colours['background'] = themeDict[element + '-background']
            self.textHighlightSample.tag_config(element, **colours)

        self.SetColourSample()

    def HelpSourceSelected(self, event):
        self.SetHelpListButtonStates()

    def SetHelpListButtonStates(self):
        if self.listHelp.size() < 1:
            self.buttonHelpListEdit.config(state=DISABLED)
            self.buttonHelpListRemove.config(state=DISABLED)
        elif self.listHelp.curselection():
            self.buttonHelpListEdit.config(state=NORMAL)
            self.buttonHelpListRemove.config(state=NORMAL)
        else:
            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])
            self.UpdateUserHelpChangedItems()
        self.SetHelpListButtonStates()

    def HelpListItemEdit(self):
        itemIndex = self.listHelp.index(ANCHOR)
        helpSource = self.userHelpList[itemIndex]
        newHelpSource = GetHelpSourceDialog(self, 'Edit Help Source', menuItem=helpSource[0], filePath=helpSource[1]).result
        if not newHelpSource or newHelpSource == helpSource:
            return
        self.userHelpList[itemIndex] = newHelpSource
        self.listHelp.delete(itemIndex)
        self.listHelp.insert(itemIndex, newHelpSource[0])
        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 section in self.changedItems"""
        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][:2], ';'))

    def LoadFontCfg(self):
        fonts = list(tkFont.families(self))
        fonts.sort()
        for font in fonts:
            self.listFontName.insert(END, font)

        configuredFont = idleConf.GetOption('main', 'EditorWindow', 'font', default='courier')
        lc_configuredFont = configuredFont.lower()
        self.fontName.set(lc_configuredFont)
        lc_fonts = [ s.lower() for s in fonts ]
        if lc_configuredFont in lc_fonts:
            currentFontIndex = lc_fonts.index(lc_configuredFont)
            self.listFontName.see(currentFontIndex)
            self.listFontName.select_set(currentFontIndex)
            self.listFontName.select_anchor(currentFontIndex)
        fontSize = idleConf.GetOption('main', 'EditorWindow', 'font-size', type='int', default='10')
        self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14', '16', '18', '20', '22'), fontSize)
        self.fontBold.set(idleConf.GetOption('main', 'EditorWindow', 'font-bold', default=0, type='bool'))
        self.SetFontSample()

    def LoadTabCfg(self):
        spaceNum = idleConf.GetOption('main', 'Indent', 'num-spaces', default=4, type='int')
        self.spaceNum.set(spaceNum)

    def LoadThemeCfg(self):
        self.themeIsBuiltin.set(idleConf.GetOption('main', 'Theme', 'default', type='bool', default=1))
        currentOption = idleConf.CurrentTheme()
        if self.themeIsBuiltin.get():
            itemList = idleConf.GetSectionList('default', 'highlight')
            itemList.sort()
            self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
            itemList = idleConf.GetSectionList('user', 'highlight')
            itemList.sort()
            if not itemList:
                self.radioThemeCustom.config(state=DISABLED)
                self.customTheme.set('- no custom themes -')
            else:
                self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
        else:
            itemList = idleConf.GetSectionList('user', 'highlight')
            itemList.sort()
            self.optMenuThemeCustom.SetMenu(itemList, currentOption)
            itemList = idleConf.GetSectionList('default', 'highlight')
            itemList.sort()
            self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
        self.SetThemeType()
        themeNames = self.themeElements.keys()
        themeNames.sort(key=lambda x: self.themeElements[x][1])
        self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
        self.PaintThemeSample()
        self.SetHighlightTarget()

    def LoadKeyCfg(self):
        self.keysAreBuiltin.set(idleConf.GetOption('main', 'Keys', 'default', type='bool', default=1))
        currentOption = idleConf.CurrentKeys()
        if self.keysAreBuiltin.get():
            itemList = idleConf.GetSectionList('default', 'keys')
            itemList.sort()
            self.optMenuKeysBuiltin.SetMenu(itemList, currentOption)
            itemList = idleConf.GetSectionList('user', 'keys')
            itemList.sort()
            if not itemList:
                self.radioKeysCustom.config(state=DISABLED)
                self.customKeys.set('- no custom keys -')
            else:
                self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
        else:
            itemList = idleConf.GetSectionList('user', 'keys')
            itemList.sort()
            self.optMenuKeysCustom.SetMenu(itemList, currentOption)
            itemList = idleConf.GetSectionList('default', 'keys')
            itemList.sort()
            self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])
        self.SetKeysType()
        keySetName = idleConf.CurrentKeys()
        self.LoadKeysList(keySetName)

    def LoadGeneralCfg(self):
        self.startupEdit.set(idleConf.GetOption('main', 'General', 'editor-on-startup', default=1, type='bool'))
        self.autoSave.set(idleConf.GetOption('main', 'General', 'autosave', default=0, type='bool'))
        self.winWidth.set(idleConf.GetOption('main', 'EditorWindow', 'width', type='int'))
        self.winHeight.set(idleConf.GetOption('main', 'EditorWindow', 'height', type='int'))
        self.paraWidth.set(idleConf.GetOption('main', 'FormatParagraph', 'paragraph', type='int'))
        self.encoding.set(idleConf.GetOption('main', 'EditorWindow', 'encoding', default='none'))
        self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
        for helpItem in self.userHelpList:
            self.listHelp.insert(END, helpItem[0])

        self.SetHelpListButtonStates()

    def LoadConfigs(self):
        """
        load configuration from default and user config files and populate
        the widgets on the config dialog pages.
        """
        self.LoadFontCfg()
        self.LoadTabCfg()
        self.LoadThemeCfg()
        self.LoadKeyCfg()
        self.LoadGeneralCfg()

    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)

    def SetUserValue(self, configType, section, item, value):
        if idleConf.defaultCfg[configType].has_option(section, item):
            if idleConf.defaultCfg[configType].Get(section, item) == value:
                return idleConf.userCfg[configType].RemoveOption(section, item)
        return idleConf.userCfg[configType].SetOption(section, item, value)

    def SaveAllChangedConfigs(self):
        """Save configuration changes to the user config file."""
        idleConf.userCfg['main'].Save()
        for configType in self.changedItems.keys():
            cfgTypeHasChanges = False
            for section in self.changedItems[configType].keys():
                if section == 'HelpFiles':
                    idleConf.userCfg['main'].remove_section('HelpFiles')
                    cfgTypeHasChanges = True
                for item in self.changedItems[configType][section].keys():
                    value = self.changedItems[configType][section][item]
                    if self.SetUserValue(configType, section, item, value):
                        cfgTypeHasChanges = True

            if cfgTypeHasChanges:
                idleConf.userCfg[configType].Save()

        for configType in ['keys', 'highlight']:
            idleConf.userCfg[configType].Save()

        self.ResetChangedItems()

    def DeactivateCurrentConfig(self):
        winInstances = self.parent.instance_dict.keys()
        for instance in winInstances:
            instance.RemoveKeybindings()

    def ActivateConfigChanges(self):
        """Dynamically apply configuration changes"""
        winInstances = self.parent.instance_dict.keys()
        for instance in winInstances:
            instance.ResetColorizer()
            instance.ResetFont()
            instance.set_notabs_indentwidth()
            instance.ApplyKeybindings()
            instance.reset_help_menu_entries()

    def Cancel(self):
        self.destroy()

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

    def Apply(self):
        self.DeactivateCurrentConfig()
        self.SaveAllChangedConfigs()
        self.ActivateConfigChanges()

    def Help(self):
        pass