blob: 10fe3bacebca5fa372eaaa10a8fe9648d35ddc2f [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
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040010menu to access the configuration GUI (configdialog.py), or manually.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000011
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 Reedydeb7bf12014-10-06 23:26:26 -040019# TODOs added Oct 2014, tjr
20
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040021from configparser import ConfigParser
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000022import os
23import sys
Guido van Rossum36e0a922007-07-20 04:05:57 +000024
Terry Jan Reedyd87d1682015-08-01 18:57:33 -040025from tkinter.font import Font, nametofont
Steven M. Gavac11ccf32001-09-24 09:43:17 +000026
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000027class InvalidConfigType(Exception): pass
28class InvalidConfigSet(Exception): pass
29class InvalidFgBg(Exception): pass
30class InvalidTheme(Exception): pass
31
Steven M. Gavac11ccf32001-09-24 09:43:17 +000032class IdleConfParser(ConfigParser):
33 """
34 A ConfigParser specialised for idle configuration file handling
35 """
36 def __init__(self, cfgFile, cfgDefaults=None):
37 """
38 cfgFile - string, fully specified configuration file name
39 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040040 self.file = cfgFile
Serhiy Storchaka89953002013-02-07 15:24:36 +020041 ConfigParser.__init__(self, defaults=cfgDefaults, strict=False)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000042
Thomas Wouterscf297e42007-02-23 15:07:44 +000043 def Get(self, section, option, type=None, default=None, raw=False):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000044 """
45 Get an option value for given section/option or return default.
46 If type is specified, return as type.
47 """
Terry Jan Reedya9421fb2014-10-22 20:15:18 -040048 # TODO Use default as fallback, at least if not None
49 # Should also print Warning(file, section, option).
50 # Currently may raise ValueError
Thomas Wouterscf297e42007-02-23 15:07:44 +000051 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000052 return default
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040053 if type == 'bool':
Thomas Wouterscf297e42007-02-23 15:07:44 +000054 return self.getboolean(section, option)
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040055 elif type == 'int':
Thomas Wouterscf297e42007-02-23 15:07:44 +000056 return self.getint(section, option)
57 else:
58 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000059
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040060 def GetOptionList(self, section):
61 "Return a list of options for given section, else []."
Steven M. Gava085eb1b2002-02-05 04:52:32 +000062 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000063 return self.options(section)
64 else: #return a default value
65 return []
66
Steven M. Gavac11ccf32001-09-24 09:43:17 +000067 def Load(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040068 "Load the configuration file from disk."
Steven M. Gavac11ccf32001-09-24 09:43:17 +000069 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000070
Steven M. Gavac11ccf32001-09-24 09:43:17 +000071class IdleUserConfParser(IdleConfParser):
72 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000073 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000074 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000075
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040076 def AddSection(self, section):
77 "If section doesn't exist, add it."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000078 if not self.has_section(section):
79 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000080
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000081 def RemoveEmptySections(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040082 "Remove any sections that have no options."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000083 for section in self.sections():
84 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000085 self.remove_section(section)
86
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000087 def IsEmpty(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040088 "Return True if no sections after removing empty sections."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000089 self.RemoveEmptySections()
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040090 return not self.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000091
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040092 def RemoveOption(self, section, option):
93 """Return True if option is removed from section, else False.
94
95 False if either section does not exist or did not have option.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000096 """
97 if self.has_section(section):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040098 return self.remove_option(section, option)
99 return False
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000100
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400101 def SetOption(self, section, option, value):
102 """Return True if option is added or changed to value, else False.
103
104 Add section if required. False means option already had value.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000105 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400106 if self.has_option(section, option):
107 if self.get(section, option) == value:
108 return False
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000109 else:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400110 self.set(section, option, value)
111 return True
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000112 else:
113 if not self.has_section(section):
114 self.add_section(section)
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400115 self.set(section, option, value)
116 return True
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000117
Steven M. Gavab77d3432002-03-02 07:16:21 +0000118 def RemoveFile(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400119 "Remove user config file self.file from disk if it exists."
Steven M. Gavab77d3432002-03-02 07:16:21 +0000120 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000121 os.remove(self.file)
122
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000123 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000124 """Update user configuration file.
125
126 Remove empty sections. If resulting config isn't empty, write the file
127 to disk. If config is empty, remove the file from disk if it exists.
128
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000129 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000130 if not self.IsEmpty():
Christian Heimesbbffeb62008-01-24 09:42:52 +0000131 fname = self.file
132 try:
133 cfgFile = open(fname, 'w')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200134 except OSError:
Christian Heimes0bd4e112008-02-12 22:59:25 +0000135 os.unlink(fname)
Christian Heimesbbffeb62008-01-24 09:42:52 +0000136 cfgFile = open(fname, 'w')
Amaury Forgeot d'Arcbbe7b0a2011-10-03 20:33:24 +0200137 with cfgFile:
138 self.write(cfgFile)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000139 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000140 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000141
142class IdleConf:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400143 """Hold config parsers for all idle config files in singleton instance.
144
145 Default config files, self.defaultCfg --
146 for config_type in self.config_types:
147 (idle install dir)/config-{config-type}.def
148
149 User config files, self.userCfg --
150 for config_type in self.config_types:
151 (user home dir)/.idlerc/config-{config-type}.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000152 """
153 def __init__(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400154 self.config_types = ('main', 'extensions', 'highlight', 'keys')
155 self.defaultCfg = {}
156 self.userCfg = {}
157 self.cfg = {} # TODO use to select userCfg vs defaultCfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000158 self.CreateConfigHandlers()
159 self.LoadCfgFiles()
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400160
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000161
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000162 def CreateConfigHandlers(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400163 "Populate default and user config parser dictionaries."
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000164 #build idle install path
165 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000166 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000167 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000168 idleDir=os.path.abspath(sys.path[0])
169 userDir=self.GetUserCfgDir()
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400170
171 defCfgFiles = {}
172 usrCfgFiles = {}
173 # TODO eliminate these temporaries by combining loops
174 for cfgType in self.config_types: #build config file names
175 defCfgFiles[cfgType] = os.path.join(
176 idleDir, 'config-' + cfgType + '.def')
177 usrCfgFiles[cfgType] = os.path.join(
178 userDir, 'config-' + cfgType + '.cfg')
179 for cfgType in self.config_types: #create config parsers
180 self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType])
181 self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000182
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000183 def GetUserCfgDir(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400184 """Return a filesystem directory for storing user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000185
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400186 Creates it if required.
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000187 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000188 cfgDir = '.idlerc'
189 userDir = os.path.expanduser('~')
190 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000191 if not os.path.exists(userDir):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400192 warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
193 userDir + ',\n but the path does not exist.')
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000194 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400195 print(warn, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200196 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000197 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000198 userDir = '~'
199 if userDir == "~": # still no path to home!
200 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
201 userDir = os.getcwd()
202 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000203 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000204 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000205 os.mkdir(userDir)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200206 except OSError:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400207 warn = ('\n Warning: unable to create user config directory\n' +
208 userDir + '\n Check path and permissions.\n Exiting!\n')
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400209 print(warn, file=sys.stderr)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000210 raise SystemExit
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400211 # TODO continue without userDIr instead of exit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000212 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000213
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000214 def GetOption(self, configType, section, option, default=None, type=None,
Thomas Wouterscf297e42007-02-23 15:07:44 +0000215 warn_on_default=True, raw=False):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400216 """Return a value for configType section option, or default.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000217
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400218 If type is not None, return a value of that type. Also pass raw
219 to the config parser. First try to return a valid value
220 (including type) from a user configuration. If that fails, try
221 the default configuration. If that fails, return default, with a
222 default of None.
223
224 Warn if either user or default configurations have an invalid value.
225 Warn if default is returned and warn_on_default is True.
Steven M. Gava429a86af2001-10-23 10:42:12 +0000226 """
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200227 try:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400228 if self.userCfg[configType].has_option(section, option):
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200229 return self.userCfg[configType].Get(section, option,
230 type=type, raw=raw)
231 except ValueError:
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400232 warning = ('\n Warning: config.py - IdleConf.GetOption -\n'
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200233 ' invalid %r value for configuration option %r\n'
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400234 ' from section %r: %r' %
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200235 (type, option, section,
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400236 self.userCfg[configType].Get(section, option, raw=raw)))
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400237 _warn(warning, configType, section, option)
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200238 try:
239 if self.defaultCfg[configType].has_option(section,option):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400240 return self.defaultCfg[configType].Get(
241 section, option, type=type, raw=raw)
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200242 except ValueError:
243 pass
244 #returning default, print warning
245 if warn_on_default:
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400246 warning = ('\n Warning: config.py - IdleConf.GetOption -\n'
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200247 ' problem retrieving configuration option %r\n'
248 ' from section %r.\n'
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400249 ' returning default value: %r' %
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200250 (option, section, default))
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400251 _warn(warning, configType, section, option)
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200252 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000253
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000254 def SetOption(self, configType, section, option, value):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400255 """Set section option to value in user config file."""
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000256 self.userCfg[configType].SetOption(section, option, value)
257
Steven M. Gava2a63a072001-10-26 06:50:54 +0000258 def GetSectionList(self, configSet, configType):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400259 """Return sections for configSet configType configuration.
260
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000261 configSet must be either 'user' or 'default'
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400262 configType must be in self.config_types.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000263 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400264 if not (configType in self.config_types):
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000265 raise InvalidConfigType('Invalid configType specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000266 if configSet == 'user':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400267 cfgParser = self.userCfg[configType]
Steven M. Gava2a63a072001-10-26 06:50:54 +0000268 elif configSet == 'default':
269 cfgParser=self.defaultCfg[configType]
270 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000271 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000272 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000273
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000274 def GetHighlight(self, theme, element, fgBg=None):
Terry Jan Reedy86757992014-10-09 18:44:32 -0400275 """Return individual theme element highlight color(s).
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400276
Terry Jan Reedy86757992014-10-09 18:44:32 -0400277 fgBg - string ('fg' or 'bg') or None.
278 If None, return a dictionary containing fg and bg colors with
279 keys 'foreground' and 'background'. Otherwise, only return
280 fg or bg color, as specified. Colors are intended to be
281 appropriate for passing to Tkinter in, e.g., a tag_config call).
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000282 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000283 if self.defaultCfg['highlight'].has_section(theme):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400284 themeDict = self.GetThemeDict('default', theme)
Steven M. Gava9f25e672002-02-11 02:51:18 +0000285 else:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400286 themeDict = self.GetThemeDict('user', theme)
287 fore = themeDict[element + '-foreground']
Terry Jan Reedy86757992014-10-09 18:44:32 -0400288 if element == 'cursor': # There is no config value for cursor bg
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400289 back = themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000290 else:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400291 back = themeDict[element + '-background']
292 highlight = {"foreground": fore, "background": back}
Terry Jan Reedy86757992014-10-09 18:44:32 -0400293 if not fgBg: # Return dict of both colors
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000294 return highlight
Terry Jan Reedy86757992014-10-09 18:44:32 -0400295 else: # Return specified color only
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000296 if fgBg == 'fg':
297 return highlight["foreground"]
298 if fgBg == 'bg':
299 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000300 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000301 raise InvalidFgBg('Invalid fgBg specified')
Steven M. Gava9f25e672002-02-11 02:51:18 +0000302
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400303 def GetThemeDict(self, type, themeName):
304 """Return {option:value} dict for elements in themeName.
305
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000306 type - string, 'default' or 'user' theme type
307 themeName - string, theme name
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400308 Values are loaded over ultimate fallback defaults to guarantee
309 that all theme elements are present in a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000310 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000311 if type == 'user':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400312 cfgParser = self.userCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000313 elif type == 'default':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400314 cfgParser = self.defaultCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000315 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000316 raise InvalidTheme('Invalid theme type specified')
Terry Jan Reedy86757992014-10-09 18:44:32 -0400317 # Provide foreground and background colors for each theme
318 # element (other than cursor) even though some values are not
319 # yet used by idle, to allow for their use in the future.
320 # Default values are generally black and white.
321 # TODO copy theme from a class attribute.
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400322 theme ={'normal-foreground':'#000000',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000323 'normal-background':'#ffffff',
324 'keyword-foreground':'#000000',
325 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000326 'builtin-foreground':'#000000',
327 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000328 'comment-foreground':'#000000',
329 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000330 'string-foreground':'#000000',
331 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000332 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000333 'definition-background':'#ffffff',
334 'hilite-foreground':'#000000',
335 'hilite-background':'gray',
336 'break-foreground':'#ffffff',
337 'break-background':'#000000',
338 'hit-foreground':'#ffffff',
339 'hit-background':'#000000',
340 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000341 'error-background':'#000000',
342 #cursor (only foreground can be set)
343 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000344 #shell window
345 'stdout-foreground':'#000000',
346 'stdout-background':'#ffffff',
347 'stderr-foreground':'#000000',
348 'stderr-background':'#ffffff',
349 'console-foreground':'#000000',
350 'console-background':'#ffffff' }
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000351 for element in theme:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400352 if not cfgParser.has_option(themeName, element):
Terry Jan Reedy86757992014-10-09 18:44:32 -0400353 # Print warning that will return a default color
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400354 warning = ('\n Warning: config.IdleConf.GetThemeDict'
Walter Dörwald70a6b492004-02-12 17:35:32 +0000355 ' -\n problem retrieving theme element %r'
356 '\n from theme %r.\n'
Terry Jan Reedy86757992014-10-09 18:44:32 -0400357 ' returning default color: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000358 (element, themeName, theme[element]))
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400359 _warn(warning, 'highlight', themeName, element)
Terry Jan Reedy86757992014-10-09 18:44:32 -0400360 theme[element] = cfgParser.Get(
361 themeName, element, default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000362 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000363
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000364 def CurrentTheme(self):
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400365 "Return the name of the currently active text color theme."
366 return self.current_colors_and_keys('Theme')
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500367
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400368 def CurrentKeys(self):
369 """Return the name of the currently active key set."""
370 return self.current_colors_and_keys('Keys')
371
372 def current_colors_and_keys(self, section):
373 """Return the currently active name for Theme or Keys section.
374
375 idlelib.config-main.def ('default') includes these sections
376
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500377 [Theme]
378 default= 1
379 name= IDLE Classic
380 name2=
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500381
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400382 [Keys]
383 default= 1
384 name=
385 name2=
386
387 Item 'name2', is used for built-in ('default') themes and keys
388 added after 2015 Oct 1 and 2016 July 1. This kludge is needed
389 because setting 'name' to a builtin not defined in older IDLEs
390 to display multiple error messages or quit.
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500391 See https://bugs.python.org/issue25313.
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400392 When default = True, 'name2' takes precedence over 'name',
393 while older IDLEs will just use name. When default = False,
394 'name2' may still be set, but it is ignored.
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500395 """
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400396 cfgname = 'highlight' if section == 'Theme' else 'keys'
Terry Jan Reedy5acf4e52016-08-24 22:08:01 -0400397 default = self.GetOption('main', section, 'default',
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500398 type='bool', default=True)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400399 name = ''
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500400 if default:
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400401 name = self.GetOption('main', section, 'name2', default='')
402 if not name:
403 name = self.GetOption('main', section, 'name', default='')
404 if name:
405 source = self.defaultCfg if default else self.userCfg
406 if source[cfgname].has_section(name):
407 return name
408 return "IDLE Classic" if section == 'Theme' else self.default_keys()
409
410 @staticmethod
411 def default_keys():
412 if sys.platform[:3] == 'win':
413 return 'IDLE Classic Windows'
414 elif sys.platform == 'darwin':
415 return 'IDLE Classic OSX'
Terry Jan Reedyc15a7c62015-11-12 15:06:07 -0500416 else:
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400417 return 'IDLE Modern Unix'
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000418
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400419 def GetExtensions(self, active_only=True,
420 editor_only=False, shell_only=False):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400421 """Return extensions in default and user config-extensions files.
422
423 If active_only True, only return active (enabled) extensions
424 and optionally only editor or shell extensions.
425 If active_only False, return all extensions.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000426 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400427 extns = self.RemoveKeyBindNames(
428 self.GetSectionList('default', 'extensions'))
429 userExtns = self.RemoveKeyBindNames(
430 self.GetSectionList('user', 'extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000431 for extn in userExtns:
432 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000433 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000434 if active_only:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400435 activeExtns = []
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000436 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000437 if self.GetOption('extensions', extn, 'enable', default=True,
438 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000439 #the extension is enabled
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400440 if editor_only or shell_only: # TODO both True contradict
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000441 if editor_only:
442 option = "enable_editor"
443 else:
444 option = "enable_shell"
445 if self.GetOption('extensions', extn,option,
446 default=True, type='bool',
447 warn_on_default=False):
448 activeExtns.append(extn)
449 else:
450 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000451 return activeExtns
452 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000453 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000454
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400455 def RemoveKeyBindNames(self, extnNameList):
456 "Return extnNameList with keybinding section names removed."
457 # TODO Easier to return filtered copy with list comp
458 names = extnNameList
459 kbNameIndicies = []
Steven M. Gavac628a062002-01-19 10:33:21 +0000460 for name in names:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000461 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000462 kbNameIndicies.append(names.index(name))
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400463 kbNameIndicies.sort(reverse=True)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000464 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000465 del(names[index])
466 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000467
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400468 def GetExtnNameForEvent(self, virtualEvent):
469 """Return the name of the extension binding virtualEvent, or None.
470
471 virtualEvent - string, name of the virtual event to test for,
472 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000473 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400474 extName = None
475 vEvent = '<<' + virtualEvent + '>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000476 for extn in self.GetExtensions(active_only=0):
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000477 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000478 if event == vEvent:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400479 extName = extn # TODO return here?
Steven M. Gavaa498af22002-02-01 01:33:36 +0000480 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000481
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400482 def GetExtensionKeys(self, extensionName):
483 """Return dict: {configurable extensionName event : active keybinding}.
484
485 Events come from default config extension_cfgBindings section.
486 Keybindings come from GetCurrentKeySet() active key dict,
487 where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000488 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400489 keysName = extensionName + '_cfgBindings'
490 activeKeys = self.GetCurrentKeySet()
491 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000492 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400493 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000494 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400495 event = '<<' + eventName + '>>'
496 binding = activeKeys[event]
497 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000498 return extKeys
499
Steven M. Gavac628a062002-01-19 10:33:21 +0000500 def __GetRawExtensionKeys(self,extensionName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400501 """Return dict {configurable extensionName event : keybinding list}.
502
503 Events come from default config extension_cfgBindings section.
504 Keybindings list come from the splitting of GetOption, which
505 tries user config before default config.
Steven M. Gavac628a062002-01-19 10:33:21 +0000506 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400507 keysName = extensionName+'_cfgBindings'
508 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000509 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400510 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000511 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400512 binding = self.GetOption(
513 'extensions', keysName, eventName, default='').split()
514 event = '<<' + eventName + '>>'
515 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000516 return extKeys
517
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400518 def GetExtensionBindings(self, extensionName):
519 """Return dict {extensionName event : active or defined keybinding}.
520
521 Augment self.GetExtensionKeys(extensionName) with mapping of non-
522 configurable events (from default config) to GetOption splits,
523 as in self.__GetRawExtensionKeys.
Steven M. Gavac628a062002-01-19 10:33:21 +0000524 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400525 bindsName = extensionName + '_bindings'
526 extBinds = self.GetExtensionKeys(extensionName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000527 #add the non-configurable bindings
528 if self.defaultCfg['extensions'].has_section(bindsName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400529 eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000530 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400531 binding = self.GetOption(
532 'extensions', bindsName, eventName, default='').split()
533 event = '<<' + eventName + '>>'
534 extBinds[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000535
536 return extBinds
537
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000538 def GetKeyBinding(self, keySetName, eventStr):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400539 """Return the keybinding list for keySetName eventStr.
540
541 keySetName - name of key binding set (config-keys section).
542 eventStr - virtual event, including brackets, as in '<<event>>'.
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000543 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400544 eventName = eventStr[2:-2] #trim off the angle brackets
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400545 binding = self.GetOption('keys', keySetName, eventName, default='',
546 warn_on_default=False).split()
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000547 return binding
548
Steven M. Gavac628a062002-01-19 10:33:21 +0000549 def GetCurrentKeySet(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400550 "Return CurrentKeys with 'darwin' modifications."
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000551 result = self.GetKeySet(self.CurrentKeys())
552
Ned Deilyb7601672014-03-27 20:49:14 -0700553 if sys.platform == "darwin":
554 # OS X Tk variants do not support the "Alt" keyboard modifier.
555 # So replace all keybingings that use "Alt" with ones that
556 # use the "Option" keyboard modifier.
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400557 # TODO (Ned?): the "Option" modifier does not work properly for
Ned Deilyb7601672014-03-27 20:49:14 -0700558 # Cocoa Tk and XQuartz Tk so we should not use it
559 # in default OS X KeySets.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000560 for k, v in result.items():
561 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
562 if v != v2:
563 result[k] = v2
564
565 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000566
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400567 def GetKeySet(self, keySetName):
568 """Return event-key dict for keySetName core plus active extensions.
569
570 If a binding defined in an extension is already in use, the
571 extension binding is disabled by being set to ''
Steven M. Gava2a63a072001-10-26 06:50:54 +0000572 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400573 keySet = self.GetCoreKeys(keySetName)
574 activeExtns = self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000575 for extn in activeExtns:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400576 extKeys = self.__GetRawExtensionKeys(extn)
Steven M. Gavac628a062002-01-19 10:33:21 +0000577 if extKeys: #the extension defines keybindings
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000578 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000579 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000580 #the binding is already in use
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400581 extKeys[event] = '' #disable this binding
582 keySet[event] = extKeys[event] #add binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000583 return keySet
584
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400585 def IsCoreBinding(self, virtualEvent):
586 """Return True if the virtual event is one of the core idle key events.
587
588 virtualEvent - string, name of the virtual event to test for,
589 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000590 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000591 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000592
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400593# TODO make keyBindins a file or class attribute used for test above
594# and copied in function below
595
Steven M. Gavac628a062002-01-19 10:33:21 +0000596 def GetCoreKeys(self, keySetName=None):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400597 """Return dict of core virtual-key keybindings for keySetName.
598
599 The default keySetName None corresponds to the keyBindings base
600 dict. If keySetName is not None, bindings from the config
601 file(s) are loaded _over_ these defaults, so if there is a
602 problem getting any core binding there will be an 'ultimate last
603 resort fallback' to the CUA-ish bindings defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000604 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000605 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000606 '<<copy>>': ['<Control-c>', '<Control-C>'],
607 '<<cut>>': ['<Control-x>', '<Control-X>'],
608 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000609 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
610 '<<center-insert>>': ['<Control-l>'],
611 '<<close-all-windows>>': ['<Control-q>'],
612 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000613 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000614 '<<end-of-file>>': ['<Control-d>'],
615 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000616 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000617 '<<history-next>>': ['<Alt-n>'],
618 '<<history-previous>>': ['<Alt-p>'],
619 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000620 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000621 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000622 '<<open-class-browser>>': ['<Alt-c>'],
623 '<<open-module>>': ['<Alt-m>'],
624 '<<open-new-window>>': ['<Control-n>'],
625 '<<open-window-from-file>>': ['<Control-o>'],
626 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000627 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000628 '<<redo>>': ['<Control-y>'],
629 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000630 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000631 '<<save-window-as-file>>': ['<Alt-s>'],
632 '<<save-window>>': ['<Control-s>'],
633 '<<select-all>>': ['<Alt-a>'],
634 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000635 '<<undo>>': ['<Control-z>'],
636 '<<find-again>>': ['<Control-g>', '<F3>'],
637 '<<find-in-files>>': ['<Alt-F3>'],
638 '<<find-selection>>': ['<Control-F3>'],
639 '<<find>>': ['<Control-f>'],
640 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000641 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000642 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlov67ac0792012-03-29 19:01:28 +0300643 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000644 '<<smart-indent>>': ['<Key-Tab>'],
645 '<<indent-region>>': ['<Control-Key-bracketright>'],
646 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
647 '<<comment-region>>': ['<Alt-Key-3>'],
648 '<<uncomment-region>>': ['<Alt-Key-4>'],
649 '<<tabify-region>>': ['<Alt-Key-5>'],
650 '<<untabify-region>>': ['<Alt-Key-6>'],
651 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000652 '<<change-indentwidth>>': ['<Alt-Key-u>'],
653 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
654 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000655 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000656 if keySetName:
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400657 if not (self.userCfg['keys'].has_section(keySetName) or
658 self.defaultCfg['keys'].has_section(keySetName)):
659 warning = (
660 '\n Warning: config.py - IdleConf.GetCoreKeys -\n'
661 ' key set %r is not defined, using default bindings.' %
662 (keySetName,)
663 )
664 _warn(warning, 'keys', keySetName)
665 else:
666 for event in keyBindings:
667 binding = self.GetKeyBinding(keySetName, event)
668 if binding:
669 keyBindings[event] = binding
670 else: #we are going to return a default, print warning
671 warning = (
672 '\n Warning: config.py - IdleConf.GetCoreKeys -\n'
673 ' problem retrieving key binding for event %r\n'
674 ' from key set %r.\n'
675 ' returning default value: %r' %
676 (event, keySetName, keyBindings[event])
677 )
678 _warn(warning, 'keys', keySetName, event)
Steven M. Gava17d01542001-12-03 00:37:28 +0000679 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000680
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400681 def GetExtraHelpSourceList(self, configSet):
682 """Return list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000683
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000684 Valid configSets are 'user' or 'default'. Return a list of tuples of
685 the form (menu_item , path_to_help_file , option), or return the empty
686 list. 'option' is the sequence number of the help resource. 'option'
687 values determine the position of the menu items on the Help menu,
688 therefore the returned list must be sorted by 'option'.
689
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000690 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400691 helpSources = []
692 if configSet == 'user':
693 cfgParser = self.userCfg['main']
694 elif configSet == 'default':
695 cfgParser = self.defaultCfg['main']
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000696 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000697 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000698 options=cfgParser.GetOptionList('HelpFiles')
699 for option in options:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400700 value=cfgParser.Get('HelpFiles', option, default=';')
701 if value.find(';') == -1: #malformed config entry with no ';'
702 menuItem = '' #make these empty
703 helpPath = '' #so value won't be added to list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000704 else: #config entry contains ';' as expected
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000705 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000706 menuItem=value[0].strip()
707 helpPath=value[1].strip()
708 if menuItem and helpPath: #neither are empty strings
709 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser4718bf82008-02-12 21:34:12 +0000710 helpSources.sort(key=lambda x: x[2])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000711 return helpSources
712
713 def GetAllExtraHelpSourcesList(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400714 """Return a list of the details of all additional help sources.
715
716 Tuples in the list are those of GetExtraHelpSourceList.
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000717 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400718 allHelpSources = (self.GetExtraHelpSourceList('default') +
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000719 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000720 return allHelpSources
721
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400722 def GetFont(self, root, configType, section):
723 """Retrieve a font from configuration (font, font-size, font-bold)
724 Intercept the special value 'TkFixedFont' and substitute
725 the actual font, factoring in some tweaks if needed for
726 appearance sakes.
727
728 The 'root' parameter can normally be any valid Tkinter widget.
729
730 Return a tuple (family, size, weight) suitable for passing
731 to tkinter.Font
732 """
733 family = self.GetOption(configType, section, 'font', default='courier')
734 size = self.GetOption(configType, section, 'font-size', type='int',
735 default='10')
736 bold = self.GetOption(configType, section, 'font-bold', default=0,
737 type='bool')
738 if (family == 'TkFixedFont'):
Terry Jan Reedy1080d132016-06-09 21:09:15 -0400739 f = Font(name='TkFixedFont', exists=True, root=root)
740 actualFont = Font.actual(f)
741 family = actualFont['family']
742 size = actualFont['size']
743 if size <= 0:
744 size = 10 # if font in pixels, ignore actual size
745 bold = actualFont['weight'] == 'bold'
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400746 return (family, size, 'bold' if bold else 'normal')
747
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000748 def LoadCfgFiles(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400749 "Load all configuration files."
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000750 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000751 self.defaultCfg[key].Load()
752 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000753
754 def SaveUserCfgFiles(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400755 "Write all loaded user configuration files to disk."
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000756 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000757 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000758
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000759
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400760idleConf = IdleConf()
761
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400762
763_warned = set()
764def _warn(msg, *key):
765 key = (msg,) + key
766 if key not in _warned:
767 try:
768 print(msg, file=sys.stderr)
769 except OSError:
770 pass
771 _warned.add(key)
772
773
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400774# TODO Revise test output, write expanded unittest
Terry Jan Reedy1080d132016-06-09 21:09:15 -0400775#
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000776if __name__ == '__main__':
Terry Jan Reedy2279aeb2016-07-05 20:09:53 -0400777 from zlib import crc32
778 line, crc = 0, 0
779
780 def sprint(obj):
781 global line, crc
782 txt = str(obj)
783 line += 1
784 crc = crc32(txt.encode(encoding='utf-8'), crc)
785 print(txt)
786 #print('***', line, crc, '***') # uncomment for diagnosis
787
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000788 def dumpCfg(cfg):
Terry Jan Reedy2279aeb2016-07-05 20:09:53 -0400789 print('\n', cfg, '\n') # has variable '0xnnnnnnnn' addresses
790 for key in sorted(cfg.keys()):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400791 sections = cfg[key].sections()
Terry Jan Reedy2279aeb2016-07-05 20:09:53 -0400792 sprint(key)
793 sprint(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000794 for section in sections:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400795 options = cfg[key].options(section)
Terry Jan Reedy2279aeb2016-07-05 20:09:53 -0400796 sprint(section)
797 sprint(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000798 for option in options:
Terry Jan Reedy2279aeb2016-07-05 20:09:53 -0400799 sprint(option + ' = ' + cfg[key].Get(section, option))
800
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000801 dumpCfg(idleConf.defaultCfg)
802 dumpCfg(idleConf.userCfg)
Terry Jan Reedy2279aeb2016-07-05 20:09:53 -0400803 print('\nlines = ', line, ', crc = ', crc, sep='')