blob: fada4eb0156f532eabd92260875d9355ff571c64 [file] [log] [blame]
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001"""Provides access to stored IDLE configuration information.
Steven M. Gavac5976402002-01-04 03:06:08 +00002
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00003Refer to the comments at the beginning of config-main.def for a description of
4the available configuration files and the design implemented to update user
5configuration information. In particular, user configuration choices which
6duplicate the defaults will be removed from the user's configuration files,
Kurt B. Kaisere66675b2003-01-27 02:36:18 +00007and if a file becomes empty, it will be deleted.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00008
9The contents of the user files may be altered using the Options/Configure IDLE
10menu to access the configuration GUI (configDialog.py), or manually.
11
12Throughout this module there is an emphasis on returning useable defaults
13when a problem occurs in returning a requested configuration value back to
14idle. This is to allow IDLE to continue to function in spite of errors in
15the retrieval of config information. When a default is returned instead of
16a requested config value, a message is printed to stderr to aid in
17configuration problem notification and resolution.
18
19"""
20import os
21import sys
22import string
Ronald Oussoren19302d92006-06-11 14:33:36 +000023import macosxSupport
Steven M. Gavac11ccf32001-09-24 09:43:17 +000024from ConfigParser import ConfigParser, NoOptionError, NoSectionError
25
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
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000042 def Get(self, section, option, type=None, default=None):
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. Kaiser6655e4b2002-12-31 16:03:23 +000047 if type=='bool':
Steven M. Gava41a85322001-10-29 08:05:34 +000048 getVal=self.getboolean
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000049 elif type=='int':
Steven M. Gava41a85322001-10-29 08:05:34 +000050 getVal=self.getint
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000051 else:
Steven M. Gava41a85322001-10-29 08:05:34 +000052 getVal=self.get
Steven M. Gavac11ccf32001-09-24 09:43:17 +000053 if self.has_option(section,option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000054 #return getVal(section, option, raw, vars, default)
Steven M. Gava429a86a2001-10-23 10:42:12 +000055 return getVal(section, option)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000056 else:
57 return default
Steven M. Gavac11ccf32001-09-24 09:43:17 +000058
Steven M. Gavac11ccf32001-09-24 09:43:17 +000059 def GetOptionList(self,section):
60 """
61 Get an option list for given section
62 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +000063 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000064 return self.options(section)
65 else: #return a default value
66 return []
67
Steven M. Gavac11ccf32001-09-24 09:43:17 +000068 def Load(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000069 """
70 Load the configuration file from disk
Steven M. Gavac11ccf32001-09-24 09:43:17 +000071 """
72 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000073
Steven M. Gavac11ccf32001-09-24 09:43:17 +000074class IdleUserConfParser(IdleConfParser):
75 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000076 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000077 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000078
79 def AddSection(self,section):
80 """
81 if section doesn't exist, add it
82 """
83 if not self.has_section(section):
84 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000085
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000086 def RemoveEmptySections(self):
87 """
88 remove any sections that have no options
89 """
90 for section in self.sections():
91 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000092 self.remove_section(section)
93
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000094 def IsEmpty(self):
95 """
96 Remove empty sections and then return 1 if parser has no sections
97 left, else return 0.
98 """
99 self.RemoveEmptySections()
100 if self.sections():
101 return 0
102 else:
103 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000104
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000105 def RemoveOption(self,section,option):
106 """
107 If section/option exists, remove it.
108 Returns 1 if option was removed, 0 otherwise.
109 """
110 if self.has_section(section):
111 return self.remove_option(section,option)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000112
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000113 def SetOption(self,section,option,value):
114 """
115 Sets option to value, adding section if required.
116 Returns 1 if option was added or changed, otherwise 0.
117 """
118 if self.has_option(section,option):
119 if self.get(section,option)==value:
120 return 0
121 else:
122 self.set(section,option,value)
123 return 1
124 else:
125 if not self.has_section(section):
126 self.add_section(section)
127 self.set(section,option,value)
128 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000129
Steven M. Gavab77d3432002-03-02 07:16:21 +0000130 def RemoveFile(self):
131 """
132 Removes the user config file from disk if it exists.
133 """
134 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000135 os.remove(self.file)
136
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000137 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000138 """Update user configuration file.
139
140 Remove empty sections. If resulting config isn't empty, write the file
141 to disk. If config is empty, remove the file from disk if it exists.
142
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000143 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000144 if not self.IsEmpty():
Kurt B. Kaiser0b45f362008-02-14 04:37:26 +0000145 fname = self.file
146 try:
147 cfgFile = open(fname, 'w')
148 except IOError:
149 os.unlink(fname)
150 cfgFile = open(fname, 'w')
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000151 self.write(cfgFile)
152 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000153 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000154
155class IdleConf:
156 """
157 holds config parsers for all idle config files:
158 default config files
159 (idle install dir)/config-main.def
160 (idle install dir)/config-extensions.def
161 (idle install dir)/config-highlight.def
162 (idle install dir)/config-keys.def
163 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000164 (user home dir)/.idlerc/config-main.cfg
165 (user home dir)/.idlerc/config-extensions.cfg
166 (user home dir)/.idlerc/config-highlight.cfg
167 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000168 """
169 def __init__(self):
170 self.defaultCfg={}
171 self.userCfg={}
172 self.cfg={}
173 self.CreateConfigHandlers()
174 self.LoadCfgFiles()
175 #self.LoadCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000176
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000177 def CreateConfigHandlers(self):
178 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000179 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000180 configurations respectively
181 """
182 #build idle install path
183 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000184 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000185 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000186 idleDir=os.path.abspath(sys.path[0])
187 userDir=self.GetUserCfgDir()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000188 configTypes=('main','extensions','highlight','keys')
189 defCfgFiles={}
190 usrCfgFiles={}
191 for cfgType in configTypes: #build config file names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000192 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
193 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000194 for cfgType in configTypes: #create config parsers
195 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
196 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000197
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000198 def GetUserCfgDir(self):
199 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000200 Creates (if required) and returns a filesystem directory for storing
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000201 user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000202
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000203 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000204 cfgDir = '.idlerc'
205 userDir = os.path.expanduser('~')
206 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000207 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000208 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
209 userDir+',\n but the path does not exist.\n')
Amaury Forgeot d'Arcf305bd32008-04-21 22:42:30 +0000210 try:
211 sys.stderr.write(warn)
212 except IOError:
213 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000214 userDir = '~'
215 if userDir == "~": # still no path to home!
216 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
217 userDir = os.getcwd()
218 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000219 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000220 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000221 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000222 except (OSError, IOError):
223 warn = ('\n Warning: unable to create user config directory\n'+
224 userDir+'\n Check path and permissions.\n Exiting!\n\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000225 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000226 raise SystemExit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000227 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000228
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000229 def GetOption(self, configType, section, option, default=None, type=None,
230 warn_on_default=True):
Steven M. Gava429a86a2001-10-23 10:42:12 +0000231 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000232 Get an option value for given config type and given general
Steven M. Gava429a86a2001-10-23 10:42:12 +0000233 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000234 return as type. Firstly the user configuration is checked, with a
235 fallback to the default configuration, and a final 'catch all'
236 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86a2001-10-23 10:42:12 +0000237 either the user or the default configuration.
238 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000239 If a default is returned, and warn_on_default is True, a warning is
240 printed to stderr.
241
Steven M. Gava429a86a2001-10-23 10:42:12 +0000242 """
243 if self.userCfg[configType].has_option(section,option):
244 return self.userCfg[configType].Get(section, option, type=type)
245 elif self.defaultCfg[configType].has_option(section,option):
246 return self.defaultCfg[configType].Get(section, option, type=type)
Steven M. Gava052937f2002-02-11 02:20:53 +0000247 else: #returning default, print warning
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000248 if warn_on_default:
249 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
250 ' problem retrieving configration option %r\n'
251 ' from section %r.\n'
252 ' returning default value: %r\n' %
253 (option, section, default))
Amaury Forgeot d'Arcf305bd32008-04-21 22:42:30 +0000254 try:
255 sys.stderr.write(warning)
256 except IOError:
257 pass
Steven M. Gava429a86a2001-10-23 10:42:12 +0000258 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000259
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000260 def SetOption(self, configType, section, option, value):
261 """In user's config file, set section's option to value.
262
263 """
264 self.userCfg[configType].SetOption(section, option, value)
265
Steven M. Gava2a63a072001-10-26 06:50:54 +0000266 def GetSectionList(self, configSet, configType):
267 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000268 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000269 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000270 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000271 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000272 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000273 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000274 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000275 if configSet == 'user':
276 cfgParser=self.userCfg[configType]
277 elif configSet == 'default':
278 cfgParser=self.defaultCfg[configType]
279 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000280 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000281 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000282
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000283 def GetHighlight(self, theme, element, fgBg=None):
284 """
285 return individual highlighting theme elements.
286 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000287 containing fg and bg colours (appropriate for passing to Tkinter in,
288 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000289 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000290 if self.defaultCfg['highlight'].has_section(theme):
291 themeDict=self.GetThemeDict('default',theme)
292 else:
293 themeDict=self.GetThemeDict('user',theme)
294 fore=themeDict[element+'-foreground']
295 if element=='cursor': #there is no config value for cursor bg
296 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000297 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000298 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000299 highlight={"foreground": fore,"background": back}
300 if not fgBg: #return dict of both colours
301 return highlight
302 else: #return specified colour only
303 if fgBg == 'fg':
304 return highlight["foreground"]
305 if fgBg == 'bg':
306 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000307 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000308 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000309
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000310 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000311 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000312 type - string, 'default' or 'user' theme type
313 themeName - string, theme name
314 Returns a dictionary which holds {option:value} for each element
315 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000316 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000317 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000318 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000319 if type == 'user':
320 cfgParser=self.userCfg['highlight']
321 elif type == 'default':
322 cfgParser=self.defaultCfg['highlight']
323 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000324 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000325 #foreground and background values are provded for each theme element
326 #(apart from cursor) even though all these values are not yet used
327 #by idle, to allow for their use in the future. Default values are
328 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000329 theme={ 'normal-foreground':'#000000',
330 'normal-background':'#ffffff',
331 'keyword-foreground':'#000000',
332 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000333 'builtin-foreground':'#000000',
334 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000335 'comment-foreground':'#000000',
336 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000337 'string-foreground':'#000000',
338 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000339 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000340 'definition-background':'#ffffff',
341 'hilite-foreground':'#000000',
342 'hilite-background':'gray',
343 'break-foreground':'#ffffff',
344 'break-background':'#000000',
345 'hit-foreground':'#ffffff',
346 'hit-background':'#000000',
347 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000348 'error-background':'#000000',
349 #cursor (only foreground can be set)
350 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000351 #shell window
352 'stdout-foreground':'#000000',
353 'stdout-background':'#ffffff',
354 'stderr-foreground':'#000000',
355 'stderr-background':'#ffffff',
356 'console-foreground':'#000000',
357 'console-background':'#ffffff' }
358 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000359 if not cfgParser.has_option(themeName,element):
360 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000361 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
362 ' -\n problem retrieving theme element %r'
363 '\n from theme %r.\n'
364 ' returning default value: %r\n' %
365 (element, themeName, theme[element]))
Amaury Forgeot d'Arcf305bd32008-04-21 22:42:30 +0000366 try:
367 sys.stderr.write(warning)
368 except IOError:
369 pass
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000370 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000371 theme[element]=colour
372 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000373
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000374 def CurrentTheme(self):
375 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000376 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000377 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000378 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000379
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000380 def CurrentKeys(self):
381 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000382 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000383 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000384 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000385
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000386 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000387 """
388 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000389 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000390 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000391 extns=self.RemoveKeyBindNames(
392 self.GetSectionList('default','extensions'))
393 userExtns=self.RemoveKeyBindNames(
394 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000395 for extn in userExtns:
396 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000397 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000398 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000399 activeExtns=[]
400 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000401 if self.GetOption('extensions', extn, 'enable', default=True,
402 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000403 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000404 if editor_only or shell_only:
405 if editor_only:
406 option = "enable_editor"
407 else:
408 option = "enable_shell"
409 if self.GetOption('extensions', extn,option,
410 default=True, type='bool',
411 warn_on_default=False):
412 activeExtns.append(extn)
413 else:
414 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000415 return activeExtns
416 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000417 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000418
Steven M. Gavac628a062002-01-19 10:33:21 +0000419 def RemoveKeyBindNames(self,extnNameList):
420 #get rid of keybinding section names
421 names=extnNameList
422 kbNameIndicies=[]
423 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000424 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000425 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000426 kbNameIndicies.sort()
427 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000428 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000429 del(names[index])
430 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000431
Steven M. Gavaa498af22002-02-01 01:33:36 +0000432 def GetExtnNameForEvent(self,virtualEvent):
433 """
434 Returns the name of the extension that virtualEvent is bound in, or
435 None if not bound in any extension.
436 virtualEvent - string, name of the virtual event to test for, without
437 the enclosing '<< >>'
438 """
439 extName=None
440 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000441 for extn in self.GetExtensions(active_only=0):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000442 for event in self.GetExtensionKeys(extn).keys():
443 if event == vEvent:
444 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000445 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000446
Steven M. Gavac628a062002-01-19 10:33:21 +0000447 def GetExtensionKeys(self,extensionName):
448 """
449 returns a dictionary of the configurable keybindings for a particular
450 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000451 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000452 """
453 keysName=extensionName+'_cfgBindings'
454 activeKeys=self.GetCurrentKeySet()
455 extKeys={}
456 if self.defaultCfg['extensions'].has_section(keysName):
457 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
458 for eventName in eventNames:
459 event='<<'+eventName+'>>'
460 binding=activeKeys[event]
461 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000462 return extKeys
463
Steven M. Gavac628a062002-01-19 10:33:21 +0000464 def __GetRawExtensionKeys(self,extensionName):
465 """
466 returns a dictionary of the configurable keybindings for a particular
467 extension, as defined in the configuration files, or an empty dictionary
468 if no bindings are found
469 """
470 keysName=extensionName+'_cfgBindings'
471 extKeys={}
472 if self.defaultCfg['extensions'].has_section(keysName):
473 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
474 for eventName in eventNames:
475 binding=self.GetOption('extensions',keysName,
476 eventName,default='').split()
477 event='<<'+eventName+'>>'
478 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000479 return extKeys
480
Steven M. Gavac628a062002-01-19 10:33:21 +0000481 def GetExtensionBindings(self,extensionName):
482 """
483 Returns a dictionary of all the event bindings for a particular
484 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000485 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000486 keybindings are disabled.
487 """
488 bindsName=extensionName+'_bindings'
489 extBinds=self.GetExtensionKeys(extensionName)
490 #add the non-configurable bindings
491 if self.defaultCfg['extensions'].has_section(bindsName):
492 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
493 for eventName in eventNames:
494 binding=self.GetOption('extensions',bindsName,
495 eventName,default='').split()
496 event='<<'+eventName+'>>'
497 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000498
499 return extBinds
500
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000501 def GetKeyBinding(self, keySetName, eventStr):
502 """
503 returns the keybinding for a specific event.
504 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000505 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000506 represented as a string, eg. '<<event>>'
507 """
508 eventName=eventStr[2:-2] #trim off the angle brackets
509 binding=self.GetOption('keys',keySetName,eventName,default='').split()
510 return binding
511
Steven M. Gavac628a062002-01-19 10:33:21 +0000512 def GetCurrentKeySet(self):
Ronald Oussoren19302d92006-06-11 14:33:36 +0000513 result = self.GetKeySet(self.CurrentKeys())
514
515 if macosxSupport.runningAsOSXApp():
516 # We're using AquaTk, replace all keybingings that use the
Tim Peters4f96f1f2006-06-11 19:42:51 +0000517 # Alt key by ones that use the Option key because the former
Ronald Oussoren19302d92006-06-11 14:33:36 +0000518 # don't work reliably.
519 for k, v in result.items():
520 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
521 if v != v2:
522 result[k] = v2
523
524 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000525
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000526 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000527 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000528 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000529 keybindings for all currently active extensions. If a binding defined
530 in an extension is already in use, that binding is disabled.
531 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000532 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000533 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000534 for extn in activeExtns:
535 extKeys=self.__GetRawExtensionKeys(extn)
536 if extKeys: #the extension defines keybindings
537 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000538 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000539 #the binding is already in use
540 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000541 keySet[event]=extKeys[event] #add binding
542 return keySet
543
Steven M. Gavaa498af22002-02-01 01:33:36 +0000544 def IsCoreBinding(self,virtualEvent):
545 """
546 returns true if the virtual event is bound in the core idle keybindings.
547 virtualEvent - string, name of the virtual event to test for, without
548 the enclosing '<< >>'
549 """
550 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000551
Steven M. Gavac628a062002-01-19 10:33:21 +0000552 def GetCoreKeys(self, keySetName=None):
553 """
554 returns the requested set of core keybindings, with fallbacks if
555 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000556 Keybindings loaded from the config file(s) are loaded _over_ these
557 defaults, so if there is a problem getting any core binding there will
558 be an 'ultimate last resort fallback' to the CUA-ish bindings
559 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000560 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000561 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000562 '<<copy>>': ['<Control-c>', '<Control-C>'],
563 '<<cut>>': ['<Control-x>', '<Control-X>'],
564 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000565 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
566 '<<center-insert>>': ['<Control-l>'],
567 '<<close-all-windows>>': ['<Control-q>'],
568 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000569 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000570 '<<end-of-file>>': ['<Control-d>'],
571 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000572 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000573 '<<history-next>>': ['<Alt-n>'],
574 '<<history-previous>>': ['<Alt-p>'],
575 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000576 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000577 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000578 '<<open-class-browser>>': ['<Alt-c>'],
579 '<<open-module>>': ['<Alt-m>'],
580 '<<open-new-window>>': ['<Control-n>'],
581 '<<open-window-from-file>>': ['<Control-o>'],
582 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000583 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000584 '<<redo>>': ['<Control-y>'],
585 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000586 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000587 '<<save-window-as-file>>': ['<Alt-s>'],
588 '<<save-window>>': ['<Control-s>'],
589 '<<select-all>>': ['<Alt-a>'],
590 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000591 '<<undo>>': ['<Control-z>'],
592 '<<find-again>>': ['<Control-g>', '<F3>'],
593 '<<find-in-files>>': ['<Alt-F3>'],
594 '<<find-selection>>': ['<Control-F3>'],
595 '<<find>>': ['<Control-f>'],
596 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000597 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000598 '<<smart-backspace>>': ['<Key-BackSpace>'],
599 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
600 '<<smart-indent>>': ['<Key-Tab>'],
601 '<<indent-region>>': ['<Control-Key-bracketright>'],
602 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
603 '<<comment-region>>': ['<Alt-Key-3>'],
604 '<<uncomment-region>>': ['<Alt-Key-4>'],
605 '<<tabify-region>>': ['<Alt-Key-5>'],
606 '<<untabify-region>>': ['<Alt-Key-6>'],
607 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000608 '<<change-indentwidth>>': ['<Alt-Key-u>'],
609 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
610 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000611 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000612 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000613 for event in keyBindings.keys():
614 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000615 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000616 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000617 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000618 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
619 ' -\n problem retrieving key binding for event %r'
620 '\n from key set %r.\n'
621 ' returning default value: %r\n' %
622 (event, keySetName, keyBindings[event]))
Amaury Forgeot d'Arcf305bd32008-04-21 22:42:30 +0000623 try:
624 sys.stderr.write(warning)
625 except IOError:
626 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000627 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000628
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000629 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000630 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000631
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000632 Valid configSets are 'user' or 'default'. Return a list of tuples of
633 the form (menu_item , path_to_help_file , option), or return the empty
634 list. 'option' is the sequence number of the help resource. 'option'
635 values determine the position of the menu items on the Help menu,
636 therefore the returned list must be sorted by 'option'.
637
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000638 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000639 helpSources=[]
640 if configSet=='user':
641 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000642 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000643 cfgParser=self.defaultCfg['main']
644 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000645 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000646 options=cfgParser.GetOptionList('HelpFiles')
647 for option in options:
648 value=cfgParser.Get('HelpFiles',option,default=';')
649 if value.find(';')==-1: #malformed config entry with no ';'
650 menuItem='' #make these empty
651 helpPath='' #so value won't be added to list
652 else: #config entry contains ';' as expected
653 value=string.split(value,';')
654 menuItem=value[0].strip()
655 helpPath=value[1].strip()
656 if menuItem and helpPath: #neither are empty strings
657 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000658 helpSources.sort(self.__helpsort)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000659 return helpSources
660
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000661 def __helpsort(self, h1, h2):
662 if int(h1[2]) < int(h2[2]):
663 return -1
664 elif int(h1[2]) > int(h2[2]):
665 return 1
666 else:
667 return 0
668
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000669 def GetAllExtraHelpSourcesList(self):
670 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000671 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000672 sources configured, or an empty list if there are none. Tuples are of
673 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000674 """
675 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000676 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000677 return allHelpSources
678
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000679 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000680 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000681 load all configuration files.
682 """
683 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000684 self.defaultCfg[key].Load()
685 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000686
687 def SaveUserCfgFiles(self):
688 """
689 write all loaded user configuration files back to disk
690 """
691 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000692 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000693
694idleConf=IdleConf()
695
696### module test
697if __name__ == '__main__':
698 def dumpCfg(cfg):
699 print '\n',cfg,'\n'
700 for key in cfg.keys():
701 sections=cfg[key].sections()
702 print key
703 print sections
704 for section in sections:
705 options=cfg[key].options(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000706 print section
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000707 print options
708 for option in options:
709 print option, '=', cfg[key].Get(section,option)
710 dumpCfg(idleConf.defaultCfg)
711 dumpCfg(idleConf.userCfg)
712 print idleConf.userCfg['main'].Get('Theme','name')
713 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')