blob: 963cf951782917245832ecbf0519439353535f9f [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
Thomas Wouters0e3f5912006-08-11 14:57:12 +000022import macosxSupport
Steven M. Gavac11ccf32001-09-24 09:43:17 +000023from ConfigParser import ConfigParser, NoOptionError, NoSectionError
24
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000025class InvalidConfigType(Exception): pass
26class InvalidConfigSet(Exception): pass
27class InvalidFgBg(Exception): pass
28class InvalidTheme(Exception): pass
29
Steven M. Gavac11ccf32001-09-24 09:43:17 +000030class IdleConfParser(ConfigParser):
31 """
32 A ConfigParser specialised for idle configuration file handling
33 """
34 def __init__(self, cfgFile, cfgDefaults=None):
35 """
36 cfgFile - string, fully specified configuration file name
37 """
38 self.file=cfgFile
39 ConfigParser.__init__(self,defaults=cfgDefaults)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000040
Thomas Wouterscf297e42007-02-23 15:07:44 +000041 def Get(self, section, option, type=None, default=None, raw=False):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000042 """
43 Get an option value for given section/option or return default.
44 If type is specified, return as type.
45 """
Thomas Wouterscf297e42007-02-23 15:07:44 +000046 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000047 return default
Thomas Wouterscf297e42007-02-23 15:07:44 +000048 if type=='bool':
49 return self.getboolean(section, option)
50 elif type=='int':
51 return self.getint(section, option)
52 else:
53 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000054
Steven M. Gavac11ccf32001-09-24 09:43:17 +000055 def GetOptionList(self,section):
56 """
57 Get an option list for given section
58 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +000059 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000060 return self.options(section)
61 else: #return a default value
62 return []
63
Steven M. Gavac11ccf32001-09-24 09:43:17 +000064 def Load(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000065 """
66 Load the configuration file from disk
Steven M. Gavac11ccf32001-09-24 09:43:17 +000067 """
68 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000069
Steven M. Gavac11ccf32001-09-24 09:43:17 +000070class IdleUserConfParser(IdleConfParser):
71 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000072 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000073 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000074
75 def AddSection(self,section):
76 """
77 if section doesn't exist, add it
78 """
79 if not self.has_section(section):
80 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000081
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000082 def RemoveEmptySections(self):
83 """
84 remove any sections that have no options
85 """
86 for section in self.sections():
87 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000088 self.remove_section(section)
89
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000090 def IsEmpty(self):
91 """
92 Remove empty sections and then return 1 if parser has no sections
93 left, else return 0.
94 """
95 self.RemoveEmptySections()
96 if self.sections():
97 return 0
98 else:
99 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000100
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000101 def RemoveOption(self,section,option):
102 """
103 If section/option exists, remove it.
104 Returns 1 if option was removed, 0 otherwise.
105 """
106 if self.has_section(section):
107 return self.remove_option(section,option)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000108
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000109 def SetOption(self,section,option,value):
110 """
111 Sets option to value, adding section if required.
112 Returns 1 if option was added or changed, otherwise 0.
113 """
114 if self.has_option(section,option):
115 if self.get(section,option)==value:
116 return 0
117 else:
118 self.set(section,option,value)
119 return 1
120 else:
121 if not self.has_section(section):
122 self.add_section(section)
123 self.set(section,option,value)
124 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000125
Steven M. Gavab77d3432002-03-02 07:16:21 +0000126 def RemoveFile(self):
127 """
128 Removes the user config file from disk if it exists.
129 """
130 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000131 os.remove(self.file)
132
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000133 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000134 """Update user configuration file.
135
136 Remove empty sections. If resulting config isn't empty, write the file
137 to disk. If config is empty, remove the file from disk if it exists.
138
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000139 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000140 if not self.IsEmpty():
141 cfgFile=open(self.file,'w')
142 self.write(cfgFile)
143 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000144 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000145
146class IdleConf:
147 """
148 holds config parsers for all idle config files:
149 default config files
150 (idle install dir)/config-main.def
151 (idle install dir)/config-extensions.def
152 (idle install dir)/config-highlight.def
153 (idle install dir)/config-keys.def
154 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000155 (user home dir)/.idlerc/config-main.cfg
156 (user home dir)/.idlerc/config-extensions.cfg
157 (user home dir)/.idlerc/config-highlight.cfg
158 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000159 """
160 def __init__(self):
161 self.defaultCfg={}
162 self.userCfg={}
163 self.cfg={}
164 self.CreateConfigHandlers()
165 self.LoadCfgFiles()
166 #self.LoadCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000167
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000168 def CreateConfigHandlers(self):
169 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000170 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000171 configurations respectively
172 """
173 #build idle install path
174 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000175 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000176 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000177 idleDir=os.path.abspath(sys.path[0])
178 userDir=self.GetUserCfgDir()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000179 configTypes=('main','extensions','highlight','keys')
180 defCfgFiles={}
181 usrCfgFiles={}
182 for cfgType in configTypes: #build config file names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000183 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
184 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000185 for cfgType in configTypes: #create config parsers
186 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
187 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000188
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000189 def GetUserCfgDir(self):
190 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000191 Creates (if required) and returns a filesystem directory for storing
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000192 user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000193
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000194 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000195 cfgDir = '.idlerc'
196 userDir = os.path.expanduser('~')
197 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000198 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000199 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
200 userDir+',\n but the path does not exist.\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000201 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000202 userDir = '~'
203 if userDir == "~": # still no path to home!
204 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
205 userDir = os.getcwd()
206 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000207 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000208 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000209 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000210 except (OSError, IOError):
211 warn = ('\n Warning: unable to create user config directory\n'+
212 userDir+'\n Check path and permissions.\n Exiting!\n\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000213 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000214 raise SystemExit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000215 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000216
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000217 def GetOption(self, configType, section, option, default=None, type=None,
Thomas Wouterscf297e42007-02-23 15:07:44 +0000218 warn_on_default=True, raw=False):
Steven M. Gava429a86af2001-10-23 10:42:12 +0000219 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000220 Get an option value for given config type and given general
Steven M. Gava429a86af2001-10-23 10:42:12 +0000221 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000222 return as type. Firstly the user configuration is checked, with a
223 fallback to the default configuration, and a final 'catch all'
224 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86af2001-10-23 10:42:12 +0000225 either the user or the default configuration.
226 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000227 If a default is returned, and warn_on_default is True, a warning is
228 printed to stderr.
229
Steven M. Gava429a86af2001-10-23 10:42:12 +0000230 """
231 if self.userCfg[configType].has_option(section,option):
Thomas Wouterscf297e42007-02-23 15:07:44 +0000232 return self.userCfg[configType].Get(section, option,
233 type=type, raw=raw)
Steven M. Gava429a86af2001-10-23 10:42:12 +0000234 elif self.defaultCfg[configType].has_option(section,option):
Thomas Wouterscf297e42007-02-23 15:07:44 +0000235 return self.defaultCfg[configType].Get(section, option,
236 type=type, raw=raw)
Steven M. Gava052937f2002-02-11 02:20:53 +0000237 else: #returning default, print warning
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000238 if warn_on_default:
239 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
240 ' problem retrieving configration option %r\n'
241 ' from section %r.\n'
242 ' returning default value: %r\n' %
243 (option, section, default))
244 sys.stderr.write(warning)
Steven M. Gava429a86af2001-10-23 10:42:12 +0000245 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000246
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000247 def SetOption(self, configType, section, option, value):
248 """In user's config file, set section's option to value.
249
250 """
251 self.userCfg[configType].SetOption(section, option, value)
252
Steven M. Gava2a63a072001-10-26 06:50:54 +0000253 def GetSectionList(self, configSet, configType):
254 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000255 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000256 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000257 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000258 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000259 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000260 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000261 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000262 if configSet == 'user':
263 cfgParser=self.userCfg[configType]
264 elif configSet == 'default':
265 cfgParser=self.defaultCfg[configType]
266 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000267 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000268 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000269
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000270 def GetHighlight(self, theme, element, fgBg=None):
271 """
272 return individual highlighting theme elements.
273 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000274 containing fg and bg colours (appropriate for passing to Tkinter in,
275 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000276 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000277 if self.defaultCfg['highlight'].has_section(theme):
278 themeDict=self.GetThemeDict('default',theme)
279 else:
280 themeDict=self.GetThemeDict('user',theme)
281 fore=themeDict[element+'-foreground']
282 if element=='cursor': #there is no config value for cursor bg
283 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000284 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000285 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000286 highlight={"foreground": fore,"background": back}
287 if not fgBg: #return dict of both colours
288 return highlight
289 else: #return specified colour only
290 if fgBg == 'fg':
291 return highlight["foreground"]
292 if fgBg == 'bg':
293 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000294 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000295 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000296
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000297 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000298 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000299 type - string, 'default' or 'user' theme type
300 themeName - string, theme name
301 Returns a dictionary which holds {option:value} for each element
302 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000303 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000304 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000305 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000306 if type == 'user':
307 cfgParser=self.userCfg['highlight']
308 elif type == 'default':
309 cfgParser=self.defaultCfg['highlight']
310 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000311 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000312 #foreground and background values are provded for each theme element
313 #(apart from cursor) even though all these values are not yet used
314 #by idle, to allow for their use in the future. Default values are
315 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000316 theme={ 'normal-foreground':'#000000',
317 'normal-background':'#ffffff',
318 'keyword-foreground':'#000000',
319 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000320 'builtin-foreground':'#000000',
321 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000322 'comment-foreground':'#000000',
323 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000324 'string-foreground':'#000000',
325 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000326 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000327 'definition-background':'#ffffff',
328 'hilite-foreground':'#000000',
329 'hilite-background':'gray',
330 'break-foreground':'#ffffff',
331 'break-background':'#000000',
332 'hit-foreground':'#ffffff',
333 'hit-background':'#000000',
334 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000335 'error-background':'#000000',
336 #cursor (only foreground can be set)
337 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000338 #shell window
339 'stdout-foreground':'#000000',
340 'stdout-background':'#ffffff',
341 'stderr-foreground':'#000000',
342 'stderr-background':'#ffffff',
343 'console-foreground':'#000000',
344 'console-background':'#ffffff' }
345 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000346 if not cfgParser.has_option(themeName,element):
347 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000348 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
349 ' -\n problem retrieving theme element %r'
350 '\n from theme %r.\n'
351 ' returning default value: %r\n' %
352 (element, themeName, theme[element]))
Steven M. Gava052937f2002-02-11 02:20:53 +0000353 sys.stderr.write(warning)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000354 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000355 theme[element]=colour
356 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000357
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000358 def CurrentTheme(self):
359 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000360 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000361 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000362 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000363
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000364 def CurrentKeys(self):
365 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000366 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000367 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000368 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000369
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000370 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000371 """
372 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000373 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000374 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000375 extns=self.RemoveKeyBindNames(
376 self.GetSectionList('default','extensions'))
377 userExtns=self.RemoveKeyBindNames(
378 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000379 for extn in userExtns:
380 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000381 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000382 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000383 activeExtns=[]
384 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000385 if self.GetOption('extensions', extn, 'enable', default=True,
386 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000387 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000388 if editor_only or shell_only:
389 if editor_only:
390 option = "enable_editor"
391 else:
392 option = "enable_shell"
393 if self.GetOption('extensions', extn,option,
394 default=True, type='bool',
395 warn_on_default=False):
396 activeExtns.append(extn)
397 else:
398 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000399 return activeExtns
400 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000401 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000402
Steven M. Gavac628a062002-01-19 10:33:21 +0000403 def RemoveKeyBindNames(self,extnNameList):
404 #get rid of keybinding section names
405 names=extnNameList
406 kbNameIndicies=[]
407 for name in names:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000408 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000409 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000410 kbNameIndicies.sort()
411 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000412 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000413 del(names[index])
414 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000415
Steven M. Gavaa498af22002-02-01 01:33:36 +0000416 def GetExtnNameForEvent(self,virtualEvent):
417 """
418 Returns the name of the extension that virtualEvent is bound in, or
419 None if not bound in any extension.
420 virtualEvent - string, name of the virtual event to test for, without
421 the enclosing '<< >>'
422 """
423 extName=None
424 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000425 for extn in self.GetExtensions(active_only=0):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000426 for event in self.GetExtensionKeys(extn).keys():
427 if event == vEvent:
428 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000429 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000430
Steven M. Gavac628a062002-01-19 10:33:21 +0000431 def GetExtensionKeys(self,extensionName):
432 """
433 returns a dictionary of the configurable keybindings for a particular
434 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000435 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000436 """
437 keysName=extensionName+'_cfgBindings'
438 activeKeys=self.GetCurrentKeySet()
439 extKeys={}
440 if self.defaultCfg['extensions'].has_section(keysName):
441 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
442 for eventName in eventNames:
443 event='<<'+eventName+'>>'
444 binding=activeKeys[event]
445 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000446 return extKeys
447
Steven M. Gavac628a062002-01-19 10:33:21 +0000448 def __GetRawExtensionKeys(self,extensionName):
449 """
450 returns a dictionary of the configurable keybindings for a particular
451 extension, as defined in the configuration files, or an empty dictionary
452 if no bindings are found
453 """
454 keysName=extensionName+'_cfgBindings'
455 extKeys={}
456 if self.defaultCfg['extensions'].has_section(keysName):
457 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
458 for eventName in eventNames:
459 binding=self.GetOption('extensions',keysName,
460 eventName,default='').split()
461 event='<<'+eventName+'>>'
462 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000463 return extKeys
464
Steven M. Gavac628a062002-01-19 10:33:21 +0000465 def GetExtensionBindings(self,extensionName):
466 """
467 Returns a dictionary of all the event bindings for a particular
468 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000469 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000470 keybindings are disabled.
471 """
472 bindsName=extensionName+'_bindings'
473 extBinds=self.GetExtensionKeys(extensionName)
474 #add the non-configurable bindings
475 if self.defaultCfg['extensions'].has_section(bindsName):
476 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
477 for eventName in eventNames:
478 binding=self.GetOption('extensions',bindsName,
479 eventName,default='').split()
480 event='<<'+eventName+'>>'
481 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000482
483 return extBinds
484
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000485 def GetKeyBinding(self, keySetName, eventStr):
486 """
487 returns the keybinding for a specific event.
488 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000489 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000490 represented as a string, eg. '<<event>>'
491 """
492 eventName=eventStr[2:-2] #trim off the angle brackets
493 binding=self.GetOption('keys',keySetName,eventName,default='').split()
494 return binding
495
Steven M. Gavac628a062002-01-19 10:33:21 +0000496 def GetCurrentKeySet(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000497 result = self.GetKeySet(self.CurrentKeys())
498
499 if macosxSupport.runningAsOSXApp():
500 # We're using AquaTk, replace all keybingings that use the
501 # Alt key by ones that use the Option key because the former
502 # don't work reliably.
503 for k, v in result.items():
504 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
505 if v != v2:
506 result[k] = v2
507
508 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000509
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000510 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000511 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000512 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000513 keybindings for all currently active extensions. If a binding defined
514 in an extension is already in use, that binding is disabled.
515 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000516 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000517 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000518 for extn in activeExtns:
519 extKeys=self.__GetRawExtensionKeys(extn)
520 if extKeys: #the extension defines keybindings
521 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000522 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000523 #the binding is already in use
524 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000525 keySet[event]=extKeys[event] #add binding
526 return keySet
527
Steven M. Gavaa498af22002-02-01 01:33:36 +0000528 def IsCoreBinding(self,virtualEvent):
529 """
530 returns true if the virtual event is bound in the core idle keybindings.
531 virtualEvent - string, name of the virtual event to test for, without
532 the enclosing '<< >>'
533 """
534 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000535
Steven M. Gavac628a062002-01-19 10:33:21 +0000536 def GetCoreKeys(self, keySetName=None):
537 """
538 returns the requested set of core keybindings, with fallbacks if
539 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000540 Keybindings loaded from the config file(s) are loaded _over_ these
541 defaults, so if there is a problem getting any core binding there will
542 be an 'ultimate last resort fallback' to the CUA-ish bindings
543 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000544 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000545 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000546 '<<copy>>': ['<Control-c>', '<Control-C>'],
547 '<<cut>>': ['<Control-x>', '<Control-X>'],
548 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000549 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
550 '<<center-insert>>': ['<Control-l>'],
551 '<<close-all-windows>>': ['<Control-q>'],
552 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000553 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000554 '<<end-of-file>>': ['<Control-d>'],
555 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000556 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000557 '<<history-next>>': ['<Alt-n>'],
558 '<<history-previous>>': ['<Alt-p>'],
559 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000560 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000561 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000562 '<<open-class-browser>>': ['<Alt-c>'],
563 '<<open-module>>': ['<Alt-m>'],
564 '<<open-new-window>>': ['<Control-n>'],
565 '<<open-window-from-file>>': ['<Control-o>'],
566 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000567 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000568 '<<redo>>': ['<Control-y>'],
569 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000570 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000571 '<<save-window-as-file>>': ['<Alt-s>'],
572 '<<save-window>>': ['<Control-s>'],
573 '<<select-all>>': ['<Alt-a>'],
574 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000575 '<<undo>>': ['<Control-z>'],
576 '<<find-again>>': ['<Control-g>', '<F3>'],
577 '<<find-in-files>>': ['<Alt-F3>'],
578 '<<find-selection>>': ['<Control-F3>'],
579 '<<find>>': ['<Control-f>'],
580 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000581 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000582 '<<smart-backspace>>': ['<Key-BackSpace>'],
583 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
584 '<<smart-indent>>': ['<Key-Tab>'],
585 '<<indent-region>>': ['<Control-Key-bracketright>'],
586 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
587 '<<comment-region>>': ['<Alt-Key-3>'],
588 '<<uncomment-region>>': ['<Alt-Key-4>'],
589 '<<tabify-region>>': ['<Alt-Key-5>'],
590 '<<untabify-region>>': ['<Alt-Key-6>'],
591 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000592 '<<change-indentwidth>>': ['<Alt-Key-u>'],
593 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
594 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000595 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000596 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000597 for event in keyBindings.keys():
598 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000599 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000600 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000601 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000602 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
603 ' -\n problem retrieving key binding for event %r'
604 '\n from key set %r.\n'
605 ' returning default value: %r\n' %
606 (event, keySetName, keyBindings[event]))
Steven M. Gava49745752002-02-18 01:43:11 +0000607 sys.stderr.write(warning)
Steven M. Gava17d01542001-12-03 00:37:28 +0000608 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000609
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000610 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000611 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000612
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000613 Valid configSets are 'user' or 'default'. Return a list of tuples of
614 the form (menu_item , path_to_help_file , option), or return the empty
615 list. 'option' is the sequence number of the help resource. 'option'
616 values determine the position of the menu items on the Help menu,
617 therefore the returned list must be sorted by 'option'.
618
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000619 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000620 helpSources=[]
621 if configSet=='user':
622 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000623 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000624 cfgParser=self.defaultCfg['main']
625 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000626 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000627 options=cfgParser.GetOptionList('HelpFiles')
628 for option in options:
629 value=cfgParser.Get('HelpFiles',option,default=';')
630 if value.find(';')==-1: #malformed config entry with no ';'
631 menuItem='' #make these empty
632 helpPath='' #so value won't be added to list
633 else: #config entry contains ';' as expected
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000634 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000635 menuItem=value[0].strip()
636 helpPath=value[1].strip()
637 if menuItem and helpPath: #neither are empty strings
638 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000639 helpSources.sort(self.__helpsort)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000640 return helpSources
641
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000642 def __helpsort(self, h1, h2):
643 if int(h1[2]) < int(h2[2]):
644 return -1
645 elif int(h1[2]) > int(h2[2]):
646 return 1
647 else:
648 return 0
649
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000650 def GetAllExtraHelpSourcesList(self):
651 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000652 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000653 sources configured, or an empty list if there are none. Tuples are of
654 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000655 """
656 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000657 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000658 return allHelpSources
659
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000660 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000661 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000662 load all configuration files.
663 """
664 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000665 self.defaultCfg[key].Load()
666 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000667
668 def SaveUserCfgFiles(self):
669 """
670 write all loaded user configuration files back to disk
671 """
672 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000673 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000674
675idleConf=IdleConf()
676
677### module test
678if __name__ == '__main__':
679 def dumpCfg(cfg):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000680 print('\n',cfg,'\n')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000681 for key in cfg.keys():
682 sections=cfg[key].sections()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000683 print(key)
684 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000685 for section in sections:
686 options=cfg[key].options(section)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000687 print(section)
688 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000689 for option in options:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000690 print(option, '=', cfg[key].Get(section,option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000691 dumpCfg(idleConf.defaultCfg)
692 dumpCfg(idleConf.userCfg)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000693 print(idleConf.userCfg['main'].Get('Theme','name'))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000694 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')