blob: 909125803b170cd0028e167b71937f8b13f66d8f [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.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000018"""
Terry Jan Reedybee003c2014-09-19 22:37:24 -040019from __future__ import print_function
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000020import os
21import sys
22import string
Terry Jan Reedy9bc50562014-07-01 18:52:31 -040023from ConfigParser import ConfigParser
Steven M. Gavac11ccf32001-09-24 09:43:17 +000024
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
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000041 def Get(self, section, option, type=None, default=None, raw=False):
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. Kaiser90f84922007-02-05 06:03:18 +000046 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000047 return default
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000048 if type=='bool':
49 return self.getboolean(section, option)
50 elif type=='int':
51 return self.getint(section, option)
52 else:
53 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000054
Steven M. Gavac11ccf32001-09-24 09:43:17 +000055 def GetOptionList(self,section):
56 """
57 Get an option list for given section
58 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +000059 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000060 return self.options(section)
61 else: #return a default value
62 return []
63
Steven M. Gavac11ccf32001-09-24 09:43:17 +000064 def Load(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000065 """
66 Load the configuration file from disk
Steven M. Gavac11ccf32001-09-24 09:43:17 +000067 """
68 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000069
Steven M. Gavac11ccf32001-09-24 09:43:17 +000070class IdleUserConfParser(IdleConfParser):
71 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000072 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000073 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000074
75 def AddSection(self,section):
76 """
77 if section doesn't exist, add it
78 """
79 if not self.has_section(section):
80 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000081
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000082 def RemoveEmptySections(self):
83 """
84 remove any sections that have no options
85 """
86 for section in self.sections():
87 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000088 self.remove_section(section)
89
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000090 def IsEmpty(self):
91 """
92 Remove empty sections and then return 1 if parser has no sections
93 left, else return 0.
94 """
95 self.RemoveEmptySections()
96 if self.sections():
97 return 0
98 else:
99 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000100
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000101 def RemoveOption(self,section,option):
102 """
103 If section/option exists, remove it.
104 Returns 1 if option was removed, 0 otherwise.
105 """
106 if self.has_section(section):
107 return self.remove_option(section,option)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000108
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000109 def SetOption(self,section,option,value):
110 """
111 Sets option to value, adding section if required.
112 Returns 1 if option was added or changed, otherwise 0.
113 """
114 if self.has_option(section,option):
115 if self.get(section,option)==value:
116 return 0
117 else:
118 self.set(section,option,value)
119 return 1
120 else:
121 if not self.has_section(section):
122 self.add_section(section)
123 self.set(section,option,value)
124 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000125
Steven M. Gavab77d3432002-03-02 07:16:21 +0000126 def RemoveFile(self):
127 """
128 Removes the user config file from disk if it exists.
129 """
130 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000131 os.remove(self.file)
132
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000133 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000134 """Update user configuration file.
135
136 Remove empty sections. If resulting config isn't empty, write the file
137 to disk. If config is empty, remove the file from disk if it exists.
138
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000139 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000140 if not self.IsEmpty():
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000141 fname = self.file
142 try:
143 cfgFile = open(fname, 'w')
144 except IOError:
Kurt B. Kaiser8d365c32008-02-12 15:45:50 +0000145 os.unlink(fname)
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000146 cfgFile = open(fname, 'w')
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000147 self.write(cfgFile)
148 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000149 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000150
151class IdleConf:
152 """
153 holds config parsers for all idle config files:
154 default config files
155 (idle install dir)/config-main.def
156 (idle install dir)/config-extensions.def
157 (idle install dir)/config-highlight.def
158 (idle install dir)/config-keys.def
159 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000160 (user home dir)/.idlerc/config-main.cfg
161 (user home dir)/.idlerc/config-extensions.cfg
162 (user home dir)/.idlerc/config-highlight.cfg
163 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000164 """
165 def __init__(self):
166 self.defaultCfg={}
167 self.userCfg={}
168 self.cfg={}
169 self.CreateConfigHandlers()
170 self.LoadCfgFiles()
171 #self.LoadCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000172
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000173 def CreateConfigHandlers(self):
174 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000175 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000176 configurations respectively
177 """
178 #build idle install path
179 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000180 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000181 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000182 idleDir=os.path.abspath(sys.path[0])
183 userDir=self.GetUserCfgDir()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000184 configTypes=('main','extensions','highlight','keys')
185 defCfgFiles={}
186 usrCfgFiles={}
187 for cfgType in configTypes: #build config file names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000188 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
189 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000190 for cfgType in configTypes: #create config parsers
191 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
192 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000193
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000194 def GetUserCfgDir(self):
195 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000196 Creates (if required) and returns a filesystem directory for storing
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000197 user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000198
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000199 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000200 cfgDir = '.idlerc'
201 userDir = os.path.expanduser('~')
202 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000203 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000204 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400205 userDir+',\n but the path does not exist.')
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000206 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400207 print(warn, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000208 except IOError:
209 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000210 userDir = '~'
211 if userDir == "~": # still no path to home!
212 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
213 userDir = os.getcwd()
214 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000215 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000216 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000217 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000218 except (OSError, IOError):
219 warn = ('\n Warning: unable to create user config directory\n'+
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400220 userDir+'\n Check path and permissions.\n Exiting!\n')
221 print(warn, file=sys.stderr)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000222 raise SystemExit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000223 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000224
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000225 def GetOption(self, configType, section, option, default=None, type=None,
Kurt B. Kaiser90f84922007-02-05 06:03:18 +0000226 warn_on_default=True, raw=False):
Steven M. Gava429a86a2001-10-23 10:42:12 +0000227 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000228 Get an option value for given config type and given general
Steven M. Gava429a86a2001-10-23 10:42:12 +0000229 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000230 return as type. Firstly the user configuration is checked, with a
231 fallback to the default configuration, and a final 'catch all'
232 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86a2001-10-23 10:42:12 +0000233 either the user or the default configuration.
234 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000235 If a default is returned, and warn_on_default is True, a warning is
236 printed to stderr.
237
Steven M. Gava429a86a2001-10-23 10:42:12 +0000238 """
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200239 try:
240 if self.userCfg[configType].has_option(section,option):
241 return self.userCfg[configType].Get(section, option,
242 type=type, raw=raw)
243 except ValueError:
244 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
245 ' invalid %r value for configuration option %r\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400246 ' from section %r: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200247 (type, option, section,
248 self.userCfg[configType].Get(section, option,
249 raw=raw)))
250 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400251 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200252 except IOError:
253 pass
254 try:
255 if self.defaultCfg[configType].has_option(section,option):
256 return self.defaultCfg[configType].Get(section, option,
257 type=type, raw=raw)
258 except ValueError:
259 pass
260 #returning default, print warning
261 if warn_on_default:
262 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
263 ' problem retrieving configuration option %r\n'
264 ' from section %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400265 ' returning default value: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200266 (option, section, default))
267 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400268 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200269 except IOError:
270 pass
271 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000272
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000273 def SetOption(self, configType, section, option, value):
274 """In user's config file, set section's option to value.
275
276 """
277 self.userCfg[configType].SetOption(section, option, value)
278
Steven M. Gava2a63a072001-10-26 06:50:54 +0000279 def GetSectionList(self, configSet, configType):
280 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000281 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000282 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000283 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000284 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000285 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000286 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000287 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000288 if configSet == 'user':
289 cfgParser=self.userCfg[configType]
290 elif configSet == 'default':
291 cfgParser=self.defaultCfg[configType]
292 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000293 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000294 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000295
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000296 def GetHighlight(self, theme, element, fgBg=None):
297 """
298 return individual highlighting theme elements.
299 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000300 containing fg and bg colours (appropriate for passing to Tkinter in,
301 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000302 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000303 if self.defaultCfg['highlight'].has_section(theme):
304 themeDict=self.GetThemeDict('default',theme)
305 else:
306 themeDict=self.GetThemeDict('user',theme)
307 fore=themeDict[element+'-foreground']
308 if element=='cursor': #there is no config value for cursor bg
309 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000310 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000311 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000312 highlight={"foreground": fore,"background": back}
313 if not fgBg: #return dict of both colours
314 return highlight
315 else: #return specified colour only
316 if fgBg == 'fg':
317 return highlight["foreground"]
318 if fgBg == 'bg':
319 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000320 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000321 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000322
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000323 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000324 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000325 type - string, 'default' or 'user' theme type
326 themeName - string, theme name
327 Returns a dictionary which holds {option:value} for each element
328 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000329 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000330 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000331 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000332 if type == 'user':
333 cfgParser=self.userCfg['highlight']
334 elif type == 'default':
335 cfgParser=self.defaultCfg['highlight']
336 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000337 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000338 #foreground and background values are provded for each theme element
339 #(apart from cursor) even though all these values are not yet used
340 #by idle, to allow for their use in the future. Default values are
341 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000342 theme={ 'normal-foreground':'#000000',
343 'normal-background':'#ffffff',
344 'keyword-foreground':'#000000',
345 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000346 'builtin-foreground':'#000000',
347 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000348 'comment-foreground':'#000000',
349 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000350 'string-foreground':'#000000',
351 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000352 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000353 'definition-background':'#ffffff',
354 'hilite-foreground':'#000000',
355 'hilite-background':'gray',
356 'break-foreground':'#ffffff',
357 'break-background':'#000000',
358 'hit-foreground':'#ffffff',
359 'hit-background':'#000000',
360 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000361 'error-background':'#000000',
362 #cursor (only foreground can be set)
363 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000364 #shell window
365 'stdout-foreground':'#000000',
366 'stdout-background':'#ffffff',
367 'stderr-foreground':'#000000',
368 'stderr-background':'#ffffff',
369 'console-foreground':'#000000',
370 'console-background':'#ffffff' }
371 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000372 if not cfgParser.has_option(themeName,element):
373 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000374 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
375 ' -\n problem retrieving theme element %r'
376 '\n from theme %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400377 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000378 (element, themeName, theme[element]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000379 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400380 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000381 except IOError:
382 pass
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000383 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000384 theme[element]=colour
385 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000386
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000387 def CurrentTheme(self):
388 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000389 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000390 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000391 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000392
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000393 def CurrentKeys(self):
394 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000395 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000396 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000397 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000398
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000399 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000400 """
401 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000402 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000403 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000404 extns=self.RemoveKeyBindNames(
405 self.GetSectionList('default','extensions'))
406 userExtns=self.RemoveKeyBindNames(
407 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000408 for extn in userExtns:
409 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000410 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000411 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000412 activeExtns=[]
413 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000414 if self.GetOption('extensions', extn, 'enable', default=True,
415 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000416 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000417 if editor_only or shell_only:
418 if editor_only:
419 option = "enable_editor"
420 else:
421 option = "enable_shell"
422 if self.GetOption('extensions', extn,option,
423 default=True, type='bool',
424 warn_on_default=False):
425 activeExtns.append(extn)
426 else:
427 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000428 return activeExtns
429 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000430 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000431
Steven M. Gavac628a062002-01-19 10:33:21 +0000432 def RemoveKeyBindNames(self,extnNameList):
433 #get rid of keybinding section names
434 names=extnNameList
435 kbNameIndicies=[]
436 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000437 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000438 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000439 kbNameIndicies.sort()
440 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000441 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000442 del(names[index])
443 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000444
Steven M. Gavaa498af22002-02-01 01:33:36 +0000445 def GetExtnNameForEvent(self,virtualEvent):
446 """
447 Returns the name of the extension that virtualEvent is bound in, or
448 None if not bound in any extension.
449 virtualEvent - string, name of the virtual event to test for, without
450 the enclosing '<< >>'
451 """
452 extName=None
453 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000454 for extn in self.GetExtensions(active_only=0):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000455 for event in self.GetExtensionKeys(extn).keys():
456 if event == vEvent:
457 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000458 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000459
Steven M. Gavac628a062002-01-19 10:33:21 +0000460 def GetExtensionKeys(self,extensionName):
461 """
462 returns a dictionary of the configurable keybindings for a particular
463 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000464 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000465 """
466 keysName=extensionName+'_cfgBindings'
467 activeKeys=self.GetCurrentKeySet()
468 extKeys={}
469 if self.defaultCfg['extensions'].has_section(keysName):
470 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
471 for eventName in eventNames:
472 event='<<'+eventName+'>>'
473 binding=activeKeys[event]
474 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000475 return extKeys
476
Steven M. Gavac628a062002-01-19 10:33:21 +0000477 def __GetRawExtensionKeys(self,extensionName):
478 """
479 returns a dictionary of the configurable keybindings for a particular
480 extension, as defined in the configuration files, or an empty dictionary
481 if no bindings are found
482 """
483 keysName=extensionName+'_cfgBindings'
484 extKeys={}
485 if self.defaultCfg['extensions'].has_section(keysName):
486 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
487 for eventName in eventNames:
488 binding=self.GetOption('extensions',keysName,
489 eventName,default='').split()
490 event='<<'+eventName+'>>'
491 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000492 return extKeys
493
Steven M. Gavac628a062002-01-19 10:33:21 +0000494 def GetExtensionBindings(self,extensionName):
495 """
496 Returns a dictionary of all the event bindings for a particular
497 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000498 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000499 keybindings are disabled.
500 """
501 bindsName=extensionName+'_bindings'
502 extBinds=self.GetExtensionKeys(extensionName)
503 #add the non-configurable bindings
504 if self.defaultCfg['extensions'].has_section(bindsName):
505 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
506 for eventName in eventNames:
507 binding=self.GetOption('extensions',bindsName,
508 eventName,default='').split()
509 event='<<'+eventName+'>>'
510 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000511
512 return extBinds
513
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000514 def GetKeyBinding(self, keySetName, eventStr):
515 """
516 returns the keybinding for a specific event.
517 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000518 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000519 represented as a string, eg. '<<event>>'
520 """
521 eventName=eventStr[2:-2] #trim off the angle brackets
522 binding=self.GetOption('keys',keySetName,eventName,default='').split()
523 return binding
524
Steven M. Gavac628a062002-01-19 10:33:21 +0000525 def GetCurrentKeySet(self):
Ronald Oussoren19302d92006-06-11 14:33:36 +0000526 result = self.GetKeySet(self.CurrentKeys())
527
Ned Deily57847df2014-03-27 20:47:04 -0700528 if sys.platform == "darwin":
529 # OS X Tk variants do not support the "Alt" keyboard modifier.
530 # So replace all keybingings that use "Alt" with ones that
531 # use the "Option" keyboard modifier.
532 # TO DO: the "Option" modifier does not work properly for
533 # Cocoa Tk and XQuartz Tk so we should not use it
534 # in default OS X KeySets.
Ronald Oussoren19302d92006-06-11 14:33:36 +0000535 for k, v in result.items():
536 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
537 if v != v2:
538 result[k] = v2
539
540 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000541
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000542 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000543 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000544 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000545 keybindings for all currently active extensions. If a binding defined
546 in an extension is already in use, that binding is disabled.
547 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000548 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000549 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000550 for extn in activeExtns:
551 extKeys=self.__GetRawExtensionKeys(extn)
552 if extKeys: #the extension defines keybindings
553 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000554 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000555 #the binding is already in use
556 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000557 keySet[event]=extKeys[event] #add binding
558 return keySet
559
Steven M. Gavaa498af22002-02-01 01:33:36 +0000560 def IsCoreBinding(self,virtualEvent):
561 """
562 returns true if the virtual event is bound in the core idle keybindings.
563 virtualEvent - string, name of the virtual event to test for, without
564 the enclosing '<< >>'
565 """
566 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000567
Steven M. Gavac628a062002-01-19 10:33:21 +0000568 def GetCoreKeys(self, keySetName=None):
569 """
570 returns the requested set of core keybindings, with fallbacks if
571 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000572 Keybindings loaded from the config file(s) are loaded _over_ these
573 defaults, so if there is a problem getting any core binding there will
574 be an 'ultimate last resort fallback' to the CUA-ish bindings
575 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000576 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000577 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000578 '<<copy>>': ['<Control-c>', '<Control-C>'],
579 '<<cut>>': ['<Control-x>', '<Control-X>'],
580 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000581 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
582 '<<center-insert>>': ['<Control-l>'],
583 '<<close-all-windows>>': ['<Control-q>'],
584 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000585 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000586 '<<end-of-file>>': ['<Control-d>'],
587 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000588 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000589 '<<history-next>>': ['<Alt-n>'],
590 '<<history-previous>>': ['<Alt-p>'],
591 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000592 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000593 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000594 '<<open-class-browser>>': ['<Alt-c>'],
595 '<<open-module>>': ['<Alt-m>'],
596 '<<open-new-window>>': ['<Control-n>'],
597 '<<open-window-from-file>>': ['<Control-o>'],
598 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000599 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000600 '<<redo>>': ['<Control-y>'],
601 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000602 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000603 '<<save-window-as-file>>': ['<Alt-s>'],
604 '<<save-window>>': ['<Control-s>'],
605 '<<select-all>>': ['<Alt-a>'],
606 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000607 '<<undo>>': ['<Control-z>'],
608 '<<find-again>>': ['<Control-g>', '<F3>'],
609 '<<find-in-files>>': ['<Alt-F3>'],
610 '<<find-selection>>': ['<Control-F3>'],
611 '<<find>>': ['<Control-f>'],
612 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000613 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000614 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlovc37db102012-03-29 19:54:58 +0300615 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000616 '<<smart-indent>>': ['<Key-Tab>'],
617 '<<indent-region>>': ['<Control-Key-bracketright>'],
618 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
619 '<<comment-region>>': ['<Alt-Key-3>'],
620 '<<uncomment-region>>': ['<Alt-Key-4>'],
621 '<<tabify-region>>': ['<Alt-Key-5>'],
622 '<<untabify-region>>': ['<Alt-Key-6>'],
623 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000624 '<<change-indentwidth>>': ['<Alt-Key-u>'],
625 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
626 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000627 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000628 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000629 for event in keyBindings.keys():
630 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000631 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000632 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000633 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000634 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
635 ' -\n problem retrieving key binding for event %r'
636 '\n from key set %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400637 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000638 (event, keySetName, keyBindings[event]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000639 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400640 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000641 except IOError:
642 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000643 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000644
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000645 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000646 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000647
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000648 Valid configSets are 'user' or 'default'. Return a list of tuples of
649 the form (menu_item , path_to_help_file , option), or return the empty
650 list. 'option' is the sequence number of the help resource. 'option'
651 values determine the position of the menu items on the Help menu,
652 therefore the returned list must be sorted by 'option'.
653
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000654 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000655 helpSources=[]
656 if configSet=='user':
657 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000658 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000659 cfgParser=self.defaultCfg['main']
660 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000661 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000662 options=cfgParser.GetOptionList('HelpFiles')
663 for option in options:
664 value=cfgParser.Get('HelpFiles',option,default=';')
665 if value.find(';')==-1: #malformed config entry with no ';'
666 menuItem='' #make these empty
667 helpPath='' #so value won't be added to list
668 else: #config entry contains ';' as expected
669 value=string.split(value,';')
670 menuItem=value[0].strip()
671 helpPath=value[1].strip()
672 if menuItem and helpPath: #neither are empty strings
673 helpSources.append( (menuItem,helpPath,option) )
Florent Xiclunaa7f242f2010-04-02 08:15:26 +0000674 helpSources.sort(key=lambda x: int(x[2]))
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000675 return helpSources
676
677 def GetAllExtraHelpSourcesList(self):
678 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000679 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000680 sources configured, or an empty list if there are none. Tuples are of
681 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000682 """
683 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000684 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000685 return allHelpSources
686
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000687 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000688 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000689 load all configuration files.
690 """
691 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000692 self.defaultCfg[key].Load()
693 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000694
695 def SaveUserCfgFiles(self):
696 """
697 write all loaded user configuration files back to disk
698 """
699 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000700 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000701
702idleConf=IdleConf()
703
704### module test
705if __name__ == '__main__':
706 def dumpCfg(cfg):
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400707 print('\n', cfg, '\n')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000708 for key in cfg.keys():
709 sections=cfg[key].sections()
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400710 print(key)
711 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000712 for section in sections:
713 options=cfg[key].options(section)
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400714 print(section)
715 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000716 for option in options:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400717 print(option, '=', cfg[key].Get(section,option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000718 dumpCfg(idleConf.defaultCfg)
719 dumpCfg(idleConf.userCfg)
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400720 print(idleConf.userCfg['main'].Get('Theme','name'))
721 #print(idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal'))