blob: b78ae752d7a8579becb43888a245f85323b8f6f0 [file] [log] [blame]
Steven M. Gava2a63a072001-10-26 06:50:54 +00001"""
Steven M. Gavaad4f5322002-01-03 12:05:17 +00002Provides access to stored idle configuration information.
Steven M. Gavaad4f5322002-01-03 12:05:17 +00003"""
Steven M. Gavac5976402002-01-04 03:06:08 +00004# Throughout this module there is an emphasis on returning useable defaults
5# when a problem occurs in returning a requested configuration value back to
6# idle. This is to allow idle to continue to function in spite of errors in
7# the retrieval of config information. When a default is returned instead of
8# a requested config value, a message is printed to stderr to aid in
9# configuration problem notification and resolution.
10
Steven M. Gavac11ccf32001-09-24 09:43:17 +000011import os
12import sys
13from ConfigParser import ConfigParser, NoOptionError, NoSectionError
14
15class IdleConfParser(ConfigParser):
16 """
17 A ConfigParser specialised for idle configuration file handling
18 """
19 def __init__(self, cfgFile, cfgDefaults=None):
20 """
21 cfgFile - string, fully specified configuration file name
22 """
23 self.file=cfgFile
24 ConfigParser.__init__(self,defaults=cfgDefaults)
25
Steven M. Gavaad4f5322002-01-03 12:05:17 +000026 def Get(self, section, option, type=None): #,default=None)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000027 """
28 Get an option value for given section/option or return default.
29 If type is specified, return as type.
30 """
Steven M. Gava41a85322001-10-29 08:05:34 +000031 if type=='bool':
32 getVal=self.getboolean
33 elif type=='int':
34 getVal=self.getint
35 else:
36 getVal=self.get
Steven M. Gavac11ccf32001-09-24 09:43:17 +000037 if self.has_option(section,option):
Steven M. Gava429a86a2001-10-23 10:42:12 +000038 #return getVal(section, option, raw, vars)
39 return getVal(section, option)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000040
Steven M. Gavac11ccf32001-09-24 09:43:17 +000041 def GetOptionList(self,section):
42 """
43 Get an option list for given section
44 """
45 if self.has_section:
46 return self.options(section)
47 else: #return a default value
48 return []
49
Steven M. Gavac11ccf32001-09-24 09:43:17 +000050 def Load(self):
51 """
52 Load the configuration file from disk
53 """
54 self.read(self.file)
55
56class IdleUserConfParser(IdleConfParser):
57 """
58 IdleConfigParser specialised for user configuration handling
59 """
60 def Save(self):
61 """
62 write loaded user configuration file back to disk
63 """
64 # this is a user config, it can be written to disk
65 self.write()
66
67class IdleConf:
68 """
69 holds config parsers for all idle config files:
70 default config files
71 (idle install dir)/config-main.def
72 (idle install dir)/config-extensions.def
73 (idle install dir)/config-highlight.def
74 (idle install dir)/config-keys.def
75 user config files
Steven M. Gavaad4f5322002-01-03 12:05:17 +000076 (user home dir)/.idlerc/config-main.cfg
77 (user home dir)/.idlerc/config-extensions.cfg
78 (user home dir)/.idlerc/config-highlight.cfg
79 (user home dir)/.idlerc/config-keys.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +000080 """
81 def __init__(self):
82 self.defaultCfg={}
83 self.userCfg={}
84 self.cfg={}
85 self.CreateConfigHandlers()
86 self.LoadCfgFiles()
87 #self.LoadCfg()
88
89 def CreateConfigHandlers(self):
90 """
Steven M. Gavaad4f5322002-01-03 12:05:17 +000091 set up a dictionary of config parsers for default and user
Steven M. Gavac11ccf32001-09-24 09:43:17 +000092 configurations respectively
93 """
94 #build idle install path
95 if __name__ != '__main__': # we were imported
96 idledir=os.path.dirname(__file__)
97 else: # we were exec'ed (for testing only)
98 idledir=os.path.abspath(sys.path[0])
99 #print idledir
100 try: #build user home path
101 userdir = os.environ['HOME'] #real home directory
102 except KeyError:
103 userdir = os.getcwd() #hack for os'es without real homedirs
104 userdir=os.path.join(userdir,'.idlerc')
105 #print userdir
106 if not os.path.exists(userdir):
107 os.mkdir(userdir)
108 configTypes=('main','extensions','highlight','keys')
109 defCfgFiles={}
110 usrCfgFiles={}
111 for cfgType in configTypes: #build config file names
112 defCfgFiles[cfgType]=os.path.join(idledir,'config-'+cfgType+'.def')
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000113 usrCfgFiles[cfgType]=os.path.join(userdir,'config-'+cfgType+'.cfg')
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000114 for cfgType in configTypes: #create config parsers
115 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
116 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
117
Steven M. Gava2a63a072001-10-26 06:50:54 +0000118 def GetOption(self, configType, section, option, default=None, type=None):
Steven M. Gava429a86a2001-10-23 10:42:12 +0000119 """
120 Get an option value for given config type and given general
121 configuration section/option or return a default. If type is specified,
122 return as type. Firstly the user configuration is checked, with a
123 fallback to the default configuration, and a final 'catch all'
124 fallback to a useable passed-in default if the option isn't present in
125 either the user or the default configuration.
126 configType must be one of ('main','extensions','highlight','keys')
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000127 If a default is returned a warning is printed to stderr.
Steven M. Gava429a86a2001-10-23 10:42:12 +0000128 """
129 if self.userCfg[configType].has_option(section,option):
130 return self.userCfg[configType].Get(section, option, type=type)
131 elif self.defaultCfg[configType].has_option(section,option):
132 return self.defaultCfg[configType].Get(section, option, type=type)
133 else:
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000134 warning=('\n Warning: configHandler.py - IdleConf.GetOption -\n'+
135 ' problem retrieving configration option '+`option`+'\n'+
136 ' from section '+`section`+'.\n'+
137 ' returning default value: '+`default`+'\n')
138 sys.stderr.write(warning)
Steven M. Gava429a86a2001-10-23 10:42:12 +0000139 return default
140
Steven M. Gava2a63a072001-10-26 06:50:54 +0000141 def GetSectionList(self, configSet, configType):
142 """
143 Get a list of sections from either the user or default config for
144 the given config type.
145 configSet must be either 'user' or 'default'
146 configType must be one of ('extensions','highlight','keys')
147 """
148 if not (configType in ('extensions','highlight','keys')):
149 raise 'Invalid configType specified'
150 if configSet == 'user':
151 cfgParser=self.userCfg[configType]
152 elif configSet == 'default':
153 cfgParser=self.defaultCfg[configType]
154 else:
155 raise 'Invalid configSet specified'
156
157 return cfgParser.sections()
158
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000159 def GetHighlight(self, theme, element, fgBg=None):
160 """
161 return individual highlighting theme elements.
162 fgBg - string ('fg'or'bg') or None, if None return a dictionary
163 containing fg and bg colours (appropriate for passing to Tkinter in,
164 e.g., a tag_config call), otherwise fg or bg colour only as specified.
165 """
Steven M. Gava99300612001-11-04 07:03:08 +0000166 #get some fallback defaults
167 defaultFg=self.GetOption('highlight', theme, 'normal' + "-foreground",
168 default='#000000')
169 defaultBg=self.GetOption('highlight', theme, 'normal' + "-background",
170 default='#ffffff')
171 #try for requested element colours
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000172 fore = self.GetOption('highlight', theme, element + "-foreground")
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000173 back = None
174 if element == 'cursor': #there is no config value for cursor bg
175 back = None
176 else:
177 back = self.GetOption('highlight', theme, element + "-background")
Steven M. Gava99300612001-11-04 07:03:08 +0000178 #fall back if required
179 if not fore: fore=defaultFg
180 if not back: back=defaultBg
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000181 highlight={"foreground": fore,"background": back}
182 if not fgBg: #return dict of both colours
183 return highlight
184 else: #return specified colour only
185 if fgBg == 'fg':
186 return highlight["foreground"]
187 if fgBg == 'bg':
188 return highlight["background"]
189 else:
190 raise 'Invalid fgBg specified'
191
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000192
Steven M. Gava2a63a072001-10-26 06:50:54 +0000193 def GetTheme(self, name=None):
194 """
195 Gets the requested theme or returns a final fallback theme in case
196 one can't be obtained from either the user or default config files.
197 """
198 pass
199
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000200 def CurrentTheme(self):
201 """
202 Returns the name of the currently active theme
203 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000204 return self.GetOption('main','Theme','name',default='')
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000205
206
207 def CurrentKeys(self):
208 """
209 Returns the name of the currently active theme
210 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000211 return self.GetOption('main','Keys','name',default='')
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000212
213 def GetExtensions(self, activeOnly=1):
214 """
215 Gets a list of all idle extensions declared in the config files.
216 activeOnly - boolean, if true only return active (enabled) extensions
217 """
218 extns=self.GetSectionList('default','extensions')
219 userExtns=self.GetSectionList('user','extensions')
220 for extn in userExtns:
221 if extn not in extns: #user has added own extension
222 extns.append(extn)
223 if activeOnly:
224 activeExtns=[]
225 for extn in extns:
226 if self.GetOption('extensions',extn,'enable',default=1,type='bool'):
227 #the extension is enabled
228 activeExtns.append(extn)
229 return activeExtns
230 else:
231 return extns
232
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000233 def GetKeyBinding(self, keySetName, eventStr):
234 """
235 returns the keybinding for a specific event.
236 keySetName - string, name of key binding set
237 eventStr - string, the virtual event we want the binding for,
238 represented as a string, eg. '<<event>>'
239 """
240 eventName=eventStr[2:-2] #trim off the angle brackets
241 binding=self.GetOption('keys',keySetName,eventName,default='').split()
242 return binding
243
Steven M. Gava17d01542001-12-03 00:37:28 +0000244 def GetKeys(self, keySetName=None):
Steven M. Gava2a63a072001-10-26 06:50:54 +0000245 """
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000246 returns the requested set of keybindings, with fallbacks if required.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000247 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000248 #keybindings loaded from the config file(s) are loaded _over_ these
249 #defaults, so if there is a problem getting any binding there will
250 #be an 'ultimate last resort fallback' to the CUA-ish bindings
251 #defined here.
252 keyBindings={
253 '<<Copy>>': ['<Control-c>', '<Control-C>'],
254 '<<Cut>>': ['<Control-x>', '<Control-X>'],
255 '<<Paste>>': ['<Control-v>', '<Control-V>'],
256 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
257 '<<center-insert>>': ['<Control-l>'],
258 '<<close-all-windows>>': ['<Control-q>'],
259 '<<close-window>>': ['<Alt-F4>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000260 '<<end-of-file>>': ['<Control-d>'],
261 '<<python-docs>>': ['<F1>'],
262 '<<python-context-help>>': ['<Shift-F1>'],
263 '<<history-next>>': ['<Alt-n>'],
264 '<<history-previous>>': ['<Alt-p>'],
265 '<<interrupt-execution>>': ['<Control-c>'],
266 '<<open-class-browser>>': ['<Alt-c>'],
267 '<<open-module>>': ['<Alt-m>'],
268 '<<open-new-window>>': ['<Control-n>'],
269 '<<open-window-from-file>>': ['<Control-o>'],
270 '<<plain-newline-and-indent>>': ['<Control-j>'],
271 '<<redo>>': ['<Control-y>'],
272 '<<remove-selection>>': ['<Escape>'],
273 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-s>'],
274 '<<save-window-as-file>>': ['<Alt-s>'],
275 '<<save-window>>': ['<Control-s>'],
276 '<<select-all>>': ['<Alt-a>'],
277 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000278 '<<undo>>': ['<Control-z>'],
279 '<<find-again>>': ['<Control-g>', '<F3>'],
280 '<<find-in-files>>': ['<Alt-F3>'],
281 '<<find-selection>>': ['<Control-F3>'],
282 '<<find>>': ['<Control-f>'],
283 '<<replace>>': ['<Control-h>'],
284 '<<goto-line>>': ['<Alt-g>'] }
285
Steven M. Gava17d01542001-12-03 00:37:28 +0000286 if keySetName:
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000287 for event in keyBindings.keys():
288 binding=self.GetKeyBinding(keySetName,event)
289 if binding: #otherwise will keep default
290 keyBindings[event]=binding
Steven M. Gava17d01542001-12-03 00:37:28 +0000291
292 return keyBindings
293
Steven M. Gava2a63a072001-10-26 06:50:54 +0000294
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000295 def LoadCfgFiles(self):
296 """
297 load all configuration files.
298 """
299 for key in self.defaultCfg.keys():
300 self.defaultCfg[key].Load()
301 self.userCfg[key].Load() #same keys
302
303 def SaveUserCfgFiles(self):
304 """
305 write all loaded user configuration files back to disk
306 """
307 for key in self.userCfg.keys():
308 self.userCfg[key].Save()
309
310idleConf=IdleConf()
311
312### module test
313if __name__ == '__main__':
314 def dumpCfg(cfg):
315 print '\n',cfg,'\n'
316 for key in cfg.keys():
317 sections=cfg[key].sections()
318 print key
319 print sections
320 for section in sections:
321 options=cfg[key].options(section)
322 print section
323 print options
324 for option in options:
325 print option, '=', cfg[key].Get(section,option)
326 dumpCfg(idleConf.defaultCfg)
327 dumpCfg(idleConf.userCfg)
328 print idleConf.userCfg['main'].Get('Theme','name')
329 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')