blob: c9ec9c32d5d5f6657d7afa4431b33c6cdd908cf2 [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
Terry Jan Reedy12352142015-08-01 18:57:27 -040026from tkFont import Font, nametofont
Steven M. Gavac11ccf32001-09-24 09:43:17 +000027
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000028class InvalidConfigType(Exception): pass
29class InvalidConfigSet(Exception): pass
30class InvalidFgBg(Exception): pass
31class InvalidTheme(Exception): pass
32
Steven M. Gavac11ccf32001-09-24 09:43:17 +000033class IdleConfParser(ConfigParser):
34 """
35 A ConfigParser specialised for idle configuration file handling
36 """
37 def __init__(self, cfgFile, cfgDefaults=None):
38 """
39 cfgFile - string, fully specified configuration file name
40 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040041 self.file = cfgFile
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -040042 ConfigParser.__init__(self, defaults=cfgDefaults)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000043
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000044 def Get(self, section, option, type=None, default=None, raw=False):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000045 """
46 Get an option value for given section/option or return default.
47 If type is specified, return as type.
48 """
Terry Jan Reedy7a162072014-10-22 20:15:12 -040049 # TODO Use default as fallback, at least if not None
50 # Should also print Warning(file, section, option).
51 # Currently may raise ValueError
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000052 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000053 return default
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040054 if type == 'bool':
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000055 return self.getboolean(section, option)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040056 elif type == 'int':
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000057 return self.getint(section, option)
58 else:
59 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000060
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040061 def GetOptionList(self, section):
62 "Return a list of options for given section, else []."
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):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040069 "Load the configuration file from disk."
Steven M. Gavac11ccf32001-09-24 09:43:17 +000070 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000071
Steven M. Gavac11ccf32001-09-24 09:43:17 +000072class IdleUserConfParser(IdleConfParser):
73 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000074 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000075 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000076
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040077 def AddSection(self, section):
78 "If section doesn't exist, add it."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000079 if not self.has_section(section):
80 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000081
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000082 def RemoveEmptySections(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040083 "Remove any sections that have no options."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000084 for section in self.sections():
85 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000086 self.remove_section(section)
87
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000088 def IsEmpty(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040089 "Return True if no sections after removing empty sections."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000090 self.RemoveEmptySections()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040091 return not self.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000092
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040093 def RemoveOption(self, section, option):
94 """Return True if option is removed from section, else False.
95
96 False if either section does not exist or did not have option.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000097 """
98 if self.has_section(section):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040099 return self.remove_option(section, option)
100 return False
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000101
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400102 def SetOption(self, section, option, value):
103 """Return True if option is added or changed to value, else False.
104
105 Add section if required. False means option already had value.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000106 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400107 if self.has_option(section, option):
108 if self.get(section, option) == value:
109 return False
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000110 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400111 self.set(section, option, value)
112 return True
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000113 else:
114 if not self.has_section(section):
115 self.add_section(section)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400116 self.set(section, option, value)
117 return True
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000118
Steven M. Gavab77d3432002-03-02 07:16:21 +0000119 def RemoveFile(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400120 "Remove user config file self.file from disk if it exists."
Steven M. Gavab77d3432002-03-02 07:16:21 +0000121 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000122 os.remove(self.file)
123
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000124 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000125 """Update user configuration file.
126
127 Remove empty sections. If resulting config isn't empty, write the file
128 to disk. If config is empty, remove the file from disk if it exists.
129
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000130 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000131 if not self.IsEmpty():
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000132 fname = self.file
133 try:
134 cfgFile = open(fname, 'w')
135 except IOError:
Kurt B. Kaiser8d365c32008-02-12 15:45:50 +0000136 os.unlink(fname)
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000137 cfgFile = open(fname, 'w')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400138 with cfgFile:
139 self.write(cfgFile)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000140 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000141 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000142
143class IdleConf:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400144 """Hold config parsers for all idle config files in singleton instance.
145
146 Default config files, self.defaultCfg --
147 for config_type in self.config_types:
148 (idle install dir)/config-{config-type}.def
149
150 User config files, self.userCfg --
151 for config_type in self.config_types:
152 (user home dir)/.idlerc/config-{config-type}.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000153 """
154 def __init__(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400155 self.config_types = ('main', 'extensions', 'highlight', 'keys')
156 self.defaultCfg = {}
157 self.userCfg = {}
158 self.cfg = {} # TODO use to select userCfg vs defaultCfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000159 self.CreateConfigHandlers()
160 self.LoadCfgFiles()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400161
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000162
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000163 def CreateConfigHandlers(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400164 "Populate default and user config parser dictionaries."
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000165 #build idle install path
166 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000167 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000168 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000169 idleDir=os.path.abspath(sys.path[0])
170 userDir=self.GetUserCfgDir()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400171
172 defCfgFiles = {}
173 usrCfgFiles = {}
174 # TODO eliminate these temporaries by combining loops
175 for cfgType in self.config_types: #build config file names
176 defCfgFiles[cfgType] = os.path.join(
177 idleDir, 'config-' + cfgType + '.def')
178 usrCfgFiles[cfgType] = os.path.join(
179 userDir, 'config-' + cfgType + '.cfg')
180 for cfgType in self.config_types: #create config parsers
181 self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType])
182 self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000183
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000184 def GetUserCfgDir(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400185 """Return a filesystem directory for storing user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000186
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400187 Creates it if required.
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000188 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000189 cfgDir = '.idlerc'
190 userDir = os.path.expanduser('~')
191 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000192 if not os.path.exists(userDir):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400193 warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
194 userDir + ',\n but the path does not exist.')
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000195 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400196 print(warn, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000197 except IOError:
198 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000199 userDir = '~'
200 if userDir == "~": # still no path to home!
201 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
202 userDir = os.getcwd()
203 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000204 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000205 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000206 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000207 except (OSError, IOError):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400208 warn = ('\n Warning: unable to create user config directory\n' +
209 userDir + '\n Check path and permissions.\n Exiting!\n')
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400210 print(warn, file=sys.stderr)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000211 raise SystemExit
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400212 # TODO continue without userDIr instead of exit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000213 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000214
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000215 def GetOption(self, configType, section, option, default=None, type=None,
Kurt B. Kaiser90f84922007-02-05 06:03:18 +0000216 warn_on_default=True, raw=False):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400217 """Return a value for configType section option, or default.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000218
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400219 If type is not None, return a value of that type. Also pass raw
220 to the config parser. First try to return a valid value
221 (including type) from a user configuration. If that fails, try
222 the default configuration. If that fails, return default, with a
223 default of None.
224
225 Warn if either user or default configurations have an invalid value.
226 Warn if default is returned and warn_on_default is True.
Steven M. Gava429a86a2001-10-23 10:42:12 +0000227 """
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200228 try:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400229 if self.userCfg[configType].has_option(section, option):
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200230 return self.userCfg[configType].Get(section, option,
231 type=type, raw=raw)
232 except ValueError:
233 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
234 ' invalid %r value for configuration option %r\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400235 ' from section %r: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200236 (type, option, section,
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400237 self.userCfg[configType].Get(section, option, raw=raw)))
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200238 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400239 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200240 except IOError:
241 pass
242 try:
243 if self.defaultCfg[configType].has_option(section,option):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400244 return self.defaultCfg[configType].Get(
245 section, option, type=type, raw=raw)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200246 except ValueError:
247 pass
248 #returning default, print warning
249 if warn_on_default:
250 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
251 ' problem retrieving configuration option %r\n'
252 ' from section %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400253 ' returning default value: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200254 (option, section, default))
255 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400256 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200257 except IOError:
258 pass
259 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000260
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000261 def SetOption(self, configType, section, option, value):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400262 """Set section option to value in user config file."""
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000263 self.userCfg[configType].SetOption(section, option, value)
264
Steven M. Gava2a63a072001-10-26 06:50:54 +0000265 def GetSectionList(self, configSet, configType):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400266 """Return sections for configSet configType configuration.
267
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000268 configSet must be either 'user' or 'default'
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400269 configType must be in self.config_types.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000270 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400271 if not (configType in self.config_types):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400272 raise InvalidConfigType('Invalid configType specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000273 if configSet == 'user':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400274 cfgParser = self.userCfg[configType]
Steven M. Gava2a63a072001-10-26 06:50:54 +0000275 elif configSet == 'default':
276 cfgParser=self.defaultCfg[configType]
277 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400278 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000279 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000280
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000281 def GetHighlight(self, theme, element, fgBg=None):
Terry Jan Reedy13755382014-10-09 18:44:26 -0400282 """Return individual theme element highlight color(s).
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400283
Terry Jan Reedy13755382014-10-09 18:44:26 -0400284 fgBg - string ('fg' or 'bg') or None.
285 If None, return a dictionary containing fg and bg colors with
286 keys 'foreground' and 'background'. Otherwise, only return
287 fg or bg color, as specified. Colors are intended to be
288 appropriate for passing to Tkinter in, e.g., a tag_config call).
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000289 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000290 if self.defaultCfg['highlight'].has_section(theme):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400291 themeDict = self.GetThemeDict('default', theme)
Steven M. Gava9f25e672002-02-11 02:51:18 +0000292 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400293 themeDict = self.GetThemeDict('user', theme)
294 fore = themeDict[element + '-foreground']
Terry Jan Reedy13755382014-10-09 18:44:26 -0400295 if element == 'cursor': # There is no config value for cursor bg
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400296 back = themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000297 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400298 back = themeDict[element + '-background']
299 highlight = {"foreground": fore, "background": back}
Terry Jan Reedy13755382014-10-09 18:44:26 -0400300 if not fgBg: # Return dict of both colors
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000301 return highlight
Terry Jan Reedy13755382014-10-09 18:44:26 -0400302 else: # Return specified color only
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000303 if fgBg == 'fg':
304 return highlight["foreground"]
305 if fgBg == 'bg':
306 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000307 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400308 raise InvalidFgBg('Invalid fgBg specified')
Steven M. Gava9f25e672002-02-11 02:51:18 +0000309
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400310 def GetThemeDict(self, type, themeName):
311 """Return {option:value} dict for elements in themeName.
312
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000313 type - string, 'default' or 'user' theme type
314 themeName - string, theme name
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400315 Values are loaded over ultimate fallback defaults to guarantee
316 that all theme elements are present in a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000317 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000318 if type == 'user':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400319 cfgParser = self.userCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000320 elif type == 'default':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400321 cfgParser = self.defaultCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000322 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400323 raise InvalidTheme('Invalid theme type specified')
Terry Jan Reedy13755382014-10-09 18:44:26 -0400324 # Provide foreground and background colors for each theme
325 # element (other than cursor) even though some values are not
326 # yet used by idle, to allow for their use in the future.
327 # Default values are generally black and white.
328 # TODO copy theme from a class attribute.
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400329 theme ={'normal-foreground':'#000000',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000330 'normal-background':'#ffffff',
331 'keyword-foreground':'#000000',
332 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000333 'builtin-foreground':'#000000',
334 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000335 'comment-foreground':'#000000',
336 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000337 'string-foreground':'#000000',
338 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000339 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000340 'definition-background':'#ffffff',
341 'hilite-foreground':'#000000',
342 'hilite-background':'gray',
343 'break-foreground':'#ffffff',
344 'break-background':'#000000',
345 'hit-foreground':'#ffffff',
346 'hit-background':'#000000',
347 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000348 'error-background':'#000000',
349 #cursor (only foreground can be set)
350 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000351 #shell window
352 'stdout-foreground':'#000000',
353 'stdout-background':'#ffffff',
354 'stderr-foreground':'#000000',
355 'stderr-background':'#ffffff',
356 'console-foreground':'#000000',
357 'console-background':'#ffffff' }
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400358 for element in theme:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400359 if not cfgParser.has_option(themeName, element):
Terry Jan Reedy13755382014-10-09 18:44:26 -0400360 # Print warning that will return a default color
361 warning = ('\n Warning: configHandler.IdleConf.GetThemeDict'
Walter Dörwald70a6b492004-02-12 17:35:32 +0000362 ' -\n problem retrieving theme element %r'
363 '\n from theme %r.\n'
Terry Jan Reedy13755382014-10-09 18:44:26 -0400364 ' returning default color: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000365 (element, themeName, theme[element]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000366 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400367 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000368 except IOError:
369 pass
Terry Jan Reedy13755382014-10-09 18:44:26 -0400370 theme[element] = cfgParser.Get(
371 themeName, element, default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000372 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000373
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000374 def CurrentTheme(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400375 "Return the name of the currently active theme."
376 return self.GetOption('main', 'Theme', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000377
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000378 def CurrentKeys(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400379 "Return the name of the currently active key set."
380 return self.GetOption('main', 'Keys', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000381
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000382 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400383 """Return extensions in default and user config-extensions files.
384
385 If active_only True, only return active (enabled) extensions
386 and optionally only editor or shell extensions.
387 If active_only False, return all extensions.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000388 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400389 extns = self.RemoveKeyBindNames(
390 self.GetSectionList('default', 'extensions'))
391 userExtns = self.RemoveKeyBindNames(
392 self.GetSectionList('user', 'extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000393 for extn in userExtns:
394 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000395 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000396 if active_only:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400397 activeExtns = []
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000398 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000399 if self.GetOption('extensions', extn, 'enable', default=True,
400 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000401 #the extension is enabled
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400402 if editor_only or shell_only: # TODO if both, contradictory
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000403 if editor_only:
404 option = "enable_editor"
405 else:
406 option = "enable_shell"
407 if self.GetOption('extensions', extn,option,
408 default=True, type='bool',
409 warn_on_default=False):
410 activeExtns.append(extn)
411 else:
412 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000413 return activeExtns
414 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000415 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000416
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400417 def RemoveKeyBindNames(self, extnNameList):
418 "Return extnNameList with keybinding section names removed."
419 # TODO Easier to return filtered copy with list comp
420 names = extnNameList
421 kbNameIndicies = []
Steven M. Gavac628a062002-01-19 10:33:21 +0000422 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000423 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000424 kbNameIndicies.append(names.index(name))
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400425 kbNameIndicies.sort(reverse=True)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000426 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000427 del(names[index])
428 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000429
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400430 def GetExtnNameForEvent(self, virtualEvent):
431 """Return the name of the extension binding virtualEvent, or None.
432
433 virtualEvent - string, name of the virtual event to test for,
434 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000435 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400436 extName = None
437 vEvent = '<<' + virtualEvent + '>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000438 for extn in self.GetExtensions(active_only=0):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400439 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000440 if event == vEvent:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400441 extName = extn # TODO return here?
Steven M. Gavaa498af22002-02-01 01:33:36 +0000442 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000443
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400444 def GetExtensionKeys(self, extensionName):
445 """Return dict: {configurable extensionName event : active keybinding}.
446
447 Events come from default config extension_cfgBindings section.
448 Keybindings come from GetCurrentKeySet() active key dict,
449 where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000450 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400451 keysName = extensionName + '_cfgBindings'
452 activeKeys = self.GetCurrentKeySet()
453 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000454 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400455 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000456 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400457 event = '<<' + eventName + '>>'
458 binding = activeKeys[event]
459 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000460 return extKeys
461
Steven M. Gavac628a062002-01-19 10:33:21 +0000462 def __GetRawExtensionKeys(self,extensionName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400463 """Return dict {configurable extensionName event : keybinding list}.
464
465 Events come from default config extension_cfgBindings section.
466 Keybindings list come from the splitting of GetOption, which
467 tries user config before default config.
Steven M. Gavac628a062002-01-19 10:33:21 +0000468 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400469 keysName = extensionName+'_cfgBindings'
470 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000471 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400472 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000473 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400474 binding = self.GetOption(
475 'extensions', keysName, eventName, default='').split()
476 event = '<<' + eventName + '>>'
477 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000478 return extKeys
479
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400480 def GetExtensionBindings(self, extensionName):
481 """Return dict {extensionName event : active or defined keybinding}.
482
483 Augment self.GetExtensionKeys(extensionName) with mapping of non-
484 configurable events (from default config) to GetOption splits,
485 as in self.__GetRawExtensionKeys.
Steven M. Gavac628a062002-01-19 10:33:21 +0000486 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400487 bindsName = extensionName + '_bindings'
488 extBinds = self.GetExtensionKeys(extensionName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000489 #add the non-configurable bindings
490 if self.defaultCfg['extensions'].has_section(bindsName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400491 eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000492 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400493 binding = self.GetOption(
494 'extensions', bindsName, eventName, default='').split()
495 event = '<<' + eventName + '>>'
496 extBinds[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000497
498 return extBinds
499
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000500 def GetKeyBinding(self, keySetName, eventStr):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400501 """Return the keybinding list for keySetName eventStr.
502
503 keySetName - name of key binding set (config-keys section).
504 eventStr - virtual event, including brackets, as in '<<event>>'.
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000505 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400506 eventName = eventStr[2:-2] #trim off the angle brackets
507 binding = self.GetOption('keys', keySetName, eventName, default='').split()
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000508 return binding
509
Steven M. Gavac628a062002-01-19 10:33:21 +0000510 def GetCurrentKeySet(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400511 "Return CurrentKeys with 'darwin' modifications."
Ronald Oussoren19302d92006-06-11 14:33:36 +0000512 result = self.GetKeySet(self.CurrentKeys())
513
Ned Deily57847df2014-03-27 20:47:04 -0700514 if sys.platform == "darwin":
515 # OS X Tk variants do not support the "Alt" keyboard modifier.
516 # So replace all keybingings that use "Alt" with ones that
517 # use the "Option" keyboard modifier.
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400518 # TODO (Ned?): the "Option" modifier does not work properly for
Ned Deily57847df2014-03-27 20:47:04 -0700519 # Cocoa Tk and XQuartz Tk so we should not use it
520 # in default OS X KeySets.
Ronald Oussoren19302d92006-06-11 14:33:36 +0000521 for k, v in result.items():
522 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
523 if v != v2:
524 result[k] = v2
525
526 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000527
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400528 def GetKeySet(self, keySetName):
529 """Return event-key dict for keySetName core plus active extensions.
530
531 If a binding defined in an extension is already in use, the
532 extension binding is disabled by being set to ''
Steven M. Gava2a63a072001-10-26 06:50:54 +0000533 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400534 keySet = self.GetCoreKeys(keySetName)
535 activeExtns = self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000536 for extn in activeExtns:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400537 extKeys = self.__GetRawExtensionKeys(extn)
Steven M. Gavac628a062002-01-19 10:33:21 +0000538 if extKeys: #the extension defines keybindings
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400539 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000540 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000541 #the binding is already in use
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400542 extKeys[event] = '' #disable this binding
543 keySet[event] = extKeys[event] #add binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000544 return keySet
545
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400546 def IsCoreBinding(self, virtualEvent):
547 """Return True if the virtual event is one of the core idle key events.
548
549 virtualEvent - string, name of the virtual event to test for,
550 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000551 """
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400552 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000553
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400554# TODO make keyBindins a file or class attribute used for test above
555# and copied in function below
556
Steven M. Gavac628a062002-01-19 10:33:21 +0000557 def GetCoreKeys(self, keySetName=None):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400558 """Return dict of core virtual-key keybindings for keySetName.
559
560 The default keySetName None corresponds to the keyBindings base
561 dict. If keySetName is not None, bindings from the config
562 file(s) are loaded _over_ these defaults, so if there is a
563 problem getting any core binding there will be an 'ultimate last
564 resort fallback' to the CUA-ish bindings defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000565 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000566 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000567 '<<copy>>': ['<Control-c>', '<Control-C>'],
568 '<<cut>>': ['<Control-x>', '<Control-X>'],
569 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000570 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
571 '<<center-insert>>': ['<Control-l>'],
572 '<<close-all-windows>>': ['<Control-q>'],
573 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000574 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000575 '<<end-of-file>>': ['<Control-d>'],
576 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000577 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000578 '<<history-next>>': ['<Alt-n>'],
579 '<<history-previous>>': ['<Alt-p>'],
580 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000581 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000582 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000583 '<<open-class-browser>>': ['<Alt-c>'],
584 '<<open-module>>': ['<Alt-m>'],
585 '<<open-new-window>>': ['<Control-n>'],
586 '<<open-window-from-file>>': ['<Control-o>'],
587 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000588 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000589 '<<redo>>': ['<Control-y>'],
590 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000591 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000592 '<<save-window-as-file>>': ['<Alt-s>'],
593 '<<save-window>>': ['<Control-s>'],
594 '<<select-all>>': ['<Alt-a>'],
595 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000596 '<<undo>>': ['<Control-z>'],
597 '<<find-again>>': ['<Control-g>', '<F3>'],
598 '<<find-in-files>>': ['<Alt-F3>'],
599 '<<find-selection>>': ['<Control-F3>'],
600 '<<find>>': ['<Control-f>'],
601 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000602 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000603 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlovc37db102012-03-29 19:54:58 +0300604 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000605 '<<smart-indent>>': ['<Key-Tab>'],
606 '<<indent-region>>': ['<Control-Key-bracketright>'],
607 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
608 '<<comment-region>>': ['<Alt-Key-3>'],
609 '<<uncomment-region>>': ['<Alt-Key-4>'],
610 '<<tabify-region>>': ['<Alt-Key-5>'],
611 '<<untabify-region>>': ['<Alt-Key-6>'],
612 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000613 '<<change-indentwidth>>': ['<Alt-Key-u>'],
614 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
615 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000616 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000617 if keySetName:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400618 for event in keyBindings:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400619 binding = self.GetKeyBinding(keySetName, event)
Steven M. Gava49745752002-02-18 01:43:11 +0000620 if binding:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400621 keyBindings[event] = binding
Steven M. Gava49745752002-02-18 01:43:11 +0000622 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000623 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
624 ' -\n problem retrieving key binding for event %r'
625 '\n from key set %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400626 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000627 (event, keySetName, keyBindings[event]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000628 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400629 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000630 except IOError:
631 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000632 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000633
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400634 def GetExtraHelpSourceList(self, configSet):
635 """Return list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000636
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000637 Valid configSets are 'user' or 'default'. Return a list of tuples of
638 the form (menu_item , path_to_help_file , option), or return the empty
639 list. 'option' is the sequence number of the help resource. 'option'
640 values determine the position of the menu items on the Help menu,
641 therefore the returned list must be sorted by 'option'.
642
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000643 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400644 helpSources = []
645 if configSet == 'user':
646 cfgParser = self.userCfg['main']
647 elif configSet == 'default':
648 cfgParser = self.defaultCfg['main']
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000649 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400650 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000651 options=cfgParser.GetOptionList('HelpFiles')
652 for option in options:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400653 value=cfgParser.Get('HelpFiles', option, default=';')
654 if value.find(';') == -1: #malformed config entry with no ';'
655 menuItem = '' #make these empty
656 helpPath = '' #so value won't be added to list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000657 else: #config entry contains ';' as expected
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400658 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000659 menuItem=value[0].strip()
660 helpPath=value[1].strip()
661 if menuItem and helpPath: #neither are empty strings
662 helpSources.append( (menuItem,helpPath,option) )
Florent Xiclunaa7f242f2010-04-02 08:15:26 +0000663 helpSources.sort(key=lambda x: int(x[2]))
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000664 return helpSources
665
666 def GetAllExtraHelpSourcesList(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400667 """Return a list of the details of all additional help sources.
668
669 Tuples in the list are those of GetExtraHelpSourceList.
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000670 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400671 allHelpSources = (self.GetExtraHelpSourceList('default') +
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000672 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000673 return allHelpSources
674
Terry Jan Reedy12352142015-08-01 18:57:27 -0400675 def GetFont(self, root, configType, section):
676 """Retrieve a font from configuration (font, font-size, font-bold)
677 Intercept the special value 'TkFixedFont' and substitute
678 the actual font, factoring in some tweaks if needed for
679 appearance sakes.
680
681 The 'root' parameter can normally be any valid Tkinter widget.
682
683 Return a tuple (family, size, weight) suitable for passing
684 to tkinter.Font
685 """
686 family = self.GetOption(configType, section, 'font', default='courier')
687 size = self.GetOption(configType, section, 'font-size', type='int',
688 default='10')
689 bold = self.GetOption(configType, section, 'font-bold', default=0,
690 type='bool')
691 if (family == 'TkFixedFont'):
692 f = Font(name='TkFixedFont', exists=True, root=root)
693 actualFont = Font.actual(f)
694 family = actualFont['family']
695 size = actualFont['size']
696 if size < 0:
697 size = 10 # if font in pixels, ignore actual size
698 bold = actualFont['weight']=='bold'
699 return (family, size, 'bold' if bold else 'normal')
700
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000701 def LoadCfgFiles(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400702 "Load all configuration files."
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400703 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000704 self.defaultCfg[key].Load()
705 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000706
707 def SaveUserCfgFiles(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400708 "Write all loaded user configuration files to disk."
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400709 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000710 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000711
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000712
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400713idleConf = IdleConf()
714
715# TODO Revise test output, write expanded unittest
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000716### module test
717if __name__ == '__main__':
718 def dumpCfg(cfg):
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400719 print('\n', cfg, '\n')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400720 for key in cfg:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400721 sections = cfg[key].sections()
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400722 print(key)
723 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000724 for section in sections:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400725 options = cfg[key].options(section)
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400726 print(section)
727 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000728 for option in options:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400729 print(option, '=', cfg[key].Get(section, option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000730 dumpCfg(idleConf.defaultCfg)
731 dumpCfg(idleConf.userCfg)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400732 print(idleConf.userCfg['main'].Get('Theme', 'name'))
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400733 #print(idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal'))