blob: 9b726e1e22d55f7ed7776fd287a6f142128e51f6 [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.
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -040018
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000019"""
Terry Jan Reedybee003c2014-09-19 22:37:24 -040020from __future__ import print_function
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000021import os
22import sys
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -040023
Terry Jan Reedy9bc50562014-07-01 18:52:31 -040024from ConfigParser import ConfigParser
Steven M. Gavac11ccf32001-09-24 09:43:17 +000025
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000026class InvalidConfigType(Exception): pass
27class InvalidConfigSet(Exception): pass
28class InvalidFgBg(Exception): pass
29class InvalidTheme(Exception): pass
30
Steven M. Gavac11ccf32001-09-24 09:43:17 +000031class IdleConfParser(ConfigParser):
32 """
33 A ConfigParser specialised for idle configuration file handling
34 """
35 def __init__(self, cfgFile, cfgDefaults=None):
36 """
37 cfgFile - string, fully specified configuration file name
38 """
39 self.file=cfgFile
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -040040 ConfigParser.__init__(self, defaults=cfgDefaults)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000041
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000042 def Get(self, section, option, type=None, default=None, raw=False):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000043 """
44 Get an option value for given section/option or return default.
45 If type is specified, return as type.
46 """
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000047 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000048 return default
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000049 if type=='bool':
50 return self.getboolean(section, option)
51 elif type=='int':
52 return self.getint(section, option)
53 else:
54 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000055
Steven M. Gavac11ccf32001-09-24 09:43:17 +000056 def GetOptionList(self,section):
57 """
58 Get an option list for given section
59 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +000060 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000061 return self.options(section)
62 else: #return a default value
63 return []
64
Steven M. Gavac11ccf32001-09-24 09:43:17 +000065 def Load(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000066 """
67 Load the configuration file from disk
Steven M. Gavac11ccf32001-09-24 09:43:17 +000068 """
69 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000070
Steven M. Gavac11ccf32001-09-24 09:43:17 +000071class IdleUserConfParser(IdleConfParser):
72 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000073 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000074 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000075
76 def AddSection(self,section):
77 """
78 if section doesn't exist, add it
79 """
80 if not self.has_section(section):
81 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000082
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000083 def RemoveEmptySections(self):
84 """
85 remove any sections that have no options
86 """
87 for section in self.sections():
88 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000089 self.remove_section(section)
90
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000091 def IsEmpty(self):
92 """
93 Remove empty sections and then return 1 if parser has no sections
94 left, else return 0.
95 """
96 self.RemoveEmptySections()
97 if self.sections():
98 return 0
99 else:
100 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000101
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000102 def RemoveOption(self,section,option):
103 """
104 If section/option exists, remove it.
105 Returns 1 if option was removed, 0 otherwise.
106 """
107 if self.has_section(section):
108 return self.remove_option(section,option)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000109
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000110 def SetOption(self,section,option,value):
111 """
112 Sets option to value, adding section if required.
113 Returns 1 if option was added or changed, otherwise 0.
114 """
115 if self.has_option(section,option):
116 if self.get(section,option)==value:
117 return 0
118 else:
119 self.set(section,option,value)
120 return 1
121 else:
122 if not self.has_section(section):
123 self.add_section(section)
124 self.set(section,option,value)
125 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000126
Steven M. Gavab77d3432002-03-02 07:16:21 +0000127 def RemoveFile(self):
128 """
129 Removes the user config file from disk if it exists.
130 """
131 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000132 os.remove(self.file)
133
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000134 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000135 """Update user configuration file.
136
137 Remove empty sections. If resulting config isn't empty, write the file
138 to disk. If config is empty, remove the file from disk if it exists.
139
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000140 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000141 if not self.IsEmpty():
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000142 fname = self.file
143 try:
144 cfgFile = open(fname, 'w')
145 except IOError:
Kurt B. Kaiser8d365c32008-02-12 15:45:50 +0000146 os.unlink(fname)
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000147 cfgFile = open(fname, 'w')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400148 with cfgFile:
149 self.write(cfgFile)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000150 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000151 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000152
153class IdleConf:
154 """
155 holds config parsers for all idle config files:
156 default config files
157 (idle install dir)/config-main.def
158 (idle install dir)/config-extensions.def
159 (idle install dir)/config-highlight.def
160 (idle install dir)/config-keys.def
161 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000162 (user home dir)/.idlerc/config-main.cfg
163 (user home dir)/.idlerc/config-extensions.cfg
164 (user home dir)/.idlerc/config-highlight.cfg
165 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000166 """
167 def __init__(self):
168 self.defaultCfg={}
169 self.userCfg={}
170 self.cfg={}
171 self.CreateConfigHandlers()
172 self.LoadCfgFiles()
173 #self.LoadCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000174
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000175 def CreateConfigHandlers(self):
176 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000177 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000178 configurations respectively
179 """
180 #build idle install path
181 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000182 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000183 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000184 idleDir=os.path.abspath(sys.path[0])
185 userDir=self.GetUserCfgDir()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000186 configTypes=('main','extensions','highlight','keys')
187 defCfgFiles={}
188 usrCfgFiles={}
189 for cfgType in configTypes: #build config file names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000190 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
191 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000192 for cfgType in configTypes: #create config parsers
193 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
194 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000195
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000196 def GetUserCfgDir(self):
197 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000198 Creates (if required) and returns a filesystem directory for storing
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000199 user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000200
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000201 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000202 cfgDir = '.idlerc'
203 userDir = os.path.expanduser('~')
204 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000205 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000206 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400207 userDir+',\n but the path does not exist.')
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000208 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400209 print(warn, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000210 except IOError:
211 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000212 userDir = '~'
213 if userDir == "~": # still no path to home!
214 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
215 userDir = os.getcwd()
216 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000217 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000218 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000219 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000220 except (OSError, IOError):
221 warn = ('\n Warning: unable to create user config directory\n'+
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400222 userDir+'\n Check path and permissions.\n Exiting!\n')
223 print(warn, file=sys.stderr)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000224 raise SystemExit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000225 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000226
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000227 def GetOption(self, configType, section, option, default=None, type=None,
Kurt B. Kaiser90f84922007-02-05 06:03:18 +0000228 warn_on_default=True, raw=False):
Steven M. Gava429a86a2001-10-23 10:42:12 +0000229 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000230 Get an option value for given config type and given general
Steven M. Gava429a86a2001-10-23 10:42:12 +0000231 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000232 return as type. Firstly the user configuration is checked, with a
233 fallback to the default configuration, and a final 'catch all'
234 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86a2001-10-23 10:42:12 +0000235 either the user or the default configuration.
236 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000237 If a default is returned, and warn_on_default is True, a warning is
238 printed to stderr.
239
Steven M. Gava429a86a2001-10-23 10:42:12 +0000240 """
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200241 try:
242 if self.userCfg[configType].has_option(section,option):
243 return self.userCfg[configType].Get(section, option,
244 type=type, raw=raw)
245 except ValueError:
246 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
247 ' invalid %r value for configuration option %r\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400248 ' from section %r: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200249 (type, option, section,
250 self.userCfg[configType].Get(section, option,
251 raw=raw)))
252 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400253 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200254 except IOError:
255 pass
256 try:
257 if self.defaultCfg[configType].has_option(section,option):
258 return self.defaultCfg[configType].Get(section, option,
259 type=type, raw=raw)
260 except ValueError:
261 pass
262 #returning default, print warning
263 if warn_on_default:
264 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
265 ' problem retrieving configuration option %r\n'
266 ' from section %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400267 ' returning default value: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200268 (option, section, default))
269 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400270 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200271 except IOError:
272 pass
273 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000274
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000275 def SetOption(self, configType, section, option, value):
276 """In user's config file, set section's option to value.
277
278 """
279 self.userCfg[configType].SetOption(section, option, value)
280
Steven M. Gava2a63a072001-10-26 06:50:54 +0000281 def GetSectionList(self, configSet, configType):
282 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000283 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000284 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000285 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000286 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000287 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000288 if not (configType in ('main','extensions','highlight','keys')):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400289 raise InvalidConfigType('Invalid configType specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000290 if configSet == 'user':
291 cfgParser=self.userCfg[configType]
292 elif configSet == 'default':
293 cfgParser=self.defaultCfg[configType]
294 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400295 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000296 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000297
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000298 def GetHighlight(self, theme, element, fgBg=None):
299 """
300 return individual highlighting theme elements.
301 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000302 containing fg and bg colours (appropriate for passing to Tkinter in,
303 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000304 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000305 if self.defaultCfg['highlight'].has_section(theme):
306 themeDict=self.GetThemeDict('default',theme)
307 else:
308 themeDict=self.GetThemeDict('user',theme)
309 fore=themeDict[element+'-foreground']
310 if element=='cursor': #there is no config value for cursor bg
311 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000312 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000313 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000314 highlight={"foreground": fore,"background": back}
315 if not fgBg: #return dict of both colours
316 return highlight
317 else: #return specified colour only
318 if fgBg == 'fg':
319 return highlight["foreground"]
320 if fgBg == 'bg':
321 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000322 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400323 raise InvalidFgBg('Invalid fgBg specified')
Steven M. Gava9f25e672002-02-11 02:51:18 +0000324
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000325 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000326 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000327 type - string, 'default' or 'user' theme type
328 themeName - string, theme name
329 Returns a dictionary which holds {option:value} for each element
330 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000331 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000332 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000333 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000334 if type == 'user':
335 cfgParser=self.userCfg['highlight']
336 elif type == 'default':
337 cfgParser=self.defaultCfg['highlight']
338 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400339 raise InvalidTheme('Invalid theme type specified')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000340 #foreground and background values are provded for each theme element
341 #(apart from cursor) even though all these values are not yet used
342 #by idle, to allow for their use in the future. Default values are
343 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000344 theme={ 'normal-foreground':'#000000',
345 'normal-background':'#ffffff',
346 'keyword-foreground':'#000000',
347 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000348 'builtin-foreground':'#000000',
349 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000350 'comment-foreground':'#000000',
351 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000352 'string-foreground':'#000000',
353 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000354 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000355 'definition-background':'#ffffff',
356 'hilite-foreground':'#000000',
357 'hilite-background':'gray',
358 'break-foreground':'#ffffff',
359 'break-background':'#000000',
360 'hit-foreground':'#ffffff',
361 'hit-background':'#000000',
362 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000363 'error-background':'#000000',
364 #cursor (only foreground can be set)
365 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000366 #shell window
367 'stdout-foreground':'#000000',
368 'stdout-background':'#ffffff',
369 'stderr-foreground':'#000000',
370 'stderr-background':'#ffffff',
371 'console-foreground':'#000000',
372 'console-background':'#ffffff' }
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400373 for element in theme:
Steven M. Gava052937f2002-02-11 02:20:53 +0000374 if not cfgParser.has_option(themeName,element):
375 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000376 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
377 ' -\n problem retrieving theme element %r'
378 '\n from theme %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400379 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000380 (element, themeName, theme[element]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000381 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400382 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000383 except IOError:
384 pass
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000385 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000386 theme[element]=colour
387 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000388
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000389 def CurrentTheme(self):
390 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000391 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000392 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000393 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000394
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000395 def CurrentKeys(self):
396 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000397 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000398 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000399 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000400
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000401 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000402 """
403 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000404 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000405 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000406 extns=self.RemoveKeyBindNames(
407 self.GetSectionList('default','extensions'))
408 userExtns=self.RemoveKeyBindNames(
409 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000410 for extn in userExtns:
411 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000412 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000413 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000414 activeExtns=[]
415 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000416 if self.GetOption('extensions', extn, 'enable', default=True,
417 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000418 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000419 if editor_only or shell_only:
420 if editor_only:
421 option = "enable_editor"
422 else:
423 option = "enable_shell"
424 if self.GetOption('extensions', extn,option,
425 default=True, type='bool',
426 warn_on_default=False):
427 activeExtns.append(extn)
428 else:
429 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000430 return activeExtns
431 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000432 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000433
Steven M. Gavac628a062002-01-19 10:33:21 +0000434 def RemoveKeyBindNames(self,extnNameList):
435 #get rid of keybinding section names
436 names=extnNameList
437 kbNameIndicies=[]
438 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000439 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000440 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000441 kbNameIndicies.sort()
442 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000443 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000444 del(names[index])
445 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000446
Steven M. Gavaa498af22002-02-01 01:33:36 +0000447 def GetExtnNameForEvent(self,virtualEvent):
448 """
449 Returns the name of the extension that virtualEvent is bound in, or
450 None if not bound in any extension.
451 virtualEvent - string, name of the virtual event to test for, without
452 the enclosing '<< >>'
453 """
454 extName=None
455 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000456 for extn in self.GetExtensions(active_only=0):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400457 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000458 if event == vEvent:
459 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000460 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000461
Steven M. Gavac628a062002-01-19 10:33:21 +0000462 def GetExtensionKeys(self,extensionName):
463 """
464 returns a dictionary of the configurable keybindings for a particular
465 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000466 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000467 """
468 keysName=extensionName+'_cfgBindings'
469 activeKeys=self.GetCurrentKeySet()
470 extKeys={}
471 if self.defaultCfg['extensions'].has_section(keysName):
472 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
473 for eventName in eventNames:
474 event='<<'+eventName+'>>'
475 binding=activeKeys[event]
476 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000477 return extKeys
478
Steven M. Gavac628a062002-01-19 10:33:21 +0000479 def __GetRawExtensionKeys(self,extensionName):
480 """
481 returns a dictionary of the configurable keybindings for a particular
482 extension, as defined in the configuration files, or an empty dictionary
483 if no bindings are found
484 """
485 keysName=extensionName+'_cfgBindings'
486 extKeys={}
487 if self.defaultCfg['extensions'].has_section(keysName):
488 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
489 for eventName in eventNames:
490 binding=self.GetOption('extensions',keysName,
491 eventName,default='').split()
492 event='<<'+eventName+'>>'
493 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000494 return extKeys
495
Steven M. Gavac628a062002-01-19 10:33:21 +0000496 def GetExtensionBindings(self,extensionName):
497 """
498 Returns a dictionary of all the event bindings for a particular
499 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000500 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000501 keybindings are disabled.
502 """
503 bindsName=extensionName+'_bindings'
504 extBinds=self.GetExtensionKeys(extensionName)
505 #add the non-configurable bindings
506 if self.defaultCfg['extensions'].has_section(bindsName):
507 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
508 for eventName in eventNames:
509 binding=self.GetOption('extensions',bindsName,
510 eventName,default='').split()
511 event='<<'+eventName+'>>'
512 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000513
514 return extBinds
515
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000516 def GetKeyBinding(self, keySetName, eventStr):
517 """
518 returns the keybinding for a specific event.
519 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000520 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000521 represented as a string, eg. '<<event>>'
522 """
523 eventName=eventStr[2:-2] #trim off the angle brackets
524 binding=self.GetOption('keys',keySetName,eventName,default='').split()
525 return binding
526
Steven M. Gavac628a062002-01-19 10:33:21 +0000527 def GetCurrentKeySet(self):
Ronald Oussoren19302d92006-06-11 14:33:36 +0000528 result = self.GetKeySet(self.CurrentKeys())
529
Ned Deily57847df2014-03-27 20:47:04 -0700530 if sys.platform == "darwin":
531 # OS X Tk variants do not support the "Alt" keyboard modifier.
532 # So replace all keybingings that use "Alt" with ones that
533 # use the "Option" keyboard modifier.
534 # TO DO: the "Option" modifier does not work properly for
535 # Cocoa Tk and XQuartz Tk so we should not use it
536 # in default OS X KeySets.
Ronald Oussoren19302d92006-06-11 14:33:36 +0000537 for k, v in result.items():
538 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
539 if v != v2:
540 result[k] = v2
541
542 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000543
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000544 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000545 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000546 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000547 keybindings for all currently active extensions. If a binding defined
548 in an extension is already in use, that binding is disabled.
549 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000550 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000551 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000552 for extn in activeExtns:
553 extKeys=self.__GetRawExtensionKeys(extn)
554 if extKeys: #the extension defines keybindings
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400555 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000556 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000557 #the binding is already in use
558 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000559 keySet[event]=extKeys[event] #add binding
560 return keySet
561
Steven M. Gavaa498af22002-02-01 01:33:36 +0000562 def IsCoreBinding(self,virtualEvent):
563 """
564 returns true if the virtual event is bound in the core idle keybindings.
565 virtualEvent - string, name of the virtual event to test for, without
566 the enclosing '<< >>'
567 """
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400568 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000569
Steven M. Gavac628a062002-01-19 10:33:21 +0000570 def GetCoreKeys(self, keySetName=None):
571 """
572 returns the requested set of core keybindings, with fallbacks if
573 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000574 Keybindings loaded from the config file(s) are loaded _over_ these
575 defaults, so if there is a problem getting any core binding there will
576 be an 'ultimate last resort fallback' to the CUA-ish bindings
577 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000578 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000579 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000580 '<<copy>>': ['<Control-c>', '<Control-C>'],
581 '<<cut>>': ['<Control-x>', '<Control-X>'],
582 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000583 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
584 '<<center-insert>>': ['<Control-l>'],
585 '<<close-all-windows>>': ['<Control-q>'],
586 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000587 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000588 '<<end-of-file>>': ['<Control-d>'],
589 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000590 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000591 '<<history-next>>': ['<Alt-n>'],
592 '<<history-previous>>': ['<Alt-p>'],
593 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000594 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000595 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000596 '<<open-class-browser>>': ['<Alt-c>'],
597 '<<open-module>>': ['<Alt-m>'],
598 '<<open-new-window>>': ['<Control-n>'],
599 '<<open-window-from-file>>': ['<Control-o>'],
600 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000601 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000602 '<<redo>>': ['<Control-y>'],
603 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000604 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000605 '<<save-window-as-file>>': ['<Alt-s>'],
606 '<<save-window>>': ['<Control-s>'],
607 '<<select-all>>': ['<Alt-a>'],
608 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000609 '<<undo>>': ['<Control-z>'],
610 '<<find-again>>': ['<Control-g>', '<F3>'],
611 '<<find-in-files>>': ['<Alt-F3>'],
612 '<<find-selection>>': ['<Control-F3>'],
613 '<<find>>': ['<Control-f>'],
614 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000615 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000616 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlovc37db102012-03-29 19:54:58 +0300617 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000618 '<<smart-indent>>': ['<Key-Tab>'],
619 '<<indent-region>>': ['<Control-Key-bracketright>'],
620 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
621 '<<comment-region>>': ['<Alt-Key-3>'],
622 '<<uncomment-region>>': ['<Alt-Key-4>'],
623 '<<tabify-region>>': ['<Alt-Key-5>'],
624 '<<untabify-region>>': ['<Alt-Key-6>'],
625 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000626 '<<change-indentwidth>>': ['<Alt-Key-u>'],
627 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
628 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000629 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000630 if keySetName:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400631 for event in keyBindings:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000632 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000633 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000634 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000635 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000636 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
637 ' -\n problem retrieving key binding for event %r'
638 '\n from key set %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400639 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000640 (event, keySetName, keyBindings[event]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000641 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400642 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000643 except IOError:
644 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000645 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000646
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000647 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000648 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000649
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000650 Valid configSets are 'user' or 'default'. Return a list of tuples of
651 the form (menu_item , path_to_help_file , option), or return the empty
652 list. 'option' is the sequence number of the help resource. 'option'
653 values determine the position of the menu items on the Help menu,
654 therefore the returned list must be sorted by 'option'.
655
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000656 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000657 helpSources=[]
658 if configSet=='user':
659 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000660 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000661 cfgParser=self.defaultCfg['main']
662 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400663 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000664 options=cfgParser.GetOptionList('HelpFiles')
665 for option in options:
666 value=cfgParser.Get('HelpFiles',option,default=';')
667 if value.find(';')==-1: #malformed config entry with no ';'
668 menuItem='' #make these empty
669 helpPath='' #so value won't be added to list
670 else: #config entry contains ';' as expected
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400671 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000672 menuItem=value[0].strip()
673 helpPath=value[1].strip()
674 if menuItem and helpPath: #neither are empty strings
675 helpSources.append( (menuItem,helpPath,option) )
Florent Xiclunaa7f242f2010-04-02 08:15:26 +0000676 helpSources.sort(key=lambda x: int(x[2]))
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000677 return helpSources
678
679 def GetAllExtraHelpSourcesList(self):
680 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000681 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000682 sources configured, or an empty list if there are none. Tuples are of
683 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000684 """
685 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000686 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000687 return allHelpSources
688
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000689 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000690 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000691 load all configuration files.
692 """
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400693 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000694 self.defaultCfg[key].Load()
695 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000696
697 def SaveUserCfgFiles(self):
698 """
699 write all loaded user configuration files back to disk
700 """
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400701 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000702 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000703
704idleConf=IdleConf()
705
706### module test
707if __name__ == '__main__':
708 def dumpCfg(cfg):
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400709 print('\n', cfg, '\n')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400710 for key in cfg:
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000711 sections=cfg[key].sections()
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400712 print(key)
713 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000714 for section in sections:
715 options=cfg[key].options(section)
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400716 print(section)
717 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000718 for option in options:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400719 print(option, '=', cfg[key].Get(section,option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000720 dumpCfg(idleConf.defaultCfg)
721 dumpCfg(idleConf.userCfg)
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400722 print(idleConf.userCfg['main'].Get('Theme','name'))
723 #print(idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal'))