blob: 5e6b81be62fac37cfdf747071e7957618820747b [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.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000018"""
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040019# TODOs added Oct 2014, tjr
20
Terry Jan Reedybee003c2014-09-19 22:37:24 -040021from __future__ import print_function
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000022import os
23import sys
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -040024
Terry Jan Reedy9bc50562014-07-01 18:52:31 -040025from ConfigParser import ConfigParser
Steven M. Gavac11ccf32001-09-24 09:43:17 +000026
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000027class InvalidConfigType(Exception): pass
28class InvalidConfigSet(Exception): pass
29class InvalidFgBg(Exception): pass
30class InvalidTheme(Exception): pass
31
Steven M. Gavac11ccf32001-09-24 09:43:17 +000032class IdleConfParser(ConfigParser):
33 """
34 A ConfigParser specialised for idle configuration file handling
35 """
36 def __init__(self, cfgFile, cfgDefaults=None):
37 """
38 cfgFile - string, fully specified configuration file name
39 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040040 self.file = cfgFile
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -040041 ConfigParser.__init__(self, defaults=cfgDefaults)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000042
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000043 def Get(self, section, option, type=None, default=None, raw=False):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000044 """
45 Get an option value for given section/option or return default.
46 If type is specified, return as type.
47 """
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000048 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000049 return default
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040050 if type == 'bool':
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000051 return self.getboolean(section, option)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040052 elif type == 'int':
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000053 return self.getint(section, option)
54 else:
55 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000056
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040057 def GetOptionList(self, section):
58 "Return a list of options for given section, else []."
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):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040065 "Load the configuration file from disk."
Steven M. Gavac11ccf32001-09-24 09:43:17 +000066 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000067
Steven M. Gavac11ccf32001-09-24 09:43:17 +000068class IdleUserConfParser(IdleConfParser):
69 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000070 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000071 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000072
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040073 def AddSection(self, section):
74 "If section doesn't exist, add it."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000075 if not self.has_section(section):
76 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000077
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000078 def RemoveEmptySections(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040079 "Remove any sections that have no options."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000080 for section in self.sections():
81 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000082 self.remove_section(section)
83
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000084 def IsEmpty(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040085 "Return True if no sections after removing empty sections."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000086 self.RemoveEmptySections()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040087 return not self.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000088
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040089 def RemoveOption(self, section, option):
90 """Return True if option is removed from section, else False.
91
92 False if either section does not exist or did not have option.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000093 """
94 if self.has_section(section):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040095 return self.remove_option(section, option)
96 return False
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000097
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040098 def SetOption(self, section, option, value):
99 """Return True if option is added or changed to value, else False.
100
101 Add section if required. False means option already had value.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000102 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400103 if self.has_option(section, option):
104 if self.get(section, option) == value:
105 return False
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000106 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400107 self.set(section, option, value)
108 return True
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000109 else:
110 if not self.has_section(section):
111 self.add_section(section)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400112 self.set(section, option, value)
113 return True
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000114
Steven M. Gavab77d3432002-03-02 07:16:21 +0000115 def RemoveFile(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400116 "Remove user config file self.file from disk if it exists."
Steven M. Gavab77d3432002-03-02 07:16:21 +0000117 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000118 os.remove(self.file)
119
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000120 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000121 """Update user configuration file.
122
123 Remove empty sections. If resulting config isn't empty, write the file
124 to disk. If config is empty, remove the file from disk if it exists.
125
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000126 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000127 if not self.IsEmpty():
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000128 fname = self.file
129 try:
130 cfgFile = open(fname, 'w')
131 except IOError:
Kurt B. Kaiser8d365c32008-02-12 15:45:50 +0000132 os.unlink(fname)
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000133 cfgFile = open(fname, 'w')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400134 with cfgFile:
135 self.write(cfgFile)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000136 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000137 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000138
139class IdleConf:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400140 """Hold config parsers for all idle config files in singleton instance.
141
142 Default config files, self.defaultCfg --
143 for config_type in self.config_types:
144 (idle install dir)/config-{config-type}.def
145
146 User config files, self.userCfg --
147 for config_type in self.config_types:
148 (user home dir)/.idlerc/config-{config-type}.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000149 """
150 def __init__(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400151 self.config_types = ('main', 'extensions', 'highlight', 'keys')
152 self.defaultCfg = {}
153 self.userCfg = {}
154 self.cfg = {} # TODO use to select userCfg vs defaultCfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000155 self.CreateConfigHandlers()
156 self.LoadCfgFiles()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400157
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000158
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000159 def CreateConfigHandlers(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400160 "Populate default and user config parser dictionaries."
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000161 #build idle install path
162 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000163 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000164 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000165 idleDir=os.path.abspath(sys.path[0])
166 userDir=self.GetUserCfgDir()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400167
168 defCfgFiles = {}
169 usrCfgFiles = {}
170 # TODO eliminate these temporaries by combining loops
171 for cfgType in self.config_types: #build config file names
172 defCfgFiles[cfgType] = os.path.join(
173 idleDir, 'config-' + cfgType + '.def')
174 usrCfgFiles[cfgType] = os.path.join(
175 userDir, 'config-' + cfgType + '.cfg')
176 for cfgType in self.config_types: #create config parsers
177 self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType])
178 self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000179
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000180 def GetUserCfgDir(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400181 """Return a filesystem directory for storing user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000182
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400183 Creates it if required.
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000184 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000185 cfgDir = '.idlerc'
186 userDir = os.path.expanduser('~')
187 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000188 if not os.path.exists(userDir):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400189 warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
190 userDir + ',\n but the path does not exist.')
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000191 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400192 print(warn, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000193 except IOError:
194 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000195 userDir = '~'
196 if userDir == "~": # still no path to home!
197 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
198 userDir = os.getcwd()
199 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000200 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000201 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000202 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000203 except (OSError, IOError):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400204 warn = ('\n Warning: unable to create user config directory\n' +
205 userDir + '\n Check path and permissions.\n Exiting!\n')
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400206 print(warn, file=sys.stderr)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000207 raise SystemExit
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400208 # TODO continue without userDIr instead of exit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000209 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000210
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000211 def GetOption(self, configType, section, option, default=None, type=None,
Kurt B. Kaiser90f84922007-02-05 06:03:18 +0000212 warn_on_default=True, raw=False):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400213 """Return a value for configType section option, or default.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000214
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400215 If type is not None, return a value of that type. Also pass raw
216 to the config parser. First try to return a valid value
217 (including type) from a user configuration. If that fails, try
218 the default configuration. If that fails, return default, with a
219 default of None.
220
221 Warn if either user or default configurations have an invalid value.
222 Warn if default is returned and warn_on_default is True.
Steven M. Gava429a86a2001-10-23 10:42:12 +0000223 """
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200224 try:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400225 if self.userCfg[configType].has_option(section, option):
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200226 return self.userCfg[configType].Get(section, option,
227 type=type, raw=raw)
228 except ValueError:
229 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
230 ' invalid %r value for configuration option %r\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400231 ' from section %r: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200232 (type, option, section,
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400233 self.userCfg[configType].Get(section, option, raw=raw)))
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200234 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400235 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200236 except IOError:
237 pass
238 try:
239 if self.defaultCfg[configType].has_option(section,option):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400240 return self.defaultCfg[configType].Get(
241 section, option, type=type, raw=raw)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200242 except ValueError:
243 pass
244 #returning default, print warning
245 if warn_on_default:
246 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
247 ' problem retrieving configuration option %r\n'
248 ' from section %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400249 ' returning default value: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200250 (option, section, default))
251 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400252 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200253 except IOError:
254 pass
255 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000256
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000257 def SetOption(self, configType, section, option, value):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400258 """Set section option to value in user config file."""
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000259 self.userCfg[configType].SetOption(section, option, value)
260
Steven M. Gava2a63a072001-10-26 06:50:54 +0000261 def GetSectionList(self, configSet, configType):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400262 """Return sections for configSet configType configuration.
263
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000264 configSet must be either 'user' or 'default'
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400265 configType must be in self.config_types.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000266 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400267 if not (configType in self.config_types):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400268 raise InvalidConfigType('Invalid configType specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000269 if configSet == 'user':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400270 cfgParser = self.userCfg[configType]
Steven M. Gava2a63a072001-10-26 06:50:54 +0000271 elif configSet == 'default':
272 cfgParser=self.defaultCfg[configType]
273 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400274 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000275 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000276
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000277 def GetHighlight(self, theme, element, fgBg=None):
Terry Jan Reedy13755382014-10-09 18:44:26 -0400278 """Return individual theme element highlight color(s).
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400279
Terry Jan Reedy13755382014-10-09 18:44:26 -0400280 fgBg - string ('fg' or 'bg') or None.
281 If None, return a dictionary containing fg and bg colors with
282 keys 'foreground' and 'background'. Otherwise, only return
283 fg or bg color, as specified. Colors are intended to be
284 appropriate for passing to Tkinter in, e.g., a tag_config call).
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000285 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000286 if self.defaultCfg['highlight'].has_section(theme):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400287 themeDict = self.GetThemeDict('default', theme)
Steven M. Gava9f25e672002-02-11 02:51:18 +0000288 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400289 themeDict = self.GetThemeDict('user', theme)
290 fore = themeDict[element + '-foreground']
Terry Jan Reedy13755382014-10-09 18:44:26 -0400291 if element == 'cursor': # There is no config value for cursor bg
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400292 back = themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000293 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400294 back = themeDict[element + '-background']
295 highlight = {"foreground": fore, "background": back}
Terry Jan Reedy13755382014-10-09 18:44:26 -0400296 if not fgBg: # Return dict of both colors
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000297 return highlight
Terry Jan Reedy13755382014-10-09 18:44:26 -0400298 else: # Return specified color only
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000299 if fgBg == 'fg':
300 return highlight["foreground"]
301 if fgBg == 'bg':
302 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000303 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400304 raise InvalidFgBg('Invalid fgBg specified')
Steven M. Gava9f25e672002-02-11 02:51:18 +0000305
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400306 def GetThemeDict(self, type, themeName):
307 """Return {option:value} dict for elements in themeName.
308
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000309 type - string, 'default' or 'user' theme type
310 themeName - string, theme name
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400311 Values are loaded over ultimate fallback defaults to guarantee
312 that all theme elements are present in a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000313 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000314 if type == 'user':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400315 cfgParser = self.userCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000316 elif type == 'default':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400317 cfgParser = self.defaultCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000318 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400319 raise InvalidTheme('Invalid theme type specified')
Terry Jan Reedy13755382014-10-09 18:44:26 -0400320 # Provide foreground and background colors for each theme
321 # element (other than cursor) even though some values are not
322 # yet used by idle, to allow for their use in the future.
323 # Default values are generally black and white.
324 # TODO copy theme from a class attribute.
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400325 theme ={'normal-foreground':'#000000',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000326 'normal-background':'#ffffff',
327 'keyword-foreground':'#000000',
328 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000329 'builtin-foreground':'#000000',
330 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000331 'comment-foreground':'#000000',
332 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000333 'string-foreground':'#000000',
334 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000335 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000336 'definition-background':'#ffffff',
337 'hilite-foreground':'#000000',
338 'hilite-background':'gray',
339 'break-foreground':'#ffffff',
340 'break-background':'#000000',
341 'hit-foreground':'#ffffff',
342 'hit-background':'#000000',
343 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000344 'error-background':'#000000',
345 #cursor (only foreground can be set)
346 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000347 #shell window
348 'stdout-foreground':'#000000',
349 'stdout-background':'#ffffff',
350 'stderr-foreground':'#000000',
351 'stderr-background':'#ffffff',
352 'console-foreground':'#000000',
353 'console-background':'#ffffff' }
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400354 for element in theme:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400355 if not cfgParser.has_option(themeName, element):
Terry Jan Reedy13755382014-10-09 18:44:26 -0400356 # Print warning that will return a default color
357 warning = ('\n Warning: configHandler.IdleConf.GetThemeDict'
Walter Dörwald70a6b492004-02-12 17:35:32 +0000358 ' -\n problem retrieving theme element %r'
359 '\n from theme %r.\n'
Terry Jan Reedy13755382014-10-09 18:44:26 -0400360 ' returning default color: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000361 (element, themeName, theme[element]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000362 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400363 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000364 except IOError:
365 pass
Terry Jan Reedy13755382014-10-09 18:44:26 -0400366 theme[element] = cfgParser.Get(
367 themeName, element, default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000368 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000369
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000370 def CurrentTheme(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400371 "Return the name of the currently active theme."
372 return self.GetOption('main', 'Theme', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000373
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000374 def CurrentKeys(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400375 "Return the name of the currently active key set."
376 return self.GetOption('main', 'Keys', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000377
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000378 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400379 """Return extensions in default and user config-extensions files.
380
381 If active_only True, only return active (enabled) extensions
382 and optionally only editor or shell extensions.
383 If active_only False, return all extensions.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000384 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400385 extns = self.RemoveKeyBindNames(
386 self.GetSectionList('default', 'extensions'))
387 userExtns = self.RemoveKeyBindNames(
388 self.GetSectionList('user', 'extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000389 for extn in userExtns:
390 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000391 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000392 if active_only:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400393 activeExtns = []
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000394 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000395 if self.GetOption('extensions', extn, 'enable', default=True,
396 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000397 #the extension is enabled
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400398 if editor_only or shell_only: # TODO if both, contradictory
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000399 if editor_only:
400 option = "enable_editor"
401 else:
402 option = "enable_shell"
403 if self.GetOption('extensions', extn,option,
404 default=True, type='bool',
405 warn_on_default=False):
406 activeExtns.append(extn)
407 else:
408 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000409 return activeExtns
410 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000411 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000412
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400413 def RemoveKeyBindNames(self, extnNameList):
414 "Return extnNameList with keybinding section names removed."
415 # TODO Easier to return filtered copy with list comp
416 names = extnNameList
417 kbNameIndicies = []
Steven M. Gavac628a062002-01-19 10:33:21 +0000418 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000419 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000420 kbNameIndicies.append(names.index(name))
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400421 kbNameIndicies.sort(reverse=True)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000422 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000423 del(names[index])
424 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000425
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400426 def GetExtnNameForEvent(self, virtualEvent):
427 """Return the name of the extension binding virtualEvent, or None.
428
429 virtualEvent - string, name of the virtual event to test for,
430 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000431 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400432 extName = None
433 vEvent = '<<' + virtualEvent + '>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000434 for extn in self.GetExtensions(active_only=0):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400435 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000436 if event == vEvent:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400437 extName = extn # TODO return here?
Steven M. Gavaa498af22002-02-01 01:33:36 +0000438 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000439
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400440 def GetExtensionKeys(self, extensionName):
441 """Return dict: {configurable extensionName event : active keybinding}.
442
443 Events come from default config extension_cfgBindings section.
444 Keybindings come from GetCurrentKeySet() active key dict,
445 where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000446 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400447 keysName = extensionName + '_cfgBindings'
448 activeKeys = self.GetCurrentKeySet()
449 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000450 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400451 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000452 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400453 event = '<<' + eventName + '>>'
454 binding = activeKeys[event]
455 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000456 return extKeys
457
Steven M. Gavac628a062002-01-19 10:33:21 +0000458 def __GetRawExtensionKeys(self,extensionName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400459 """Return dict {configurable extensionName event : keybinding list}.
460
461 Events come from default config extension_cfgBindings section.
462 Keybindings list come from the splitting of GetOption, which
463 tries user config before default config.
Steven M. Gavac628a062002-01-19 10:33:21 +0000464 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400465 keysName = extensionName+'_cfgBindings'
466 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000467 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400468 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000469 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400470 binding = self.GetOption(
471 'extensions', keysName, eventName, default='').split()
472 event = '<<' + eventName + '>>'
473 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000474 return extKeys
475
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400476 def GetExtensionBindings(self, extensionName):
477 """Return dict {extensionName event : active or defined keybinding}.
478
479 Augment self.GetExtensionKeys(extensionName) with mapping of non-
480 configurable events (from default config) to GetOption splits,
481 as in self.__GetRawExtensionKeys.
Steven M. Gavac628a062002-01-19 10:33:21 +0000482 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400483 bindsName = extensionName + '_bindings'
484 extBinds = self.GetExtensionKeys(extensionName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000485 #add the non-configurable bindings
486 if self.defaultCfg['extensions'].has_section(bindsName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400487 eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000488 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400489 binding = self.GetOption(
490 'extensions', bindsName, eventName, default='').split()
491 event = '<<' + eventName + '>>'
492 extBinds[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000493
494 return extBinds
495
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000496 def GetKeyBinding(self, keySetName, eventStr):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400497 """Return the keybinding list for keySetName eventStr.
498
499 keySetName - name of key binding set (config-keys section).
500 eventStr - virtual event, including brackets, as in '<<event>>'.
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000501 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400502 eventName = eventStr[2:-2] #trim off the angle brackets
503 binding = self.GetOption('keys', keySetName, eventName, default='').split()
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000504 return binding
505
Steven M. Gavac628a062002-01-19 10:33:21 +0000506 def GetCurrentKeySet(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400507 "Return CurrentKeys with 'darwin' modifications."
Ronald Oussoren19302d92006-06-11 14:33:36 +0000508 result = self.GetKeySet(self.CurrentKeys())
509
Ned Deily57847df2014-03-27 20:47:04 -0700510 if sys.platform == "darwin":
511 # OS X Tk variants do not support the "Alt" keyboard modifier.
512 # So replace all keybingings that use "Alt" with ones that
513 # use the "Option" keyboard modifier.
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400514 # TODO (Ned?): the "Option" modifier does not work properly for
Ned Deily57847df2014-03-27 20:47:04 -0700515 # Cocoa Tk and XQuartz Tk so we should not use it
516 # in default OS X KeySets.
Ronald Oussoren19302d92006-06-11 14:33:36 +0000517 for k, v in result.items():
518 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
519 if v != v2:
520 result[k] = v2
521
522 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000523
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400524 def GetKeySet(self, keySetName):
525 """Return event-key dict for keySetName core plus active extensions.
526
527 If a binding defined in an extension is already in use, the
528 extension binding is disabled by being set to ''
Steven M. Gava2a63a072001-10-26 06:50:54 +0000529 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400530 keySet = self.GetCoreKeys(keySetName)
531 activeExtns = self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000532 for extn in activeExtns:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400533 extKeys = self.__GetRawExtensionKeys(extn)
Steven M. Gavac628a062002-01-19 10:33:21 +0000534 if extKeys: #the extension defines keybindings
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400535 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000536 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000537 #the binding is already in use
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400538 extKeys[event] = '' #disable this binding
539 keySet[event] = extKeys[event] #add binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000540 return keySet
541
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400542 def IsCoreBinding(self, virtualEvent):
543 """Return True if the virtual event is one of the core idle key events.
544
545 virtualEvent - string, name of the virtual event to test for,
546 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000547 """
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400548 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000549
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400550# TODO make keyBindins a file or class attribute used for test above
551# and copied in function below
552
Steven M. Gavac628a062002-01-19 10:33:21 +0000553 def GetCoreKeys(self, keySetName=None):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400554 """Return dict of core virtual-key keybindings for keySetName.
555
556 The default keySetName None corresponds to the keyBindings base
557 dict. If keySetName is not None, bindings from the config
558 file(s) are loaded _over_ these defaults, so if there is a
559 problem getting any core binding there will be an 'ultimate last
560 resort fallback' to the CUA-ish bindings defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000561 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000562 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000563 '<<copy>>': ['<Control-c>', '<Control-C>'],
564 '<<cut>>': ['<Control-x>', '<Control-X>'],
565 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000566 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
567 '<<center-insert>>': ['<Control-l>'],
568 '<<close-all-windows>>': ['<Control-q>'],
569 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000570 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000571 '<<end-of-file>>': ['<Control-d>'],
572 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000573 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000574 '<<history-next>>': ['<Alt-n>'],
575 '<<history-previous>>': ['<Alt-p>'],
576 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000577 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000578 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000579 '<<open-class-browser>>': ['<Alt-c>'],
580 '<<open-module>>': ['<Alt-m>'],
581 '<<open-new-window>>': ['<Control-n>'],
582 '<<open-window-from-file>>': ['<Control-o>'],
583 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000584 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000585 '<<redo>>': ['<Control-y>'],
586 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000587 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000588 '<<save-window-as-file>>': ['<Alt-s>'],
589 '<<save-window>>': ['<Control-s>'],
590 '<<select-all>>': ['<Alt-a>'],
591 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000592 '<<undo>>': ['<Control-z>'],
593 '<<find-again>>': ['<Control-g>', '<F3>'],
594 '<<find-in-files>>': ['<Alt-F3>'],
595 '<<find-selection>>': ['<Control-F3>'],
596 '<<find>>': ['<Control-f>'],
597 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000598 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000599 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlovc37db102012-03-29 19:54:58 +0300600 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000601 '<<smart-indent>>': ['<Key-Tab>'],
602 '<<indent-region>>': ['<Control-Key-bracketright>'],
603 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
604 '<<comment-region>>': ['<Alt-Key-3>'],
605 '<<uncomment-region>>': ['<Alt-Key-4>'],
606 '<<tabify-region>>': ['<Alt-Key-5>'],
607 '<<untabify-region>>': ['<Alt-Key-6>'],
608 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000609 '<<change-indentwidth>>': ['<Alt-Key-u>'],
610 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
611 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000612 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000613 if keySetName:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400614 for event in keyBindings:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400615 binding = self.GetKeyBinding(keySetName, event)
Steven M. Gava49745752002-02-18 01:43:11 +0000616 if binding:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400617 keyBindings[event] = binding
Steven M. Gava49745752002-02-18 01:43:11 +0000618 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000619 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
620 ' -\n problem retrieving key binding for event %r'
621 '\n from key set %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400622 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000623 (event, keySetName, keyBindings[event]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000624 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400625 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000626 except IOError:
627 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000628 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000629
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400630 def GetExtraHelpSourceList(self, configSet):
631 """Return list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000632
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000633 Valid configSets are 'user' or 'default'. Return a list of tuples of
634 the form (menu_item , path_to_help_file , option), or return the empty
635 list. 'option' is the sequence number of the help resource. 'option'
636 values determine the position of the menu items on the Help menu,
637 therefore the returned list must be sorted by 'option'.
638
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000639 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400640 helpSources = []
641 if configSet == 'user':
642 cfgParser = self.userCfg['main']
643 elif configSet == 'default':
644 cfgParser = self.defaultCfg['main']
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000645 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400646 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000647 options=cfgParser.GetOptionList('HelpFiles')
648 for option in options:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400649 value=cfgParser.Get('HelpFiles', option, default=';')
650 if value.find(';') == -1: #malformed config entry with no ';'
651 menuItem = '' #make these empty
652 helpPath = '' #so value won't be added to list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000653 else: #config entry contains ';' as expected
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400654 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000655 menuItem=value[0].strip()
656 helpPath=value[1].strip()
657 if menuItem and helpPath: #neither are empty strings
658 helpSources.append( (menuItem,helpPath,option) )
Florent Xiclunaa7f242f2010-04-02 08:15:26 +0000659 helpSources.sort(key=lambda x: int(x[2]))
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000660 return helpSources
661
662 def GetAllExtraHelpSourcesList(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400663 """Return a list of the details of all additional help sources.
664
665 Tuples in the list are those of GetExtraHelpSourceList.
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000666 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400667 allHelpSources = (self.GetExtraHelpSourceList('default') +
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000668 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000669 return allHelpSources
670
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000671 def LoadCfgFiles(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400672 "Load all configuration files."
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400673 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000674 self.defaultCfg[key].Load()
675 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000676
677 def SaveUserCfgFiles(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400678 "Write all loaded user configuration files to disk."
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400679 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000680 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000681
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000682
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400683idleConf = IdleConf()
684
685# TODO Revise test output, write expanded unittest
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000686### module test
687if __name__ == '__main__':
688 def dumpCfg(cfg):
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400689 print('\n', cfg, '\n')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400690 for key in cfg:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400691 sections = cfg[key].sections()
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400692 print(key)
693 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000694 for section in sections:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400695 options = cfg[key].options(section)
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400696 print(section)
697 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000698 for option in options:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400699 print(option, '=', cfg[key].Get(section, option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000700 dumpCfg(idleConf.defaultCfg)
701 dumpCfg(idleConf.userCfg)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400702 print(idleConf.userCfg['main'].Get('Theme', 'name'))
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400703 #print(idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal'))