blob: a974d54bce48e46a16aab89a6e1661cd320be2ea [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
Guido van Rossum36e0a922007-07-20 04:05:57 +000022
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000023from idlelib import macosxSupport
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +000024from configparser import ConfigParser, NoOptionError, NoSectionError
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
Serhiy Storchaka89953002013-02-07 15:24:36 +020040 ConfigParser.__init__(self, defaults=cfgDefaults, strict=False)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000041
Thomas Wouterscf297e42007-02-23 15:07:44 +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 """
Thomas Wouterscf297e42007-02-23 15:07:44 +000047 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000048 return default
Thomas Wouterscf297e42007-02-23 15:07:44 +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():
Christian Heimesbbffeb62008-01-24 09:42:52 +0000142 fname = self.file
143 try:
144 cfgFile = open(fname, 'w')
Terry Jan Reedyba6c0d32013-06-08 00:22:45 -0400145 except OSError:
Christian Heimes0bd4e112008-02-12 22:59:25 +0000146 os.unlink(fname)
Christian Heimesbbffeb62008-01-24 09:42:52 +0000147 cfgFile = open(fname, 'w')
Amaury Forgeot d'Arcbbe7b0a2011-10-03 20:33:24 +0200148 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 '+
207 userDir+',\n but the path does not exist.\n')
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000208 try:
209 sys.stderr.write(warn)
Terry Jan Reedyba6c0d32013-06-08 00:22:45 -0400210 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000211 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)
Terry Jan Reedyba6c0d32013-06-08 00:22:45 -0400220 except OSError:
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000221 warn = ('\n Warning: unable to create user config directory\n'+
222 userDir+'\n Check path and permissions.\n Exiting!\n\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000223 sys.stderr.write(warn)
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,
Thomas Wouterscf297e42007-02-23 15:07:44 +0000228 warn_on_default=True, raw=False):
Steven M. Gava429a86af2001-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. Gava429a86af2001-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. Gava429a86af2001-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. Gava429a86af2001-10-23 10:42:12 +0000240 """
Andrew Svetlov8a495a42012-12-24 13:15:43 +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'
248 ' from section %r: %r\n' %
249 (type, option, section,
250 self.userCfg[configType].Get(section, option,
251 raw=raw)))
252 try:
253 sys.stderr.write(warning)
Terry Jan Reedyba6c0d32013-06-08 00:22:45 -0400254 except OSError:
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200255 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'
267 ' returning default value: %r\n' %
268 (option, section, default))
269 try:
270 sys.stderr.write(warning)
Terry Jan Reedyba6c0d32013-06-08 00:22:45 -0400271 except OSError:
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200272 pass
273 return default
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000274 def SetOption(self, configType, section, option, value):
275 """In user's config file, set section's option to value.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000276 """
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')):
Kurt B. Kaiserad667422007-08-23 01:06:15 +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:
Kurt B. Kaiserad667422007-08-23 01:06:15 +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:
Kurt B. Kaiserad667422007-08-23 01:06:15 +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:
Kurt B. Kaiserad667422007-08-23 01:06:15 +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' }
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000371 for element in theme:
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'
377 ' returning default value: %r\n' %
378 (element, themeName, theme[element]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000379 try:
380 sys.stderr.write(warning)
Terry Jan Reedyba6c0d32013-06-08 00:22:45 -0400381 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000382 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:
Thomas Wouters0e3f5912006-08-11 14:57:12 +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):
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000455 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000456 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):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000526 result = self.GetKeySet(self.CurrentKeys())
527
528 if macosxSupport.runningAsOSXApp():
529 # We're using AquaTk, replace all keybingings that use the
530 # Alt key by ones that use the Option key because the former
531 # don't work reliably.
532 for k, v in result.items():
533 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
534 if v != v2:
535 result[k] = v2
536
537 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000538
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000539 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000540 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000541 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000542 keybindings for all currently active extensions. If a binding defined
543 in an extension is already in use, that binding is disabled.
544 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000545 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000546 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000547 for extn in activeExtns:
548 extKeys=self.__GetRawExtensionKeys(extn)
549 if extKeys: #the extension defines keybindings
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000550 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000551 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000552 #the binding is already in use
553 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000554 keySet[event]=extKeys[event] #add binding
555 return keySet
556
Steven M. Gavaa498af22002-02-01 01:33:36 +0000557 def IsCoreBinding(self,virtualEvent):
558 """
559 returns true if the virtual event is bound in the core idle keybindings.
560 virtualEvent - string, name of the virtual event to test for, without
561 the enclosing '<< >>'
562 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000563 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000564
Steven M. Gavac628a062002-01-19 10:33:21 +0000565 def GetCoreKeys(self, keySetName=None):
566 """
567 returns the requested set of core keybindings, with fallbacks if
568 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000569 Keybindings loaded from the config file(s) are loaded _over_ these
570 defaults, so if there is a problem getting any core binding there will
571 be an 'ultimate last resort fallback' to the CUA-ish bindings
572 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000573 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000574 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000575 '<<copy>>': ['<Control-c>', '<Control-C>'],
576 '<<cut>>': ['<Control-x>', '<Control-X>'],
577 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000578 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
579 '<<center-insert>>': ['<Control-l>'],
580 '<<close-all-windows>>': ['<Control-q>'],
581 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000582 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000583 '<<end-of-file>>': ['<Control-d>'],
584 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000585 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000586 '<<history-next>>': ['<Alt-n>'],
587 '<<history-previous>>': ['<Alt-p>'],
588 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000589 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000590 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000591 '<<open-class-browser>>': ['<Alt-c>'],
592 '<<open-module>>': ['<Alt-m>'],
593 '<<open-new-window>>': ['<Control-n>'],
594 '<<open-window-from-file>>': ['<Control-o>'],
595 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000596 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000597 '<<redo>>': ['<Control-y>'],
598 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000599 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000600 '<<save-window-as-file>>': ['<Alt-s>'],
601 '<<save-window>>': ['<Control-s>'],
602 '<<select-all>>': ['<Alt-a>'],
603 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000604 '<<undo>>': ['<Control-z>'],
605 '<<find-again>>': ['<Control-g>', '<F3>'],
606 '<<find-in-files>>': ['<Alt-F3>'],
607 '<<find-selection>>': ['<Control-F3>'],
608 '<<find>>': ['<Control-f>'],
609 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000610 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000611 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlov67ac0792012-03-29 19:01:28 +0300612 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000613 '<<smart-indent>>': ['<Key-Tab>'],
614 '<<indent-region>>': ['<Control-Key-bracketright>'],
615 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
616 '<<comment-region>>': ['<Alt-Key-3>'],
617 '<<uncomment-region>>': ['<Alt-Key-4>'],
618 '<<tabify-region>>': ['<Alt-Key-5>'],
619 '<<untabify-region>>': ['<Alt-Key-6>'],
620 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000621 '<<change-indentwidth>>': ['<Alt-Key-u>'],
622 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
623 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000624 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000625 if keySetName:
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000626 for event in keyBindings:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000627 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000628 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000629 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000630 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000631 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
632 ' -\n problem retrieving key binding for event %r'
633 '\n from key set %r.\n'
634 ' returning default value: %r\n' %
635 (event, keySetName, keyBindings[event]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000636 try:
637 sys.stderr.write(warning)
Terry Jan Reedyba6c0d32013-06-08 00:22:45 -0400638 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000639 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000640 return keyBindings
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000641 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000642 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000643 Valid configSets are 'user' or 'default'. Return a list of tuples of
644 the form (menu_item , path_to_help_file , option), or return the empty
645 list. 'option' is the sequence number of the help resource. 'option'
646 values determine the position of the menu items on the Help menu,
647 therefore the returned list must be sorted by 'option'.
648
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000649 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000650 helpSources=[]
651 if configSet=='user':
652 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000653 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000654 cfgParser=self.defaultCfg['main']
655 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000656 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000657 options=cfgParser.GetOptionList('HelpFiles')
658 for option in options:
659 value=cfgParser.Get('HelpFiles',option,default=';')
660 if value.find(';')==-1: #malformed config entry with no ';'
661 menuItem='' #make these empty
662 helpPath='' #so value won't be added to list
663 else: #config entry contains ';' as expected
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000664 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000665 menuItem=value[0].strip()
666 helpPath=value[1].strip()
667 if menuItem and helpPath: #neither are empty strings
668 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser4718bf82008-02-12 21:34:12 +0000669 helpSources.sort(key=lambda x: x[2])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000670 return helpSources
671
672 def GetAllExtraHelpSourcesList(self):
673 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000674 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000675 sources configured, or an empty list if there are none. Tuples are of
676 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000677 """
678 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000679 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000680 return allHelpSources
681
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000682 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000683 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000684 load all configuration files.
685 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000686 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000687 self.defaultCfg[key].Load()
688 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000689
690 def SaveUserCfgFiles(self):
691 """
692 write all loaded user configuration files back to disk
693 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000694 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000695 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000696
697idleConf=IdleConf()
698
699### module test
700if __name__ == '__main__':
701 def dumpCfg(cfg):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000702 print('\n',cfg,'\n')
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000703 for key in cfg:
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000704 sections=cfg[key].sections()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000705 print(key)
706 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000707 for section in sections:
708 options=cfg[key].options(section)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000709 print(section)
710 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000711 for option in options:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000712 print(option, '=', cfg[key].Get(section,option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000713 dumpCfg(idleConf.defaultCfg)
714 dumpCfg(idleConf.userCfg)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000715 print(idleConf.userCfg['main'].Get('Theme','name'))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000716 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')