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