home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / configDialog.py < prev    next >
Text File  |  2003-12-30  |  55KB  |  1,184 lines

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