blob: 370f3707def932703f9742a287a7d6894bca880c [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. Gava429a86af2001-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
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000218 def GetOption(self, configType, section, option, default=None, type=None,
219 warn_on_default=True):
Steven M. Gava429a86af2001-10-23 10:42:12 +0000220 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000221 Get an option value for given config type and given general
Steven M. Gava429a86af2001-10-23 10:42:12 +0000222 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000223 return as type. Firstly the user configuration is checked, with a
224 fallback to the default configuration, and a final 'catch all'
225 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86af2001-10-23 10:42:12 +0000226 either the user or the default configuration.
227 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000228 If a default is returned, and warn_on_default is True, a warning is
229 printed to stderr.
230
Steven M. Gava429a86af2001-10-23 10:42:12 +0000231 """
232 if self.userCfg[configType].has_option(section,option):
233 return self.userCfg[configType].Get(section, option, type=type)
234 elif self.defaultCfg[configType].has_option(section,option):
235 return self.defaultCfg[configType].Get(section, option, type=type)
Steven M. Gava052937f2002-02-11 02:20:53 +0000236 else: #returning default, print warning
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000237 if warn_on_default:
238 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
239 ' problem retrieving configration option %r\n'
240 ' from section %r.\n'
241 ' returning default value: %r\n' %
242 (option, section, default))
243 sys.stderr.write(warning)
Steven M. Gava429a86af2001-10-23 10:42:12 +0000244 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000245
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000246 def SetOption(self, configType, section, option, value):
247 """In user's config file, set section's option to value.
248
249 """
250 self.userCfg[configType].SetOption(section, option, value)
251
Steven M. Gava2a63a072001-10-26 06:50:54 +0000252 def GetSectionList(self, configSet, configType):
253 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000254 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000255 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000256 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000257 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000258 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000259 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000260 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000261 if configSet == 'user':
262 cfgParser=self.userCfg[configType]
263 elif configSet == 'default':
264 cfgParser=self.defaultCfg[configType]
265 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000266 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000267 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000268
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000269 def GetHighlight(self, theme, element, fgBg=None):
270 """
271 return individual highlighting theme elements.
272 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000273 containing fg and bg colours (appropriate for passing to Tkinter in,
274 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000275 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000276 if self.defaultCfg['highlight'].has_section(theme):
277 themeDict=self.GetThemeDict('default',theme)
278 else:
279 themeDict=self.GetThemeDict('user',theme)
280 fore=themeDict[element+'-foreground']
281 if element=='cursor': #there is no config value for cursor bg
282 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000283 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000284 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000285 highlight={"foreground": fore,"background": back}
286 if not fgBg: #return dict of both colours
287 return highlight
288 else: #return specified colour only
289 if fgBg == 'fg':
290 return highlight["foreground"]
291 if fgBg == 'bg':
292 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000293 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000294 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000295
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000296 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000297 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000298 type - string, 'default' or 'user' theme type
299 themeName - string, theme name
300 Returns a dictionary which holds {option:value} for each element
301 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000302 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000303 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000304 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000305 if type == 'user':
306 cfgParser=self.userCfg['highlight']
307 elif type == 'default':
308 cfgParser=self.defaultCfg['highlight']
309 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000310 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000311 #foreground and background values are provded for each theme element
312 #(apart from cursor) even though all these values are not yet used
313 #by idle, to allow for their use in the future. Default values are
314 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000315 theme={ 'normal-foreground':'#000000',
316 'normal-background':'#ffffff',
317 'keyword-foreground':'#000000',
318 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000319 'builtin-foreground':'#000000',
320 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000321 'comment-foreground':'#000000',
322 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000323 'string-foreground':'#000000',
324 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000325 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000326 'definition-background':'#ffffff',
327 'hilite-foreground':'#000000',
328 'hilite-background':'gray',
329 'break-foreground':'#ffffff',
330 'break-background':'#000000',
331 'hit-foreground':'#ffffff',
332 'hit-background':'#000000',
333 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000334 'error-background':'#000000',
335 #cursor (only foreground can be set)
336 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000337 #shell window
338 'stdout-foreground':'#000000',
339 'stdout-background':'#ffffff',
340 'stderr-foreground':'#000000',
341 'stderr-background':'#ffffff',
342 'console-foreground':'#000000',
343 'console-background':'#ffffff' }
344 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000345 if not cfgParser.has_option(themeName,element):
346 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000347 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
348 ' -\n problem retrieving theme element %r'
349 '\n from theme %r.\n'
350 ' returning default value: %r\n' %
351 (element, themeName, theme[element]))
Steven M. Gava052937f2002-02-11 02:20:53 +0000352 sys.stderr.write(warning)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000353 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000354 theme[element]=colour
355 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000356
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000357 def CurrentTheme(self):
358 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000359 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000360 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000361 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000362
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000363 def CurrentKeys(self):
364 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000365 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000366 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000367 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000368
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000369 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000370 """
371 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000372 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000373 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000374 extns=self.RemoveKeyBindNames(
375 self.GetSectionList('default','extensions'))
376 userExtns=self.RemoveKeyBindNames(
377 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000378 for extn in userExtns:
379 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000380 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000381 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000382 activeExtns=[]
383 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000384 if self.GetOption('extensions', extn, 'enable', default=True,
385 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000386 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000387 if editor_only or shell_only:
388 if editor_only:
389 option = "enable_editor"
390 else:
391 option = "enable_shell"
392 if self.GetOption('extensions', extn,option,
393 default=True, type='bool',
394 warn_on_default=False):
395 activeExtns.append(extn)
396 else:
397 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000398 return activeExtns
399 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000400 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000401
Steven M. Gavac628a062002-01-19 10:33:21 +0000402 def RemoveKeyBindNames(self,extnNameList):
403 #get rid of keybinding section names
404 names=extnNameList
405 kbNameIndicies=[]
406 for name in names:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000407 if name.endswith('_bindings') or name.endswith('_cfgBindings'):
408 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000409 kbNameIndicies.sort()
410 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000411 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000412 del(names[index])
413 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000414
Steven M. Gavaa498af22002-02-01 01:33:36 +0000415 def GetExtnNameForEvent(self,virtualEvent):
416 """
417 Returns the name of the extension that virtualEvent is bound in, or
418 None if not bound in any extension.
419 virtualEvent - string, name of the virtual event to test for, without
420 the enclosing '<< >>'
421 """
422 extName=None
423 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000424 for extn in self.GetExtensions(active_only=0):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000425 for event in self.GetExtensionKeys(extn).keys():
426 if event == vEvent:
427 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000428 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000429
Steven M. Gavac628a062002-01-19 10:33:21 +0000430 def GetExtensionKeys(self,extensionName):
431 """
432 returns a dictionary of the configurable keybindings for a particular
433 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000434 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000435 """
436 keysName=extensionName+'_cfgBindings'
437 activeKeys=self.GetCurrentKeySet()
438 extKeys={}
439 if self.defaultCfg['extensions'].has_section(keysName):
440 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
441 for eventName in eventNames:
442 event='<<'+eventName+'>>'
443 binding=activeKeys[event]
444 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000445 return extKeys
446
Steven M. Gavac628a062002-01-19 10:33:21 +0000447 def __GetRawExtensionKeys(self,extensionName):
448 """
449 returns a dictionary of the configurable keybindings for a particular
450 extension, as defined in the configuration files, or an empty dictionary
451 if no bindings are found
452 """
453 keysName=extensionName+'_cfgBindings'
454 extKeys={}
455 if self.defaultCfg['extensions'].has_section(keysName):
456 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
457 for eventName in eventNames:
458 binding=self.GetOption('extensions',keysName,
459 eventName,default='').split()
460 event='<<'+eventName+'>>'
461 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000462 return extKeys
463
Steven M. Gavac628a062002-01-19 10:33:21 +0000464 def GetExtensionBindings(self,extensionName):
465 """
466 Returns a dictionary of all the event bindings for a particular
467 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000468 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000469 keybindings are disabled.
470 """
471 bindsName=extensionName+'_bindings'
472 extBinds=self.GetExtensionKeys(extensionName)
473 #add the non-configurable bindings
474 if self.defaultCfg['extensions'].has_section(bindsName):
475 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
476 for eventName in eventNames:
477 binding=self.GetOption('extensions',bindsName,
478 eventName,default='').split()
479 event='<<'+eventName+'>>'
480 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000481
482 return extBinds
483
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000484 def GetKeyBinding(self, keySetName, eventStr):
485 """
486 returns the keybinding for a specific event.
487 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000488 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000489 represented as a string, eg. '<<event>>'
490 """
491 eventName=eventStr[2:-2] #trim off the angle brackets
492 binding=self.GetOption('keys',keySetName,eventName,default='').split()
493 return binding
494
Steven M. Gavac628a062002-01-19 10:33:21 +0000495 def GetCurrentKeySet(self):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000496 return self.GetKeySet(self.CurrentKeys())
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000497
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000498 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000499 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000500 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000501 keybindings for all currently active extensions. If a binding defined
502 in an extension is already in use, that binding is disabled.
503 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000504 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000505 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000506 for extn in activeExtns:
507 extKeys=self.__GetRawExtensionKeys(extn)
508 if extKeys: #the extension defines keybindings
509 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000510 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000511 #the binding is already in use
512 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000513 keySet[event]=extKeys[event] #add binding
514 return keySet
515
Steven M. Gavaa498af22002-02-01 01:33:36 +0000516 def IsCoreBinding(self,virtualEvent):
517 """
518 returns true if the virtual event is bound in the core idle keybindings.
519 virtualEvent - string, name of the virtual event to test for, without
520 the enclosing '<< >>'
521 """
522 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000523
Steven M. Gavac628a062002-01-19 10:33:21 +0000524 def GetCoreKeys(self, keySetName=None):
525 """
526 returns the requested set of core keybindings, with fallbacks if
527 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000528 Keybindings loaded from the config file(s) are loaded _over_ these
529 defaults, so if there is a problem getting any core binding there will
530 be an 'ultimate last resort fallback' to the CUA-ish bindings
531 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000532 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000533 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000534 '<<copy>>': ['<Control-c>', '<Control-C>'],
535 '<<cut>>': ['<Control-x>', '<Control-X>'],
536 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000537 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
538 '<<center-insert>>': ['<Control-l>'],
539 '<<close-all-windows>>': ['<Control-q>'],
540 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000541 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000542 '<<end-of-file>>': ['<Control-d>'],
543 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000544 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000545 '<<history-next>>': ['<Alt-n>'],
546 '<<history-previous>>': ['<Alt-p>'],
547 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000548 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000549 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000550 '<<open-class-browser>>': ['<Alt-c>'],
551 '<<open-module>>': ['<Alt-m>'],
552 '<<open-new-window>>': ['<Control-n>'],
553 '<<open-window-from-file>>': ['<Control-o>'],
554 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000555 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000556 '<<redo>>': ['<Control-y>'],
557 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000558 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000559 '<<save-window-as-file>>': ['<Alt-s>'],
560 '<<save-window>>': ['<Control-s>'],
561 '<<select-all>>': ['<Alt-a>'],
562 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000563 '<<undo>>': ['<Control-z>'],
564 '<<find-again>>': ['<Control-g>', '<F3>'],
565 '<<find-in-files>>': ['<Alt-F3>'],
566 '<<find-selection>>': ['<Control-F3>'],
567 '<<find>>': ['<Control-f>'],
568 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000569 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000570 '<<smart-backspace>>': ['<Key-BackSpace>'],
571 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
572 '<<smart-indent>>': ['<Key-Tab>'],
573 '<<indent-region>>': ['<Control-Key-bracketright>'],
574 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
575 '<<comment-region>>': ['<Alt-Key-3>'],
576 '<<uncomment-region>>': ['<Alt-Key-4>'],
577 '<<tabify-region>>': ['<Alt-Key-5>'],
578 '<<untabify-region>>': ['<Alt-Key-6>'],
579 '<<toggle-tabs>>': ['<Alt-Key-t>'],
580 '<<change-indentwidth>>': ['<Alt-Key-u>']
581 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000582 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000583 for event in keyBindings.keys():
584 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000585 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000586 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000587 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000588 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
589 ' -\n problem retrieving key binding for event %r'
590 '\n from key set %r.\n'
591 ' returning default value: %r\n' %
592 (event, keySetName, keyBindings[event]))
Steven M. Gava49745752002-02-18 01:43:11 +0000593 sys.stderr.write(warning)
Steven M. Gava17d01542001-12-03 00:37:28 +0000594 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000595
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000596 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000597 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000598
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000599 Valid configSets are 'user' or 'default'. Return a list of tuples of
600 the form (menu_item , path_to_help_file , option), or return the empty
601 list. 'option' is the sequence number of the help resource. 'option'
602 values determine the position of the menu items on the Help menu,
603 therefore the returned list must be sorted by 'option'.
604
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000605 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000606 helpSources=[]
607 if configSet=='user':
608 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000609 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000610 cfgParser=self.defaultCfg['main']
611 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000612 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000613 options=cfgParser.GetOptionList('HelpFiles')
614 for option in options:
615 value=cfgParser.Get('HelpFiles',option,default=';')
616 if value.find(';')==-1: #malformed config entry with no ';'
617 menuItem='' #make these empty
618 helpPath='' #so value won't be added to list
619 else: #config entry contains ';' as expected
620 value=string.split(value,';')
621 menuItem=value[0].strip()
622 helpPath=value[1].strip()
623 if menuItem and helpPath: #neither are empty strings
624 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000625 helpSources.sort(self.__helpsort)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000626 return helpSources
627
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000628 def __helpsort(self, h1, h2):
629 if int(h1[2]) < int(h2[2]):
630 return -1
631 elif int(h1[2]) > int(h2[2]):
632 return 1
633 else:
634 return 0
635
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000636 def GetAllExtraHelpSourcesList(self):
637 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000638 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000639 sources configured, or an empty list if there are none. Tuples are of
640 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000641 """
642 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000643 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000644 return allHelpSources
645
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000646 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000647 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000648 load all configuration files.
649 """
650 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000651 self.defaultCfg[key].Load()
652 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000653
654 def SaveUserCfgFiles(self):
655 """
656 write all loaded user configuration files back to disk
657 """
658 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000659 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000660
661idleConf=IdleConf()
662
663### module test
664if __name__ == '__main__':
665 def dumpCfg(cfg):
666 print '\n',cfg,'\n'
667 for key in cfg.keys():
668 sections=cfg[key].sections()
669 print key
670 print sections
671 for section in sections:
672 options=cfg[key].options(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000673 print section
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000674 print options
675 for option in options:
676 print option, '=', cfg[key].Get(section,option)
677 dumpCfg(idleConf.defaultCfg)
678 dumpCfg(idleConf.userCfg)
679 print idleConf.userCfg['main'].Get('Theme','name')
680 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')