blob: 899e50a978b5171f858ae11730c0b8c75a411eb3 [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 Reedy99c1a6d2015-09-04 04:37:02 -040026from Tkinter import TkVersion
Terry Jan Reedy12352142015-08-01 18:57:27 -040027from tkFont import Font, nametofont
Steven M. Gavac11ccf32001-09-24 09:43:17 +000028
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000029class InvalidConfigType(Exception): pass
30class InvalidConfigSet(Exception): pass
31class InvalidFgBg(Exception): pass
32class InvalidTheme(Exception): pass
33
Steven M. Gavac11ccf32001-09-24 09:43:17 +000034class IdleConfParser(ConfigParser):
35 """
36 A ConfigParser specialised for idle configuration file handling
37 """
38 def __init__(self, cfgFile, cfgDefaults=None):
39 """
40 cfgFile - string, fully specified configuration file name
41 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040042 self.file = cfgFile
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -040043 ConfigParser.__init__(self, defaults=cfgDefaults)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000044
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000045 def Get(self, section, option, type=None, default=None, raw=False):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000046 """
47 Get an option value for given section/option or return default.
48 If type is specified, return as type.
49 """
Terry Jan Reedy7a162072014-10-22 20:15:12 -040050 # TODO Use default as fallback, at least if not None
51 # Should also print Warning(file, section, option).
52 # Currently may raise ValueError
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000053 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000054 return default
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040055 if type == 'bool':
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000056 return self.getboolean(section, option)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040057 elif type == 'int':
Kurt B. Kaiser90f84922007-02-05 06:03:18 +000058 return self.getint(section, option)
59 else:
60 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000061
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040062 def GetOptionList(self, section):
63 "Return a list of options for given section, else []."
Steven M. Gava085eb1b2002-02-05 04:52:32 +000064 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000065 return self.options(section)
66 else: #return a default value
67 return []
68
Steven M. Gavac11ccf32001-09-24 09:43:17 +000069 def Load(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040070 "Load the configuration file from disk."
Steven M. Gavac11ccf32001-09-24 09:43:17 +000071 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000072
Steven M. Gavac11ccf32001-09-24 09:43:17 +000073class IdleUserConfParser(IdleConfParser):
74 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000075 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000076 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000077
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040078 def AddSection(self, section):
79 "If section doesn't exist, add it."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000080 if not self.has_section(section):
81 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000082
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000083 def RemoveEmptySections(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040084 "Remove any sections that have no options."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000085 for section in self.sections():
86 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000087 self.remove_section(section)
88
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000089 def IsEmpty(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040090 "Return True if no sections after removing empty sections."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000091 self.RemoveEmptySections()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040092 return not self.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000093
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -040094 def RemoveOption(self, section, option):
95 """Return True if option is removed from section, else False.
96
97 False if either section does not exist or did not have option.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000098 """
99 if self.has_section(section):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400100 return self.remove_option(section, option)
101 return False
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000102
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400103 def SetOption(self, section, option, value):
104 """Return True if option is added or changed to value, else False.
105
106 Add section if required. False means option already had value.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000107 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400108 if self.has_option(section, option):
109 if self.get(section, option) == value:
110 return False
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000111 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400112 self.set(section, option, value)
113 return True
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000114 else:
115 if not self.has_section(section):
116 self.add_section(section)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400117 self.set(section, option, value)
118 return True
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000119
Steven M. Gavab77d3432002-03-02 07:16:21 +0000120 def RemoveFile(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400121 "Remove user config file self.file from disk if it exists."
Steven M. Gavab77d3432002-03-02 07:16:21 +0000122 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000123 os.remove(self.file)
124
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000125 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000126 """Update user configuration file.
127
128 Remove empty sections. If resulting config isn't empty, write the file
129 to disk. If config is empty, remove the file from disk if it exists.
130
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000131 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000132 if not self.IsEmpty():
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000133 fname = self.file
134 try:
135 cfgFile = open(fname, 'w')
136 except IOError:
Kurt B. Kaiser8d365c32008-02-12 15:45:50 +0000137 os.unlink(fname)
Kurt B. Kaiserb4aaa762008-01-23 22:19:23 +0000138 cfgFile = open(fname, 'w')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400139 with cfgFile:
140 self.write(cfgFile)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000141 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000142 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000143
144class IdleConf:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400145 """Hold config parsers for all idle config files in singleton instance.
146
147 Default config files, self.defaultCfg --
148 for config_type in self.config_types:
149 (idle install dir)/config-{config-type}.def
150
151 User config files, self.userCfg --
152 for config_type in self.config_types:
153 (user home dir)/.idlerc/config-{config-type}.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000154 """
155 def __init__(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400156 self.config_types = ('main', 'extensions', 'highlight', 'keys')
157 self.defaultCfg = {}
158 self.userCfg = {}
159 self.cfg = {} # TODO use to select userCfg vs defaultCfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000160 self.CreateConfigHandlers()
161 self.LoadCfgFiles()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400162
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000163
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000164 def CreateConfigHandlers(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400165 "Populate default and user config parser dictionaries."
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000166 #build idle install path
167 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000168 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000169 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000170 idleDir=os.path.abspath(sys.path[0])
171 userDir=self.GetUserCfgDir()
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400172
173 defCfgFiles = {}
174 usrCfgFiles = {}
175 # TODO eliminate these temporaries by combining loops
176 for cfgType in self.config_types: #build config file names
177 defCfgFiles[cfgType] = os.path.join(
178 idleDir, 'config-' + cfgType + '.def')
179 usrCfgFiles[cfgType] = os.path.join(
180 userDir, 'config-' + cfgType + '.cfg')
181 for cfgType in self.config_types: #create config parsers
182 self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType])
183 self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000184
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000185 def GetUserCfgDir(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400186 """Return a filesystem directory for storing user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000187
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400188 Creates it if required.
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000189 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000190 cfgDir = '.idlerc'
191 userDir = os.path.expanduser('~')
192 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000193 if not os.path.exists(userDir):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400194 warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
195 userDir + ',\n but the path does not exist.')
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000196 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400197 print(warn, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000198 except IOError:
199 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000200 userDir = '~'
201 if userDir == "~": # still no path to home!
202 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
203 userDir = os.getcwd()
204 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000205 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000206 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000207 os.mkdir(userDir)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000208 except (OSError, IOError):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400209 warn = ('\n Warning: unable to create user config directory\n' +
210 userDir + '\n Check path and permissions.\n Exiting!\n')
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400211 print(warn, file=sys.stderr)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000212 raise SystemExit
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400213 # TODO continue without userDIr instead of exit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000214 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000215
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000216 def GetOption(self, configType, section, option, default=None, type=None,
Kurt B. Kaiser90f84922007-02-05 06:03:18 +0000217 warn_on_default=True, raw=False):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400218 """Return a value for configType section option, or default.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000219
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400220 If type is not None, return a value of that type. Also pass raw
221 to the config parser. First try to return a valid value
222 (including type) from a user configuration. If that fails, try
223 the default configuration. If that fails, return default, with a
224 default of None.
225
226 Warn if either user or default configurations have an invalid value.
227 Warn if default is returned and warn_on_default is True.
Steven M. Gava429a86a2001-10-23 10:42:12 +0000228 """
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200229 try:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400230 if self.userCfg[configType].has_option(section, option):
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200231 return self.userCfg[configType].Get(section, option,
232 type=type, raw=raw)
233 except ValueError:
234 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
235 ' invalid %r value for configuration option %r\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400236 ' from section %r: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200237 (type, option, section,
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400238 self.userCfg[configType].Get(section, option, raw=raw)))
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200239 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400240 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200241 except IOError:
242 pass
243 try:
244 if self.defaultCfg[configType].has_option(section,option):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400245 return self.defaultCfg[configType].Get(
246 section, option, type=type, raw=raw)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200247 except ValueError:
248 pass
249 #returning default, print warning
250 if warn_on_default:
251 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
252 ' problem retrieving configuration option %r\n'
253 ' from section %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400254 ' returning default value: %r' %
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200255 (option, section, default))
256 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400257 print(warning, file=sys.stderr)
Andrew Svetlovd8590ff2012-12-24 13:17:59 +0200258 except IOError:
259 pass
260 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000261
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000262 def SetOption(self, configType, section, option, value):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400263 """Set section option to value in user config file."""
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000264 self.userCfg[configType].SetOption(section, option, value)
265
Steven M. Gava2a63a072001-10-26 06:50:54 +0000266 def GetSectionList(self, configSet, configType):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400267 """Return sections for configSet configType configuration.
268
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000269 configSet must be either 'user' or 'default'
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400270 configType must be in self.config_types.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000271 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400272 if not (configType in self.config_types):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400273 raise InvalidConfigType('Invalid configType specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000274 if configSet == 'user':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400275 cfgParser = self.userCfg[configType]
Steven M. Gava2a63a072001-10-26 06:50:54 +0000276 elif configSet == 'default':
277 cfgParser=self.defaultCfg[configType]
278 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400279 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000280 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000281
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000282 def GetHighlight(self, theme, element, fgBg=None):
Terry Jan Reedy13755382014-10-09 18:44:26 -0400283 """Return individual theme element highlight color(s).
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400284
Terry Jan Reedy13755382014-10-09 18:44:26 -0400285 fgBg - string ('fg' or 'bg') or None.
286 If None, return a dictionary containing fg and bg colors with
287 keys 'foreground' and 'background'. Otherwise, only return
288 fg or bg color, as specified. Colors are intended to be
289 appropriate for passing to Tkinter in, e.g., a tag_config call).
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000290 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000291 if self.defaultCfg['highlight'].has_section(theme):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400292 themeDict = self.GetThemeDict('default', theme)
Steven M. Gava9f25e672002-02-11 02:51:18 +0000293 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400294 themeDict = self.GetThemeDict('user', theme)
295 fore = themeDict[element + '-foreground']
Terry Jan Reedy13755382014-10-09 18:44:26 -0400296 if element == 'cursor': # There is no config value for cursor bg
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400297 back = themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000298 else:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400299 back = themeDict[element + '-background']
300 highlight = {"foreground": fore, "background": back}
Terry Jan Reedy13755382014-10-09 18:44:26 -0400301 if not fgBg: # Return dict of both colors
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000302 return highlight
Terry Jan Reedy13755382014-10-09 18:44:26 -0400303 else: # Return specified color only
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000304 if fgBg == 'fg':
305 return highlight["foreground"]
306 if fgBg == 'bg':
307 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000308 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400309 raise InvalidFgBg('Invalid fgBg specified')
Steven M. Gava9f25e672002-02-11 02:51:18 +0000310
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400311 def GetThemeDict(self, type, themeName):
312 """Return {option:value} dict for elements in themeName.
313
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000314 type - string, 'default' or 'user' theme type
315 themeName - string, theme name
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400316 Values are loaded over ultimate fallback defaults to guarantee
317 that all theme elements are present in a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000318 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000319 if type == 'user':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400320 cfgParser = self.userCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000321 elif type == 'default':
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400322 cfgParser = self.defaultCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000323 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400324 raise InvalidTheme('Invalid theme type specified')
Terry Jan Reedy13755382014-10-09 18:44:26 -0400325 # Provide foreground and background colors for each theme
326 # element (other than cursor) even though some values are not
327 # yet used by idle, to allow for their use in the future.
328 # Default values are generally black and white.
329 # TODO copy theme from a class attribute.
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400330 theme ={'normal-foreground':'#000000',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000331 'normal-background':'#ffffff',
332 'keyword-foreground':'#000000',
333 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000334 'builtin-foreground':'#000000',
335 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000336 'comment-foreground':'#000000',
337 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000338 'string-foreground':'#000000',
339 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000340 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000341 'definition-background':'#ffffff',
342 'hilite-foreground':'#000000',
343 'hilite-background':'gray',
344 'break-foreground':'#ffffff',
345 'break-background':'#000000',
346 'hit-foreground':'#ffffff',
347 'hit-background':'#000000',
348 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000349 'error-background':'#000000',
350 #cursor (only foreground can be set)
351 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000352 #shell window
353 'stdout-foreground':'#000000',
354 'stdout-background':'#ffffff',
355 'stderr-foreground':'#000000',
356 'stderr-background':'#ffffff',
357 'console-foreground':'#000000',
358 'console-background':'#ffffff' }
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400359 for element in theme:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400360 if not cfgParser.has_option(themeName, element):
Terry Jan Reedy13755382014-10-09 18:44:26 -0400361 # Print warning that will return a default color
362 warning = ('\n Warning: configHandler.IdleConf.GetThemeDict'
Walter Dörwald70a6b492004-02-12 17:35:32 +0000363 ' -\n problem retrieving theme element %r'
364 '\n from theme %r.\n'
Terry Jan Reedy13755382014-10-09 18:44:26 -0400365 ' returning default color: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000366 (element, themeName, theme[element]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000367 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400368 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000369 except IOError:
370 pass
Terry Jan Reedy13755382014-10-09 18:44:26 -0400371 theme[element] = cfgParser.Get(
372 themeName, element, default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000373 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000374
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000375 def CurrentTheme(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400376 "Return the name of the currently active theme."
377 return self.GetOption('main', 'Theme', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000378
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000379 def CurrentKeys(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400380 "Return the name of the currently active key set."
381 return self.GetOption('main', 'Keys', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000382
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000383 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400384 """Return extensions in default and user config-extensions files.
385
386 If active_only True, only return active (enabled) extensions
387 and optionally only editor or shell extensions.
388 If active_only False, return all extensions.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000389 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400390 extns = self.RemoveKeyBindNames(
391 self.GetSectionList('default', 'extensions'))
392 userExtns = self.RemoveKeyBindNames(
393 self.GetSectionList('user', 'extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000394 for extn in userExtns:
395 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000396 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000397 if active_only:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400398 activeExtns = []
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000399 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000400 if self.GetOption('extensions', extn, 'enable', default=True,
401 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000402 #the extension is enabled
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400403 if editor_only or shell_only: # TODO if both, contradictory
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000404 if editor_only:
405 option = "enable_editor"
406 else:
407 option = "enable_shell"
408 if self.GetOption('extensions', extn,option,
409 default=True, type='bool',
410 warn_on_default=False):
411 activeExtns.append(extn)
412 else:
413 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000414 return activeExtns
415 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000416 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000417
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400418 def RemoveKeyBindNames(self, extnNameList):
419 "Return extnNameList with keybinding section names removed."
420 # TODO Easier to return filtered copy with list comp
421 names = extnNameList
422 kbNameIndicies = []
Steven M. Gavac628a062002-01-19 10:33:21 +0000423 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000424 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000425 kbNameIndicies.append(names.index(name))
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400426 kbNameIndicies.sort(reverse=True)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000427 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000428 del(names[index])
429 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000430
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400431 def GetExtnNameForEvent(self, virtualEvent):
432 """Return the name of the extension binding virtualEvent, or None.
433
434 virtualEvent - string, name of the virtual event to test for,
435 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000436 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400437 extName = None
438 vEvent = '<<' + virtualEvent + '>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000439 for extn in self.GetExtensions(active_only=0):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400440 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000441 if event == vEvent:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400442 extName = extn # TODO return here?
Steven M. Gavaa498af22002-02-01 01:33:36 +0000443 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000444
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400445 def GetExtensionKeys(self, extensionName):
446 """Return dict: {configurable extensionName event : active keybinding}.
447
448 Events come from default config extension_cfgBindings section.
449 Keybindings come from GetCurrentKeySet() active key dict,
450 where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000451 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400452 keysName = extensionName + '_cfgBindings'
453 activeKeys = self.GetCurrentKeySet()
454 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000455 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400456 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000457 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400458 event = '<<' + eventName + '>>'
459 binding = activeKeys[event]
460 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000461 return extKeys
462
Steven M. Gavac628a062002-01-19 10:33:21 +0000463 def __GetRawExtensionKeys(self,extensionName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400464 """Return dict {configurable extensionName event : keybinding list}.
465
466 Events come from default config extension_cfgBindings section.
467 Keybindings list come from the splitting of GetOption, which
468 tries user config before default config.
Steven M. Gavac628a062002-01-19 10:33:21 +0000469 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400470 keysName = extensionName+'_cfgBindings'
471 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000472 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400473 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000474 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400475 binding = self.GetOption(
476 'extensions', keysName, eventName, default='').split()
477 event = '<<' + eventName + '>>'
478 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000479 return extKeys
480
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400481 def GetExtensionBindings(self, extensionName):
482 """Return dict {extensionName event : active or defined keybinding}.
483
484 Augment self.GetExtensionKeys(extensionName) with mapping of non-
485 configurable events (from default config) to GetOption splits,
486 as in self.__GetRawExtensionKeys.
Steven M. Gavac628a062002-01-19 10:33:21 +0000487 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400488 bindsName = extensionName + '_bindings'
489 extBinds = self.GetExtensionKeys(extensionName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000490 #add the non-configurable bindings
491 if self.defaultCfg['extensions'].has_section(bindsName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400492 eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000493 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400494 binding = self.GetOption(
495 'extensions', bindsName, eventName, default='').split()
496 event = '<<' + eventName + '>>'
497 extBinds[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000498
499 return extBinds
500
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000501 def GetKeyBinding(self, keySetName, eventStr):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400502 """Return the keybinding list for keySetName eventStr.
503
504 keySetName - name of key binding set (config-keys section).
505 eventStr - virtual event, including brackets, as in '<<event>>'.
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000506 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400507 eventName = eventStr[2:-2] #trim off the angle brackets
508 binding = self.GetOption('keys', keySetName, eventName, default='').split()
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000509 return binding
510
Steven M. Gavac628a062002-01-19 10:33:21 +0000511 def GetCurrentKeySet(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400512 "Return CurrentKeys with 'darwin' modifications."
Ronald Oussoren19302d92006-06-11 14:33:36 +0000513 result = self.GetKeySet(self.CurrentKeys())
514
Ned Deily57847df2014-03-27 20:47:04 -0700515 if sys.platform == "darwin":
516 # OS X Tk variants do not support the "Alt" keyboard modifier.
517 # So replace all keybingings that use "Alt" with ones that
518 # use the "Option" keyboard modifier.
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400519 # TODO (Ned?): the "Option" modifier does not work properly for
Ned Deily57847df2014-03-27 20:47:04 -0700520 # Cocoa Tk and XQuartz Tk so we should not use it
521 # in default OS X KeySets.
Ronald Oussoren19302d92006-06-11 14:33:36 +0000522 for k, v in result.items():
523 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
524 if v != v2:
525 result[k] = v2
526
527 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000528
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400529 def GetKeySet(self, keySetName):
530 """Return event-key dict for keySetName core plus active extensions.
531
532 If a binding defined in an extension is already in use, the
533 extension binding is disabled by being set to ''
Steven M. Gava2a63a072001-10-26 06:50:54 +0000534 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400535 keySet = self.GetCoreKeys(keySetName)
536 activeExtns = self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000537 for extn in activeExtns:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400538 extKeys = self.__GetRawExtensionKeys(extn)
Steven M. Gavac628a062002-01-19 10:33:21 +0000539 if extKeys: #the extension defines keybindings
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400540 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000541 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000542 #the binding is already in use
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400543 extKeys[event] = '' #disable this binding
544 keySet[event] = extKeys[event] #add binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000545 return keySet
546
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400547 def IsCoreBinding(self, virtualEvent):
548 """Return True if the virtual event is one of the core idle key events.
549
550 virtualEvent - string, name of the virtual event to test for,
551 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000552 """
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400553 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000554
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400555# TODO make keyBindins a file or class attribute used for test above
556# and copied in function below
557
Steven M. Gavac628a062002-01-19 10:33:21 +0000558 def GetCoreKeys(self, keySetName=None):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400559 """Return dict of core virtual-key keybindings for keySetName.
560
561 The default keySetName None corresponds to the keyBindings base
562 dict. If keySetName is not None, bindings from the config
563 file(s) are loaded _over_ these defaults, so if there is a
564 problem getting any core binding there will be an 'ultimate last
565 resort fallback' to the CUA-ish bindings defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000566 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000567 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000568 '<<copy>>': ['<Control-c>', '<Control-C>'],
569 '<<cut>>': ['<Control-x>', '<Control-X>'],
570 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000571 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
572 '<<center-insert>>': ['<Control-l>'],
573 '<<close-all-windows>>': ['<Control-q>'],
574 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000575 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000576 '<<end-of-file>>': ['<Control-d>'],
577 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000578 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000579 '<<history-next>>': ['<Alt-n>'],
580 '<<history-previous>>': ['<Alt-p>'],
581 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000582 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000583 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000584 '<<open-class-browser>>': ['<Alt-c>'],
585 '<<open-module>>': ['<Alt-m>'],
586 '<<open-new-window>>': ['<Control-n>'],
587 '<<open-window-from-file>>': ['<Control-o>'],
588 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000589 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000590 '<<redo>>': ['<Control-y>'],
591 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000592 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000593 '<<save-window-as-file>>': ['<Alt-s>'],
594 '<<save-window>>': ['<Control-s>'],
595 '<<select-all>>': ['<Alt-a>'],
596 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000597 '<<undo>>': ['<Control-z>'],
598 '<<find-again>>': ['<Control-g>', '<F3>'],
599 '<<find-in-files>>': ['<Alt-F3>'],
600 '<<find-selection>>': ['<Control-F3>'],
601 '<<find>>': ['<Control-f>'],
602 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000603 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000604 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlovc37db102012-03-29 19:54:58 +0300605 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000606 '<<smart-indent>>': ['<Key-Tab>'],
607 '<<indent-region>>': ['<Control-Key-bracketright>'],
608 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
609 '<<comment-region>>': ['<Alt-Key-3>'],
610 '<<uncomment-region>>': ['<Alt-Key-4>'],
611 '<<tabify-region>>': ['<Alt-Key-5>'],
612 '<<untabify-region>>': ['<Alt-Key-6>'],
613 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000614 '<<change-indentwidth>>': ['<Alt-Key-u>'],
615 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
616 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000617 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000618 if keySetName:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400619 for event in keyBindings:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400620 binding = self.GetKeyBinding(keySetName, event)
Steven M. Gava49745752002-02-18 01:43:11 +0000621 if binding:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400622 keyBindings[event] = binding
Steven M. Gava49745752002-02-18 01:43:11 +0000623 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000624 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
625 ' -\n problem retrieving key binding for event %r'
626 '\n from key set %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400627 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000628 (event, keySetName, keyBindings[event]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000629 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400630 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000631 except IOError:
632 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000633 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000634
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400635 def GetExtraHelpSourceList(self, configSet):
636 """Return list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000637
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000638 Valid configSets are 'user' or 'default'. Return a list of tuples of
639 the form (menu_item , path_to_help_file , option), or return the empty
640 list. 'option' is the sequence number of the help resource. 'option'
641 values determine the position of the menu items on the Help menu,
642 therefore the returned list must be sorted by 'option'.
643
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000644 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400645 helpSources = []
646 if configSet == 'user':
647 cfgParser = self.userCfg['main']
648 elif configSet == 'default':
649 cfgParser = self.defaultCfg['main']
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000650 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400651 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000652 options=cfgParser.GetOptionList('HelpFiles')
653 for option in options:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400654 value=cfgParser.Get('HelpFiles', option, default=';')
655 if value.find(';') == -1: #malformed config entry with no ';'
656 menuItem = '' #make these empty
657 helpPath = '' #so value won't be added to list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000658 else: #config entry contains ';' as expected
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400659 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000660 menuItem=value[0].strip()
661 helpPath=value[1].strip()
662 if menuItem and helpPath: #neither are empty strings
663 helpSources.append( (menuItem,helpPath,option) )
Florent Xiclunaa7f242f2010-04-02 08:15:26 +0000664 helpSources.sort(key=lambda x: int(x[2]))
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000665 return helpSources
666
667 def GetAllExtraHelpSourcesList(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400668 """Return a list of the details of all additional help sources.
669
670 Tuples in the list are those of GetExtraHelpSourceList.
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000671 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400672 allHelpSources = (self.GetExtraHelpSourceList('default') +
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000673 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000674 return allHelpSources
675
Terry Jan Reedy12352142015-08-01 18:57:27 -0400676 def GetFont(self, root, configType, section):
677 """Retrieve a font from configuration (font, font-size, font-bold)
678 Intercept the special value 'TkFixedFont' and substitute
679 the actual font, factoring in some tweaks if needed for
680 appearance sakes.
681
682 The 'root' parameter can normally be any valid Tkinter widget.
683
684 Return a tuple (family, size, weight) suitable for passing
685 to tkinter.Font
686 """
687 family = self.GetOption(configType, section, 'font', default='courier')
688 size = self.GetOption(configType, section, 'font-size', type='int',
689 default='10')
690 bold = self.GetOption(configType, section, 'font-bold', default=0,
691 type='bool')
692 if (family == 'TkFixedFont'):
Terry Jan Reedy99c1a6d2015-09-04 04:37:02 -0400693 if TkVersion < 8.5:
694 family = 'Courier'
695 else:
696 f = Font(name='TkFixedFont', exists=True, root=root)
697 actualFont = Font.actual(f)
698 family = actualFont['family']
699 size = actualFont['size']
700 if size < 0:
701 size = 10 # if font in pixels, ignore actual size
702 bold = actualFont['weight']=='bold'
Terry Jan Reedy12352142015-08-01 18:57:27 -0400703 return (family, size, 'bold' if bold else 'normal')
704
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000705 def LoadCfgFiles(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400706 "Load all configuration files."
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400707 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000708 self.defaultCfg[key].Load()
709 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000710
711 def SaveUserCfgFiles(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400712 "Write all loaded user configuration files to disk."
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400713 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000714 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000715
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000716
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400717idleConf = IdleConf()
718
719# TODO Revise test output, write expanded unittest
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000720### module test
721if __name__ == '__main__':
722 def dumpCfg(cfg):
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400723 print('\n', cfg, '\n')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400724 for key in cfg:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400725 sections = cfg[key].sections()
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400726 print(key)
727 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000728 for section in sections:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400729 options = cfg[key].options(section)
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400730 print(section)
731 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000732 for option in options:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400733 print(option, '=', cfg[key].Get(section, option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000734 dumpCfg(idleConf.defaultCfg)
735 dumpCfg(idleConf.userCfg)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400736 print(idleConf.userCfg['main'].Get('Theme', 'name'))
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400737 #print(idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal'))