blob: 3ddb4ed82d0948519d072fefc05f08f97ded5226 [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')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000210 sys.stderr.write(warn)
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,
227 warn_on_default=True):
Steven M. Gava429a86a2001-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. Gava429a86a2001-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. Gava429a86a2001-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. Gava429a86a2001-10-23 10:42:12 +0000239 """
240 if self.userCfg[configType].has_option(section,option):
241 return self.userCfg[configType].Get(section, option, type=type)
242 elif self.defaultCfg[configType].has_option(section,option):
243 return self.defaultCfg[configType].Get(section, option, type=type)
Steven M. Gava052937f2002-02-11 02:20:53 +0000244 else: #returning default, print warning
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000245 if warn_on_default:
246 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
247 ' problem retrieving configration option %r\n'
248 ' from section %r.\n'
249 ' returning default value: %r\n' %
250 (option, section, default))
251 sys.stderr.write(warning)
Steven M. Gava429a86a2001-10-23 10:42:12 +0000252 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000253
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000254 def SetOption(self, configType, section, option, value):
255 """In user's config file, set section's option to value.
256
257 """
258 self.userCfg[configType].SetOption(section, option, value)
259
Steven M. Gava2a63a072001-10-26 06:50:54 +0000260 def GetSectionList(self, configSet, configType):
261 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000262 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000263 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000264 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000265 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000266 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000267 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000268 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000269 if configSet == 'user':
270 cfgParser=self.userCfg[configType]
271 elif configSet == 'default':
272 cfgParser=self.defaultCfg[configType]
273 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000274 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000275 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000276
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000277 def GetHighlight(self, theme, element, fgBg=None):
278 """
279 return individual highlighting theme elements.
280 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000281 containing fg and bg colours (appropriate for passing to Tkinter in,
282 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000283 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000284 if self.defaultCfg['highlight'].has_section(theme):
285 themeDict=self.GetThemeDict('default',theme)
286 else:
287 themeDict=self.GetThemeDict('user',theme)
288 fore=themeDict[element+'-foreground']
289 if element=='cursor': #there is no config value for cursor bg
290 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000291 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000292 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000293 highlight={"foreground": fore,"background": back}
294 if not fgBg: #return dict of both colours
295 return highlight
296 else: #return specified colour only
297 if fgBg == 'fg':
298 return highlight["foreground"]
299 if fgBg == 'bg':
300 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000301 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000302 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000303
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000304 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000305 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000306 type - string, 'default' or 'user' theme type
307 themeName - string, theme name
308 Returns a dictionary which holds {option:value} for each element
309 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000310 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000311 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000312 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000313 if type == 'user':
314 cfgParser=self.userCfg['highlight']
315 elif type == 'default':
316 cfgParser=self.defaultCfg['highlight']
317 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000318 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000319 #foreground and background values are provded for each theme element
320 #(apart from cursor) even though all these values are not yet used
321 #by idle, to allow for their use in the future. Default values are
322 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000323 theme={ 'normal-foreground':'#000000',
324 'normal-background':'#ffffff',
325 'keyword-foreground':'#000000',
326 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000327 'builtin-foreground':'#000000',
328 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000329 'comment-foreground':'#000000',
330 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000331 'string-foreground':'#000000',
332 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000333 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000334 'definition-background':'#ffffff',
335 'hilite-foreground':'#000000',
336 'hilite-background':'gray',
337 'break-foreground':'#ffffff',
338 'break-background':'#000000',
339 'hit-foreground':'#ffffff',
340 'hit-background':'#000000',
341 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000342 'error-background':'#000000',
343 #cursor (only foreground can be set)
344 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000345 #shell window
346 'stdout-foreground':'#000000',
347 'stdout-background':'#ffffff',
348 'stderr-foreground':'#000000',
349 'stderr-background':'#ffffff',
350 'console-foreground':'#000000',
351 'console-background':'#ffffff' }
352 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000353 if not cfgParser.has_option(themeName,element):
354 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000355 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
356 ' -\n problem retrieving theme element %r'
357 '\n from theme %r.\n'
358 ' returning default value: %r\n' %
359 (element, themeName, theme[element]))
Steven M. Gava052937f2002-02-11 02:20:53 +0000360 sys.stderr.write(warning)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000361 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000362 theme[element]=colour
363 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000364
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000365 def CurrentTheme(self):
366 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000367 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000368 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000369 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000370
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000371 def CurrentKeys(self):
372 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000373 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000374 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000375 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000376
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000377 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000378 """
379 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000380 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000381 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000382 extns=self.RemoveKeyBindNames(
383 self.GetSectionList('default','extensions'))
384 userExtns=self.RemoveKeyBindNames(
385 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000386 for extn in userExtns:
387 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000388 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000389 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000390 activeExtns=[]
391 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000392 if self.GetOption('extensions', extn, 'enable', default=True,
393 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000394 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000395 if editor_only or shell_only:
396 if editor_only:
397 option = "enable_editor"
398 else:
399 option = "enable_shell"
400 if self.GetOption('extensions', extn,option,
401 default=True, type='bool',
402 warn_on_default=False):
403 activeExtns.append(extn)
404 else:
405 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000406 return activeExtns
407 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000408 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000409
Steven M. Gavac628a062002-01-19 10:33:21 +0000410 def RemoveKeyBindNames(self,extnNameList):
411 #get rid of keybinding section names
412 names=extnNameList
413 kbNameIndicies=[]
414 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000415 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000416 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000417 kbNameIndicies.sort()
418 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000419 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000420 del(names[index])
421 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000422
Steven M. Gavaa498af22002-02-01 01:33:36 +0000423 def GetExtnNameForEvent(self,virtualEvent):
424 """
425 Returns the name of the extension that virtualEvent is bound in, or
426 None if not bound in any extension.
427 virtualEvent - string, name of the virtual event to test for, without
428 the enclosing '<< >>'
429 """
430 extName=None
431 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000432 for extn in self.GetExtensions(active_only=0):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000433 for event in self.GetExtensionKeys(extn).keys():
434 if event == vEvent:
435 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000436 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000437
Steven M. Gavac628a062002-01-19 10:33:21 +0000438 def GetExtensionKeys(self,extensionName):
439 """
440 returns a dictionary of the configurable keybindings for a particular
441 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000442 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000443 """
444 keysName=extensionName+'_cfgBindings'
445 activeKeys=self.GetCurrentKeySet()
446 extKeys={}
447 if self.defaultCfg['extensions'].has_section(keysName):
448 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
449 for eventName in eventNames:
450 event='<<'+eventName+'>>'
451 binding=activeKeys[event]
452 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000453 return extKeys
454
Steven M. Gavac628a062002-01-19 10:33:21 +0000455 def __GetRawExtensionKeys(self,extensionName):
456 """
457 returns a dictionary of the configurable keybindings for a particular
458 extension, as defined in the configuration files, or an empty dictionary
459 if no bindings are found
460 """
461 keysName=extensionName+'_cfgBindings'
462 extKeys={}
463 if self.defaultCfg['extensions'].has_section(keysName):
464 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
465 for eventName in eventNames:
466 binding=self.GetOption('extensions',keysName,
467 eventName,default='').split()
468 event='<<'+eventName+'>>'
469 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000470 return extKeys
471
Steven M. Gavac628a062002-01-19 10:33:21 +0000472 def GetExtensionBindings(self,extensionName):
473 """
474 Returns a dictionary of all the event bindings for a particular
475 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000476 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000477 keybindings are disabled.
478 """
479 bindsName=extensionName+'_bindings'
480 extBinds=self.GetExtensionKeys(extensionName)
481 #add the non-configurable bindings
482 if self.defaultCfg['extensions'].has_section(bindsName):
483 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
484 for eventName in eventNames:
485 binding=self.GetOption('extensions',bindsName,
486 eventName,default='').split()
487 event='<<'+eventName+'>>'
488 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000489
490 return extBinds
491
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000492 def GetKeyBinding(self, keySetName, eventStr):
493 """
494 returns the keybinding for a specific event.
495 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000496 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000497 represented as a string, eg. '<<event>>'
498 """
499 eventName=eventStr[2:-2] #trim off the angle brackets
500 binding=self.GetOption('keys',keySetName,eventName,default='').split()
501 return binding
502
Steven M. Gavac628a062002-01-19 10:33:21 +0000503 def GetCurrentKeySet(self):
Ronald Oussoren19302d92006-06-11 14:33:36 +0000504 result = self.GetKeySet(self.CurrentKeys())
505
506 if macosxSupport.runningAsOSXApp():
507 # We're using AquaTk, replace all keybingings that use the
Tim Peters4f96f1f2006-06-11 19:42:51 +0000508 # Alt key by ones that use the Option key because the former
Ronald Oussoren19302d92006-06-11 14:33:36 +0000509 # don't work reliably.
510 for k, v in result.items():
511 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
512 if v != v2:
513 result[k] = v2
514
515 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000516
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000517 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000518 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000519 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000520 keybindings for all currently active extensions. If a binding defined
521 in an extension is already in use, that binding is disabled.
522 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000523 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000524 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000525 for extn in activeExtns:
526 extKeys=self.__GetRawExtensionKeys(extn)
527 if extKeys: #the extension defines keybindings
528 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000529 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000530 #the binding is already in use
531 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000532 keySet[event]=extKeys[event] #add binding
533 return keySet
534
Steven M. Gavaa498af22002-02-01 01:33:36 +0000535 def IsCoreBinding(self,virtualEvent):
536 """
537 returns true if the virtual event is bound in the core idle keybindings.
538 virtualEvent - string, name of the virtual event to test for, without
539 the enclosing '<< >>'
540 """
541 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000542
Steven M. Gavac628a062002-01-19 10:33:21 +0000543 def GetCoreKeys(self, keySetName=None):
544 """
545 returns the requested set of core keybindings, with fallbacks if
546 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000547 Keybindings loaded from the config file(s) are loaded _over_ these
548 defaults, so if there is a problem getting any core binding there will
549 be an 'ultimate last resort fallback' to the CUA-ish bindings
550 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000551 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000552 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000553 '<<copy>>': ['<Control-c>', '<Control-C>'],
554 '<<cut>>': ['<Control-x>', '<Control-X>'],
555 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000556 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
557 '<<center-insert>>': ['<Control-l>'],
558 '<<close-all-windows>>': ['<Control-q>'],
559 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000560 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000561 '<<end-of-file>>': ['<Control-d>'],
562 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000563 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000564 '<<history-next>>': ['<Alt-n>'],
565 '<<history-previous>>': ['<Alt-p>'],
566 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000567 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000568 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000569 '<<open-class-browser>>': ['<Alt-c>'],
570 '<<open-module>>': ['<Alt-m>'],
571 '<<open-new-window>>': ['<Control-n>'],
572 '<<open-window-from-file>>': ['<Control-o>'],
573 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000574 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000575 '<<redo>>': ['<Control-y>'],
576 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000577 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000578 '<<save-window-as-file>>': ['<Alt-s>'],
579 '<<save-window>>': ['<Control-s>'],
580 '<<select-all>>': ['<Alt-a>'],
581 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000582 '<<undo>>': ['<Control-z>'],
583 '<<find-again>>': ['<Control-g>', '<F3>'],
584 '<<find-in-files>>': ['<Alt-F3>'],
585 '<<find-selection>>': ['<Control-F3>'],
586 '<<find>>': ['<Control-f>'],
587 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000588 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000589 '<<smart-backspace>>': ['<Key-BackSpace>'],
590 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
591 '<<smart-indent>>': ['<Key-Tab>'],
592 '<<indent-region>>': ['<Control-Key-bracketright>'],
593 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
594 '<<comment-region>>': ['<Alt-Key-3>'],
595 '<<uncomment-region>>': ['<Alt-Key-4>'],
596 '<<tabify-region>>': ['<Alt-Key-5>'],
597 '<<untabify-region>>': ['<Alt-Key-6>'],
598 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000599 '<<change-indentwidth>>': ['<Alt-Key-u>'],
600 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
601 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000602 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000603 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000604 for event in keyBindings.keys():
605 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000606 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000607 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000608 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000609 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
610 ' -\n problem retrieving key binding for event %r'
611 '\n from key set %r.\n'
612 ' returning default value: %r\n' %
613 (event, keySetName, keyBindings[event]))
Steven M. Gava49745752002-02-18 01:43:11 +0000614 sys.stderr.write(warning)
Steven M. Gava17d01542001-12-03 00:37:28 +0000615 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000616
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000617 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000618 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000619
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000620 Valid configSets are 'user' or 'default'. Return a list of tuples of
621 the form (menu_item , path_to_help_file , option), or return the empty
622 list. 'option' is the sequence number of the help resource. 'option'
623 values determine the position of the menu items on the Help menu,
624 therefore the returned list must be sorted by 'option'.
625
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000626 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000627 helpSources=[]
628 if configSet=='user':
629 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000630 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000631 cfgParser=self.defaultCfg['main']
632 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000633 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000634 options=cfgParser.GetOptionList('HelpFiles')
635 for option in options:
636 value=cfgParser.Get('HelpFiles',option,default=';')
637 if value.find(';')==-1: #malformed config entry with no ';'
638 menuItem='' #make these empty
639 helpPath='' #so value won't be added to list
640 else: #config entry contains ';' as expected
641 value=string.split(value,';')
642 menuItem=value[0].strip()
643 helpPath=value[1].strip()
644 if menuItem and helpPath: #neither are empty strings
645 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000646 helpSources.sort(self.__helpsort)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000647 return helpSources
648
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000649 def __helpsort(self, h1, h2):
650 if int(h1[2]) < int(h2[2]):
651 return -1
652 elif int(h1[2]) > int(h2[2]):
653 return 1
654 else:
655 return 0
656
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000657 def GetAllExtraHelpSourcesList(self):
658 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000659 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000660 sources configured, or an empty list if there are none. Tuples are of
661 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000662 """
663 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000664 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000665 return allHelpSources
666
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000667 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000668 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000669 load all configuration files.
670 """
671 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000672 self.defaultCfg[key].Load()
673 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000674
675 def SaveUserCfgFiles(self):
676 """
677 write all loaded user configuration files back to disk
678 """
679 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000680 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000681
682idleConf=IdleConf()
683
684### module test
685if __name__ == '__main__':
686 def dumpCfg(cfg):
687 print '\n',cfg,'\n'
688 for key in cfg.keys():
689 sections=cfg[key].sections()
690 print key
691 print sections
692 for section in sections:
693 options=cfg[key].options(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000694 print section
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000695 print options
696 for option in options:
697 print option, '=', cfg[key].Get(section,option)
698 dumpCfg(idleConf.defaultCfg)
699 dumpCfg(idleConf.userCfg)
700 print idleConf.userCfg['main'].Get('Theme','name')
701 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')