blob: 4d87de0605b3803550233f79112b05a0b2b31411 [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
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000021import os
22import sys
Guido van Rossum36e0a922007-07-20 04:05:57 +000023
Terry Jan Reedy44f09eb2014-07-01 18:52:37 -040024from configparser import ConfigParser
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)))
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200237 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400238 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200239 except OSError:
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200240 pass
241 try:
242 if self.defaultCfg[configType].has_option(section,option):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400243 return self.defaultCfg[configType].Get(
244 section, option, type=type, raw=raw)
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200245 except ValueError:
246 pass
247 #returning default, print warning
248 if warn_on_default:
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400249 warning = ('\n Warning: config.py - IdleConf.GetOption -\n'
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200250 ' problem retrieving configuration option %r\n'
251 ' from section %r.\n'
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400252 ' returning default value: %r' %
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200253 (option, section, default))
254 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400255 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200256 except OSError:
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200257 pass
258 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000259
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000260 def SetOption(self, configType, section, option, value):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400261 """Set section option to value in user config file."""
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000262 self.userCfg[configType].SetOption(section, option, value)
263
Steven M. Gava2a63a072001-10-26 06:50:54 +0000264 def GetSectionList(self, configSet, configType):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400265 """Return sections for configSet configType configuration.
266
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000267 configSet must be either 'user' or 'default'
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400268 configType must be in self.config_types.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000269 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400270 if not (configType in self.config_types):
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000271 raise InvalidConfigType('Invalid configType specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000272 if configSet == 'user':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400273 cfgParser = self.userCfg[configType]
Steven M. Gava2a63a072001-10-26 06:50:54 +0000274 elif configSet == 'default':
275 cfgParser=self.defaultCfg[configType]
276 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000277 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000278 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000279
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000280 def GetHighlight(self, theme, element, fgBg=None):
Terry Jan Reedy86757992014-10-09 18:44:32 -0400281 """Return individual theme element highlight color(s).
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400282
Terry Jan Reedy86757992014-10-09 18:44:32 -0400283 fgBg - string ('fg' or 'bg') or None.
284 If None, return a dictionary containing fg and bg colors with
285 keys 'foreground' and 'background'. Otherwise, only return
286 fg or bg color, as specified. Colors are intended to be
287 appropriate for passing to Tkinter in, e.g., a tag_config call).
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000288 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000289 if self.defaultCfg['highlight'].has_section(theme):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400290 themeDict = self.GetThemeDict('default', theme)
Steven M. Gava9f25e672002-02-11 02:51:18 +0000291 else:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400292 themeDict = self.GetThemeDict('user', theme)
293 fore = themeDict[element + '-foreground']
Terry Jan Reedy86757992014-10-09 18:44:32 -0400294 if element == 'cursor': # There is no config value for cursor bg
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400295 back = themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000296 else:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400297 back = themeDict[element + '-background']
298 highlight = {"foreground": fore, "background": back}
Terry Jan Reedy86757992014-10-09 18:44:32 -0400299 if not fgBg: # Return dict of both colors
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000300 return highlight
Terry Jan Reedy86757992014-10-09 18:44:32 -0400301 else: # Return specified color only
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000302 if fgBg == 'fg':
303 return highlight["foreground"]
304 if fgBg == 'bg':
305 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000306 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000307 raise InvalidFgBg('Invalid fgBg specified')
Steven M. Gava9f25e672002-02-11 02:51:18 +0000308
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400309 def GetThemeDict(self, type, themeName):
310 """Return {option:value} dict for elements in themeName.
311
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000312 type - string, 'default' or 'user' theme type
313 themeName - string, theme name
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400314 Values are loaded over ultimate fallback defaults to guarantee
315 that all theme elements are present in a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000316 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000317 if type == 'user':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400318 cfgParser = self.userCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000319 elif type == 'default':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400320 cfgParser = self.defaultCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000321 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000322 raise InvalidTheme('Invalid theme type specified')
Terry Jan Reedy86757992014-10-09 18:44:32 -0400323 # Provide foreground and background colors for each theme
324 # element (other than cursor) even though some values are not
325 # yet used by idle, to allow for their use in the future.
326 # Default values are generally black and white.
327 # TODO copy theme from a class attribute.
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400328 theme ={'normal-foreground':'#000000',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000329 'normal-background':'#ffffff',
330 'keyword-foreground':'#000000',
331 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000332 'builtin-foreground':'#000000',
333 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000334 'comment-foreground':'#000000',
335 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000336 'string-foreground':'#000000',
337 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000338 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000339 'definition-background':'#ffffff',
340 'hilite-foreground':'#000000',
341 'hilite-background':'gray',
342 'break-foreground':'#ffffff',
343 'break-background':'#000000',
344 'hit-foreground':'#ffffff',
345 'hit-background':'#000000',
346 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000347 'error-background':'#000000',
348 #cursor (only foreground can be set)
349 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000350 #shell window
351 'stdout-foreground':'#000000',
352 'stdout-background':'#ffffff',
353 'stderr-foreground':'#000000',
354 'stderr-background':'#ffffff',
355 'console-foreground':'#000000',
356 'console-background':'#ffffff' }
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000357 for element in theme:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400358 if not cfgParser.has_option(themeName, element):
Terry Jan Reedy86757992014-10-09 18:44:32 -0400359 # Print warning that will return a default color
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400360 warning = ('\n Warning: config.IdleConf.GetThemeDict'
Walter Dörwald70a6b492004-02-12 17:35:32 +0000361 ' -\n problem retrieving theme element %r'
362 '\n from theme %r.\n'
Terry Jan Reedy86757992014-10-09 18:44:32 -0400363 ' returning default color: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000364 (element, themeName, theme[element]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000365 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400366 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200367 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000368 pass
Terry Jan Reedy86757992014-10-09 18:44:32 -0400369 theme[element] = cfgParser.Get(
370 themeName, element, default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000371 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000372
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000373 def CurrentTheme(self):
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500374 """Return the name of the currently active text color theme.
375
376 idlelib.config-main.def includes this section
377 [Theme]
378 default= 1
379 name= IDLE Classic
380 name2=
381 # name2 set in user config-main.cfg for themes added after 2015 Oct 1
382
383 Item name2 is needed because setting name to a new builtin
384 causes older IDLEs to display multiple error messages or quit.
385 See https://bugs.python.org/issue25313.
386 When default = True, name2 takes precedence over name,
387 while older IDLEs will just use name.
388 """
389 default = self.GetOption('main', 'Theme', 'default',
390 type='bool', default=True)
391 if default:
392 theme = self.GetOption('main', 'Theme', 'name2', default='')
393 if default and not theme or not default:
394 theme = self.GetOption('main', 'Theme', 'name', default='')
395 source = self.defaultCfg if default else self.userCfg
396 if source['highlight'].has_section(theme):
Terry Jan Reedy5496ba22015-11-12 15:24:33 -0500397 return theme
Terry Jan Reedyc15a7c62015-11-12 15:06:07 -0500398 else:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500399 return "IDLE Classic"
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000400
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000401 def CurrentKeys(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400402 "Return the name of the currently active key set."
403 return self.GetOption('main', 'Keys', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000404
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000405 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400406 """Return extensions in default and user config-extensions files.
407
408 If active_only True, only return active (enabled) extensions
409 and optionally only editor or shell extensions.
410 If active_only False, return all extensions.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000411 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400412 extns = self.RemoveKeyBindNames(
413 self.GetSectionList('default', 'extensions'))
414 userExtns = self.RemoveKeyBindNames(
415 self.GetSectionList('user', 'extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000416 for extn in userExtns:
417 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000418 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000419 if active_only:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400420 activeExtns = []
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000421 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000422 if self.GetOption('extensions', extn, 'enable', default=True,
423 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000424 #the extension is enabled
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400425 if editor_only or shell_only: # TODO if both, contradictory
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000426 if editor_only:
427 option = "enable_editor"
428 else:
429 option = "enable_shell"
430 if self.GetOption('extensions', extn,option,
431 default=True, type='bool',
432 warn_on_default=False):
433 activeExtns.append(extn)
434 else:
435 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000436 return activeExtns
437 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000438 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000439
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400440 def RemoveKeyBindNames(self, extnNameList):
441 "Return extnNameList with keybinding section names removed."
442 # TODO Easier to return filtered copy with list comp
443 names = extnNameList
444 kbNameIndicies = []
Steven M. Gavac628a062002-01-19 10:33:21 +0000445 for name in names:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000446 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000447 kbNameIndicies.append(names.index(name))
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400448 kbNameIndicies.sort(reverse=True)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000449 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000450 del(names[index])
451 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000452
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400453 def GetExtnNameForEvent(self, virtualEvent):
454 """Return the name of the extension binding virtualEvent, or None.
455
456 virtualEvent - string, name of the virtual event to test for,
457 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000458 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400459 extName = None
460 vEvent = '<<' + virtualEvent + '>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000461 for extn in self.GetExtensions(active_only=0):
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000462 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000463 if event == vEvent:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400464 extName = extn # TODO return here?
Steven M. Gavaa498af22002-02-01 01:33:36 +0000465 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000466
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400467 def GetExtensionKeys(self, extensionName):
468 """Return dict: {configurable extensionName event : active keybinding}.
469
470 Events come from default config extension_cfgBindings section.
471 Keybindings come from GetCurrentKeySet() active key dict,
472 where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000473 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400474 keysName = extensionName + '_cfgBindings'
475 activeKeys = self.GetCurrentKeySet()
476 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000477 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400478 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000479 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400480 event = '<<' + eventName + '>>'
481 binding = activeKeys[event]
482 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000483 return extKeys
484
Steven M. Gavac628a062002-01-19 10:33:21 +0000485 def __GetRawExtensionKeys(self,extensionName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400486 """Return dict {configurable extensionName event : keybinding list}.
487
488 Events come from default config extension_cfgBindings section.
489 Keybindings list come from the splitting of GetOption, which
490 tries user config before default config.
Steven M. Gavac628a062002-01-19 10:33:21 +0000491 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400492 keysName = extensionName+'_cfgBindings'
493 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000494 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400495 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000496 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400497 binding = self.GetOption(
498 'extensions', keysName, eventName, default='').split()
499 event = '<<' + eventName + '>>'
500 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000501 return extKeys
502
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400503 def GetExtensionBindings(self, extensionName):
504 """Return dict {extensionName event : active or defined keybinding}.
505
506 Augment self.GetExtensionKeys(extensionName) with mapping of non-
507 configurable events (from default config) to GetOption splits,
508 as in self.__GetRawExtensionKeys.
Steven M. Gavac628a062002-01-19 10:33:21 +0000509 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400510 bindsName = extensionName + '_bindings'
511 extBinds = self.GetExtensionKeys(extensionName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000512 #add the non-configurable bindings
513 if self.defaultCfg['extensions'].has_section(bindsName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400514 eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000515 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400516 binding = self.GetOption(
517 'extensions', bindsName, eventName, default='').split()
518 event = '<<' + eventName + '>>'
519 extBinds[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000520
521 return extBinds
522
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000523 def GetKeyBinding(self, keySetName, eventStr):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400524 """Return the keybinding list for keySetName eventStr.
525
526 keySetName - name of key binding set (config-keys section).
527 eventStr - virtual event, including brackets, as in '<<event>>'.
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000528 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400529 eventName = eventStr[2:-2] #trim off the angle brackets
530 binding = self.GetOption('keys', keySetName, eventName, default='').split()
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000531 return binding
532
Steven M. Gavac628a062002-01-19 10:33:21 +0000533 def GetCurrentKeySet(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400534 "Return CurrentKeys with 'darwin' modifications."
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000535 result = self.GetKeySet(self.CurrentKeys())
536
Ned Deilyb7601672014-03-27 20:49:14 -0700537 if sys.platform == "darwin":
538 # OS X Tk variants do not support the "Alt" keyboard modifier.
539 # So replace all keybingings that use "Alt" with ones that
540 # use the "Option" keyboard modifier.
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400541 # TODO (Ned?): the "Option" modifier does not work properly for
Ned Deilyb7601672014-03-27 20:49:14 -0700542 # Cocoa Tk and XQuartz Tk so we should not use it
543 # in default OS X KeySets.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000544 for k, v in result.items():
545 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
546 if v != v2:
547 result[k] = v2
548
549 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000550
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400551 def GetKeySet(self, keySetName):
552 """Return event-key dict for keySetName core plus active extensions.
553
554 If a binding defined in an extension is already in use, the
555 extension binding is disabled by being set to ''
Steven M. Gava2a63a072001-10-26 06:50:54 +0000556 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400557 keySet = self.GetCoreKeys(keySetName)
558 activeExtns = self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000559 for extn in activeExtns:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400560 extKeys = self.__GetRawExtensionKeys(extn)
Steven M. Gavac628a062002-01-19 10:33:21 +0000561 if extKeys: #the extension defines keybindings
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000562 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000563 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000564 #the binding is already in use
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400565 extKeys[event] = '' #disable this binding
566 keySet[event] = extKeys[event] #add binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000567 return keySet
568
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400569 def IsCoreBinding(self, virtualEvent):
570 """Return True if the virtual event is one of the core idle key events.
571
572 virtualEvent - string, name of the virtual event to test for,
573 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000574 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000575 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000576
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400577# TODO make keyBindins a file or class attribute used for test above
578# and copied in function below
579
Steven M. Gavac628a062002-01-19 10:33:21 +0000580 def GetCoreKeys(self, keySetName=None):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400581 """Return dict of core virtual-key keybindings for keySetName.
582
583 The default keySetName None corresponds to the keyBindings base
584 dict. If keySetName is not None, bindings from the config
585 file(s) are loaded _over_ these defaults, so if there is a
586 problem getting any core binding there will be an 'ultimate last
587 resort fallback' to the CUA-ish bindings defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000588 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000589 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000590 '<<copy>>': ['<Control-c>', '<Control-C>'],
591 '<<cut>>': ['<Control-x>', '<Control-X>'],
592 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000593 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
594 '<<center-insert>>': ['<Control-l>'],
595 '<<close-all-windows>>': ['<Control-q>'],
596 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000597 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000598 '<<end-of-file>>': ['<Control-d>'],
599 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000600 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000601 '<<history-next>>': ['<Alt-n>'],
602 '<<history-previous>>': ['<Alt-p>'],
603 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000604 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000605 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000606 '<<open-class-browser>>': ['<Alt-c>'],
607 '<<open-module>>': ['<Alt-m>'],
608 '<<open-new-window>>': ['<Control-n>'],
609 '<<open-window-from-file>>': ['<Control-o>'],
610 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000611 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000612 '<<redo>>': ['<Control-y>'],
613 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000614 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000615 '<<save-window-as-file>>': ['<Alt-s>'],
616 '<<save-window>>': ['<Control-s>'],
617 '<<select-all>>': ['<Alt-a>'],
618 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000619 '<<undo>>': ['<Control-z>'],
620 '<<find-again>>': ['<Control-g>', '<F3>'],
621 '<<find-in-files>>': ['<Alt-F3>'],
622 '<<find-selection>>': ['<Control-F3>'],
623 '<<find>>': ['<Control-f>'],
624 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000625 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000626 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlov67ac0792012-03-29 19:01:28 +0300627 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000628 '<<smart-indent>>': ['<Key-Tab>'],
629 '<<indent-region>>': ['<Control-Key-bracketright>'],
630 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
631 '<<comment-region>>': ['<Alt-Key-3>'],
632 '<<uncomment-region>>': ['<Alt-Key-4>'],
633 '<<tabify-region>>': ['<Alt-Key-5>'],
634 '<<untabify-region>>': ['<Alt-Key-6>'],
635 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000636 '<<change-indentwidth>>': ['<Alt-Key-u>'],
637 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
638 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000639 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000640 if keySetName:
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000641 for event in keyBindings:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400642 binding = self.GetKeyBinding(keySetName, event)
Steven M. Gava49745752002-02-18 01:43:11 +0000643 if binding:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400644 keyBindings[event] = binding
Steven M. Gava49745752002-02-18 01:43:11 +0000645 else: #we are going to return a default, print warning
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400646 warning=('\n Warning: config.py - IdleConf.GetCoreKeys'
Walter Dörwald70a6b492004-02-12 17:35:32 +0000647 ' -\n problem retrieving key binding for event %r'
648 '\n from key set %r.\n'
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400649 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000650 (event, keySetName, keyBindings[event]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000651 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400652 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200653 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000654 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000655 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000656
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400657 def GetExtraHelpSourceList(self, configSet):
658 """Return list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000659
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000660 Valid configSets are 'user' or 'default'. Return a list of tuples of
661 the form (menu_item , path_to_help_file , option), or return the empty
662 list. 'option' is the sequence number of the help resource. 'option'
663 values determine the position of the menu items on the Help menu,
664 therefore the returned list must be sorted by 'option'.
665
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000666 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400667 helpSources = []
668 if configSet == 'user':
669 cfgParser = self.userCfg['main']
670 elif configSet == 'default':
671 cfgParser = self.defaultCfg['main']
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000672 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000673 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000674 options=cfgParser.GetOptionList('HelpFiles')
675 for option in options:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400676 value=cfgParser.Get('HelpFiles', option, default=';')
677 if value.find(';') == -1: #malformed config entry with no ';'
678 menuItem = '' #make these empty
679 helpPath = '' #so value won't be added to list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000680 else: #config entry contains ';' as expected
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000681 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000682 menuItem=value[0].strip()
683 helpPath=value[1].strip()
684 if menuItem and helpPath: #neither are empty strings
685 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser4718bf82008-02-12 21:34:12 +0000686 helpSources.sort(key=lambda x: x[2])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000687 return helpSources
688
689 def GetAllExtraHelpSourcesList(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400690 """Return a list of the details of all additional help sources.
691
692 Tuples in the list are those of GetExtraHelpSourceList.
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000693 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400694 allHelpSources = (self.GetExtraHelpSourceList('default') +
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000695 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000696 return allHelpSources
697
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400698 def GetFont(self, root, configType, section):
699 """Retrieve a font from configuration (font, font-size, font-bold)
700 Intercept the special value 'TkFixedFont' and substitute
701 the actual font, factoring in some tweaks if needed for
702 appearance sakes.
703
704 The 'root' parameter can normally be any valid Tkinter widget.
705
706 Return a tuple (family, size, weight) suitable for passing
707 to tkinter.Font
708 """
709 family = self.GetOption(configType, section, 'font', default='courier')
710 size = self.GetOption(configType, section, 'font-size', type='int',
711 default='10')
712 bold = self.GetOption(configType, section, 'font-bold', default=0,
713 type='bool')
714 if (family == 'TkFixedFont'):
Terry Jan Reedy1080d132016-06-09 21:09:15 -0400715 f = Font(name='TkFixedFont', exists=True, root=root)
716 actualFont = Font.actual(f)
717 family = actualFont['family']
718 size = actualFont['size']
719 if size <= 0:
720 size = 10 # if font in pixels, ignore actual size
721 bold = actualFont['weight'] == 'bold'
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400722 return (family, size, 'bold' if bold else 'normal')
723
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000724 def LoadCfgFiles(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400725 "Load all configuration files."
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000726 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000727 self.defaultCfg[key].Load()
728 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000729
730 def SaveUserCfgFiles(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400731 "Write all loaded user configuration files to disk."
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000732 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000733 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000734
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000735
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400736idleConf = IdleConf()
737
738# TODO Revise test output, write expanded unittest
Terry Jan Reedy1080d132016-06-09 21:09:15 -0400739#
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000740if __name__ == '__main__':
741 def dumpCfg(cfg):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400742 print('\n', cfg, '\n')
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000743 for key in cfg:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400744 sections = cfg[key].sections()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000745 print(key)
746 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000747 for section in sections:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400748 options = cfg[key].options(section)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000749 print(section)
750 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000751 for option in options:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400752 print(option, '=', cfg[key].Get(section, option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000753 dumpCfg(idleConf.defaultCfg)
754 dumpCfg(idleConf.userCfg)
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400755 print(idleConf.userCfg['main'].Get('Theme', 'name'))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000756 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')