blob: 8ac1f6097a84957d48c8e33da700abe7c53b6bd1 [file] [log] [blame]
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001"""Provides access to stored IDLE configuration information.
Steven M. Gavac5976402002-01-04 03:06:08 +00002
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00003Refer to the comments at the beginning of config-main.def for a description of
4the available configuration files and the design implemented to update user
5configuration information. In particular, user configuration choices which
6duplicate the defaults will be removed from the user's configuration files,
Kurt B. Kaisere66675b2003-01-27 02:36:18 +00007and if a file becomes empty, it will be deleted.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00008
9The contents of the user files may be altered using the Options/Configure IDLE
10menu to access the configuration GUI (configDialog.py), or manually.
11
12Throughout this module there is an emphasis on returning useable defaults
13when a problem occurs in returning a requested configuration value back to
14idle. This is to allow IDLE to continue to function in spite of errors in
15the retrieval of config information. When a default is returned instead of
16a requested config value, a message is printed to stderr to aid in
17configuration problem notification and resolution.
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +000018"""
Terry Jan 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 Reedy4fda56f2015-09-04 04:37:56 -040025from tkinter import TkVersion
Terry Jan Reedyd87d1682015-08-01 18:57:33 -040026from tkinter.font import Font, nametofont
Steven M. Gavac11ccf32001-09-24 09:43:17 +000027
Neal Norwitz5b0b00f2002-11-30 19:10:19 +000028class InvalidConfigType(Exception): pass
29class InvalidConfigSet(Exception): pass
30class InvalidFgBg(Exception): pass
31class InvalidTheme(Exception): pass
32
Steven M. Gavac11ccf32001-09-24 09:43:17 +000033class IdleConfParser(ConfigParser):
34 """
35 A ConfigParser specialised for idle configuration file handling
36 """
37 def __init__(self, cfgFile, cfgDefaults=None):
38 """
39 cfgFile - string, fully specified configuration file name
40 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040041 self.file = cfgFile
Serhiy Storchaka89953002013-02-07 15:24:36 +020042 ConfigParser.__init__(self, defaults=cfgDefaults, strict=False)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000043
Thomas Wouterscf297e42007-02-23 15:07:44 +000044 def Get(self, section, option, type=None, default=None, raw=False):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000045 """
46 Get an option value for given section/option or return default.
47 If type is specified, return as type.
48 """
Terry Jan Reedya9421fb2014-10-22 20:15:18 -040049 # TODO Use default as fallback, at least if not None
50 # Should also print Warning(file, section, option).
51 # Currently may raise ValueError
Thomas Wouterscf297e42007-02-23 15:07:44 +000052 if not self.has_option(section, option):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +000053 return default
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040054 if type == 'bool':
Thomas Wouterscf297e42007-02-23 15:07:44 +000055 return self.getboolean(section, option)
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040056 elif type == 'int':
Thomas Wouterscf297e42007-02-23 15:07:44 +000057 return self.getint(section, option)
58 else:
59 return self.get(section, option, raw=raw)
Steven M. Gavac11ccf32001-09-24 09:43:17 +000060
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040061 def GetOptionList(self, section):
62 "Return a list of options for given section, else []."
Steven M. Gava085eb1b2002-02-05 04:52:32 +000063 if self.has_section(section):
Steven M. Gavac11ccf32001-09-24 09:43:17 +000064 return self.options(section)
65 else: #return a default value
66 return []
67
Steven M. Gavac11ccf32001-09-24 09:43:17 +000068 def Load(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040069 "Load the configuration file from disk."
Steven M. Gavac11ccf32001-09-24 09:43:17 +000070 self.read(self.file)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000071
Steven M. Gavac11ccf32001-09-24 09:43:17 +000072class IdleUserConfParser(IdleConfParser):
73 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000074 IdleConfigParser specialised for user configuration handling.
Steven M. Gavac11ccf32001-09-24 09:43:17 +000075 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000076
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040077 def AddSection(self, section):
78 "If section doesn't exist, add it."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000079 if not self.has_section(section):
80 self.add_section(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000081
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000082 def RemoveEmptySections(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040083 "Remove any sections that have no options."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000084 for section in self.sections():
85 if not self.GetOptionList(section):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000086 self.remove_section(section)
87
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000088 def IsEmpty(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040089 "Return True if no sections after removing empty sections."
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000090 self.RemoveEmptySections()
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040091 return not self.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000092
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040093 def RemoveOption(self, section, option):
94 """Return True if option is removed from section, else False.
95
96 False if either section does not exist or did not have option.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +000097 """
98 if self.has_section(section):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -040099 return self.remove_option(section, option)
100 return False
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000101
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400102 def SetOption(self, section, option, value):
103 """Return True if option is added or changed to value, else False.
104
105 Add section if required. False means option already had value.
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000106 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400107 if self.has_option(section, option):
108 if self.get(section, option) == value:
109 return False
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000110 else:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400111 self.set(section, option, value)
112 return True
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000113 else:
114 if not self.has_section(section):
115 self.add_section(section)
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400116 self.set(section, option, value)
117 return True
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000118
Steven M. Gavab77d3432002-03-02 07:16:21 +0000119 def RemoveFile(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400120 "Remove user config file self.file from disk if it exists."
Steven M. Gavab77d3432002-03-02 07:16:21 +0000121 if os.path.exists(self.file):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000122 os.remove(self.file)
123
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000124 def Save(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000125 """Update user configuration file.
126
127 Remove empty sections. If resulting config isn't empty, write the file
128 to disk. If config is empty, remove the file from disk if it exists.
129
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000130 """
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000131 if not self.IsEmpty():
Christian Heimesbbffeb62008-01-24 09:42:52 +0000132 fname = self.file
133 try:
134 cfgFile = open(fname, 'w')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200135 except OSError:
Christian Heimes0bd4e112008-02-12 22:59:25 +0000136 os.unlink(fname)
Christian Heimesbbffeb62008-01-24 09:42:52 +0000137 cfgFile = open(fname, 'w')
Amaury Forgeot d'Arcbbe7b0a2011-10-03 20:33:24 +0200138 with cfgFile:
139 self.write(cfgFile)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +0000140 else:
Steven M. Gavab77d3432002-03-02 07:16:21 +0000141 self.RemoveFile()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000142
143class IdleConf:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400144 """Hold config parsers for all idle config files in singleton instance.
145
146 Default config files, self.defaultCfg --
147 for config_type in self.config_types:
148 (idle install dir)/config-{config-type}.def
149
150 User config files, self.userCfg --
151 for config_type in self.config_types:
152 (user home dir)/.idlerc/config-{config-type}.cfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000153 """
154 def __init__(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400155 self.config_types = ('main', 'extensions', 'highlight', 'keys')
156 self.defaultCfg = {}
157 self.userCfg = {}
158 self.cfg = {} # TODO use to select userCfg vs defaultCfg
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000159 self.CreateConfigHandlers()
160 self.LoadCfgFiles()
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400161
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000162
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000163 def CreateConfigHandlers(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400164 "Populate default and user config parser dictionaries."
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000165 #build idle install path
166 if __name__ != '__main__': # we were imported
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000167 idleDir=os.path.dirname(__file__)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000168 else: # we were exec'ed (for testing only)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000169 idleDir=os.path.abspath(sys.path[0])
170 userDir=self.GetUserCfgDir()
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400171
172 defCfgFiles = {}
173 usrCfgFiles = {}
174 # TODO eliminate these temporaries by combining loops
175 for cfgType in self.config_types: #build config file names
176 defCfgFiles[cfgType] = os.path.join(
177 idleDir, 'config-' + cfgType + '.def')
178 usrCfgFiles[cfgType] = os.path.join(
179 userDir, 'config-' + cfgType + '.cfg')
180 for cfgType in self.config_types: #create config parsers
181 self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType])
182 self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000183
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000184 def GetUserCfgDir(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400185 """Return a filesystem directory for storing user config files.
Tim Peters608c2ff2005-01-13 17:37:38 +0000186
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400187 Creates it if required.
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000188 """
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000189 cfgDir = '.idlerc'
190 userDir = os.path.expanduser('~')
191 if userDir != '~': # expanduser() found user home dir
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000192 if not os.path.exists(userDir):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400193 warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
194 userDir + ',\n but the path does not exist.')
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000195 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400196 print(warn, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200197 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000198 pass
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000199 userDir = '~'
200 if userDir == "~": # still no path to home!
201 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
202 userDir = os.getcwd()
203 userDir = os.path.join(userDir, cfgDir)
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000204 if not os.path.exists(userDir):
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000205 try:
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000206 os.mkdir(userDir)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200207 except OSError:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400208 warn = ('\n Warning: unable to create user config directory\n' +
209 userDir + '\n Check path and permissions.\n Exiting!\n')
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400210 print(warn, file=sys.stderr)
Kurt B. Kaiser1b6f3982005-01-11 19:29:39 +0000211 raise SystemExit
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400212 # TODO continue without userDIr instead of exit
Steven M. Gava7cff66d2002-02-01 03:02:37 +0000213 return userDir
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000214
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000215 def GetOption(self, configType, section, option, default=None, type=None,
Thomas Wouterscf297e42007-02-23 15:07:44 +0000216 warn_on_default=True, raw=False):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400217 """Return a value for configType section option, or default.
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000218
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400219 If type is not None, return a value of that type. Also pass raw
220 to the config parser. First try to return a valid value
221 (including type) from a user configuration. If that fails, try
222 the default configuration. If that fails, return default, with a
223 default of None.
224
225 Warn if either user or default configurations have an invalid value.
226 Warn if default is returned and warn_on_default is True.
Steven M. Gava429a86af2001-10-23 10:42:12 +0000227 """
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200228 try:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400229 if self.userCfg[configType].has_option(section, option):
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200230 return self.userCfg[configType].Get(section, option,
231 type=type, raw=raw)
232 except ValueError:
233 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
234 ' invalid %r value for configuration option %r\n'
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400235 ' from section %r: %r' %
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200236 (type, option, section,
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400237 self.userCfg[configType].Get(section, option, raw=raw)))
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200238 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400239 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200240 except OSError:
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200241 pass
242 try:
243 if self.defaultCfg[configType].has_option(section,option):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400244 return self.defaultCfg[configType].Get(
245 section, option, type=type, raw=raw)
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200246 except ValueError:
247 pass
248 #returning default, print warning
249 if warn_on_default:
250 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
251 ' problem retrieving configuration option %r\n'
252 ' from section %r.\n'
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400253 ' returning default value: %r' %
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200254 (option, section, default))
255 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400256 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200257 except OSError:
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200258 pass
259 return default
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000260
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000261 def SetOption(self, configType, section, option, value):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400262 """Set section option to value in user config file."""
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000263 self.userCfg[configType].SetOption(section, option, value)
264
Steven M. Gava2a63a072001-10-26 06:50:54 +0000265 def GetSectionList(self, configSet, configType):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400266 """Return sections for configSet configType configuration.
267
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000268 configSet must be either 'user' or 'default'
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400269 configType must be in self.config_types.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000270 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400271 if not (configType in self.config_types):
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000272 raise InvalidConfigType('Invalid configType specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000273 if configSet == 'user':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400274 cfgParser = self.userCfg[configType]
Steven M. Gava2a63a072001-10-26 06:50:54 +0000275 elif configSet == 'default':
276 cfgParser=self.defaultCfg[configType]
277 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000278 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava2a63a072001-10-26 06:50:54 +0000279 return cfgParser.sections()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000280
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000281 def GetHighlight(self, theme, element, fgBg=None):
Terry Jan Reedy86757992014-10-09 18:44:32 -0400282 """Return individual theme element highlight color(s).
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400283
Terry Jan Reedy86757992014-10-09 18:44:32 -0400284 fgBg - string ('fg' or 'bg') or None.
285 If None, return a dictionary containing fg and bg colors with
286 keys 'foreground' and 'background'. Otherwise, only return
287 fg or bg color, as specified. Colors are intended to be
288 appropriate for passing to Tkinter in, e.g., a tag_config call).
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000289 """
Steven M. Gava9f25e672002-02-11 02:51:18 +0000290 if self.defaultCfg['highlight'].has_section(theme):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400291 themeDict = self.GetThemeDict('default', theme)
Steven M. Gava9f25e672002-02-11 02:51:18 +0000292 else:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400293 themeDict = self.GetThemeDict('user', theme)
294 fore = themeDict[element + '-foreground']
Terry Jan Reedy86757992014-10-09 18:44:32 -0400295 if element == 'cursor': # There is no config value for cursor bg
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400296 back = themeDict['normal-background']
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000297 else:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400298 back = themeDict[element + '-background']
299 highlight = {"foreground": fore, "background": back}
Terry Jan Reedy86757992014-10-09 18:44:32 -0400300 if not fgBg: # Return dict of both colors
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000301 return highlight
Terry Jan Reedy86757992014-10-09 18:44:32 -0400302 else: # Return specified color only
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000303 if fgBg == 'fg':
304 return highlight["foreground"]
305 if fgBg == 'bg':
306 return highlight["background"]
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000307 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000308 raise InvalidFgBg('Invalid fgBg specified')
Steven M. Gava9f25e672002-02-11 02:51:18 +0000309
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400310 def GetThemeDict(self, type, themeName):
311 """Return {option:value} dict for elements in themeName.
312
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000313 type - string, 'default' or 'user' theme type
314 themeName - string, theme name
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400315 Values are loaded over ultimate fallback defaults to guarantee
316 that all theme elements are present in a newly created theme.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000317 """
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000318 if type == 'user':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400319 cfgParser = self.userCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000320 elif type == 'default':
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400321 cfgParser = self.defaultCfg['highlight']
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000322 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000323 raise InvalidTheme('Invalid theme type specified')
Terry Jan Reedy86757992014-10-09 18:44:32 -0400324 # Provide foreground and background colors for each theme
325 # element (other than cursor) even though some values are not
326 # yet used by idle, to allow for their use in the future.
327 # Default values are generally black and white.
328 # TODO copy theme from a class attribute.
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400329 theme ={'normal-foreground':'#000000',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000330 'normal-background':'#ffffff',
331 'keyword-foreground':'#000000',
332 'keyword-background':'#ffffff',
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000333 'builtin-foreground':'#000000',
334 'builtin-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000335 'comment-foreground':'#000000',
336 'comment-background':'#ffffff',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000337 'string-foreground':'#000000',
338 'string-background':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000339 'definition-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000340 'definition-background':'#ffffff',
341 'hilite-foreground':'#000000',
342 'hilite-background':'gray',
343 'break-foreground':'#ffffff',
344 'break-background':'#000000',
345 'hit-foreground':'#ffffff',
346 'hit-background':'#000000',
347 'error-foreground':'#ffffff',
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000348 'error-background':'#000000',
349 #cursor (only foreground can be set)
350 'cursor-foreground':'#000000',
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000351 #shell window
352 'stdout-foreground':'#000000',
353 'stdout-background':'#ffffff',
354 'stderr-foreground':'#000000',
355 'stderr-background':'#ffffff',
356 'console-foreground':'#000000',
357 'console-background':'#ffffff' }
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000358 for element in theme:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400359 if not cfgParser.has_option(themeName, element):
Terry Jan Reedy86757992014-10-09 18:44:32 -0400360 # Print warning that will return a default color
361 warning = ('\n Warning: configHandler.IdleConf.GetThemeDict'
Walter Dörwald70a6b492004-02-12 17:35:32 +0000362 ' -\n problem retrieving theme element %r'
363 '\n from theme %r.\n'
Terry Jan Reedy86757992014-10-09 18:44:32 -0400364 ' returning default color: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000365 (element, themeName, theme[element]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000366 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400367 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200368 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000369 pass
Terry Jan Reedy86757992014-10-09 18:44:32 -0400370 theme[element] = cfgParser.Get(
371 themeName, element, default=theme[element])
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000372 return theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000373
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000374 def CurrentTheme(self):
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500375 """Return the name of the currently active text color theme.
376
377 idlelib.config-main.def includes this section
378 [Theme]
379 default= 1
380 name= IDLE Classic
381 name2=
382 # name2 set in user config-main.cfg for themes added after 2015 Oct 1
383
384 Item name2 is needed because setting name to a new builtin
385 causes older IDLEs to display multiple error messages or quit.
386 See https://bugs.python.org/issue25313.
387 When default = True, name2 takes precedence over name,
388 while older IDLEs will just use name.
389 """
390 default = self.GetOption('main', 'Theme', 'default',
391 type='bool', default=True)
392 if default:
393 theme = self.GetOption('main', 'Theme', 'name2', default='')
394 if default and not theme or not default:
395 theme = self.GetOption('main', 'Theme', 'name', default='')
396 source = self.defaultCfg if default else self.userCfg
397 if source['highlight'].has_section(theme):
Terry Jan Reedy5496ba22015-11-12 15:24:33 -0500398 return theme
Terry Jan Reedyc15a7c62015-11-12 15:06:07 -0500399 else:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500400 return "IDLE Classic"
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000401
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000402 def CurrentKeys(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400403 "Return the name of the currently active key set."
404 return self.GetOption('main', 'Keys', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000405
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000406 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400407 """Return extensions in default and user config-extensions files.
408
409 If active_only True, only return active (enabled) extensions
410 and optionally only editor or shell extensions.
411 If active_only False, return all extensions.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000412 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400413 extns = self.RemoveKeyBindNames(
414 self.GetSectionList('default', 'extensions'))
415 userExtns = self.RemoveKeyBindNames(
416 self.GetSectionList('user', 'extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000417 for extn in userExtns:
418 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000419 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000420 if active_only:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400421 activeExtns = []
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000422 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000423 if self.GetOption('extensions', extn, 'enable', default=True,
424 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000425 #the extension is enabled
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400426 if editor_only or shell_only: # TODO if both, contradictory
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000427 if editor_only:
428 option = "enable_editor"
429 else:
430 option = "enable_shell"
431 if self.GetOption('extensions', extn,option,
432 default=True, type='bool',
433 warn_on_default=False):
434 activeExtns.append(extn)
435 else:
436 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000437 return activeExtns
438 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000439 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000440
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400441 def RemoveKeyBindNames(self, extnNameList):
442 "Return extnNameList with keybinding section names removed."
443 # TODO Easier to return filtered copy with list comp
444 names = extnNameList
445 kbNameIndicies = []
Steven M. Gavac628a062002-01-19 10:33:21 +0000446 for name in names:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000447 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000448 kbNameIndicies.append(names.index(name))
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400449 kbNameIndicies.sort(reverse=True)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000450 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000451 del(names[index])
452 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000453
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400454 def GetExtnNameForEvent(self, virtualEvent):
455 """Return the name of the extension binding virtualEvent, or None.
456
457 virtualEvent - string, name of the virtual event to test for,
458 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000459 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400460 extName = None
461 vEvent = '<<' + virtualEvent + '>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000462 for extn in self.GetExtensions(active_only=0):
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000463 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000464 if event == vEvent:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400465 extName = extn # TODO return here?
Steven M. Gavaa498af22002-02-01 01:33:36 +0000466 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000467
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400468 def GetExtensionKeys(self, extensionName):
469 """Return dict: {configurable extensionName event : active keybinding}.
470
471 Events come from default config extension_cfgBindings section.
472 Keybindings come from GetCurrentKeySet() active key dict,
473 where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000474 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400475 keysName = extensionName + '_cfgBindings'
476 activeKeys = self.GetCurrentKeySet()
477 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000478 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400479 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000480 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400481 event = '<<' + eventName + '>>'
482 binding = activeKeys[event]
483 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000484 return extKeys
485
Steven M. Gavac628a062002-01-19 10:33:21 +0000486 def __GetRawExtensionKeys(self,extensionName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400487 """Return dict {configurable extensionName event : keybinding list}.
488
489 Events come from default config extension_cfgBindings section.
490 Keybindings list come from the splitting of GetOption, which
491 tries user config before default config.
Steven M. Gavac628a062002-01-19 10:33:21 +0000492 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400493 keysName = extensionName+'_cfgBindings'
494 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000495 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400496 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000497 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400498 binding = self.GetOption(
499 'extensions', keysName, eventName, default='').split()
500 event = '<<' + eventName + '>>'
501 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000502 return extKeys
503
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400504 def GetExtensionBindings(self, extensionName):
505 """Return dict {extensionName event : active or defined keybinding}.
506
507 Augment self.GetExtensionKeys(extensionName) with mapping of non-
508 configurable events (from default config) to GetOption splits,
509 as in self.__GetRawExtensionKeys.
Steven M. Gavac628a062002-01-19 10:33:21 +0000510 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400511 bindsName = extensionName + '_bindings'
512 extBinds = self.GetExtensionKeys(extensionName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000513 #add the non-configurable bindings
514 if self.defaultCfg['extensions'].has_section(bindsName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400515 eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000516 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400517 binding = self.GetOption(
518 'extensions', bindsName, eventName, default='').split()
519 event = '<<' + eventName + '>>'
520 extBinds[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000521
522 return extBinds
523
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000524 def GetKeyBinding(self, keySetName, eventStr):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400525 """Return the keybinding list for keySetName eventStr.
526
527 keySetName - name of key binding set (config-keys section).
528 eventStr - virtual event, including brackets, as in '<<event>>'.
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000529 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400530 eventName = eventStr[2:-2] #trim off the angle brackets
531 binding = self.GetOption('keys', keySetName, eventName, default='').split()
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000532 return binding
533
Steven M. Gavac628a062002-01-19 10:33:21 +0000534 def GetCurrentKeySet(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400535 "Return CurrentKeys with 'darwin' modifications."
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000536 result = self.GetKeySet(self.CurrentKeys())
537
Ned Deilyb7601672014-03-27 20:49:14 -0700538 if sys.platform == "darwin":
539 # OS X Tk variants do not support the "Alt" keyboard modifier.
540 # So replace all keybingings that use "Alt" with ones that
541 # use the "Option" keyboard modifier.
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400542 # TODO (Ned?): the "Option" modifier does not work properly for
Ned Deilyb7601672014-03-27 20:49:14 -0700543 # Cocoa Tk and XQuartz Tk so we should not use it
544 # in default OS X KeySets.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000545 for k, v in result.items():
546 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
547 if v != v2:
548 result[k] = v2
549
550 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000551
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400552 def GetKeySet(self, keySetName):
553 """Return event-key dict for keySetName core plus active extensions.
554
555 If a binding defined in an extension is already in use, the
556 extension binding is disabled by being set to ''
Steven M. Gava2a63a072001-10-26 06:50:54 +0000557 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400558 keySet = self.GetCoreKeys(keySetName)
559 activeExtns = self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000560 for extn in activeExtns:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400561 extKeys = self.__GetRawExtensionKeys(extn)
Steven M. Gavac628a062002-01-19 10:33:21 +0000562 if extKeys: #the extension defines keybindings
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000563 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000564 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000565 #the binding is already in use
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400566 extKeys[event] = '' #disable this binding
567 keySet[event] = extKeys[event] #add binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000568 return keySet
569
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400570 def IsCoreBinding(self, virtualEvent):
571 """Return True if the virtual event is one of the core idle key events.
572
573 virtualEvent - string, name of the virtual event to test for,
574 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000575 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000576 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000577
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400578# TODO make keyBindins a file or class attribute used for test above
579# and copied in function below
580
Steven M. Gavac628a062002-01-19 10:33:21 +0000581 def GetCoreKeys(self, keySetName=None):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400582 """Return dict of core virtual-key keybindings for keySetName.
583
584 The default keySetName None corresponds to the keyBindings base
585 dict. If keySetName is not None, bindings from the config
586 file(s) are loaded _over_ these defaults, so if there is a
587 problem getting any core binding there will be an 'ultimate last
588 resort fallback' to the CUA-ish bindings defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000589 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000590 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000591 '<<copy>>': ['<Control-c>', '<Control-C>'],
592 '<<cut>>': ['<Control-x>', '<Control-X>'],
593 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000594 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
595 '<<center-insert>>': ['<Control-l>'],
596 '<<close-all-windows>>': ['<Control-q>'],
597 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000598 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000599 '<<end-of-file>>': ['<Control-d>'],
600 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000601 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000602 '<<history-next>>': ['<Alt-n>'],
603 '<<history-previous>>': ['<Alt-p>'],
604 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000605 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000606 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000607 '<<open-class-browser>>': ['<Alt-c>'],
608 '<<open-module>>': ['<Alt-m>'],
609 '<<open-new-window>>': ['<Control-n>'],
610 '<<open-window-from-file>>': ['<Control-o>'],
611 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000612 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000613 '<<redo>>': ['<Control-y>'],
614 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000615 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000616 '<<save-window-as-file>>': ['<Alt-s>'],
617 '<<save-window>>': ['<Control-s>'],
618 '<<select-all>>': ['<Alt-a>'],
619 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000620 '<<undo>>': ['<Control-z>'],
621 '<<find-again>>': ['<Control-g>', '<F3>'],
622 '<<find-in-files>>': ['<Alt-F3>'],
623 '<<find-selection>>': ['<Control-F3>'],
624 '<<find>>': ['<Control-f>'],
625 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000626 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000627 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlov67ac0792012-03-29 19:01:28 +0300628 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000629 '<<smart-indent>>': ['<Key-Tab>'],
630 '<<indent-region>>': ['<Control-Key-bracketright>'],
631 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
632 '<<comment-region>>': ['<Alt-Key-3>'],
633 '<<uncomment-region>>': ['<Alt-Key-4>'],
634 '<<tabify-region>>': ['<Alt-Key-5>'],
635 '<<untabify-region>>': ['<Alt-Key-6>'],
636 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000637 '<<change-indentwidth>>': ['<Alt-Key-u>'],
638 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
639 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000640 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000641 if keySetName:
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000642 for event in keyBindings:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400643 binding = self.GetKeyBinding(keySetName, event)
Steven M. Gava49745752002-02-18 01:43:11 +0000644 if binding:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400645 keyBindings[event] = binding
Steven M. Gava49745752002-02-18 01:43:11 +0000646 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000647 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
648 ' -\n problem retrieving key binding for event %r'
649 '\n from key set %r.\n'
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400650 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000651 (event, keySetName, keyBindings[event]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000652 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400653 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200654 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000655 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000656 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000657
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400658 def GetExtraHelpSourceList(self, configSet):
659 """Return list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000660
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000661 Valid configSets are 'user' or 'default'. Return a list of tuples of
662 the form (menu_item , path_to_help_file , option), or return the empty
663 list. 'option' is the sequence number of the help resource. 'option'
664 values determine the position of the menu items on the Help menu,
665 therefore the returned list must be sorted by 'option'.
666
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000667 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400668 helpSources = []
669 if configSet == 'user':
670 cfgParser = self.userCfg['main']
671 elif configSet == 'default':
672 cfgParser = self.defaultCfg['main']
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000673 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000674 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000675 options=cfgParser.GetOptionList('HelpFiles')
676 for option in options:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400677 value=cfgParser.Get('HelpFiles', option, default=';')
678 if value.find(';') == -1: #malformed config entry with no ';'
679 menuItem = '' #make these empty
680 helpPath = '' #so value won't be added to list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000681 else: #config entry contains ';' as expected
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000682 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000683 menuItem=value[0].strip()
684 helpPath=value[1].strip()
685 if menuItem and helpPath: #neither are empty strings
686 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser4718bf82008-02-12 21:34:12 +0000687 helpSources.sort(key=lambda x: x[2])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000688 return helpSources
689
690 def GetAllExtraHelpSourcesList(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400691 """Return a list of the details of all additional help sources.
692
693 Tuples in the list are those of GetExtraHelpSourceList.
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000694 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400695 allHelpSources = (self.GetExtraHelpSourceList('default') +
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000696 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000697 return allHelpSources
698
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400699 def GetFont(self, root, configType, section):
700 """Retrieve a font from configuration (font, font-size, font-bold)
701 Intercept the special value 'TkFixedFont' and substitute
702 the actual font, factoring in some tweaks if needed for
703 appearance sakes.
704
705 The 'root' parameter can normally be any valid Tkinter widget.
706
707 Return a tuple (family, size, weight) suitable for passing
708 to tkinter.Font
709 """
710 family = self.GetOption(configType, section, 'font', default='courier')
711 size = self.GetOption(configType, section, 'font-size', type='int',
712 default='10')
713 bold = self.GetOption(configType, section, 'font-bold', default=0,
714 type='bool')
715 if (family == 'TkFixedFont'):
Terry Jan Reedy4fda56f2015-09-04 04:37:56 -0400716 if TkVersion < 8.5:
717 family = 'Courier'
718 else:
719 f = Font(name='TkFixedFont', exists=True, root=root)
720 actualFont = Font.actual(f)
721 family = actualFont['family']
722 size = actualFont['size']
Terry Jan Reedy5917f972016-05-22 14:35:24 -0400723 if size <= 0:
Terry Jan Reedy4fda56f2015-09-04 04:37:56 -0400724 size = 10 # if font in pixels, ignore actual size
725 bold = actualFont['weight']=='bold'
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400726 return (family, size, 'bold' if bold else 'normal')
727
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000728 def LoadCfgFiles(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400729 "Load all configuration files."
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000730 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000731 self.defaultCfg[key].Load()
732 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000733
734 def SaveUserCfgFiles(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400735 "Write all loaded user configuration files to disk."
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000736 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000737 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000738
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000739
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400740idleConf = IdleConf()
741
742# TODO Revise test output, write expanded unittest
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000743### module test
744if __name__ == '__main__':
745 def dumpCfg(cfg):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400746 print('\n', cfg, '\n')
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000747 for key in cfg:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400748 sections = cfg[key].sections()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000749 print(key)
750 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000751 for section in sections:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400752 options = cfg[key].options(section)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000753 print(section)
754 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000755 for option in options:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400756 print(option, '=', cfg[key].Get(section, option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000757 dumpCfg(idleConf.defaultCfg)
758 dumpCfg(idleConf.userCfg)
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400759 print(idleConf.userCfg['main'].Get('Theme', 'name'))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000760 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')