blob: 9fc4faa2c65ed346d868f09578cc8b107ad0b752 [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 Reedy35aa5d02015-11-12 15:02:50 -0500376 """Return the name of the currently active text color theme.
377
378 idlelib.config-main.def includes this section
379 [Theme]
380 default= 1
381 name= IDLE Classic
382 name2=
383 # name2 set in user config-main.cfg for themes added after 2015 Oct 1
384
385 Item name2 is needed because setting name to a new builtin
386 causes older IDLEs to display multiple error messages or quit.
387 See https://bugs.python.org/issue25313.
388 When default = True, name2 takes precedence over name,
389 while older IDLEs will just use name.
390 """
391 default = self.GetOption('main', 'Theme', 'default',
392 type='bool', default=True)
393 if default:
394 theme = self.GetOption('main', 'Theme', 'name2', default='')
395 if default and not theme or not default:
396 theme = self.GetOption('main', 'Theme', 'name', default='')
397 source = self.defaultCfg if default else self.userCfg
398 if source['highlight'].has_section(theme):
399 return theme
Terry Jan Reedy55ee13d2015-11-12 15:06:02 -0500400 else:
Terry Jan Reedy35aa5d02015-11-12 15:02:50 -0500401 return "IDLE Classic"
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000402
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000403 def CurrentKeys(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400404 "Return the name of the currently active key set."
405 return self.GetOption('main', 'Keys', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000406
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000407 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400408 """Return extensions in default and user config-extensions files.
409
410 If active_only True, only return active (enabled) extensions
411 and optionally only editor or shell extensions.
412 If active_only False, return all extensions.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000413 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400414 extns = self.RemoveKeyBindNames(
415 self.GetSectionList('default', 'extensions'))
416 userExtns = self.RemoveKeyBindNames(
417 self.GetSectionList('user', 'extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000418 for extn in userExtns:
419 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000420 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000421 if active_only:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400422 activeExtns = []
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000423 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000424 if self.GetOption('extensions', extn, 'enable', default=True,
425 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000426 #the extension is enabled
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400427 if editor_only or shell_only: # TODO if both, contradictory
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000428 if editor_only:
429 option = "enable_editor"
430 else:
431 option = "enable_shell"
432 if self.GetOption('extensions', extn,option,
433 default=True, type='bool',
434 warn_on_default=False):
435 activeExtns.append(extn)
436 else:
437 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000438 return activeExtns
439 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000440 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000441
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400442 def RemoveKeyBindNames(self, extnNameList):
443 "Return extnNameList with keybinding section names removed."
444 # TODO Easier to return filtered copy with list comp
445 names = extnNameList
446 kbNameIndicies = []
Steven M. Gavac628a062002-01-19 10:33:21 +0000447 for name in names:
Georg Brandlb2afe852006-06-09 20:43:48 +0000448 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000449 kbNameIndicies.append(names.index(name))
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400450 kbNameIndicies.sort(reverse=True)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000451 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000452 del(names[index])
453 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000454
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400455 def GetExtnNameForEvent(self, virtualEvent):
456 """Return the name of the extension binding virtualEvent, or None.
457
458 virtualEvent - string, name of the virtual event to test for,
459 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000460 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400461 extName = None
462 vEvent = '<<' + virtualEvent + '>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000463 for extn in self.GetExtensions(active_only=0):
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400464 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000465 if event == vEvent:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400466 extName = extn # TODO return here?
Steven M. Gavaa498af22002-02-01 01:33:36 +0000467 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000468
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400469 def GetExtensionKeys(self, extensionName):
470 """Return dict: {configurable extensionName event : active keybinding}.
471
472 Events come from default config extension_cfgBindings section.
473 Keybindings come from GetCurrentKeySet() active key dict,
474 where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000475 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400476 keysName = extensionName + '_cfgBindings'
477 activeKeys = self.GetCurrentKeySet()
478 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000479 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400480 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000481 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400482 event = '<<' + eventName + '>>'
483 binding = activeKeys[event]
484 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000485 return extKeys
486
Steven M. Gavac628a062002-01-19 10:33:21 +0000487 def __GetRawExtensionKeys(self,extensionName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400488 """Return dict {configurable extensionName event : keybinding list}.
489
490 Events come from default config extension_cfgBindings section.
491 Keybindings list come from the splitting of GetOption, which
492 tries user config before default config.
Steven M. Gavac628a062002-01-19 10:33:21 +0000493 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400494 keysName = extensionName+'_cfgBindings'
495 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000496 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400497 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000498 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400499 binding = self.GetOption(
500 'extensions', keysName, eventName, default='').split()
501 event = '<<' + eventName + '>>'
502 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000503 return extKeys
504
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400505 def GetExtensionBindings(self, extensionName):
506 """Return dict {extensionName event : active or defined keybinding}.
507
508 Augment self.GetExtensionKeys(extensionName) with mapping of non-
509 configurable events (from default config) to GetOption splits,
510 as in self.__GetRawExtensionKeys.
Steven M. Gavac628a062002-01-19 10:33:21 +0000511 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400512 bindsName = extensionName + '_bindings'
513 extBinds = self.GetExtensionKeys(extensionName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000514 #add the non-configurable bindings
515 if self.defaultCfg['extensions'].has_section(bindsName):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400516 eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000517 for eventName in eventNames:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400518 binding = self.GetOption(
519 'extensions', bindsName, eventName, default='').split()
520 event = '<<' + eventName + '>>'
521 extBinds[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000522
523 return extBinds
524
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000525 def GetKeyBinding(self, keySetName, eventStr):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400526 """Return the keybinding list for keySetName eventStr.
527
528 keySetName - name of key binding set (config-keys section).
529 eventStr - virtual event, including brackets, as in '<<event>>'.
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000530 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400531 eventName = eventStr[2:-2] #trim off the angle brackets
532 binding = self.GetOption('keys', keySetName, eventName, default='').split()
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000533 return binding
534
Steven M. Gavac628a062002-01-19 10:33:21 +0000535 def GetCurrentKeySet(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400536 "Return CurrentKeys with 'darwin' modifications."
Ronald Oussoren19302d92006-06-11 14:33:36 +0000537 result = self.GetKeySet(self.CurrentKeys())
538
Ned Deily57847df2014-03-27 20:47:04 -0700539 if sys.platform == "darwin":
540 # OS X Tk variants do not support the "Alt" keyboard modifier.
541 # So replace all keybingings that use "Alt" with ones that
542 # use the "Option" keyboard modifier.
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400543 # TODO (Ned?): the "Option" modifier does not work properly for
Ned Deily57847df2014-03-27 20:47:04 -0700544 # Cocoa Tk and XQuartz Tk so we should not use it
545 # in default OS X KeySets.
Ronald Oussoren19302d92006-06-11 14:33:36 +0000546 for k, v in result.items():
547 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
548 if v != v2:
549 result[k] = v2
550
551 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000552
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400553 def GetKeySet(self, keySetName):
554 """Return event-key dict for keySetName core plus active extensions.
555
556 If a binding defined in an extension is already in use, the
557 extension binding is disabled by being set to ''
Steven M. Gava2a63a072001-10-26 06:50:54 +0000558 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400559 keySet = self.GetCoreKeys(keySetName)
560 activeExtns = self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000561 for extn in activeExtns:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400562 extKeys = self.__GetRawExtensionKeys(extn)
Steven M. Gavac628a062002-01-19 10:33:21 +0000563 if extKeys: #the extension defines keybindings
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400564 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000565 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000566 #the binding is already in use
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400567 extKeys[event] = '' #disable this binding
568 keySet[event] = extKeys[event] #add binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000569 return keySet
570
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400571 def IsCoreBinding(self, virtualEvent):
572 """Return True if the virtual event is one of the core idle key events.
573
574 virtualEvent - string, name of the virtual event to test for,
575 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000576 """
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400577 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000578
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400579# TODO make keyBindins a file or class attribute used for test above
580# and copied in function below
581
Steven M. Gavac628a062002-01-19 10:33:21 +0000582 def GetCoreKeys(self, keySetName=None):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400583 """Return dict of core virtual-key keybindings for keySetName.
584
585 The default keySetName None corresponds to the keyBindings base
586 dict. If keySetName is not None, bindings from the config
587 file(s) are loaded _over_ these defaults, so if there is a
588 problem getting any core binding there will be an 'ultimate last
589 resort fallback' to the CUA-ish bindings defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000590 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000591 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000592 '<<copy>>': ['<Control-c>', '<Control-C>'],
593 '<<cut>>': ['<Control-x>', '<Control-X>'],
594 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000595 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
596 '<<center-insert>>': ['<Control-l>'],
597 '<<close-all-windows>>': ['<Control-q>'],
598 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000599 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000600 '<<end-of-file>>': ['<Control-d>'],
601 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000602 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000603 '<<history-next>>': ['<Alt-n>'],
604 '<<history-previous>>': ['<Alt-p>'],
605 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000606 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000607 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000608 '<<open-class-browser>>': ['<Alt-c>'],
609 '<<open-module>>': ['<Alt-m>'],
610 '<<open-new-window>>': ['<Control-n>'],
611 '<<open-window-from-file>>': ['<Control-o>'],
612 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000613 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000614 '<<redo>>': ['<Control-y>'],
615 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000616 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000617 '<<save-window-as-file>>': ['<Alt-s>'],
618 '<<save-window>>': ['<Control-s>'],
619 '<<select-all>>': ['<Alt-a>'],
620 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000621 '<<undo>>': ['<Control-z>'],
622 '<<find-again>>': ['<Control-g>', '<F3>'],
623 '<<find-in-files>>': ['<Alt-F3>'],
624 '<<find-selection>>': ['<Control-F3>'],
625 '<<find>>': ['<Control-f>'],
626 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000627 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000628 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlovc37db102012-03-29 19:54:58 +0300629 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000630 '<<smart-indent>>': ['<Key-Tab>'],
631 '<<indent-region>>': ['<Control-Key-bracketright>'],
632 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
633 '<<comment-region>>': ['<Alt-Key-3>'],
634 '<<uncomment-region>>': ['<Alt-Key-4>'],
635 '<<tabify-region>>': ['<Alt-Key-5>'],
636 '<<untabify-region>>': ['<Alt-Key-6>'],
637 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000638 '<<change-indentwidth>>': ['<Alt-Key-u>'],
639 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
640 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000641 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000642 if keySetName:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400643 for event in keyBindings:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400644 binding = self.GetKeyBinding(keySetName, event)
Steven M. Gava49745752002-02-18 01:43:11 +0000645 if binding:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400646 keyBindings[event] = binding
Steven M. Gava49745752002-02-18 01:43:11 +0000647 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000648 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
649 ' -\n problem retrieving key binding for event %r'
650 '\n from key set %r.\n'
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400651 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000652 (event, keySetName, keyBindings[event]))
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000653 try:
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400654 print(warning, file=sys.stderr)
Amaury Forgeot d'Arcdd8d8242008-04-21 22:35:30 +0000655 except IOError:
656 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000657 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000658
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400659 def GetExtraHelpSourceList(self, configSet):
660 """Return list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000661
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000662 Valid configSets are 'user' or 'default'. Return a list of tuples of
663 the form (menu_item , path_to_help_file , option), or return the empty
664 list. 'option' is the sequence number of the help resource. 'option'
665 values determine the position of the menu items on the Help menu,
666 therefore the returned list must be sorted by 'option'.
667
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000668 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400669 helpSources = []
670 if configSet == 'user':
671 cfgParser = self.userCfg['main']
672 elif configSet == 'default':
673 cfgParser = self.defaultCfg['main']
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000674 else:
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400675 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000676 options=cfgParser.GetOptionList('HelpFiles')
677 for option in options:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400678 value=cfgParser.Get('HelpFiles', option, default=';')
679 if value.find(';') == -1: #malformed config entry with no ';'
680 menuItem = '' #make these empty
681 helpPath = '' #so value won't be added to list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000682 else: #config entry contains ';' as expected
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400683 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000684 menuItem=value[0].strip()
685 helpPath=value[1].strip()
686 if menuItem and helpPath: #neither are empty strings
687 helpSources.append( (menuItem,helpPath,option) )
Florent Xiclunaa7f242f2010-04-02 08:15:26 +0000688 helpSources.sort(key=lambda x: int(x[2]))
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000689 return helpSources
690
691 def GetAllExtraHelpSourcesList(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400692 """Return a list of the details of all additional help sources.
693
694 Tuples in the list are those of GetExtraHelpSourceList.
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000695 """
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400696 allHelpSources = (self.GetExtraHelpSourceList('default') +
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000697 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000698 return allHelpSources
699
Terry Jan Reedy12352142015-08-01 18:57:27 -0400700 def GetFont(self, root, configType, section):
701 """Retrieve a font from configuration (font, font-size, font-bold)
702 Intercept the special value 'TkFixedFont' and substitute
703 the actual font, factoring in some tweaks if needed for
704 appearance sakes.
705
706 The 'root' parameter can normally be any valid Tkinter widget.
707
708 Return a tuple (family, size, weight) suitable for passing
709 to tkinter.Font
710 """
711 family = self.GetOption(configType, section, 'font', default='courier')
712 size = self.GetOption(configType, section, 'font-size', type='int',
713 default='10')
714 bold = self.GetOption(configType, section, 'font-bold', default=0,
715 type='bool')
716 if (family == 'TkFixedFont'):
Terry Jan Reedy99c1a6d2015-09-04 04:37:02 -0400717 if TkVersion < 8.5:
718 family = 'Courier'
719 else:
720 f = Font(name='TkFixedFont', exists=True, root=root)
721 actualFont = Font.actual(f)
722 family = actualFont['family']
723 size = actualFont['size']
724 if size < 0:
725 size = 10 # if font in pixels, ignore actual size
726 bold = actualFont['weight']=='bold'
Terry Jan Reedy12352142015-08-01 18:57:27 -0400727 return (family, size, 'bold' if bold else 'normal')
728
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000729 def LoadCfgFiles(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400730 "Load all configuration files."
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400731 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000732 self.defaultCfg[key].Load()
733 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000734
735 def SaveUserCfgFiles(self):
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400736 "Write all loaded user configuration files to disk."
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400737 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000738 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000739
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000740
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400741idleConf = IdleConf()
742
743# TODO Revise test output, write expanded unittest
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000744### module test
745if __name__ == '__main__':
746 def dumpCfg(cfg):
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400747 print('\n', cfg, '\n')
Terry Jan Reedyf8b7e782014-10-06 01:32:21 -0400748 for key in cfg:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400749 sections = cfg[key].sections()
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400750 print(key)
751 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000752 for section in sections:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400753 options = cfg[key].options(section)
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400754 print(section)
755 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000756 for option in options:
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400757 print(option, '=', cfg[key].Get(section, option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000758 dumpCfg(idleConf.defaultCfg)
759 dumpCfg(idleConf.userCfg)
Terry Jan Reedy163d7fb2014-10-06 23:26:17 -0400760 print(idleConf.userCfg['main'].Get('Theme', 'name'))
Terry Jan Reedybee003c2014-09-19 22:37:24 -0400761 #print(idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal'))