python-2.5.2/win32/Lib/idlelib/configHandler.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """Provides access to stored IDLE configuration information.
       
     2 
       
     3 Refer to the comments at the beginning of config-main.def for a description of
       
     4 the available configuration files and the design implemented to update user
       
     5 configuration information.  In particular, user configuration choices which
       
     6 duplicate the defaults will be removed from the user's configuration files,
       
     7 and if a file becomes empty, it will be deleted.
       
     8 
       
     9 The contents of the user files may be altered using the Options/Configure IDLE
       
    10 menu to access the configuration GUI (configDialog.py), or manually.
       
    11 
       
    12 Throughout this module there is an emphasis on returning useable defaults
       
    13 when a problem occurs in returning a requested configuration value back to
       
    14 idle. This is to allow IDLE to continue to function in spite of errors in
       
    15 the retrieval of config information. When a default is returned instead of
       
    16 a requested config value, a message is printed to stderr to aid in
       
    17 configuration problem notification and resolution.
       
    18 
       
    19 """
       
    20 import os
       
    21 import sys
       
    22 import string
       
    23 import macosxSupport
       
    24 from ConfigParser import ConfigParser, NoOptionError, NoSectionError
       
    25 
       
    26 class InvalidConfigType(Exception): pass
       
    27 class InvalidConfigSet(Exception): pass
       
    28 class InvalidFgBg(Exception): pass
       
    29 class InvalidTheme(Exception): pass
       
    30 
       
    31 class IdleConfParser(ConfigParser):
       
    32     """
       
    33     A ConfigParser specialised for idle configuration file handling
       
    34     """
       
    35     def __init__(self, cfgFile, cfgDefaults=None):
       
    36         """
       
    37         cfgFile - string, fully specified configuration file name
       
    38         """
       
    39         self.file=cfgFile
       
    40         ConfigParser.__init__(self,defaults=cfgDefaults)
       
    41 
       
    42     def Get(self, section, option, type=None, default=None):
       
    43         """
       
    44         Get an option value for given section/option or return default.
       
    45         If type is specified, return as type.
       
    46         """
       
    47         if type=='bool':
       
    48             getVal=self.getboolean
       
    49         elif type=='int':
       
    50             getVal=self.getint
       
    51         else:
       
    52             getVal=self.get
       
    53         if self.has_option(section,option):
       
    54             #return getVal(section, option, raw, vars, default)
       
    55             return getVal(section, option)
       
    56         else:
       
    57             return default
       
    58 
       
    59     def GetOptionList(self,section):
       
    60         """
       
    61         Get an option list for given section
       
    62         """
       
    63         if self.has_section(section):
       
    64             return self.options(section)
       
    65         else:  #return a default value
       
    66             return []
       
    67 
       
    68     def Load(self):
       
    69         """
       
    70         Load the configuration file from disk
       
    71         """
       
    72         self.read(self.file)
       
    73 
       
    74 class IdleUserConfParser(IdleConfParser):
       
    75     """
       
    76     IdleConfigParser specialised for user configuration handling.
       
    77     """
       
    78 
       
    79     def AddSection(self,section):
       
    80         """
       
    81         if section doesn't exist, add it
       
    82         """
       
    83         if not self.has_section(section):
       
    84             self.add_section(section)
       
    85 
       
    86     def RemoveEmptySections(self):
       
    87         """
       
    88         remove any sections that have no options
       
    89         """
       
    90         for section in self.sections():
       
    91             if not self.GetOptionList(section):
       
    92                 self.remove_section(section)
       
    93 
       
    94     def IsEmpty(self):
       
    95         """
       
    96         Remove empty sections and then return 1 if parser has no sections
       
    97         left, else return 0.
       
    98         """
       
    99         self.RemoveEmptySections()
       
   100         if self.sections():
       
   101             return 0
       
   102         else:
       
   103             return 1
       
   104 
       
   105     def RemoveOption(self,section,option):
       
   106         """
       
   107         If section/option exists, remove it.
       
   108         Returns 1 if option was removed, 0 otherwise.
       
   109         """
       
   110         if self.has_section(section):
       
   111             return self.remove_option(section,option)
       
   112 
       
   113     def SetOption(self,section,option,value):
       
   114         """
       
   115         Sets option to value, adding section if required.
       
   116         Returns 1 if option was added or changed, otherwise 0.
       
   117         """
       
   118         if self.has_option(section,option):
       
   119             if self.get(section,option)==value:
       
   120                 return 0
       
   121             else:
       
   122                 self.set(section,option,value)
       
   123                 return 1
       
   124         else:
       
   125             if not self.has_section(section):
       
   126                 self.add_section(section)
       
   127             self.set(section,option,value)
       
   128             return 1
       
   129 
       
   130     def RemoveFile(self):
       
   131         """
       
   132         Removes the user config file from disk if it exists.
       
   133         """
       
   134         if os.path.exists(self.file):
       
   135             os.remove(self.file)
       
   136 
       
   137     def Save(self):
       
   138         """Update user configuration file.
       
   139 
       
   140         Remove empty sections. If resulting config isn't empty, write the file
       
   141         to disk. If config is empty, remove the file from disk if it exists.
       
   142 
       
   143         """
       
   144         if not self.IsEmpty():
       
   145             fname = self.file
       
   146             try:
       
   147                 cfgFile = open(fname, 'w')
       
   148             except IOError:
       
   149                 os.unlink(fname)
       
   150                 cfgFile = open(fname, 'w')
       
   151             self.write(cfgFile)
       
   152         else:
       
   153             self.RemoveFile()
       
   154 
       
   155 class IdleConf:
       
   156     """
       
   157     holds config parsers for all idle config files:
       
   158     default config files
       
   159         (idle install dir)/config-main.def
       
   160         (idle install dir)/config-extensions.def
       
   161         (idle install dir)/config-highlight.def
       
   162         (idle install dir)/config-keys.def
       
   163     user config  files
       
   164         (user home dir)/.idlerc/config-main.cfg
       
   165         (user home dir)/.idlerc/config-extensions.cfg
       
   166         (user home dir)/.idlerc/config-highlight.cfg
       
   167         (user home dir)/.idlerc/config-keys.cfg
       
   168     """
       
   169     def __init__(self):
       
   170         self.defaultCfg={}
       
   171         self.userCfg={}
       
   172         self.cfg={}
       
   173         self.CreateConfigHandlers()
       
   174         self.LoadCfgFiles()
       
   175         #self.LoadCfg()
       
   176 
       
   177     def CreateConfigHandlers(self):
       
   178         """
       
   179         set up a dictionary of config parsers for default and user
       
   180         configurations respectively
       
   181         """
       
   182         #build idle install path
       
   183         if __name__ != '__main__': # we were imported
       
   184             idleDir=os.path.dirname(__file__)
       
   185         else: # we were exec'ed (for testing only)
       
   186             idleDir=os.path.abspath(sys.path[0])
       
   187         userDir=self.GetUserCfgDir()
       
   188         configTypes=('main','extensions','highlight','keys')
       
   189         defCfgFiles={}
       
   190         usrCfgFiles={}
       
   191         for cfgType in configTypes: #build config file names
       
   192             defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
       
   193             usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
       
   194         for cfgType in configTypes: #create config parsers
       
   195             self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
       
   196             self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
       
   197 
       
   198     def GetUserCfgDir(self):
       
   199         """
       
   200         Creates (if required) and returns a filesystem directory for storing
       
   201         user config files.
       
   202 
       
   203         """
       
   204         cfgDir = '.idlerc'
       
   205         userDir = os.path.expanduser('~')
       
   206         if userDir != '~': # expanduser() found user home dir
       
   207             if not os.path.exists(userDir):
       
   208                 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
       
   209                         userDir+',\n but the path does not exist.\n')
       
   210                 sys.stderr.write(warn)
       
   211                 userDir = '~'
       
   212         if userDir == "~": # still no path to home!
       
   213             # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
       
   214             userDir = os.getcwd()
       
   215         userDir = os.path.join(userDir, cfgDir)
       
   216         if not os.path.exists(userDir):
       
   217             try:
       
   218                 os.mkdir(userDir)
       
   219             except (OSError, IOError):
       
   220                 warn = ('\n Warning: unable to create user config directory\n'+
       
   221                         userDir+'\n Check path and permissions.\n Exiting!\n\n')
       
   222                 sys.stderr.write(warn)
       
   223                 raise SystemExit
       
   224         return userDir
       
   225 
       
   226     def GetOption(self, configType, section, option, default=None, type=None,
       
   227                   warn_on_default=True):
       
   228         """
       
   229         Get an option value for given config type and given general
       
   230         configuration section/option or return a default. If type is specified,
       
   231         return as type. Firstly the user configuration is checked, with a
       
   232         fallback to the default configuration, and a final 'catch all'
       
   233         fallback to a useable passed-in default if the option isn't present in
       
   234         either the user or the default configuration.
       
   235         configType must be one of ('main','extensions','highlight','keys')
       
   236         If a default is returned, and warn_on_default is True, a warning is
       
   237         printed to stderr.
       
   238 
       
   239         """
       
   240         if self.userCfg[configType].has_option(section,option):
       
   241             return self.userCfg[configType].Get(section, option, type=type)
       
   242         elif self.defaultCfg[configType].has_option(section,option):
       
   243             return self.defaultCfg[configType].Get(section, option, type=type)
       
   244         else: #returning default, print warning
       
   245             if warn_on_default:
       
   246                 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
       
   247                            ' problem retrieving configration option %r\n'
       
   248                            ' from section %r.\n'
       
   249                            ' returning default value: %r\n' %
       
   250                            (option, section, default))
       
   251                 sys.stderr.write(warning)
       
   252             return default
       
   253 
       
   254     def SetOption(self, configType, section, option, value):
       
   255         """In user's config file, set section's option to value.
       
   256 
       
   257         """
       
   258         self.userCfg[configType].SetOption(section, option, value)
       
   259 
       
   260     def GetSectionList(self, configSet, configType):
       
   261         """
       
   262         Get a list of sections from either the user or default config for
       
   263         the given config type.
       
   264         configSet must be either 'user' or 'default'
       
   265         configType must be one of ('main','extensions','highlight','keys')
       
   266         """
       
   267         if not (configType in ('main','extensions','highlight','keys')):
       
   268             raise InvalidConfigType, 'Invalid configType specified'
       
   269         if configSet == 'user':
       
   270             cfgParser=self.userCfg[configType]
       
   271         elif configSet == 'default':
       
   272             cfgParser=self.defaultCfg[configType]
       
   273         else:
       
   274             raise InvalidConfigSet, 'Invalid configSet specified'
       
   275         return cfgParser.sections()
       
   276 
       
   277     def GetHighlight(self, theme, element, fgBg=None):
       
   278         """
       
   279         return individual highlighting theme elements.
       
   280         fgBg - string ('fg'or'bg') or None, if None return a dictionary
       
   281         containing fg and bg colours (appropriate for passing to Tkinter in,
       
   282         e.g., a tag_config call), otherwise fg or bg colour only as specified.
       
   283         """
       
   284         if self.defaultCfg['highlight'].has_section(theme):
       
   285             themeDict=self.GetThemeDict('default',theme)
       
   286         else:
       
   287             themeDict=self.GetThemeDict('user',theme)
       
   288         fore=themeDict[element+'-foreground']
       
   289         if element=='cursor': #there is no config value for cursor bg
       
   290             back=themeDict['normal-background']
       
   291         else:
       
   292             back=themeDict[element+'-background']
       
   293         highlight={"foreground": fore,"background": back}
       
   294         if not fgBg: #return dict of both colours
       
   295             return highlight
       
   296         else: #return specified colour only
       
   297             if fgBg == 'fg':
       
   298                 return highlight["foreground"]
       
   299             if fgBg == 'bg':
       
   300                 return highlight["background"]
       
   301             else:
       
   302                 raise InvalidFgBg, 'Invalid fgBg specified'
       
   303 
       
   304     def GetThemeDict(self,type,themeName):
       
   305         """
       
   306         type - string, 'default' or 'user' theme type
       
   307         themeName - string, theme name
       
   308         Returns a dictionary which holds {option:value} for each element
       
   309         in the specified theme. Values are loaded over a set of ultimate last
       
   310         fallback defaults to guarantee that all theme elements are present in
       
   311         a newly created theme.
       
   312         """
       
   313         if type == 'user':
       
   314             cfgParser=self.userCfg['highlight']
       
   315         elif type == 'default':
       
   316             cfgParser=self.defaultCfg['highlight']
       
   317         else:
       
   318             raise InvalidTheme, 'Invalid theme type specified'
       
   319         #foreground and background values are provded for each theme element
       
   320         #(apart from cursor) even though all these values are not yet used
       
   321         #by idle, to allow for their use in the future. Default values are
       
   322         #generally black and white.
       
   323         theme={ 'normal-foreground':'#000000',
       
   324                 'normal-background':'#ffffff',
       
   325                 'keyword-foreground':'#000000',
       
   326                 'keyword-background':'#ffffff',
       
   327                 'builtin-foreground':'#000000',
       
   328                 'builtin-background':'#ffffff',
       
   329                 'comment-foreground':'#000000',
       
   330                 'comment-background':'#ffffff',
       
   331                 'string-foreground':'#000000',
       
   332                 'string-background':'#ffffff',
       
   333                 'definition-foreground':'#000000',
       
   334                 'definition-background':'#ffffff',
       
   335                 'hilite-foreground':'#000000',
       
   336                 'hilite-background':'gray',
       
   337                 'break-foreground':'#ffffff',
       
   338                 'break-background':'#000000',
       
   339                 'hit-foreground':'#ffffff',
       
   340                 'hit-background':'#000000',
       
   341                 'error-foreground':'#ffffff',
       
   342                 'error-background':'#000000',
       
   343                 #cursor (only foreground can be set)
       
   344                 'cursor-foreground':'#000000',
       
   345                 #shell window
       
   346                 'stdout-foreground':'#000000',
       
   347                 'stdout-background':'#ffffff',
       
   348                 'stderr-foreground':'#000000',
       
   349                 'stderr-background':'#ffffff',
       
   350                 'console-foreground':'#000000',
       
   351                 'console-background':'#ffffff' }
       
   352         for element in theme.keys():
       
   353             if not cfgParser.has_option(themeName,element):
       
   354                 #we are going to return a default, print warning
       
   355                 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
       
   356                            ' -\n problem retrieving theme element %r'
       
   357                            '\n from theme %r.\n'
       
   358                            ' returning default value: %r\n' %
       
   359                            (element, themeName, theme[element]))
       
   360                 sys.stderr.write(warning)
       
   361             colour=cfgParser.Get(themeName,element,default=theme[element])
       
   362             theme[element]=colour
       
   363         return theme
       
   364 
       
   365     def CurrentTheme(self):
       
   366         """
       
   367         Returns the name of the currently active theme
       
   368         """
       
   369         return self.GetOption('main','Theme','name',default='')
       
   370 
       
   371     def CurrentKeys(self):
       
   372         """
       
   373         Returns the name of the currently active key set
       
   374         """
       
   375         return self.GetOption('main','Keys','name',default='')
       
   376 
       
   377     def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
       
   378         """
       
   379         Gets a list of all idle extensions declared in the config files.
       
   380         active_only - boolean, if true only return active (enabled) extensions
       
   381         """
       
   382         extns=self.RemoveKeyBindNames(
       
   383                 self.GetSectionList('default','extensions'))
       
   384         userExtns=self.RemoveKeyBindNames(
       
   385                 self.GetSectionList('user','extensions'))
       
   386         for extn in userExtns:
       
   387             if extn not in extns: #user has added own extension
       
   388                 extns.append(extn)
       
   389         if active_only:
       
   390             activeExtns=[]
       
   391             for extn in extns:
       
   392                 if self.GetOption('extensions', extn, 'enable', default=True,
       
   393                                   type='bool'):
       
   394                     #the extension is enabled
       
   395                     if editor_only or shell_only:
       
   396                         if editor_only:
       
   397                             option = "enable_editor"
       
   398                         else:
       
   399                             option = "enable_shell"
       
   400                         if self.GetOption('extensions', extn,option,
       
   401                                           default=True, type='bool',
       
   402                                           warn_on_default=False):
       
   403                             activeExtns.append(extn)
       
   404                     else:
       
   405                         activeExtns.append(extn)
       
   406             return activeExtns
       
   407         else:
       
   408             return extns
       
   409 
       
   410     def RemoveKeyBindNames(self,extnNameList):
       
   411         #get rid of keybinding section names
       
   412         names=extnNameList
       
   413         kbNameIndicies=[]
       
   414         for name in names:
       
   415             if name.endswith(('_bindings', '_cfgBindings')):
       
   416                 kbNameIndicies.append(names.index(name))
       
   417         kbNameIndicies.sort()
       
   418         kbNameIndicies.reverse()
       
   419         for index in kbNameIndicies: #delete each keybinding section name
       
   420             del(names[index])
       
   421         return names
       
   422 
       
   423     def GetExtnNameForEvent(self,virtualEvent):
       
   424         """
       
   425         Returns the name of the extension that virtualEvent is bound in, or
       
   426         None if not bound in any extension.
       
   427         virtualEvent - string, name of the virtual event to test for, without
       
   428                        the enclosing '<< >>'
       
   429         """
       
   430         extName=None
       
   431         vEvent='<<'+virtualEvent+'>>'
       
   432         for extn in self.GetExtensions(active_only=0):
       
   433             for event in self.GetExtensionKeys(extn).keys():
       
   434                 if event == vEvent:
       
   435                     extName=extn
       
   436         return extName
       
   437 
       
   438     def GetExtensionKeys(self,extensionName):
       
   439         """
       
   440         returns a dictionary of the configurable keybindings for a particular
       
   441         extension,as they exist in the dictionary returned by GetCurrentKeySet;
       
   442         that is, where previously used bindings are disabled.
       
   443         """
       
   444         keysName=extensionName+'_cfgBindings'
       
   445         activeKeys=self.GetCurrentKeySet()
       
   446         extKeys={}
       
   447         if self.defaultCfg['extensions'].has_section(keysName):
       
   448             eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
       
   449             for eventName in eventNames:
       
   450                 event='<<'+eventName+'>>'
       
   451                 binding=activeKeys[event]
       
   452                 extKeys[event]=binding
       
   453         return extKeys
       
   454 
       
   455     def __GetRawExtensionKeys(self,extensionName):
       
   456         """
       
   457         returns a dictionary of the configurable keybindings for a particular
       
   458         extension, as defined in the configuration files, or an empty dictionary
       
   459         if no bindings are found
       
   460         """
       
   461         keysName=extensionName+'_cfgBindings'
       
   462         extKeys={}
       
   463         if self.defaultCfg['extensions'].has_section(keysName):
       
   464             eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
       
   465             for eventName in eventNames:
       
   466                 binding=self.GetOption('extensions',keysName,
       
   467                         eventName,default='').split()
       
   468                 event='<<'+eventName+'>>'
       
   469                 extKeys[event]=binding
       
   470         return extKeys
       
   471 
       
   472     def GetExtensionBindings(self,extensionName):
       
   473         """
       
   474         Returns a dictionary of all the event bindings for a particular
       
   475         extension. The configurable keybindings are returned as they exist in
       
   476         the dictionary returned by GetCurrentKeySet; that is, where re-used
       
   477         keybindings are disabled.
       
   478         """
       
   479         bindsName=extensionName+'_bindings'
       
   480         extBinds=self.GetExtensionKeys(extensionName)
       
   481         #add the non-configurable bindings
       
   482         if self.defaultCfg['extensions'].has_section(bindsName):
       
   483             eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
       
   484             for eventName in eventNames:
       
   485                 binding=self.GetOption('extensions',bindsName,
       
   486                         eventName,default='').split()
       
   487                 event='<<'+eventName+'>>'
       
   488                 extBinds[event]=binding
       
   489 
       
   490         return extBinds
       
   491 
       
   492     def GetKeyBinding(self, keySetName, eventStr):
       
   493         """
       
   494         returns the keybinding for a specific event.
       
   495         keySetName - string, name of key binding set
       
   496         eventStr - string, the virtual event we want the binding for,
       
   497                    represented as a string, eg. '<<event>>'
       
   498         """
       
   499         eventName=eventStr[2:-2] #trim off the angle brackets
       
   500         binding=self.GetOption('keys',keySetName,eventName,default='').split()
       
   501         return binding
       
   502 
       
   503     def GetCurrentKeySet(self):
       
   504         result = self.GetKeySet(self.CurrentKeys())
       
   505 
       
   506         if macosxSupport.runningAsOSXApp():
       
   507             # We're using AquaTk, replace all keybingings that use the
       
   508             # Alt key by ones that use the Option key because the former
       
   509             # don't work reliably.
       
   510             for k, v in result.items():
       
   511                 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
       
   512                 if v != v2:
       
   513                     result[k] = v2
       
   514 
       
   515         return result
       
   516 
       
   517     def GetKeySet(self,keySetName):
       
   518         """
       
   519         Returns a dictionary of: all requested core keybindings, plus the
       
   520         keybindings for all currently active extensions. If a binding defined
       
   521         in an extension is already in use, that binding is disabled.
       
   522         """
       
   523         keySet=self.GetCoreKeys(keySetName)
       
   524         activeExtns=self.GetExtensions(active_only=1)
       
   525         for extn in activeExtns:
       
   526             extKeys=self.__GetRawExtensionKeys(extn)
       
   527             if extKeys: #the extension defines keybindings
       
   528                 for event in extKeys.keys():
       
   529                     if extKeys[event] in keySet.values():
       
   530                         #the binding is already in use
       
   531                         extKeys[event]='' #disable this binding
       
   532                     keySet[event]=extKeys[event] #add binding
       
   533         return keySet
       
   534 
       
   535     def IsCoreBinding(self,virtualEvent):
       
   536         """
       
   537         returns true if the virtual event is bound in the core idle keybindings.
       
   538         virtualEvent - string, name of the virtual event to test for, without
       
   539                        the enclosing '<< >>'
       
   540         """
       
   541         return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
       
   542 
       
   543     def GetCoreKeys(self, keySetName=None):
       
   544         """
       
   545         returns the requested set of core keybindings, with fallbacks if
       
   546         required.
       
   547         Keybindings loaded from the config file(s) are loaded _over_ these
       
   548         defaults, so if there is a problem getting any core binding there will
       
   549         be an 'ultimate last resort fallback' to the CUA-ish bindings
       
   550         defined here.
       
   551         """
       
   552         keyBindings={
       
   553             '<<copy>>': ['<Control-c>', '<Control-C>'],
       
   554             '<<cut>>': ['<Control-x>', '<Control-X>'],
       
   555             '<<paste>>': ['<Control-v>', '<Control-V>'],
       
   556             '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
       
   557             '<<center-insert>>': ['<Control-l>'],
       
   558             '<<close-all-windows>>': ['<Control-q>'],
       
   559             '<<close-window>>': ['<Alt-F4>'],
       
   560             '<<do-nothing>>': ['<Control-x>'],
       
   561             '<<end-of-file>>': ['<Control-d>'],
       
   562             '<<python-docs>>': ['<F1>'],
       
   563             '<<python-context-help>>': ['<Shift-F1>'],
       
   564             '<<history-next>>': ['<Alt-n>'],
       
   565             '<<history-previous>>': ['<Alt-p>'],
       
   566             '<<interrupt-execution>>': ['<Control-c>'],
       
   567             '<<view-restart>>': ['<F6>'],
       
   568             '<<restart-shell>>': ['<Control-F6>'],
       
   569             '<<open-class-browser>>': ['<Alt-c>'],
       
   570             '<<open-module>>': ['<Alt-m>'],
       
   571             '<<open-new-window>>': ['<Control-n>'],
       
   572             '<<open-window-from-file>>': ['<Control-o>'],
       
   573             '<<plain-newline-and-indent>>': ['<Control-j>'],
       
   574             '<<print-window>>': ['<Control-p>'],
       
   575             '<<redo>>': ['<Control-y>'],
       
   576             '<<remove-selection>>': ['<Escape>'],
       
   577             '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
       
   578             '<<save-window-as-file>>': ['<Alt-s>'],
       
   579             '<<save-window>>': ['<Control-s>'],
       
   580             '<<select-all>>': ['<Alt-a>'],
       
   581             '<<toggle-auto-coloring>>': ['<Control-slash>'],
       
   582             '<<undo>>': ['<Control-z>'],
       
   583             '<<find-again>>': ['<Control-g>', '<F3>'],
       
   584             '<<find-in-files>>': ['<Alt-F3>'],
       
   585             '<<find-selection>>': ['<Control-F3>'],
       
   586             '<<find>>': ['<Control-f>'],
       
   587             '<<replace>>': ['<Control-h>'],
       
   588             '<<goto-line>>': ['<Alt-g>'],
       
   589             '<<smart-backspace>>': ['<Key-BackSpace>'],
       
   590             '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
       
   591             '<<smart-indent>>': ['<Key-Tab>'],
       
   592             '<<indent-region>>': ['<Control-Key-bracketright>'],
       
   593             '<<dedent-region>>': ['<Control-Key-bracketleft>'],
       
   594             '<<comment-region>>': ['<Alt-Key-3>'],
       
   595             '<<uncomment-region>>': ['<Alt-Key-4>'],
       
   596             '<<tabify-region>>': ['<Alt-Key-5>'],
       
   597             '<<untabify-region>>': ['<Alt-Key-6>'],
       
   598             '<<toggle-tabs>>': ['<Alt-Key-t>'],
       
   599             '<<change-indentwidth>>': ['<Alt-Key-u>'],
       
   600             '<<del-word-left>>': ['<Control-Key-BackSpace>'],
       
   601             '<<del-word-right>>': ['<Control-Key-Delete>']
       
   602             }
       
   603         if keySetName:
       
   604             for event in keyBindings.keys():
       
   605                 binding=self.GetKeyBinding(keySetName,event)
       
   606                 if binding:
       
   607                     keyBindings[event]=binding
       
   608                 else: #we are going to return a default, print warning
       
   609                     warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
       
   610                                ' -\n problem retrieving key binding for event %r'
       
   611                                '\n from key set %r.\n'
       
   612                                ' returning default value: %r\n' %
       
   613                                (event, keySetName, keyBindings[event]))
       
   614                     sys.stderr.write(warning)
       
   615         return keyBindings
       
   616 
       
   617     def GetExtraHelpSourceList(self,configSet):
       
   618         """Fetch list of extra help sources from a given configSet.
       
   619 
       
   620         Valid configSets are 'user' or 'default'.  Return a list of tuples of
       
   621         the form (menu_item , path_to_help_file , option), or return the empty
       
   622         list.  'option' is the sequence number of the help resource.  'option'
       
   623         values determine the position of the menu items on the Help menu,
       
   624         therefore the returned list must be sorted by 'option'.
       
   625 
       
   626         """
       
   627         helpSources=[]
       
   628         if configSet=='user':
       
   629             cfgParser=self.userCfg['main']
       
   630         elif configSet=='default':
       
   631             cfgParser=self.defaultCfg['main']
       
   632         else:
       
   633             raise InvalidConfigSet, 'Invalid configSet specified'
       
   634         options=cfgParser.GetOptionList('HelpFiles')
       
   635         for option in options:
       
   636             value=cfgParser.Get('HelpFiles',option,default=';')
       
   637             if value.find(';')==-1: #malformed config entry with no ';'
       
   638                 menuItem='' #make these empty
       
   639                 helpPath='' #so value won't be added to list
       
   640             else: #config entry contains ';' as expected
       
   641                 value=string.split(value,';')
       
   642                 menuItem=value[0].strip()
       
   643                 helpPath=value[1].strip()
       
   644             if menuItem and helpPath: #neither are empty strings
       
   645                 helpSources.append( (menuItem,helpPath,option) )
       
   646         helpSources.sort(self.__helpsort)
       
   647         return helpSources
       
   648 
       
   649     def __helpsort(self, h1, h2):
       
   650         if int(h1[2]) < int(h2[2]):
       
   651             return -1
       
   652         elif int(h1[2]) > int(h2[2]):
       
   653             return 1
       
   654         else:
       
   655             return 0
       
   656 
       
   657     def GetAllExtraHelpSourcesList(self):
       
   658         """
       
   659         Returns a list of tuples containing the details of all additional help
       
   660         sources configured, or an empty list if there are none. Tuples are of
       
   661         the format returned by GetExtraHelpSourceList.
       
   662         """
       
   663         allHelpSources=( self.GetExtraHelpSourceList('default')+
       
   664                 self.GetExtraHelpSourceList('user') )
       
   665         return allHelpSources
       
   666 
       
   667     def LoadCfgFiles(self):
       
   668         """
       
   669         load all configuration files.
       
   670         """
       
   671         for key in self.defaultCfg.keys():
       
   672             self.defaultCfg[key].Load()
       
   673             self.userCfg[key].Load() #same keys
       
   674 
       
   675     def SaveUserCfgFiles(self):
       
   676         """
       
   677         write all loaded user configuration files back to disk
       
   678         """
       
   679         for key in self.userCfg.keys():
       
   680             self.userCfg[key].Save()
       
   681 
       
   682 idleConf=IdleConf()
       
   683 
       
   684 ### module test
       
   685 if __name__ == '__main__':
       
   686     def dumpCfg(cfg):
       
   687         print '\n',cfg,'\n'
       
   688         for key in cfg.keys():
       
   689             sections=cfg[key].sections()
       
   690             print key
       
   691             print sections
       
   692             for section in sections:
       
   693                 options=cfg[key].options(section)
       
   694                 print section
       
   695                 print options
       
   696                 for option in options:
       
   697                     print option, '=', cfg[key].Get(section,option)
       
   698     dumpCfg(idleConf.defaultCfg)
       
   699     dumpCfg(idleConf.userCfg)
       
   700     print idleConf.userCfg['main'].Get('Theme','name')
       
   701     #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')