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