blob: 73b8db5b23261bce4ab04724a27ed0eef41bcd14 [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
40 ConfigParser.__init__(self,defaults=cfgDefaults)
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')
145 except IOError:
Christian Heimes0bd4e112008-02-12 22:59:25 +0000146 os.unlink(fname)
Christian Heimesbbffeb62008-01-24 09:42:52 +0000147 cfgFile = open(fname, 'w')
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000148 self.write(cfgFile)
149 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000150 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000151
152class IdleConf:
153 """
154 holds config parsers for all idle config files:
155 default config files
156 (idle install dir)/config-main.def
157 (idle install dir)/config-extensions.def
158 (idle install dir)/config-highlight.def
159 (idle install dir)/config-keys.def
160 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000161 (user home dir)/.idlerc/config-main.cfg
162 (user home dir)/.idlerc/config-extensions.cfg
163 (user home dir)/.idlerc/config-highlight.cfg
164 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000165 """
166 def __init__(self):
167 self.defaultCfg={}
168 self.userCfg={}
169 self.cfg={}
170 self.CreateConfigHandlers()
171 self.LoadCfgFiles()
172 #self.LoadCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000173
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000174 def CreateConfigHandlers(self):
175 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000176 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000177 configurations respectively
178 """
179 #build idle install path
180 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000181 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000182 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000183 idleDir=os.path.abspath(sys.path[0])
184 userDir=self.GetUserCfgDir()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000185 configTypes=('main','extensions','highlight','keys')
186 defCfgFiles={}
187 usrCfgFiles={}
188 for cfgType in configTypes: #build config file names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000189 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
190 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000191 for cfgType in configTypes: #create config parsers
192 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
193 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000194
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000195 def GetUserCfgDir(self):
196 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000197 Creates (if required) and returns a filesystem directory for storing
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000198 user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000199
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000200 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000201 cfgDir = '.idlerc'
202 userDir = os.path.expanduser('~')
203 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000204 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000205 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
206 userDir+',\n but the path does not exist.\n')
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000207 try:
208 sys.stderr.write(warn)
209 except IOError:
210 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000211 userDir = '~'
212 if userDir == "~": # still no path to home!
213 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
214 userDir = os.getcwd()
215 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000216 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000217 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000218 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000219 except (OSError, IOError):
220 warn = ('\n Warning: unable to create user config directory\n'+
221 userDir+'\n Check path and permissions.\n Exiting!\n\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000222 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000223 raise SystemExit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000224 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000225
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000226 def GetOption(self, configType, section, option, default=None, type=None,
Thomas Wouterscf297e42007-02-23 15:07:44 +0000227 warn_on_default=True, raw=False):
Steven M. Gava429a86af2001-10-23 10:42:12 +0000228 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000229 Get an option value for given config type and given general
Steven M. Gava429a86af2001-10-23 10:42:12 +0000230 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000231 return as type. Firstly the user configuration is checked, with a
232 fallback to the default configuration, and a final 'catch all'
233 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86af2001-10-23 10:42:12 +0000234 either the user or the default configuration.
235 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000236 If a default is returned, and warn_on_default is True, a warning is
237 printed to stderr.
238
Steven M. Gava429a86af2001-10-23 10:42:12 +0000239 """
240 if self.userCfg[configType].has_option(section,option):
Thomas Wouterscf297e42007-02-23 15:07:44 +0000241 return self.userCfg[configType].Get(section, option,
242 type=type, raw=raw)
Steven M. Gava429a86af2001-10-23 10:42:12 +0000243 elif self.defaultCfg[configType].has_option(section,option):
Thomas Wouterscf297e42007-02-23 15:07:44 +0000244 return self.defaultCfg[configType].Get(section, option,
245 type=type, raw=raw)
Steven M. Gava052937f2002-02-11 02:20:53 +0000246 else: #returning default, print warning
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000247 if warn_on_default:
248 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
Benjamin Peterson5879d412009-03-30 14:51:56 +0000249 ' problem retrieving configuration option %r\n'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000250 ' from section %r.\n'
251 ' returning default value: %r\n' %
252 (option, section, default))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000253 try:
254 sys.stderr.write(warning)
255 except IOError:
256 pass
Steven M. Gava429a86af2001-10-23 10:42:12 +0000257 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000258
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000259 def SetOption(self, configType, section, option, value):
260 """In user's config file, set section's option to value.
261
262 """
263 self.userCfg[configType].SetOption(section, option, value)
264
Steven M. Gava2a63a072001-10-26 06:50:54 +0000265 def GetSectionList(self, configSet, configType):
266 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000267 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000268 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000269 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000270 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000271 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000272 if not (configType in ('main','extensions','highlight','keys')):
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000273 raise InvalidConfigType('Invalid configType specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000274 if configSet == 'user':
275 cfgParser=self.userCfg[configType]
276 elif configSet == 'default':
277 cfgParser=self.defaultCfg[configType]
278 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000279 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000280 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000281
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000282 def GetHighlight(self, theme, element, fgBg=None):
283 """
284 return individual highlighting theme elements.
285 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000286 containing fg and bg colours (appropriate for passing to Tkinter in,
287 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000288 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000289 if self.defaultCfg['highlight'].has_section(theme):
290 themeDict=self.GetThemeDict('default',theme)
291 else:
292 themeDict=self.GetThemeDict('user',theme)
293 fore=themeDict[element+'-foreground']
294 if element=='cursor': #there is no config value for cursor bg
295 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000296 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000297 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000298 highlight={"foreground": fore,"background": back}
299 if not fgBg: #return dict of both colours
300 return highlight
301 else: #return specified colour only
302 if fgBg == 'fg':
303 return highlight["foreground"]
304 if fgBg == 'bg':
305 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000306 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000307 raise InvalidFgBg('Invalid fgBg specified')
Steven M. Gava9f25e672002-02-11 02:51:18 +0000308
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000309 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000310 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000311 type - string, 'default' or 'user' theme type
312 themeName - string, theme name
313 Returns a dictionary which holds {option:value} for each element
314 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000315 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000316 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000317 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000318 if type == 'user':
319 cfgParser=self.userCfg['highlight']
320 elif type == 'default':
321 cfgParser=self.defaultCfg['highlight']
322 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000323 raise InvalidTheme('Invalid theme type specified')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000324 #foreground and background values are provded for each theme element
325 #(apart from cursor) even though all these values are not yet used
326 #by idle, to allow for their use in the future. Default values are
327 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000328 theme={ 'normal-foreground':'#000000',
329 'normal-background':'#ffffff',
330 'keyword-foreground':'#000000',
331 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000332 'builtin-foreground':'#000000',
333 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000334 'comment-foreground':'#000000',
335 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000336 'string-foreground':'#000000',
337 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000338 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000339 'definition-background':'#ffffff',
340 'hilite-foreground':'#000000',
341 'hilite-background':'gray',
342 'break-foreground':'#ffffff',
343 'break-background':'#000000',
344 'hit-foreground':'#ffffff',
345 'hit-background':'#000000',
346 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000347 'error-background':'#000000',
348 #cursor (only foreground can be set)
349 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000350 #shell window
351 'stdout-foreground':'#000000',
352 'stdout-background':'#ffffff',
353 'stderr-foreground':'#000000',
354 'stderr-background':'#ffffff',
355 'console-foreground':'#000000',
356 'console-background':'#ffffff' }
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000357 for element in theme:
Steven M. Gava052937f2002-02-11 02:20:53 +0000358 if not cfgParser.has_option(themeName,element):
359 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000360 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
361 ' -\n problem retrieving theme element %r'
362 '\n from theme %r.\n'
363 ' returning default value: %r\n' %
364 (element, themeName, theme[element]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000365 try:
366 sys.stderr.write(warning)
367 except IOError:
368 pass
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000369 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000370 theme[element]=colour
371 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000372
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000373 def CurrentTheme(self):
374 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000375 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000376 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000377 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000378
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000379 def CurrentKeys(self):
380 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000381 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000382 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000383 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000384
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000385 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000386 """
387 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000388 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000389 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000390 extns=self.RemoveKeyBindNames(
391 self.GetSectionList('default','extensions'))
392 userExtns=self.RemoveKeyBindNames(
393 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000394 for extn in userExtns:
395 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000396 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000397 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000398 activeExtns=[]
399 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000400 if self.GetOption('extensions', extn, 'enable', default=True,
401 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000402 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000403 if editor_only or shell_only:
404 if editor_only:
405 option = "enable_editor"
406 else:
407 option = "enable_shell"
408 if self.GetOption('extensions', extn,option,
409 default=True, type='bool',
410 warn_on_default=False):
411 activeExtns.append(extn)
412 else:
413 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000414 return activeExtns
415 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000416 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000417
Steven M. Gavac628a062002-01-19 10:33:21 +0000418 def RemoveKeyBindNames(self,extnNameList):
419 #get rid of keybinding section names
420 names=extnNameList
421 kbNameIndicies=[]
422 for name in names:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000423 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000424 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000425 kbNameIndicies.sort()
426 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000427 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000428 del(names[index])
429 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000430
Steven M. Gavaa498af22002-02-01 01:33:36 +0000431 def GetExtnNameForEvent(self,virtualEvent):
432 """
433 Returns the name of the extension that virtualEvent is bound in, or
434 None if not bound in any extension.
435 virtualEvent - string, name of the virtual event to test for, without
436 the enclosing '<< >>'
437 """
438 extName=None
439 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000440 for extn in self.GetExtensions(active_only=0):
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000441 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000442 if event == vEvent:
443 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000444 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000445
Steven M. Gavac628a062002-01-19 10:33:21 +0000446 def GetExtensionKeys(self,extensionName):
447 """
448 returns a dictionary of the configurable keybindings for a particular
449 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000450 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000451 """
452 keysName=extensionName+'_cfgBindings'
453 activeKeys=self.GetCurrentKeySet()
454 extKeys={}
455 if self.defaultCfg['extensions'].has_section(keysName):
456 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
457 for eventName in eventNames:
458 event='<<'+eventName+'>>'
459 binding=activeKeys[event]
460 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000461 return extKeys
462
Steven M. Gavac628a062002-01-19 10:33:21 +0000463 def __GetRawExtensionKeys(self,extensionName):
464 """
465 returns a dictionary of the configurable keybindings for a particular
466 extension, as defined in the configuration files, or an empty dictionary
467 if no bindings are found
468 """
469 keysName=extensionName+'_cfgBindings'
470 extKeys={}
471 if self.defaultCfg['extensions'].has_section(keysName):
472 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
473 for eventName in eventNames:
474 binding=self.GetOption('extensions',keysName,
475 eventName,default='').split()
476 event='<<'+eventName+'>>'
477 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000478 return extKeys
479
Steven M. Gavac628a062002-01-19 10:33:21 +0000480 def GetExtensionBindings(self,extensionName):
481 """
482 Returns a dictionary of all the event bindings for a particular
483 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000484 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000485 keybindings are disabled.
486 """
487 bindsName=extensionName+'_bindings'
488 extBinds=self.GetExtensionKeys(extensionName)
489 #add the non-configurable bindings
490 if self.defaultCfg['extensions'].has_section(bindsName):
491 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
492 for eventName in eventNames:
493 binding=self.GetOption('extensions',bindsName,
494 eventName,default='').split()
495 event='<<'+eventName+'>>'
496 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000497
498 return extBinds
499
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000500 def GetKeyBinding(self, keySetName, eventStr):
501 """
502 returns the keybinding for a specific event.
503 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000504 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000505 represented as a string, eg. '<<event>>'
506 """
507 eventName=eventStr[2:-2] #trim off the angle brackets
508 binding=self.GetOption('keys',keySetName,eventName,default='').split()
509 return binding
510
Steven M. Gavac628a062002-01-19 10:33:21 +0000511 def GetCurrentKeySet(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000512 result = self.GetKeySet(self.CurrentKeys())
513
514 if macosxSupport.runningAsOSXApp():
515 # We're using AquaTk, replace all keybingings that use the
516 # Alt key by ones that use the Option key because the former
517 # don't work reliably.
518 for k, v in result.items():
519 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
520 if v != v2:
521 result[k] = v2
522
523 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000524
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000525 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000526 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000527 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000528 keybindings for all currently active extensions. If a binding defined
529 in an extension is already in use, that binding is disabled.
530 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000531 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000532 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000533 for extn in activeExtns:
534 extKeys=self.__GetRawExtensionKeys(extn)
535 if extKeys: #the extension defines keybindings
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000536 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000537 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000538 #the binding is already in use
539 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000540 keySet[event]=extKeys[event] #add binding
541 return keySet
542
Steven M. Gavaa498af22002-02-01 01:33:36 +0000543 def IsCoreBinding(self,virtualEvent):
544 """
545 returns true if the virtual event is bound in the core idle keybindings.
546 virtualEvent - string, name of the virtual event to test for, without
547 the enclosing '<< >>'
548 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000549 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000550
Steven M. Gavac628a062002-01-19 10:33:21 +0000551 def GetCoreKeys(self, keySetName=None):
552 """
553 returns the requested set of core keybindings, with fallbacks if
554 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000555 Keybindings loaded from the config file(s) are loaded _over_ these
556 defaults, so if there is a problem getting any core binding there will
557 be an 'ultimate last resort fallback' to the CUA-ish bindings
558 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000559 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000560 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000561 '<<copy>>': ['<Control-c>', '<Control-C>'],
562 '<<cut>>': ['<Control-x>', '<Control-X>'],
563 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000564 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
565 '<<center-insert>>': ['<Control-l>'],
566 '<<close-all-windows>>': ['<Control-q>'],
567 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000568 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000569 '<<end-of-file>>': ['<Control-d>'],
570 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000571 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000572 '<<history-next>>': ['<Alt-n>'],
573 '<<history-previous>>': ['<Alt-p>'],
574 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000575 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000576 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000577 '<<open-class-browser>>': ['<Alt-c>'],
578 '<<open-module>>': ['<Alt-m>'],
579 '<<open-new-window>>': ['<Control-n>'],
580 '<<open-window-from-file>>': ['<Control-o>'],
581 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000582 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000583 '<<redo>>': ['<Control-y>'],
584 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000585 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000586 '<<save-window-as-file>>': ['<Alt-s>'],
587 '<<save-window>>': ['<Control-s>'],
588 '<<select-all>>': ['<Alt-a>'],
589 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000590 '<<undo>>': ['<Control-z>'],
591 '<<find-again>>': ['<Control-g>', '<F3>'],
592 '<<find-in-files>>': ['<Alt-F3>'],
593 '<<find-selection>>': ['<Control-F3>'],
594 '<<find>>': ['<Control-f>'],
595 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000596 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000597 '<<smart-backspace>>': ['<Key-BackSpace>'],
598 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
599 '<<smart-indent>>': ['<Key-Tab>'],
600 '<<indent-region>>': ['<Control-Key-bracketright>'],
601 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
602 '<<comment-region>>': ['<Alt-Key-3>'],
603 '<<uncomment-region>>': ['<Alt-Key-4>'],
604 '<<tabify-region>>': ['<Alt-Key-5>'],
605 '<<untabify-region>>': ['<Alt-Key-6>'],
606 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000607 '<<change-indentwidth>>': ['<Alt-Key-u>'],
608 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
609 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000610 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000611 if keySetName:
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000612 for event in keyBindings:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000613 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000614 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000615 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000616 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000617 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
618 ' -\n problem retrieving key binding for event %r'
619 '\n from key set %r.\n'
620 ' returning default value: %r\n' %
621 (event, keySetName, keyBindings[event]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000622 try:
623 sys.stderr.write(warning)
624 except IOError:
625 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000626 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000627
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000628 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000629 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000630
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000631 Valid configSets are 'user' or 'default'. Return a list of tuples of
632 the form (menu_item , path_to_help_file , option), or return the empty
633 list. 'option' is the sequence number of the help resource. 'option'
634 values determine the position of the menu items on the Help menu,
635 therefore the returned list must be sorted by 'option'.
636
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000637 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000638 helpSources=[]
639 if configSet=='user':
640 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000641 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000642 cfgParser=self.defaultCfg['main']
643 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000644 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000645 options=cfgParser.GetOptionList('HelpFiles')
646 for option in options:
647 value=cfgParser.Get('HelpFiles',option,default=';')
648 if value.find(';')==-1: #malformed config entry with no ';'
649 menuItem='' #make these empty
650 helpPath='' #so value won't be added to list
651 else: #config entry contains ';' as expected
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000652 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000653 menuItem=value[0].strip()
654 helpPath=value[1].strip()
655 if menuItem and helpPath: #neither are empty strings
656 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser4718bf82008-02-12 21:34:12 +0000657 helpSources.sort(key=lambda x: x[2])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000658 return helpSources
659
660 def GetAllExtraHelpSourcesList(self):
661 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000662 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000663 sources configured, or an empty list if there are none. Tuples are of
664 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000665 """
666 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000667 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000668 return allHelpSources
669
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000670 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000671 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000672 load all configuration files.
673 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000674 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000675 self.defaultCfg[key].Load()
676 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000677
678 def SaveUserCfgFiles(self):
679 """
680 write all loaded user configuration files back to disk
681 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000682 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000683 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000684
685idleConf=IdleConf()
686
687### module test
688if __name__ == '__main__':
689 def dumpCfg(cfg):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000690 print('\n',cfg,'\n')
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000691 for key in cfg:
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000692 sections=cfg[key].sections()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000693 print(key)
694 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000695 for section in sections:
696 options=cfg[key].options(section)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000697 print(section)
698 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000699 for option in options:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000700 print(option, '=', cfg[key].Get(section,option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000701 dumpCfg(idleConf.defaultCfg)
702 dumpCfg(idleConf.userCfg)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000703 print(idleConf.userCfg['main'].Get('Theme','name'))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000704 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')