blob: ea913c7f809211c0e8c2395cb5f0c1cc3aba25f1 [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
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
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000041 def Get(self, section, option, type=None, default=None):
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 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000046 if type=='bool':
Steven M. Gava41a85322001-10-29 08:05:34 +000047 getVal=self.getboolean
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000048 elif type=='int':
Steven M. Gava41a85322001-10-29 08:05:34 +000049 getVal=self.getint
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000050 else:
Steven M. Gava41a85322001-10-29 08:05:34 +000051 getVal=self.get
Steven M. Gavac11ccf32001-09-24 09:43:17 +000052 if self.has_option(section,option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000053 #return getVal(section, option, raw, vars, default)
Steven M. Gava429a86af2001-10-23 10:42:12 +000054 return getVal(section, option)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000055 else:
56 return default
Steven M. Gavac11ccf32001-09-24 09:43:17 +000057
Steven M. Gavac11ccf32001-09-24 09:43:17 +000058 def GetOptionList(self,section):
59 """
60 Get an option list for given section
61 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +000062 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000063 return self.options(section)
64 else: #return a default value
65 return []
66
Steven M. Gavac11ccf32001-09-24 09:43:17 +000067 def Load(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000068 """
69 Load the configuration file from disk
Steven M. Gavac11ccf32001-09-24 09:43:17 +000070 """
71 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000072
Steven M. Gavac11ccf32001-09-24 09:43:17 +000073class IdleUserConfParser(IdleConfParser):
74 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000075 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000076 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000077
78 def AddSection(self,section):
79 """
80 if section doesn't exist, add it
81 """
82 if not self.has_section(section):
83 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000084
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000085 def RemoveEmptySections(self):
86 """
87 remove any sections that have no options
88 """
89 for section in self.sections():
90 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000091 self.remove_section(section)
92
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000093 def IsEmpty(self):
94 """
95 Remove empty sections and then return 1 if parser has no sections
96 left, else return 0.
97 """
98 self.RemoveEmptySections()
99 if self.sections():
100 return 0
101 else:
102 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000103
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000104 def RemoveOption(self,section,option):
105 """
106 If section/option exists, remove it.
107 Returns 1 if option was removed, 0 otherwise.
108 """
109 if self.has_section(section):
110 return self.remove_option(section,option)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000111
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000112 def SetOption(self,section,option,value):
113 """
114 Sets option to value, adding section if required.
115 Returns 1 if option was added or changed, otherwise 0.
116 """
117 if self.has_option(section,option):
118 if self.get(section,option)==value:
119 return 0
120 else:
121 self.set(section,option,value)
122 return 1
123 else:
124 if not self.has_section(section):
125 self.add_section(section)
126 self.set(section,option,value)
127 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000128
Steven M. Gavab77d3432002-03-02 07:16:21 +0000129 def RemoveFile(self):
130 """
131 Removes the user config file from disk if it exists.
132 """
133 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000134 os.remove(self.file)
135
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000136 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000137 """Update user configuration file.
138
139 Remove empty sections. If resulting config isn't empty, write the file
140 to disk. If config is empty, remove the file from disk if it exists.
141
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000142 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000143 if not self.IsEmpty():
144 cfgFile=open(self.file,'w')
145 self.write(cfgFile)
146 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000147 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000148
149class IdleConf:
150 """
151 holds config parsers for all idle config files:
152 default config files
153 (idle install dir)/config-main.def
154 (idle install dir)/config-extensions.def
155 (idle install dir)/config-highlight.def
156 (idle install dir)/config-keys.def
157 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000158 (user home dir)/.idlerc/config-main.cfg
159 (user home dir)/.idlerc/config-extensions.cfg
160 (user home dir)/.idlerc/config-highlight.cfg
161 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000162 """
163 def __init__(self):
164 self.defaultCfg={}
165 self.userCfg={}
166 self.cfg={}
167 self.CreateConfigHandlers()
168 self.LoadCfgFiles()
169 #self.LoadCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000170
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000171 def CreateConfigHandlers(self):
172 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000173 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000174 configurations respectively
175 """
176 #build idle install path
177 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000178 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000179 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000180 idleDir=os.path.abspath(sys.path[0])
181 userDir=self.GetUserCfgDir()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000182 configTypes=('main','extensions','highlight','keys')
183 defCfgFiles={}
184 usrCfgFiles={}
185 for cfgType in configTypes: #build config file names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000186 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
187 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000188 for cfgType in configTypes: #create config parsers
189 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
190 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000191
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000192 def GetUserCfgDir(self):
193 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000194 Creates (if required) and returns a filesystem directory for storing
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000195 user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000196
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000197 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000198 cfgDir = '.idlerc'
199 userDir = os.path.expanduser('~')
200 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000201 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000202 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
203 userDir+',\n but the path does not exist.\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000204 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000205 userDir = '~'
206 if userDir == "~": # still no path to home!
207 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
208 userDir = os.getcwd()
209 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000210 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000211 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000212 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000213 except (OSError, IOError):
214 warn = ('\n Warning: unable to create user config directory\n'+
215 userDir+'\n Check path and permissions.\n Exiting!\n\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000216 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000217 raise SystemExit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000218 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000219
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000220 def GetOption(self, configType, section, option, default=None, type=None,
221 warn_on_default=True):
Steven M. Gava429a86af2001-10-23 10:42:12 +0000222 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000223 Get an option value for given config type and given general
Steven M. Gava429a86af2001-10-23 10:42:12 +0000224 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000225 return as type. Firstly the user configuration is checked, with a
226 fallback to the default configuration, and a final 'catch all'
227 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86af2001-10-23 10:42:12 +0000228 either the user or the default configuration.
229 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000230 If a default is returned, and warn_on_default is True, a warning is
231 printed to stderr.
232
Steven M. Gava429a86af2001-10-23 10:42:12 +0000233 """
234 if self.userCfg[configType].has_option(section,option):
235 return self.userCfg[configType].Get(section, option, type=type)
236 elif self.defaultCfg[configType].has_option(section,option):
237 return self.defaultCfg[configType].Get(section, option, type=type)
Steven M. Gava052937f2002-02-11 02:20:53 +0000238 else: #returning default, print warning
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000239 if warn_on_default:
240 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
241 ' problem retrieving configration option %r\n'
242 ' from section %r.\n'
243 ' returning default value: %r\n' %
244 (option, section, default))
245 sys.stderr.write(warning)
Steven M. Gava429a86af2001-10-23 10:42:12 +0000246 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000247
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000248 def SetOption(self, configType, section, option, value):
249 """In user's config file, set section's option to value.
250
251 """
252 self.userCfg[configType].SetOption(section, option, value)
253
Steven M. Gava2a63a072001-10-26 06:50:54 +0000254 def GetSectionList(self, configSet, configType):
255 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000256 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000257 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000258 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000259 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000260 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000261 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000262 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000263 if configSet == 'user':
264 cfgParser=self.userCfg[configType]
265 elif configSet == 'default':
266 cfgParser=self.defaultCfg[configType]
267 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000268 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000269 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000270
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000271 def GetHighlight(self, theme, element, fgBg=None):
272 """
273 return individual highlighting theme elements.
274 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000275 containing fg and bg colours (appropriate for passing to Tkinter in,
276 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000277 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000278 if self.defaultCfg['highlight'].has_section(theme):
279 themeDict=self.GetThemeDict('default',theme)
280 else:
281 themeDict=self.GetThemeDict('user',theme)
282 fore=themeDict[element+'-foreground']
283 if element=='cursor': #there is no config value for cursor bg
284 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000285 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000286 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000287 highlight={"foreground": fore,"background": back}
288 if not fgBg: #return dict of both colours
289 return highlight
290 else: #return specified colour only
291 if fgBg == 'fg':
292 return highlight["foreground"]
293 if fgBg == 'bg':
294 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000295 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000296 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000297
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000298 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000299 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000300 type - string, 'default' or 'user' theme type
301 themeName - string, theme name
302 Returns a dictionary which holds {option:value} for each element
303 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000304 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000305 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000306 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000307 if type == 'user':
308 cfgParser=self.userCfg['highlight']
309 elif type == 'default':
310 cfgParser=self.defaultCfg['highlight']
311 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000312 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000313 #foreground and background values are provded for each theme element
314 #(apart from cursor) even though all these values are not yet used
315 #by idle, to allow for their use in the future. Default values are
316 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000317 theme={ 'normal-foreground':'#000000',
318 'normal-background':'#ffffff',
319 'keyword-foreground':'#000000',
320 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000321 'builtin-foreground':'#000000',
322 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000323 'comment-foreground':'#000000',
324 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000325 'string-foreground':'#000000',
326 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000327 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000328 'definition-background':'#ffffff',
329 'hilite-foreground':'#000000',
330 'hilite-background':'gray',
331 'break-foreground':'#ffffff',
332 'break-background':'#000000',
333 'hit-foreground':'#ffffff',
334 'hit-background':'#000000',
335 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000336 'error-background':'#000000',
337 #cursor (only foreground can be set)
338 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000339 #shell window
340 'stdout-foreground':'#000000',
341 'stdout-background':'#ffffff',
342 'stderr-foreground':'#000000',
343 'stderr-background':'#ffffff',
344 'console-foreground':'#000000',
345 'console-background':'#ffffff' }
346 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000347 if not cfgParser.has_option(themeName,element):
348 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000349 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
350 ' -\n problem retrieving theme element %r'
351 '\n from theme %r.\n'
352 ' returning default value: %r\n' %
353 (element, themeName, theme[element]))
Steven M. Gava052937f2002-02-11 02:20:53 +0000354 sys.stderr.write(warning)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000355 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000356 theme[element]=colour
357 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000358
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000359 def CurrentTheme(self):
360 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000361 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000362 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000363 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000364
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000365 def CurrentKeys(self):
366 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000367 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000368 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000369 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000370
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000371 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000372 """
373 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000374 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000375 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000376 extns=self.RemoveKeyBindNames(
377 self.GetSectionList('default','extensions'))
378 userExtns=self.RemoveKeyBindNames(
379 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000380 for extn in userExtns:
381 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000382 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000383 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000384 activeExtns=[]
385 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000386 if self.GetOption('extensions', extn, 'enable', default=True,
387 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000388 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000389 if editor_only or shell_only:
390 if editor_only:
391 option = "enable_editor"
392 else:
393 option = "enable_shell"
394 if self.GetOption('extensions', extn,option,
395 default=True, type='bool',
396 warn_on_default=False):
397 activeExtns.append(extn)
398 else:
399 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000400 return activeExtns
401 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000402 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000403
Steven M. Gavac628a062002-01-19 10:33:21 +0000404 def RemoveKeyBindNames(self,extnNameList):
405 #get rid of keybinding section names
406 names=extnNameList
407 kbNameIndicies=[]
408 for name in names:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000409 if name.endswith('_bindings') or name.endswith('_cfgBindings'):
410 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000411 kbNameIndicies.sort()
412 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000413 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000414 del(names[index])
415 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000416
Steven M. Gavaa498af22002-02-01 01:33:36 +0000417 def GetExtnNameForEvent(self,virtualEvent):
418 """
419 Returns the name of the extension that virtualEvent is bound in, or
420 None if not bound in any extension.
421 virtualEvent - string, name of the virtual event to test for, without
422 the enclosing '<< >>'
423 """
424 extName=None
425 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000426 for extn in self.GetExtensions(active_only=0):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000427 for event in self.GetExtensionKeys(extn).keys():
428 if event == vEvent:
429 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000430 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000431
Steven M. Gavac628a062002-01-19 10:33:21 +0000432 def GetExtensionKeys(self,extensionName):
433 """
434 returns a dictionary of the configurable keybindings for a particular
435 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000436 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000437 """
438 keysName=extensionName+'_cfgBindings'
439 activeKeys=self.GetCurrentKeySet()
440 extKeys={}
441 if self.defaultCfg['extensions'].has_section(keysName):
442 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
443 for eventName in eventNames:
444 event='<<'+eventName+'>>'
445 binding=activeKeys[event]
446 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000447 return extKeys
448
Steven M. Gavac628a062002-01-19 10:33:21 +0000449 def __GetRawExtensionKeys(self,extensionName):
450 """
451 returns a dictionary of the configurable keybindings for a particular
452 extension, as defined in the configuration files, or an empty dictionary
453 if no bindings are found
454 """
455 keysName=extensionName+'_cfgBindings'
456 extKeys={}
457 if self.defaultCfg['extensions'].has_section(keysName):
458 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
459 for eventName in eventNames:
460 binding=self.GetOption('extensions',keysName,
461 eventName,default='').split()
462 event='<<'+eventName+'>>'
463 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000464 return extKeys
465
Steven M. Gavac628a062002-01-19 10:33:21 +0000466 def GetExtensionBindings(self,extensionName):
467 """
468 Returns a dictionary of all the event bindings for a particular
469 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000470 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000471 keybindings are disabled.
472 """
473 bindsName=extensionName+'_bindings'
474 extBinds=self.GetExtensionKeys(extensionName)
475 #add the non-configurable bindings
476 if self.defaultCfg['extensions'].has_section(bindsName):
477 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
478 for eventName in eventNames:
479 binding=self.GetOption('extensions',bindsName,
480 eventName,default='').split()
481 event='<<'+eventName+'>>'
482 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000483
484 return extBinds
485
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000486 def GetKeyBinding(self, keySetName, eventStr):
487 """
488 returns the keybinding for a specific event.
489 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000490 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000491 represented as a string, eg. '<<event>>'
492 """
493 eventName=eventStr[2:-2] #trim off the angle brackets
494 binding=self.GetOption('keys',keySetName,eventName,default='').split()
495 return binding
496
Steven M. Gavac628a062002-01-19 10:33:21 +0000497 def GetCurrentKeySet(self):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000498 return self.GetKeySet(self.CurrentKeys())
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000499
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000500 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000501 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000502 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000503 keybindings for all currently active extensions. If a binding defined
504 in an extension is already in use, that binding is disabled.
505 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000506 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000507 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000508 for extn in activeExtns:
509 extKeys=self.__GetRawExtensionKeys(extn)
510 if extKeys: #the extension defines keybindings
511 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000512 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000513 #the binding is already in use
514 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000515 keySet[event]=extKeys[event] #add binding
516 return keySet
517
Steven M. Gavaa498af22002-02-01 01:33:36 +0000518 def IsCoreBinding(self,virtualEvent):
519 """
520 returns true if the virtual event is bound in the core idle keybindings.
521 virtualEvent - string, name of the virtual event to test for, without
522 the enclosing '<< >>'
523 """
524 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000525
Steven M. Gavac628a062002-01-19 10:33:21 +0000526 def GetCoreKeys(self, keySetName=None):
527 """
528 returns the requested set of core keybindings, with fallbacks if
529 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000530 Keybindings loaded from the config file(s) are loaded _over_ these
531 defaults, so if there is a problem getting any core binding there will
532 be an 'ultimate last resort fallback' to the CUA-ish bindings
533 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000534 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000535 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000536 '<<copy>>': ['<Control-c>', '<Control-C>'],
537 '<<cut>>': ['<Control-x>', '<Control-X>'],
538 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000539 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
540 '<<center-insert>>': ['<Control-l>'],
541 '<<close-all-windows>>': ['<Control-q>'],
542 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000543 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000544 '<<end-of-file>>': ['<Control-d>'],
545 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000546 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000547 '<<history-next>>': ['<Alt-n>'],
548 '<<history-previous>>': ['<Alt-p>'],
549 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000550 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000551 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000552 '<<open-class-browser>>': ['<Alt-c>'],
553 '<<open-module>>': ['<Alt-m>'],
554 '<<open-new-window>>': ['<Control-n>'],
555 '<<open-window-from-file>>': ['<Control-o>'],
556 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000557 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000558 '<<redo>>': ['<Control-y>'],
559 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000560 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000561 '<<save-window-as-file>>': ['<Alt-s>'],
562 '<<save-window>>': ['<Control-s>'],
563 '<<select-all>>': ['<Alt-a>'],
564 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000565 '<<undo>>': ['<Control-z>'],
566 '<<find-again>>': ['<Control-g>', '<F3>'],
567 '<<find-in-files>>': ['<Alt-F3>'],
568 '<<find-selection>>': ['<Control-F3>'],
569 '<<find>>': ['<Control-f>'],
570 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000571 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000572 '<<smart-backspace>>': ['<Key-BackSpace>'],
573 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
574 '<<smart-indent>>': ['<Key-Tab>'],
575 '<<indent-region>>': ['<Control-Key-bracketright>'],
576 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
577 '<<comment-region>>': ['<Alt-Key-3>'],
578 '<<uncomment-region>>': ['<Alt-Key-4>'],
579 '<<tabify-region>>': ['<Alt-Key-5>'],
580 '<<untabify-region>>': ['<Alt-Key-6>'],
581 '<<toggle-tabs>>': ['<Alt-Key-t>'],
582 '<<change-indentwidth>>': ['<Alt-Key-u>']
583 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000584 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000585 for event in keyBindings.keys():
586 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000587 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000588 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000589 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000590 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
591 ' -\n problem retrieving key binding for event %r'
592 '\n from key set %r.\n'
593 ' returning default value: %r\n' %
594 (event, keySetName, keyBindings[event]))
Steven M. Gava49745752002-02-18 01:43:11 +0000595 sys.stderr.write(warning)
Steven M. Gava17d01542001-12-03 00:37:28 +0000596 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000597
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000598 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000599 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000600
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000601 Valid configSets are 'user' or 'default'. Return a list of tuples of
602 the form (menu_item , path_to_help_file , option), or return the empty
603 list. 'option' is the sequence number of the help resource. 'option'
604 values determine the position of the menu items on the Help menu,
605 therefore the returned list must be sorted by 'option'.
606
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000607 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000608 helpSources=[]
609 if configSet=='user':
610 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000611 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000612 cfgParser=self.defaultCfg['main']
613 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000614 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000615 options=cfgParser.GetOptionList('HelpFiles')
616 for option in options:
617 value=cfgParser.Get('HelpFiles',option,default=';')
618 if value.find(';')==-1: #malformed config entry with no ';'
619 menuItem='' #make these empty
620 helpPath='' #so value won't be added to list
621 else: #config entry contains ';' as expected
622 value=string.split(value,';')
623 menuItem=value[0].strip()
624 helpPath=value[1].strip()
625 if menuItem and helpPath: #neither are empty strings
626 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000627 helpSources.sort(self.__helpsort)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000628 return helpSources
629
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000630 def __helpsort(self, h1, h2):
631 if int(h1[2]) < int(h2[2]):
632 return -1
633 elif int(h1[2]) > int(h2[2]):
634 return 1
635 else:
636 return 0
637
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000638 def GetAllExtraHelpSourcesList(self):
639 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000640 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000641 sources configured, or an empty list if there are none. Tuples are of
642 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000643 """
644 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000645 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000646 return allHelpSources
647
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000648 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000649 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000650 load all configuration files.
651 """
652 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000653 self.defaultCfg[key].Load()
654 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000655
656 def SaveUserCfgFiles(self):
657 """
658 write all loaded user configuration files back to disk
659 """
660 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000661 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000662
663idleConf=IdleConf()
664
665### module test
666if __name__ == '__main__':
667 def dumpCfg(cfg):
668 print '\n',cfg,'\n'
669 for key in cfg.keys():
670 sections=cfg[key].sections()
671 print key
672 print sections
673 for section in sections:
674 options=cfg[key].options(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000675 print section
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000676 print options
677 for option in options:
678 print option, '=', cfg[key].Get(section,option)
679 dumpCfg(idleConf.defaultCfg)
680 dumpCfg(idleConf.userCfg)
681 print idleConf.userCfg['main'].Get('Theme','name')
682 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')