blob: d1c2b3c5f849c550312a7517411d71e2200e4fa3 [file] [log] [blame]
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001"""Provides access to stored IDLE configuration information.
Steven M. Gavac5976402002-01-04 03:06:08 +00002
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00003Refer to the comments at the beginning of config-main.def for a description of
4the available configuration files and the design implemented to update user
5configuration information. In particular, user configuration choices which
6duplicate the defaults will be removed from the user's configuration files,
Kurt B. Kaisere66675b2003-01-27 02:36:18 +00007and if a file becomes empty, it will be deleted.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00008
9The contents of the user files may be altered using the Options/Configure IDLE
10menu to access the configuration GUI (configDialog.py), or manually.
11
12Throughout this module there is an emphasis on returning useable defaults
13when a problem occurs in returning a requested configuration value back to
14idle. This is to allow IDLE to continue to function in spite of errors in
15the retrieval of config information. When a default is returned instead of
16a requested config value, a message is printed to stderr to aid in
17configuration problem notification and resolution.
18
19"""
20import os
21import sys
22import string
Steven M. Gavac11ccf32001-09-24 09:43:17 +000023from ConfigParser import ConfigParser, NoOptionError, NoSectionError
24
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000025class InvalidConfigType(Exception): pass
26class InvalidConfigSet(Exception): pass
27class InvalidFgBg(Exception): pass
28class InvalidTheme(Exception): pass
29
Steven M. Gavac11ccf32001-09-24 09:43:17 +000030class IdleConfParser(ConfigParser):
31 """
32 A ConfigParser specialised for idle configuration file handling
33 """
34 def __init__(self, cfgFile, cfgDefaults=None):
35 """
36 cfgFile - string, fully specified configuration file name
37 """
38 self.file=cfgFile
39 ConfigParser.__init__(self,defaults=cfgDefaults)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000040
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000041 def Get(self, section, option, type=None, default=None):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000042 """
43 Get an option value for given section/option or return default.
44 If type is specified, return as type.
45 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000046 if type=='bool':
Steven M. Gava41a85322001-10-29 08:05:34 +000047 getVal=self.getboolean
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000048 elif type=='int':
Steven M. Gava41a85322001-10-29 08:05:34 +000049 getVal=self.getint
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000050 else:
Steven M. Gava41a85322001-10-29 08:05:34 +000051 getVal=self.get
Steven M. Gavac11ccf32001-09-24 09:43:17 +000052 if self.has_option(section,option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000053 #return getVal(section, option, raw, vars, default)
Steven M. Gava429a86a2001-10-23 10:42:12 +000054 return getVal(section, option)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000055 else:
56 return default
Steven M. Gavac11ccf32001-09-24 09:43:17 +000057
Steven M. Gavac11ccf32001-09-24 09:43:17 +000058 def GetOptionList(self,section):
59 """
60 Get an option list for given section
61 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +000062 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000063 return self.options(section)
64 else: #return a default value
65 return []
66
Steven M. Gavac11ccf32001-09-24 09:43:17 +000067 def Load(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000068 """
69 Load the configuration file from disk
Steven M. Gavac11ccf32001-09-24 09:43:17 +000070 """
71 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000072
Steven M. Gavac11ccf32001-09-24 09:43:17 +000073class IdleUserConfParser(IdleConfParser):
74 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000075 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000076 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000077
78 def AddSection(self,section):
79 """
80 if section doesn't exist, add it
81 """
82 if not self.has_section(section):
83 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000084
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000085 def RemoveEmptySections(self):
86 """
87 remove any sections that have no options
88 """
89 for section in self.sections():
90 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000091 self.remove_section(section)
92
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000093 def IsEmpty(self):
94 """
95 Remove empty sections and then return 1 if parser has no sections
96 left, else return 0.
97 """
98 self.RemoveEmptySections()
99 if self.sections():
100 return 0
101 else:
102 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000103
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000104 def RemoveOption(self,section,option):
105 """
106 If section/option exists, remove it.
107 Returns 1 if option was removed, 0 otherwise.
108 """
109 if self.has_section(section):
110 return self.remove_option(section,option)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000111
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000112 def SetOption(self,section,option,value):
113 """
114 Sets option to value, adding section if required.
115 Returns 1 if option was added or changed, otherwise 0.
116 """
117 if self.has_option(section,option):
118 if self.get(section,option)==value:
119 return 0
120 else:
121 self.set(section,option,value)
122 return 1
123 else:
124 if not self.has_section(section):
125 self.add_section(section)
126 self.set(section,option,value)
127 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000128
Steven M. Gavab77d3432002-03-02 07:16:21 +0000129 def RemoveFile(self):
130 """
131 Removes the user config file from disk if it exists.
132 """
133 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000134 os.remove(self.file)
135
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000136 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000137 """Update user configuration file.
138
139 Remove empty sections. If resulting config isn't empty, write the file
140 to disk. If config is empty, remove the file from disk if it exists.
141
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000142 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000143 if not self.IsEmpty():
144 cfgFile=open(self.file,'w')
145 self.write(cfgFile)
146 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000147 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000148
149class IdleConf:
150 """
151 holds config parsers for all idle config files:
152 default config files
153 (idle install dir)/config-main.def
154 (idle install dir)/config-extensions.def
155 (idle install dir)/config-highlight.def
156 (idle install dir)/config-keys.def
157 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000158 (user home dir)/.idlerc/config-main.cfg
159 (user home dir)/.idlerc/config-extensions.cfg
160 (user home dir)/.idlerc/config-highlight.cfg
161 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000162 """
163 def __init__(self):
164 self.defaultCfg={}
165 self.userCfg={}
166 self.cfg={}
167 self.CreateConfigHandlers()
168 self.LoadCfgFiles()
169 #self.LoadCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000170
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000171 def CreateConfigHandlers(self):
172 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000173 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000174 configurations respectively
175 """
176 #build idle install path
177 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000178 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000179 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000180 idleDir=os.path.abspath(sys.path[0])
181 userDir=self.GetUserCfgDir()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000182 configTypes=('main','extensions','highlight','keys')
183 defCfgFiles={}
184 usrCfgFiles={}
185 for cfgType in configTypes: #build config file names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000186 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
187 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000188 for cfgType in configTypes: #create config parsers
189 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
190 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000191
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000192 def GetUserCfgDir(self):
193 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000194 Creates (if required) and returns a filesystem directory for storing
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000195 user config files.
196 """
197 cfgDir='.idlerc'
198 userDir=os.path.expanduser('~')
199 if userDir != '~': #'HOME' exists as a key in os.environ
200 if not os.path.exists(userDir):
201 warn=('\n Warning: HOME environment variable points to\n '+
202 userDir+'\n but the path does not exist.\n')
203 sys.stderr.write(warn)
204 userDir='~'
205 if userDir=='~': #we still don't have a home directory
206 #traditionally idle has defaulted to os.getcwd(), is this adeqate?
207 userDir = os.getcwd() #hack for no real homedir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000208 userDir=os.path.join(userDir,cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000209 if not os.path.exists(userDir):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000210 try: #make the config dir if it doesn't exist yet
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000211 os.mkdir(userDir)
212 except IOError:
213 warn=('\n Warning: unable to create user config directory\n '+
214 userDir+'\n')
215 sys.stderr.write(warn)
216 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000217
Steven M. Gava2a63a072001-10-26 06:50:54 +0000218 def GetOption(self, configType, section, option, default=None, type=None):
Steven M. Gava429a86a2001-10-23 10:42:12 +0000219 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000220 Get an option value for given config type and given general
Steven M. Gava429a86a2001-10-23 10:42:12 +0000221 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000222 return as type. Firstly the user configuration is checked, with a
223 fallback to the default configuration, and a final 'catch all'
224 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86a2001-10-23 10:42:12 +0000225 either the user or the default configuration.
226 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000227 If a default is returned a warning is printed to stderr.
Steven M. Gava429a86a2001-10-23 10:42:12 +0000228 """
229 if self.userCfg[configType].has_option(section,option):
230 return self.userCfg[configType].Get(section, option, type=type)
231 elif self.defaultCfg[configType].has_option(section,option):
232 return self.defaultCfg[configType].Get(section, option, type=type)
Steven M. Gava052937f2002-02-11 02:20:53 +0000233 else: #returning default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000234 warning=('\n Warning: configHandler.py - IdleConf.GetOption -\n'
235 ' problem retrieving configration option %r\n'
236 ' from section %r.\n'
237 ' returning default value: %r\n' %
238 (option, section, default))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000239 sys.stderr.write(warning)
Steven M. Gava429a86a2001-10-23 10:42:12 +0000240 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000241
Steven M. Gava2a63a072001-10-26 06:50:54 +0000242 def GetSectionList(self, configSet, configType):
243 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000244 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000245 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000246 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000247 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000248 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000249 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000250 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000251 if configSet == 'user':
252 cfgParser=self.userCfg[configType]
253 elif configSet == 'default':
254 cfgParser=self.defaultCfg[configType]
255 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000256 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000257 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000258
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000259 def GetHighlight(self, theme, element, fgBg=None):
260 """
261 return individual highlighting theme elements.
262 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000263 containing fg and bg colours (appropriate for passing to Tkinter in,
264 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000265 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000266 if self.defaultCfg['highlight'].has_section(theme):
267 themeDict=self.GetThemeDict('default',theme)
268 else:
269 themeDict=self.GetThemeDict('user',theme)
270 fore=themeDict[element+'-foreground']
271 if element=='cursor': #there is no config value for cursor bg
272 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000273 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000274 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000275 highlight={"foreground": fore,"background": back}
276 if not fgBg: #return dict of both colours
277 return highlight
278 else: #return specified colour only
279 if fgBg == 'fg':
280 return highlight["foreground"]
281 if fgBg == 'bg':
282 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000283 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000284 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000285
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000286 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000287 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000288 type - string, 'default' or 'user' theme type
289 themeName - string, theme name
290 Returns a dictionary which holds {option:value} for each element
291 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000292 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000293 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000294 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000295 if type == 'user':
296 cfgParser=self.userCfg['highlight']
297 elif type == 'default':
298 cfgParser=self.defaultCfg['highlight']
299 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000300 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000301 #foreground and background values are provded for each theme element
302 #(apart from cursor) even though all these values are not yet used
303 #by idle, to allow for their use in the future. Default values are
304 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000305 theme={ 'normal-foreground':'#000000',
306 'normal-background':'#ffffff',
307 'keyword-foreground':'#000000',
308 'keyword-background':'#ffffff',
309 'comment-foreground':'#000000',
310 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000311 'string-foreground':'#000000',
312 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000313 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000314 'definition-background':'#ffffff',
315 'hilite-foreground':'#000000',
316 'hilite-background':'gray',
317 'break-foreground':'#ffffff',
318 'break-background':'#000000',
319 'hit-foreground':'#ffffff',
320 'hit-background':'#000000',
321 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000322 'error-background':'#000000',
323 #cursor (only foreground can be set)
324 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000325 #shell window
326 'stdout-foreground':'#000000',
327 'stdout-background':'#ffffff',
328 'stderr-foreground':'#000000',
329 'stderr-background':'#ffffff',
330 'console-foreground':'#000000',
331 'console-background':'#ffffff' }
332 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000333 if not cfgParser.has_option(themeName,element):
334 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000335 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
336 ' -\n problem retrieving theme element %r'
337 '\n from theme %r.\n'
338 ' returning default value: %r\n' %
339 (element, themeName, theme[element]))
Steven M. Gava052937f2002-02-11 02:20:53 +0000340 sys.stderr.write(warning)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000341 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000342 theme[element]=colour
343 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000344
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000345 def CurrentTheme(self):
346 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000347 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000348 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000349 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000350
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000351 def CurrentKeys(self):
352 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000353 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000354 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000355 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000356
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000357 def GetExtensions(self, activeOnly=1):
358 """
359 Gets a list of all idle extensions declared in the config files.
360 activeOnly - boolean, if true only return active (enabled) extensions
361 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000362 extns=self.RemoveKeyBindNames(
363 self.GetSectionList('default','extensions'))
364 userExtns=self.RemoveKeyBindNames(
365 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000366 for extn in userExtns:
367 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000368 extns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000369 if activeOnly:
370 activeExtns=[]
371 for extn in extns:
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000372 if self.GetOption('extensions',extn,'enable',default=1,
373 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000374 #the extension is enabled
375 activeExtns.append(extn)
376 return activeExtns
377 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000378 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000379
Steven M. Gavac628a062002-01-19 10:33:21 +0000380 def RemoveKeyBindNames(self,extnNameList):
381 #get rid of keybinding section names
382 names=extnNameList
383 kbNameIndicies=[]
384 for name in names:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000385 if name.endswith('_bindings') or name.endswith('_cfgBindings'):
386 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000387 kbNameIndicies.sort()
388 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000389 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000390 del(names[index])
391 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000392
Steven M. Gavaa498af22002-02-01 01:33:36 +0000393 def GetExtnNameForEvent(self,virtualEvent):
394 """
395 Returns the name of the extension that virtualEvent is bound in, or
396 None if not bound in any extension.
397 virtualEvent - string, name of the virtual event to test for, without
398 the enclosing '<< >>'
399 """
400 extName=None
401 vEvent='<<'+virtualEvent+'>>'
402 for extn in self.GetExtensions(activeOnly=0):
403 for event in self.GetExtensionKeys(extn).keys():
404 if event == vEvent:
405 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000406 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000407
Steven M. Gavac628a062002-01-19 10:33:21 +0000408 def GetExtensionKeys(self,extensionName):
409 """
410 returns a dictionary of the configurable keybindings for a particular
411 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000412 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000413 """
414 keysName=extensionName+'_cfgBindings'
415 activeKeys=self.GetCurrentKeySet()
416 extKeys={}
417 if self.defaultCfg['extensions'].has_section(keysName):
418 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
419 for eventName in eventNames:
420 event='<<'+eventName+'>>'
421 binding=activeKeys[event]
422 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000423 return extKeys
424
Steven M. Gavac628a062002-01-19 10:33:21 +0000425 def __GetRawExtensionKeys(self,extensionName):
426 """
427 returns a dictionary of the configurable keybindings for a particular
428 extension, as defined in the configuration files, or an empty dictionary
429 if no bindings are found
430 """
431 keysName=extensionName+'_cfgBindings'
432 extKeys={}
433 if self.defaultCfg['extensions'].has_section(keysName):
434 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
435 for eventName in eventNames:
436 binding=self.GetOption('extensions',keysName,
437 eventName,default='').split()
438 event='<<'+eventName+'>>'
439 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000440 return extKeys
441
Steven M. Gavac628a062002-01-19 10:33:21 +0000442 def GetExtensionBindings(self,extensionName):
443 """
444 Returns a dictionary of all the event bindings for a particular
445 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000446 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000447 keybindings are disabled.
448 """
449 bindsName=extensionName+'_bindings'
450 extBinds=self.GetExtensionKeys(extensionName)
451 #add the non-configurable bindings
452 if self.defaultCfg['extensions'].has_section(bindsName):
453 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
454 for eventName in eventNames:
455 binding=self.GetOption('extensions',bindsName,
456 eventName,default='').split()
457 event='<<'+eventName+'>>'
458 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000459
460 return extBinds
461
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000462 def GetKeyBinding(self, keySetName, eventStr):
463 """
464 returns the keybinding for a specific event.
465 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000466 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000467 represented as a string, eg. '<<event>>'
468 """
469 eventName=eventStr[2:-2] #trim off the angle brackets
470 binding=self.GetOption('keys',keySetName,eventName,default='').split()
471 return binding
472
Steven M. Gavac628a062002-01-19 10:33:21 +0000473 def GetCurrentKeySet(self):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000474 return self.GetKeySet(self.CurrentKeys())
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000475
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000476 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000477 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000478 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000479 keybindings for all currently active extensions. If a binding defined
480 in an extension is already in use, that binding is disabled.
481 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000482 keySet=self.GetCoreKeys(keySetName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000483 activeExtns=self.GetExtensions(activeOnly=1)
484 for extn in activeExtns:
485 extKeys=self.__GetRawExtensionKeys(extn)
486 if extKeys: #the extension defines keybindings
487 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000488 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000489 #the binding is already in use
490 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000491 keySet[event]=extKeys[event] #add binding
492 return keySet
493
Steven M. Gavaa498af22002-02-01 01:33:36 +0000494 def IsCoreBinding(self,virtualEvent):
495 """
496 returns true if the virtual event is bound in the core idle keybindings.
497 virtualEvent - string, name of the virtual event to test for, without
498 the enclosing '<< >>'
499 """
500 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000501
Steven M. Gavac628a062002-01-19 10:33:21 +0000502 def GetCoreKeys(self, keySetName=None):
503 """
504 returns the requested set of core keybindings, with fallbacks if
505 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000506 Keybindings loaded from the config file(s) are loaded _over_ these
507 defaults, so if there is a problem getting any core binding there will
508 be an 'ultimate last resort fallback' to the CUA-ish bindings
509 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000510 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000511 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000512 '<<copy>>': ['<Control-c>', '<Control-C>'],
513 '<<cut>>': ['<Control-x>', '<Control-X>'],
514 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000515 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
516 '<<center-insert>>': ['<Control-l>'],
517 '<<close-all-windows>>': ['<Control-q>'],
518 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000519 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000520 '<<end-of-file>>': ['<Control-d>'],
521 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000522 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000523 '<<history-next>>': ['<Alt-n>'],
524 '<<history-previous>>': ['<Alt-p>'],
525 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000526 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000527 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000528 '<<open-class-browser>>': ['<Alt-c>'],
529 '<<open-module>>': ['<Alt-m>'],
530 '<<open-new-window>>': ['<Control-n>'],
531 '<<open-window-from-file>>': ['<Control-o>'],
532 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000533 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000534 '<<redo>>': ['<Control-y>'],
535 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000536 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000537 '<<save-window-as-file>>': ['<Alt-s>'],
538 '<<save-window>>': ['<Control-s>'],
539 '<<select-all>>': ['<Alt-a>'],
540 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000541 '<<undo>>': ['<Control-z>'],
542 '<<find-again>>': ['<Control-g>', '<F3>'],
543 '<<find-in-files>>': ['<Alt-F3>'],
544 '<<find-selection>>': ['<Control-F3>'],
545 '<<find>>': ['<Control-f>'],
546 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000547 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000548 '<<smart-backspace>>': ['<Key-BackSpace>'],
549 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
550 '<<smart-indent>>': ['<Key-Tab>'],
551 '<<indent-region>>': ['<Control-Key-bracketright>'],
552 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
553 '<<comment-region>>': ['<Alt-Key-3>'],
554 '<<uncomment-region>>': ['<Alt-Key-4>'],
555 '<<tabify-region>>': ['<Alt-Key-5>'],
556 '<<untabify-region>>': ['<Alt-Key-6>'],
557 '<<toggle-tabs>>': ['<Alt-Key-t>'],
558 '<<change-indentwidth>>': ['<Alt-Key-u>']
559 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000560 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000561 for event in keyBindings.keys():
562 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000563 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000564 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000565 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000566 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
567 ' -\n problem retrieving key binding for event %r'
568 '\n from key set %r.\n'
569 ' returning default value: %r\n' %
570 (event, keySetName, keyBindings[event]))
Steven M. Gava49745752002-02-18 01:43:11 +0000571 sys.stderr.write(warning)
Steven M. Gava17d01542001-12-03 00:37:28 +0000572 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000573
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000574 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000575 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000576
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000577 Valid configSets are 'user' or 'default'. Return a list of tuples of
578 the form (menu_item , path_to_help_file , option), or return the empty
579 list. 'option' is the sequence number of the help resource. 'option'
580 values determine the position of the menu items on the Help menu,
581 therefore the returned list must be sorted by 'option'.
582
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000583 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000584 helpSources=[]
585 if configSet=='user':
586 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000587 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000588 cfgParser=self.defaultCfg['main']
589 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000590 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000591 options=cfgParser.GetOptionList('HelpFiles')
592 for option in options:
593 value=cfgParser.Get('HelpFiles',option,default=';')
594 if value.find(';')==-1: #malformed config entry with no ';'
595 menuItem='' #make these empty
596 helpPath='' #so value won't be added to list
597 else: #config entry contains ';' as expected
598 value=string.split(value,';')
599 menuItem=value[0].strip()
600 helpPath=value[1].strip()
601 if menuItem and helpPath: #neither are empty strings
602 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000603 helpSources.sort(self.__helpsort)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000604 return helpSources
605
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000606 def __helpsort(self, h1, h2):
607 if int(h1[2]) < int(h2[2]):
608 return -1
609 elif int(h1[2]) > int(h2[2]):
610 return 1
611 else:
612 return 0
613
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000614 def GetAllExtraHelpSourcesList(self):
615 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000616 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000617 sources configured, or an empty list if there are none. Tuples are of
618 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000619 """
620 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000621 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000622 return allHelpSources
623
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000624 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000625 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000626 load all configuration files.
627 """
628 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000629 self.defaultCfg[key].Load()
630 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000631
632 def SaveUserCfgFiles(self):
633 """
634 write all loaded user configuration files back to disk
635 """
636 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000637 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000638
639idleConf=IdleConf()
640
641### module test
642if __name__ == '__main__':
643 def dumpCfg(cfg):
644 print '\n',cfg,'\n'
645 for key in cfg.keys():
646 sections=cfg[key].sections()
647 print key
648 print sections
649 for section in sections:
650 options=cfg[key].options(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000651 print section
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000652 print options
653 for option in options:
654 print option, '=', cfg[key].Get(section,option)
655 dumpCfg(idleConf.defaultCfg)
656 dumpCfg(idleConf.userCfg)
657 print idleConf.userCfg['main'].Get('Theme','name')
658 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')