blob: 5ae643a71d6c66a10e47e9ce2ce3d0da8ff41113 [file] [log] [blame]
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001"""Provides access to stored IDLE configuration information.
Steven M. Gavac5976402002-01-04 03:06:08 +00002
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00003Refer to the comments at the beginning of config-main.def for a description of
4the available configuration files and the design implemented to update user
5configuration information. In particular, user configuration choices which
6duplicate the defaults will be removed from the user's configuration files,
Kurt B. Kaisere66675b2003-01-27 02:36:18 +00007and if a file becomes empty, it will be deleted.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00008
9The contents of the user files may be altered using the Options/Configure IDLE
10menu to access the configuration GUI (configDialog.py), or manually.
11
12Throughout this module there is an emphasis on returning useable defaults
13when a problem occurs in returning a requested configuration value back to
14idle. This is to allow IDLE to continue to function in spite of errors in
15the retrieval of config information. When a default is returned instead of
16a requested config value, a message is printed to stderr to aid in
17configuration problem notification and resolution.
18
19"""
20import os
21import sys
22import string
Ronald Oussoren19302d92006-06-11 14:33:36 +000023import macosxSupport
Steven M. Gavac11ccf32001-09-24 09:43:17 +000024from ConfigParser import ConfigParser, NoOptionError, NoSectionError
25
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000026class InvalidConfigType(Exception): pass
27class InvalidConfigSet(Exception): pass
28class InvalidFgBg(Exception): pass
29class InvalidTheme(Exception): pass
30
Steven M. Gavac11ccf32001-09-24 09:43:17 +000031class IdleConfParser(ConfigParser):
32 """
33 A ConfigParser specialised for idle configuration file handling
34 """
35 def __init__(self, cfgFile, cfgDefaults=None):
36 """
37 cfgFile - string, fully specified configuration file name
38 """
39 self.file=cfgFile
40 ConfigParser.__init__(self,defaults=cfgDefaults)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000041
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000042 def Get(self, section, option, type=None, default=None):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000043 """
44 Get an option value for given section/option or return default.
45 If type is specified, return as type.
46 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000047 if type=='bool':
Steven M. Gava41a85322001-10-29 08:05:34 +000048 getVal=self.getboolean
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000049 elif type=='int':
Steven M. Gava41a85322001-10-29 08:05:34 +000050 getVal=self.getint
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000051 else:
Steven M. Gava41a85322001-10-29 08:05:34 +000052 getVal=self.get
Steven M. Gavac11ccf32001-09-24 09:43:17 +000053 if self.has_option(section,option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000054 #return getVal(section, option, raw, vars, default)
Steven M. Gava429a86a2001-10-23 10:42:12 +000055 return getVal(section, option)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000056 else:
57 return default
Steven M. Gavac11ccf32001-09-24 09:43:17 +000058
Steven M. Gavac11ccf32001-09-24 09:43:17 +000059 def GetOptionList(self,section):
60 """
61 Get an option list for given section
62 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +000063 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000064 return self.options(section)
65 else: #return a default value
66 return []
67
Steven M. Gavac11ccf32001-09-24 09:43:17 +000068 def Load(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000069 """
70 Load the configuration file from disk
Steven M. Gavac11ccf32001-09-24 09:43:17 +000071 """
72 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000073
Steven M. Gavac11ccf32001-09-24 09:43:17 +000074class IdleUserConfParser(IdleConfParser):
75 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000076 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000077 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000078
79 def AddSection(self,section):
80 """
81 if section doesn't exist, add it
82 """
83 if not self.has_section(section):
84 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000085
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000086 def RemoveEmptySections(self):
87 """
88 remove any sections that have no options
89 """
90 for section in self.sections():
91 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000092 self.remove_section(section)
93
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000094 def IsEmpty(self):
95 """
96 Remove empty sections and then return 1 if parser has no sections
97 left, else return 0.
98 """
99 self.RemoveEmptySections()
100 if self.sections():
101 return 0
102 else:
103 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000104
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000105 def RemoveOption(self,section,option):
106 """
107 If section/option exists, remove it.
108 Returns 1 if option was removed, 0 otherwise.
109 """
110 if self.has_section(section):
111 return self.remove_option(section,option)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000112
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000113 def SetOption(self,section,option,value):
114 """
115 Sets option to value, adding section if required.
116 Returns 1 if option was added or changed, otherwise 0.
117 """
118 if self.has_option(section,option):
119 if self.get(section,option)==value:
120 return 0
121 else:
122 self.set(section,option,value)
123 return 1
124 else:
125 if not self.has_section(section):
126 self.add_section(section)
127 self.set(section,option,value)
128 return 1
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000129
Steven M. Gavab77d3432002-03-02 07:16:21 +0000130 def RemoveFile(self):
131 """
132 Removes the user config file from disk if it exists.
133 """
134 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000135 os.remove(self.file)
136
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000137 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000138 """Update user configuration file.
139
140 Remove empty sections. If resulting config isn't empty, write the file
141 to disk. If config is empty, remove the file from disk if it exists.
142
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000143 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000144 if not self.IsEmpty():
145 cfgFile=open(self.file,'w')
146 self.write(cfgFile)
147 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000148 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000149
150class IdleConf:
151 """
152 holds config parsers for all idle config files:
153 default config files
154 (idle install dir)/config-main.def
155 (idle install dir)/config-extensions.def
156 (idle install dir)/config-highlight.def
157 (idle install dir)/config-keys.def
158 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000159 (user home dir)/.idlerc/config-main.cfg
160 (user home dir)/.idlerc/config-extensions.cfg
161 (user home dir)/.idlerc/config-highlight.cfg
162 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000163 """
164 def __init__(self):
165 self.defaultCfg={}
166 self.userCfg={}
167 self.cfg={}
168 self.CreateConfigHandlers()
169 self.LoadCfgFiles()
170 #self.LoadCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000171
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000172 def CreateConfigHandlers(self):
173 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000174 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000175 configurations respectively
176 """
177 #build idle install path
178 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000179 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000180 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000181 idleDir=os.path.abspath(sys.path[0])
182 userDir=self.GetUserCfgDir()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000183 configTypes=('main','extensions','highlight','keys')
184 defCfgFiles={}
185 usrCfgFiles={}
186 for cfgType in configTypes: #build config file names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000187 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
188 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000189 for cfgType in configTypes: #create config parsers
190 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
191 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000192
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000193 def GetUserCfgDir(self):
194 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000195 Creates (if required) and returns a filesystem directory for storing
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000196 user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000197
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000198 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000199 cfgDir = '.idlerc'
200 userDir = os.path.expanduser('~')
201 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000202 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000203 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
204 userDir+',\n but the path does not exist.\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000205 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000206 userDir = '~'
207 if userDir == "~": # still no path to home!
208 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
209 userDir = os.getcwd()
210 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000211 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000212 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000213 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000214 except (OSError, IOError):
215 warn = ('\n Warning: unable to create user config directory\n'+
216 userDir+'\n Check path and permissions.\n Exiting!\n\n')
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000217 sys.stderr.write(warn)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000218 raise SystemExit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000219 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000220
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000221 def GetOption(self, configType, section, option, default=None, type=None,
222 warn_on_default=True):
Steven M. Gava429a86a2001-10-23 10:42:12 +0000223 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000224 Get an option value for given config type and given general
Steven M. Gava429a86a2001-10-23 10:42:12 +0000225 configuration section/option or return a default. If type is specified,
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000226 return as type. Firstly the user configuration is checked, with a
227 fallback to the default configuration, and a final 'catch all'
228 fallback to a useable passed-in default if the option isn't present in
Steven M. Gava429a86a2001-10-23 10:42:12 +0000229 either the user or the default configuration.
230 configType must be one of ('main','extensions','highlight','keys')
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000231 If a default is returned, and warn_on_default is True, a warning is
232 printed to stderr.
233
Steven M. Gava429a86a2001-10-23 10:42:12 +0000234 """
235 if self.userCfg[configType].has_option(section,option):
236 return self.userCfg[configType].Get(section, option, type=type)
237 elif self.defaultCfg[configType].has_option(section,option):
238 return self.defaultCfg[configType].Get(section, option, type=type)
Steven M. Gava052937f2002-02-11 02:20:53 +0000239 else: #returning default, print warning
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000240 if warn_on_default:
241 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
242 ' problem retrieving configration option %r\n'
243 ' from section %r.\n'
244 ' returning default value: %r\n' %
245 (option, section, default))
246 sys.stderr.write(warning)
Steven M. Gava429a86a2001-10-23 10:42:12 +0000247 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000248
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000249 def SetOption(self, configType, section, option, value):
250 """In user's config file, set section's option to value.
251
252 """
253 self.userCfg[configType].SetOption(section, option, value)
254
Steven M. Gava2a63a072001-10-26 06:50:54 +0000255 def GetSectionList(self, configSet, configType):
256 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000257 Get a list of sections from either the user or default config for
Steven M. Gava2a63a072001-10-26 06:50:54 +0000258 the given config type.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000259 configSet must be either 'user' or 'default'
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000260 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000261 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000262 if not (configType in ('main','extensions','highlight','keys')):
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000263 raise InvalidConfigType, 'Invalid configType specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000264 if configSet == 'user':
265 cfgParser=self.userCfg[configType]
266 elif configSet == 'default':
267 cfgParser=self.defaultCfg[configType]
268 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000269 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava2a63a072001-10-26 06:50:54 +0000270 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000271
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000272 def GetHighlight(self, theme, element, fgBg=None):
273 """
274 return individual highlighting theme elements.
275 fgBg - string ('fg'or'bg') or None, if None return a dictionary
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000276 containing fg and bg colours (appropriate for passing to Tkinter in,
277 e.g., a tag_config call), otherwise fg or bg colour only as specified.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000278 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000279 if self.defaultCfg['highlight'].has_section(theme):
280 themeDict=self.GetThemeDict('default',theme)
281 else:
282 themeDict=self.GetThemeDict('user',theme)
283 fore=themeDict[element+'-foreground']
284 if element=='cursor': #there is no config value for cursor bg
285 back=themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000286 else:
Steven M. Gava9f25e672002-02-11 02:51:18 +0000287 back=themeDict[element+'-background']
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000288 highlight={"foreground": fore,"background": back}
289 if not fgBg: #return dict of both colours
290 return highlight
291 else: #return specified colour only
292 if fgBg == 'fg':
293 return highlight["foreground"]
294 if fgBg == 'bg':
295 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000296 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000297 raise InvalidFgBg, 'Invalid fgBg specified'
Steven M. Gava9f25e672002-02-11 02:51:18 +0000298
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000299 def GetThemeDict(self,type,themeName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000300 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000301 type - string, 'default' or 'user' theme type
302 themeName - string, theme name
303 Returns a dictionary which holds {option:value} for each element
304 in the specified theme. Values are loaded over a set of ultimate last
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000305 fallback defaults to guarantee that all theme elements are present in
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000306 a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000307 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000308 if type == 'user':
309 cfgParser=self.userCfg['highlight']
310 elif type == 'default':
311 cfgParser=self.defaultCfg['highlight']
312 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000313 raise InvalidTheme, 'Invalid theme type specified'
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000314 #foreground and background values are provded for each theme element
315 #(apart from cursor) even though all these values are not yet used
316 #by idle, to allow for their use in the future. Default values are
317 #generally black and white.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000318 theme={ 'normal-foreground':'#000000',
319 'normal-background':'#ffffff',
320 'keyword-foreground':'#000000',
321 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000322 'builtin-foreground':'#000000',
323 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000324 'comment-foreground':'#000000',
325 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000326 'string-foreground':'#000000',
327 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000328 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000329 'definition-background':'#ffffff',
330 'hilite-foreground':'#000000',
331 'hilite-background':'gray',
332 'break-foreground':'#ffffff',
333 'break-background':'#000000',
334 'hit-foreground':'#ffffff',
335 'hit-background':'#000000',
336 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000337 'error-background':'#000000',
338 #cursor (only foreground can be set)
339 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000340 #shell window
341 'stdout-foreground':'#000000',
342 'stdout-background':'#ffffff',
343 'stderr-foreground':'#000000',
344 'stderr-background':'#ffffff',
345 'console-foreground':'#000000',
346 'console-background':'#ffffff' }
347 for element in theme.keys():
Steven M. Gava052937f2002-02-11 02:20:53 +0000348 if not cfgParser.has_option(themeName,element):
349 #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000350 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
351 ' -\n problem retrieving theme element %r'
352 '\n from theme %r.\n'
353 ' returning default value: %r\n' %
354 (element, themeName, theme[element]))
Steven M. Gava052937f2002-02-11 02:20:53 +0000355 sys.stderr.write(warning)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000356 colour=cfgParser.Get(themeName,element,default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000357 theme[element]=colour
358 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000359
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000360 def CurrentTheme(self):
361 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000362 Returns the name of the currently active theme
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000363 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000364 return self.GetOption('main','Theme','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000365
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000366 def CurrentKeys(self):
367 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000368 Returns the name of the currently active key set
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000369 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000370 return self.GetOption('main','Keys','name',default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000371
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000372 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000373 """
374 Gets a list of all idle extensions declared in the config files.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000375 active_only - boolean, if true only return active (enabled) extensions
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000376 """
Steven M. Gavac628a062002-01-19 10:33:21 +0000377 extns=self.RemoveKeyBindNames(
378 self.GetSectionList('default','extensions'))
379 userExtns=self.RemoveKeyBindNames(
380 self.GetSectionList('user','extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000381 for extn in userExtns:
382 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000383 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000384 if active_only:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000385 activeExtns=[]
386 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000387 if self.GetOption('extensions', extn, 'enable', default=True,
388 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000389 #the extension is enabled
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000390 if editor_only or shell_only:
391 if editor_only:
392 option = "enable_editor"
393 else:
394 option = "enable_shell"
395 if self.GetOption('extensions', extn,option,
396 default=True, type='bool',
397 warn_on_default=False):
398 activeExtns.append(extn)
399 else:
400 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000401 return activeExtns
402 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000403 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000404
Steven M. Gavac628a062002-01-19 10:33:21 +0000405 def RemoveKeyBindNames(self,extnNameList):
406 #get rid of keybinding section names
407 names=extnNameList
408 kbNameIndicies=[]
409 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000410 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000411 kbNameIndicies.append(names.index(name))
Steven M. Gavac628a062002-01-19 10:33:21 +0000412 kbNameIndicies.sort()
413 kbNameIndicies.reverse()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000414 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000415 del(names[index])
416 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000417
Steven M. Gavaa498af22002-02-01 01:33:36 +0000418 def GetExtnNameForEvent(self,virtualEvent):
419 """
420 Returns the name of the extension that virtualEvent is bound in, or
421 None if not bound in any extension.
422 virtualEvent - string, name of the virtual event to test for, without
423 the enclosing '<< >>'
424 """
425 extName=None
426 vEvent='<<'+virtualEvent+'>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000427 for extn in self.GetExtensions(active_only=0):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000428 for event in self.GetExtensionKeys(extn).keys():
429 if event == vEvent:
430 extName=extn
Steven M. Gavaa498af22002-02-01 01:33:36 +0000431 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000432
Steven M. Gavac628a062002-01-19 10:33:21 +0000433 def GetExtensionKeys(self,extensionName):
434 """
435 returns a dictionary of the configurable keybindings for a particular
436 extension,as they exist in the dictionary returned by GetCurrentKeySet;
Steven M. Gavaa498af22002-02-01 01:33:36 +0000437 that is, where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000438 """
439 keysName=extensionName+'_cfgBindings'
440 activeKeys=self.GetCurrentKeySet()
441 extKeys={}
442 if self.defaultCfg['extensions'].has_section(keysName):
443 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
444 for eventName in eventNames:
445 event='<<'+eventName+'>>'
446 binding=activeKeys[event]
447 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000448 return extKeys
449
Steven M. Gavac628a062002-01-19 10:33:21 +0000450 def __GetRawExtensionKeys(self,extensionName):
451 """
452 returns a dictionary of the configurable keybindings for a particular
453 extension, as defined in the configuration files, or an empty dictionary
454 if no bindings are found
455 """
456 keysName=extensionName+'_cfgBindings'
457 extKeys={}
458 if self.defaultCfg['extensions'].has_section(keysName):
459 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
460 for eventName in eventNames:
461 binding=self.GetOption('extensions',keysName,
462 eventName,default='').split()
463 event='<<'+eventName+'>>'
464 extKeys[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000465 return extKeys
466
Steven M. Gavac628a062002-01-19 10:33:21 +0000467 def GetExtensionBindings(self,extensionName):
468 """
469 Returns a dictionary of all the event bindings for a particular
470 extension. The configurable keybindings are returned as they exist in
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000471 the dictionary returned by GetCurrentKeySet; that is, where re-used
Steven M. Gavac628a062002-01-19 10:33:21 +0000472 keybindings are disabled.
473 """
474 bindsName=extensionName+'_bindings'
475 extBinds=self.GetExtensionKeys(extensionName)
476 #add the non-configurable bindings
477 if self.defaultCfg['extensions'].has_section(bindsName):
478 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
479 for eventName in eventNames:
480 binding=self.GetOption('extensions',bindsName,
481 eventName,default='').split()
482 event='<<'+eventName+'>>'
483 extBinds[event]=binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000484
485 return extBinds
486
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000487 def GetKeyBinding(self, keySetName, eventStr):
488 """
489 returns the keybinding for a specific event.
490 keySetName - string, name of key binding set
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000491 eventStr - string, the virtual event we want the binding for,
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000492 represented as a string, eg. '<<event>>'
493 """
494 eventName=eventStr[2:-2] #trim off the angle brackets
495 binding=self.GetOption('keys',keySetName,eventName,default='').split()
496 return binding
497
Steven M. Gavac628a062002-01-19 10:33:21 +0000498 def GetCurrentKeySet(self):
Ronald Oussoren19302d92006-06-11 14:33:36 +0000499 result = self.GetKeySet(self.CurrentKeys())
500
501 if macosxSupport.runningAsOSXApp():
502 # We're using AquaTk, replace all keybingings that use the
503 # Alt key by ones that use the Option key because the former
504 # don't work reliably.
505 for k, v in result.items():
506 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
507 if v != v2:
508 result[k] = v2
509
510 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000511
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000512 def GetKeySet(self,keySetName):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000513 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000514 Returns a dictionary of: all requested core keybindings, plus the
Steven M. Gavac628a062002-01-19 10:33:21 +0000515 keybindings for all currently active extensions. If a binding defined
516 in an extension is already in use, that binding is disabled.
517 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000518 keySet=self.GetCoreKeys(keySetName)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000519 activeExtns=self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000520 for extn in activeExtns:
521 extKeys=self.__GetRawExtensionKeys(extn)
522 if extKeys: #the extension defines keybindings
523 for event in extKeys.keys():
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000524 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000525 #the binding is already in use
526 extKeys[event]='' #disable this binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000527 keySet[event]=extKeys[event] #add binding
528 return keySet
529
Steven M. Gavaa498af22002-02-01 01:33:36 +0000530 def IsCoreBinding(self,virtualEvent):
531 """
532 returns true if the virtual event is bound in the core idle keybindings.
533 virtualEvent - string, name of the virtual event to test for, without
534 the enclosing '<< >>'
535 """
536 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000537
Steven M. Gavac628a062002-01-19 10:33:21 +0000538 def GetCoreKeys(self, keySetName=None):
539 """
540 returns the requested set of core keybindings, with fallbacks if
541 required.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000542 Keybindings loaded from the config file(s) are loaded _over_ these
543 defaults, so if there is a problem getting any core binding there will
544 be an 'ultimate last resort fallback' to the CUA-ish bindings
545 defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000546 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000547 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000548 '<<copy>>': ['<Control-c>', '<Control-C>'],
549 '<<cut>>': ['<Control-x>', '<Control-X>'],
550 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000551 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
552 '<<center-insert>>': ['<Control-l>'],
553 '<<close-all-windows>>': ['<Control-q>'],
554 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000555 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000556 '<<end-of-file>>': ['<Control-d>'],
557 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000558 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000559 '<<history-next>>': ['<Alt-n>'],
560 '<<history-previous>>': ['<Alt-p>'],
561 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000562 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000563 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000564 '<<open-class-browser>>': ['<Alt-c>'],
565 '<<open-module>>': ['<Alt-m>'],
566 '<<open-new-window>>': ['<Control-n>'],
567 '<<open-window-from-file>>': ['<Control-o>'],
568 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000569 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000570 '<<redo>>': ['<Control-y>'],
571 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000572 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000573 '<<save-window-as-file>>': ['<Alt-s>'],
574 '<<save-window>>': ['<Control-s>'],
575 '<<select-all>>': ['<Alt-a>'],
576 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000577 '<<undo>>': ['<Control-z>'],
578 '<<find-again>>': ['<Control-g>', '<F3>'],
579 '<<find-in-files>>': ['<Alt-F3>'],
580 '<<find-selection>>': ['<Control-F3>'],
581 '<<find>>': ['<Control-f>'],
582 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000583 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000584 '<<smart-backspace>>': ['<Key-BackSpace>'],
585 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
586 '<<smart-indent>>': ['<Key-Tab>'],
587 '<<indent-region>>': ['<Control-Key-bracketright>'],
588 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
589 '<<comment-region>>': ['<Alt-Key-3>'],
590 '<<uncomment-region>>': ['<Alt-Key-4>'],
591 '<<tabify-region>>': ['<Alt-Key-5>'],
592 '<<untabify-region>>': ['<Alt-Key-6>'],
593 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000594 '<<change-indentwidth>>': ['<Alt-Key-u>'],
595 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
596 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000597 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000598 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000599 for event in keyBindings.keys():
600 binding=self.GetKeyBinding(keySetName,event)
Steven M. Gava49745752002-02-18 01:43:11 +0000601 if binding:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000602 keyBindings[event]=binding
Steven M. Gava49745752002-02-18 01:43:11 +0000603 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000604 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
605 ' -\n problem retrieving key binding for event %r'
606 '\n from key set %r.\n'
607 ' returning default value: %r\n' %
608 (event, keySetName, keyBindings[event]))
Steven M. Gava49745752002-02-18 01:43:11 +0000609 sys.stderr.write(warning)
Steven M. Gava17d01542001-12-03 00:37:28 +0000610 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000611
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000612 def GetExtraHelpSourceList(self,configSet):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000613 """Fetch list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000614
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000615 Valid configSets are 'user' or 'default'. Return a list of tuples of
616 the form (menu_item , path_to_help_file , option), or return the empty
617 list. 'option' is the sequence number of the help resource. 'option'
618 values determine the position of the menu items on the Help menu,
619 therefore the returned list must be sorted by 'option'.
620
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000621 """
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000622 helpSources=[]
623 if configSet=='user':
624 cfgParser=self.userCfg['main']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000625 elif configSet=='default':
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000626 cfgParser=self.defaultCfg['main']
627 else:
Neal Norwitz5b0b00f2002-11-30 19:10:19 +0000628 raise InvalidConfigSet, 'Invalid configSet specified'
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000629 options=cfgParser.GetOptionList('HelpFiles')
630 for option in options:
631 value=cfgParser.Get('HelpFiles',option,default=';')
632 if value.find(';')==-1: #malformed config entry with no ';'
633 menuItem='' #make these empty
634 helpPath='' #so value won't be added to list
635 else: #config entry contains ';' as expected
636 value=string.split(value,';')
637 menuItem=value[0].strip()
638 helpPath=value[1].strip()
639 if menuItem and helpPath: #neither are empty strings
640 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000641 helpSources.sort(self.__helpsort)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000642 return helpSources
643
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000644 def __helpsort(self, h1, h2):
645 if int(h1[2]) < int(h2[2]):
646 return -1
647 elif int(h1[2]) > int(h2[2]):
648 return 1
649 else:
650 return 0
651
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000652 def GetAllExtraHelpSourcesList(self):
653 """
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000654 Returns a list of tuples containing the details of all additional help
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000655 sources configured, or an empty list if there are none. Tuples are of
656 the format returned by GetExtraHelpSourceList.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000657 """
658 allHelpSources=( self.GetExtraHelpSourceList('default')+
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000659 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000660 return allHelpSources
661
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000662 def LoadCfgFiles(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000663 """
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000664 load all configuration files.
665 """
666 for key in self.defaultCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000667 self.defaultCfg[key].Load()
668 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000669
670 def SaveUserCfgFiles(self):
671 """
672 write all loaded user configuration files back to disk
673 """
674 for key in self.userCfg.keys():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000675 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000676
677idleConf=IdleConf()
678
679### module test
680if __name__ == '__main__':
681 def dumpCfg(cfg):
682 print '\n',cfg,'\n'
683 for key in cfg.keys():
684 sections=cfg[key].sections()
685 print key
686 print sections
687 for section in sections:
688 options=cfg[key].options(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000689 print section
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000690 print options
691 for option in options:
692 print option, '=', cfg[key].Get(section,option)
693 dumpCfg(idleConf.defaultCfg)
694 dumpCfg(idleConf.userCfg)
695 print idleConf.userCfg['main'].Get('Theme','name')
696 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')