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