blob: 08f0b3d3338fac14ab253d1435fa6a9c5f1fc00b [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
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000042 def Get(self, section, option, type=None, default=None, raw=False):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000043 """
44 Get an option value for given section/option or return default.
45 If type is specified, return as type.
46 """
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000047 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000048 return default
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000049 if type=='bool':
50 return self.getboolean(section, option)
51 elif type=='int':
52 return self.getint(section, option)
53 else:
54 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000055
Steven M. Gavac11ccf32001-09-24 09:43:17 +000056 def GetOptionList(self,section):
57 """
58 Get an option list for given section
59 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +000060 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000061 return self.options(section)
62 else: #return a default value
63 return []
64
Steven M. Gavac11ccf32001-09-24 09:43:17 +000065 def Load(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000066 """
67 Load the configuration file from disk
Steven M. Gavac11ccf32001-09-24 09:43:17 +000068 """
69 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000070
Steven M. Gavac11ccf32001-09-24 09:43:17 +000071class IdleUserConfParser(IdleConfParser):
72 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000073 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000074 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000075
76 def AddSection(self,section):
77 """
78 if section doesn't exist, add it
79 """
80 if not self.has_section(section):
81 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000082
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000083 def RemoveEmptySections(self):
84 """
85 remove any sections that have no options
86 """
87 for section in self.sections():
88 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000089 self.remove_section(section)
90
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000091 def IsEmpty(self):
92 """
93 Remove empty sections and then return 1 if parser has no sections
94 left, else return 0.
95 """
96 self.RemoveEmptySections()
97 if self.sections():
98 return 0
99 else:
100 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000101
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000102 def RemoveOption(self,section,option):
103 """
104 If section/option exists, remove it.
105 Returns 1 if option was removed, 0 otherwise.
106 """
107 if self.has_section(section):
108 return self.remove_option(section,option)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000109
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000110 def SetOption(self,section,option,value):
111 """
112 Sets option to value, adding section if required.
113 Returns 1 if option was added or changed, otherwise 0.
114 """
115 if self.has_option(section,option):
116 if self.get(section,option)==value:
117 return 0
118 else:
119 self.set(section,option,value)
120 return 1
121 else:
122 if not self.has_section(section):
123 self.add_section(section)
124 self.set(section,option,value)
125 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000126
Steven M. Gavab77d3432002-03-02 07:16:21 +0000127 def RemoveFile(self):
128 """
129 Removes the user config file from disk if it exists.
130 """
131 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000132 os.remove(self.file)
133
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000134 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000135 """Update user configuration file.
136
137 Remove empty sections. If resulting config isn't empty, write the file
138 to disk. If config is empty, remove the file from disk if it exists.
139
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000140 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000141 if not self.IsEmpty():
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000142 fname = self.file
143 try:
144 cfgFile = open(fname, 'w')
145 except IOError:
146 fname.unlink()
147 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')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000207 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000208 userDir = '~'
209 if userDir == "~": # still no path to home!
210 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
211 userDir = os.getcwd()
212 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000213 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000214 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000215 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000216 except (OSError, IOError):
217 warn = ('\n Warning: unable to create user config directory\n'+
218 userDir+'\n Check path and permissions.\n Exiting!\n\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000219 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000220 raise SystemExit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000221 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000222
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000223 def GetOption(self, configType, section, option, default=None, type=None,
Kurt B. Kaiser90f84922007-02-05 06:03:18 +0000224 warn_on_default=True, raw=False):
Steven M. Gava429a86a2001-10-23 10:42:12 +0000225 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000226 Get an option value for given config type and given general
Steven M. Gava429a86a2001-10-23 10:42:12 +0000227 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000228 return as type. Firstly the user configuration is checked, with a
229 fallback to the default configuration, and a final 'catch all'
230 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86a2001-10-23 10:42:12 +0000231 either the user or the default configuration.
232 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000233 If a default is returned, and warn_on_default is True, a warning is
234 printed to stderr.
235
Steven M. Gava429a86a2001-10-23 10:42:12 +0000236 """
237 if self.userCfg[configType].has_option(section,option):
Kurt B. Kaiser90f84922007-02-05 06:03:18 +0000238 return self.userCfg[configType].Get(section, option,
239 type=type, raw=raw)
Steven M. Gava429a86a2001-10-23 10:42:12 +0000240 elif self.defaultCfg[configType].has_option(section,option):
Kurt B. Kaiser90f84922007-02-05 06:03:18 +0000241 return self.defaultCfg[configType].Get(section, option,
242 type=type, raw=raw)
Steven M. Gava052937f2002-02-11 02:20:53 +0000243 else: #returning default, print warning
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000244 if warn_on_default:
245 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
246 ' problem retrieving configration option %r\n'
247 ' from section %r.\n'
248 ' returning default value: %r\n' %
249 (option, section, default))
250 sys.stderr.write(warning)
Steven M. Gava429a86a2001-10-23 10:42:12 +0000251 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000252
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000253 def SetOption(self, configType, section, option, value):
254 """In user's config file, set section's option to value.
255
256 """
257 self.userCfg[configType].SetOption(section, option, value)
258
Steven M. Gava2a63a072001-10-26 06:50:54 +0000259 def GetSectionList(self, configSet, configType):
260 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000261 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000262 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000263 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000264 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000265 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000266 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000267 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000268 if configSet == 'user':
269 cfgParser=self.userCfg[configType]
270 elif configSet == 'default':
271 cfgParser=self.defaultCfg[configType]
272 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000273 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000274 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000275
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000276 def GetHighlight(self, theme, element, fgBg=None):
277 """
278 return individual highlighting theme elements.
279 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000280 containing fg and bg colours (appropriate for passing to Tkinter in,
281 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000282 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000283 if self.defaultCfg['highlight'].has_section(theme):
284 themeDict=self.GetThemeDict('default',theme)
285 else:
286 themeDict=self.GetThemeDict('user',theme)
287 fore=themeDict[element+'-foreground']
288 if element=='cursor': #there is no config value for cursor bg
289 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000290 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000291 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000292 highlight={"foreground": fore,"background": back}
293 if not fgBg: #return dict of both colours
294 return highlight
295 else: #return specified colour only
296 if fgBg == 'fg':
297 return highlight["foreground"]
298 if fgBg == 'bg':
299 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000300 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000301 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000302
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000303 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000304 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000305 type - string, 'default' or 'user' theme type
306 themeName - string, theme name
307 Returns a dictionary which holds {option:value} for each element
308 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000309 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000310 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000311 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000312 if type == 'user':
313 cfgParser=self.userCfg['highlight']
314 elif type == 'default':
315 cfgParser=self.defaultCfg['highlight']
316 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000317 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000318 #foreground and background values are provded for each theme element
319 #(apart from cursor) even though all these values are not yet used
320 #by idle, to allow for their use in the future. Default values are
321 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000322 theme={ 'normal-foreground':'#000000',
323 'normal-background':'#ffffff',
324 'keyword-foreground':'#000000',
325 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000326 'builtin-foreground':'#000000',
327 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000328 'comment-foreground':'#000000',
329 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000330 'string-foreground':'#000000',
331 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000332 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000333 'definition-background':'#ffffff',
334 'hilite-foreground':'#000000',
335 'hilite-background':'gray',
336 'break-foreground':'#ffffff',
337 'break-background':'#000000',
338 'hit-foreground':'#ffffff',
339 'hit-background':'#000000',
340 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000341 'error-background':'#000000',
342 #cursor (only foreground can be set)
343 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000344 #shell window
345 'stdout-foreground':'#000000',
346 'stdout-background':'#ffffff',
347 'stderr-foreground':'#000000',
348 'stderr-background':'#ffffff',
349 'console-foreground':'#000000',
350 'console-background':'#ffffff' }
351 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000352 if not cfgParser.has_option(themeName,element):
353 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000354 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
355 ' -\n problem retrieving theme element %r'
356 '\n from theme %r.\n'
357 ' returning default value: %r\n' %
358 (element, themeName, theme[element]))
Steven M. Gava052937f2002-02-11 02:20:53 +0000359 sys.stderr.write(warning)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000360 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000361 theme[element]=colour
362 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000363
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000364 def CurrentTheme(self):
365 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000366 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000367 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000368 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000369
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000370 def CurrentKeys(self):
371 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000372 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000373 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000374 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000375
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000376 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000377 """
378 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000379 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000380 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000381 extns=self.RemoveKeyBindNames(
382 self.GetSectionList('default','extensions'))
383 userExtns=self.RemoveKeyBindNames(
384 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000385 for extn in userExtns:
386 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000387 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000388 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000389 activeExtns=[]
390 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000391 if self.GetOption('extensions', extn, 'enable', default=True,
392 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000393 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000394 if editor_only or shell_only:
395 if editor_only:
396 option = "enable_editor"
397 else:
398 option = "enable_shell"
399 if self.GetOption('extensions', extn,option,
400 default=True, type='bool',
401 warn_on_default=False):
402 activeExtns.append(extn)
403 else:
404 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000405 return activeExtns
406 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000407 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000408
Steven M. Gavac628a062002-01-19 10:33:21 +0000409 def RemoveKeyBindNames(self,extnNameList):
410 #get rid of keybinding section names
411 names=extnNameList
412 kbNameIndicies=[]
413 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000414 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000415 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000416 kbNameIndicies.sort()
417 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000418 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000419 del(names[index])
420 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000421
Steven M. Gavaa498af22002-02-01 01:33:36 +0000422 def GetExtnNameForEvent(self,virtualEvent):
423 """
424 Returns the name of the extension that virtualEvent is bound in, or
425 None if not bound in any extension.
426 virtualEvent - string, name of the virtual event to test for, without
427 the enclosing '<< >>'
428 """
429 extName=None
430 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000431 for extn in self.GetExtensions(active_only=0):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000432 for event in self.GetExtensionKeys(extn).keys():
433 if event == vEvent:
434 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000435 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000436
Steven M. Gavac628a062002-01-19 10:33:21 +0000437 def GetExtensionKeys(self,extensionName):
438 """
439 returns a dictionary of the configurable keybindings for a particular
440 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000441 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000442 """
443 keysName=extensionName+'_cfgBindings'
444 activeKeys=self.GetCurrentKeySet()
445 extKeys={}
446 if self.defaultCfg['extensions'].has_section(keysName):
447 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
448 for eventName in eventNames:
449 event='<<'+eventName+'>>'
450 binding=activeKeys[event]
451 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000452 return extKeys
453
Steven M. Gavac628a062002-01-19 10:33:21 +0000454 def __GetRawExtensionKeys(self,extensionName):
455 """
456 returns a dictionary of the configurable keybindings for a particular
457 extension, as defined in the configuration files, or an empty dictionary
458 if no bindings are found
459 """
460 keysName=extensionName+'_cfgBindings'
461 extKeys={}
462 if self.defaultCfg['extensions'].has_section(keysName):
463 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
464 for eventName in eventNames:
465 binding=self.GetOption('extensions',keysName,
466 eventName,default='').split()
467 event='<<'+eventName+'>>'
468 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000469 return extKeys
470
Steven M. Gavac628a062002-01-19 10:33:21 +0000471 def GetExtensionBindings(self,extensionName):
472 """
473 Returns a dictionary of all the event bindings for a particular
474 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000475 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000476 keybindings are disabled.
477 """
478 bindsName=extensionName+'_bindings'
479 extBinds=self.GetExtensionKeys(extensionName)
480 #add the non-configurable bindings
481 if self.defaultCfg['extensions'].has_section(bindsName):
482 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
483 for eventName in eventNames:
484 binding=self.GetOption('extensions',bindsName,
485 eventName,default='').split()
486 event='<<'+eventName+'>>'
487 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000488
489 return extBinds
490
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000491 def GetKeyBinding(self, keySetName, eventStr):
492 """
493 returns the keybinding for a specific event.
494 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000495 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000496 represented as a string, eg. '<<event>>'
497 """
498 eventName=eventStr[2:-2] #trim off the angle brackets
499 binding=self.GetOption('keys',keySetName,eventName,default='').split()
500 return binding
501
Steven M. Gavac628a062002-01-19 10:33:21 +0000502 def GetCurrentKeySet(self):
Ronald Oussoren19302d92006-06-11 14:33:36 +0000503 result = self.GetKeySet(self.CurrentKeys())
504
505 if macosxSupport.runningAsOSXApp():
506 # We're using AquaTk, replace all keybingings that use the
Tim Peters4f96f1f2006-06-11 19:42:51 +0000507 # Alt key by ones that use the Option key because the former
Ronald Oussoren19302d92006-06-11 14:33:36 +0000508 # don't work reliably.
509 for k, v in result.items():
510 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
511 if v != v2:
512 result[k] = v2
513
514 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000515
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000516 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000517 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000518 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000519 keybindings for all currently active extensions. If a binding defined
520 in an extension is already in use, that binding is disabled.
521 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000522 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000523 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000524 for extn in activeExtns:
525 extKeys=self.__GetRawExtensionKeys(extn)
526 if extKeys: #the extension defines keybindings
527 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000528 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000529 #the binding is already in use
530 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000531 keySet[event]=extKeys[event] #add binding
532 return keySet
533
Steven M. Gavaa498af22002-02-01 01:33:36 +0000534 def IsCoreBinding(self,virtualEvent):
535 """
536 returns true if the virtual event is bound in the core idle keybindings.
537 virtualEvent - string, name of the virtual event to test for, without
538 the enclosing '<< >>'
539 """
540 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000541
Steven M. Gavac628a062002-01-19 10:33:21 +0000542 def GetCoreKeys(self, keySetName=None):
543 """
544 returns the requested set of core keybindings, with fallbacks if
545 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000546 Keybindings loaded from the config file(s) are loaded _over_ these
547 defaults, so if there is a problem getting any core binding there will
548 be an 'ultimate last resort fallback' to the CUA-ish bindings
549 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000550 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000551 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000552 '<<copy>>': ['<Control-c>', '<Control-C>'],
553 '<<cut>>': ['<Control-x>', '<Control-X>'],
554 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000555 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
556 '<<center-insert>>': ['<Control-l>'],
557 '<<close-all-windows>>': ['<Control-q>'],
558 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000559 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000560 '<<end-of-file>>': ['<Control-d>'],
561 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000562 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000563 '<<history-next>>': ['<Alt-n>'],
564 '<<history-previous>>': ['<Alt-p>'],
565 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000566 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000567 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000568 '<<open-class-browser>>': ['<Alt-c>'],
569 '<<open-module>>': ['<Alt-m>'],
570 '<<open-new-window>>': ['<Control-n>'],
571 '<<open-window-from-file>>': ['<Control-o>'],
572 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000573 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000574 '<<redo>>': ['<Control-y>'],
575 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000576 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000577 '<<save-window-as-file>>': ['<Alt-s>'],
578 '<<save-window>>': ['<Control-s>'],
579 '<<select-all>>': ['<Alt-a>'],
580 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000581 '<<undo>>': ['<Control-z>'],
582 '<<find-again>>': ['<Control-g>', '<F3>'],
583 '<<find-in-files>>': ['<Alt-F3>'],
584 '<<find-selection>>': ['<Control-F3>'],
585 '<<find>>': ['<Control-f>'],
586 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000587 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000588 '<<smart-backspace>>': ['<Key-BackSpace>'],
589 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
590 '<<smart-indent>>': ['<Key-Tab>'],
591 '<<indent-region>>': ['<Control-Key-bracketright>'],
592 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
593 '<<comment-region>>': ['<Alt-Key-3>'],
594 '<<uncomment-region>>': ['<Alt-Key-4>'],
595 '<<tabify-region>>': ['<Alt-Key-5>'],
596 '<<untabify-region>>': ['<Alt-Key-6>'],
597 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000598 '<<change-indentwidth>>': ['<Alt-Key-u>'],
599 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
600 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000601 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000602 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000603 for event in keyBindings.keys():
604 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000605 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000606 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000607 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000608 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
609 ' -\n problem retrieving key binding for event %r'
610 '\n from key set %r.\n'
611 ' returning default value: %r\n' %
612 (event, keySetName, keyBindings[event]))
Steven M. Gava49745752002-02-18 01:43:11 +0000613 sys.stderr.write(warning)
Steven M. Gava17d01542001-12-03 00:37:28 +0000614 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000615
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000616 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000617 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000618
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000619 Valid configSets are 'user' or 'default'. Return a list of tuples of
620 the form (menu_item , path_to_help_file , option), or return the empty
621 list. 'option' is the sequence number of the help resource. 'option'
622 values determine the position of the menu items on the Help menu,
623 therefore the returned list must be sorted by 'option'.
624
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000625 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000626 helpSources=[]
627 if configSet=='user':
628 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000629 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000630 cfgParser=self.defaultCfg['main']
631 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000632 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000633 options=cfgParser.GetOptionList('HelpFiles')
634 for option in options:
635 value=cfgParser.Get('HelpFiles',option,default=';')
636 if value.find(';')==-1: #malformed config entry with no ';'
637 menuItem='' #make these empty
638 helpPath='' #so value won't be added to list
639 else: #config entry contains ';' as expected
640 value=string.split(value,';')
641 menuItem=value[0].strip()
642 helpPath=value[1].strip()
643 if menuItem and helpPath: #neither are empty strings
644 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000645 helpSources.sort(self.__helpsort)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000646 return helpSources
647
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000648 def __helpsort(self, h1, h2):
649 if int(h1[2]) < int(h2[2]):
650 return -1
651 elif int(h1[2]) > int(h2[2]):
652 return 1
653 else:
654 return 0
655
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000656 def GetAllExtraHelpSourcesList(self):
657 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000658 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000659 sources configured, or an empty list if there are none. Tuples are of
660 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000661 """
662 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000663 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000664 return allHelpSources
665
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000666 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000667 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000668 load all configuration files.
669 """
670 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000671 self.defaultCfg[key].Load()
672 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000673
674 def SaveUserCfgFiles(self):
675 """
676 write all loaded user configuration files back to disk
677 """
678 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000679 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000680
681idleConf=IdleConf()
682
683### module test
684if __name__ == '__main__':
685 def dumpCfg(cfg):
686 print '\n',cfg,'\n'
687 for key in cfg.keys():
688 sections=cfg[key].sections()
689 print key
690 print sections
691 for section in sections:
692 options=cfg[key].options(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000693 print section
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000694 print options
695 for option in options:
696 print option, '=', cfg[key].Get(section,option)
697 dumpCfg(idleConf.defaultCfg)
698 dumpCfg(idleConf.userCfg)
699 print idleConf.userCfg['main'].Get('Theme','name')
700 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')