blob: db3bcbc4387f4d8ba106b2f759ebeea52c2f4421 [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 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:
232 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
233 ' 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:
249 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
250 ' 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
360 warning = ('\n Warning: configHandler.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 Reedydeb7bf12014-10-06 23:26:26 -0400374 "Return the name of the currently active theme."
375 return self.GetOption('main', 'Theme', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000376
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000377 def CurrentKeys(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400378 "Return the name of the currently active key set."
379 return self.GetOption('main', 'Keys', 'name', default='')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000380
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000381 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400382 """Return extensions in default and user config-extensions files.
383
384 If active_only True, only return active (enabled) extensions
385 and optionally only editor or shell extensions.
386 If active_only False, return all extensions.
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000387 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400388 extns = self.RemoveKeyBindNames(
389 self.GetSectionList('default', 'extensions'))
390 userExtns = self.RemoveKeyBindNames(
391 self.GetSectionList('user', 'extensions'))
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000392 for extn in userExtns:
393 if extn not in extns: #user has added own extension
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000394 extns.append(extn)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000395 if active_only:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400396 activeExtns = []
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000397 for extn in extns:
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000398 if self.GetOption('extensions', extn, 'enable', default=True,
399 type='bool'):
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000400 #the extension is enabled
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400401 if editor_only or shell_only: # TODO if both, contradictory
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000402 if editor_only:
403 option = "enable_editor"
404 else:
405 option = "enable_shell"
406 if self.GetOption('extensions', extn,option,
407 default=True, type='bool',
408 warn_on_default=False):
409 activeExtns.append(extn)
410 else:
411 activeExtns.append(extn)
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000412 return activeExtns
413 else:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000414 return extns
Steven M. Gavaad4f5322002-01-03 12:05:17 +0000415
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400416 def RemoveKeyBindNames(self, extnNameList):
417 "Return extnNameList with keybinding section names removed."
418 # TODO Easier to return filtered copy with list comp
419 names = extnNameList
420 kbNameIndicies = []
Steven M. Gavac628a062002-01-19 10:33:21 +0000421 for name in names:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000422 if name.endswith(('_bindings', '_cfgBindings')):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000423 kbNameIndicies.append(names.index(name))
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400424 kbNameIndicies.sort(reverse=True)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000425 for index in kbNameIndicies: #delete each keybinding section name
Steven M. Gavac628a062002-01-19 10:33:21 +0000426 del(names[index])
427 return names
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000428
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400429 def GetExtnNameForEvent(self, virtualEvent):
430 """Return the name of the extension binding virtualEvent, or None.
431
432 virtualEvent - string, name of the virtual event to test for,
433 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000434 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400435 extName = None
436 vEvent = '<<' + virtualEvent + '>>'
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +0000437 for extn in self.GetExtensions(active_only=0):
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000438 for event in self.GetExtensionKeys(extn):
Steven M. Gavaa498af22002-02-01 01:33:36 +0000439 if event == vEvent:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400440 extName = extn # TODO return here?
Steven M. Gavaa498af22002-02-01 01:33:36 +0000441 return extName
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000442
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400443 def GetExtensionKeys(self, extensionName):
444 """Return dict: {configurable extensionName event : active keybinding}.
445
446 Events come from default config extension_cfgBindings section.
447 Keybindings come from GetCurrentKeySet() active key dict,
448 where previously used bindings are disabled.
Steven M. Gavac628a062002-01-19 10:33:21 +0000449 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400450 keysName = extensionName + '_cfgBindings'
451 activeKeys = self.GetCurrentKeySet()
452 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000453 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400454 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000455 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400456 event = '<<' + eventName + '>>'
457 binding = activeKeys[event]
458 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000459 return extKeys
460
Steven M. Gavac628a062002-01-19 10:33:21 +0000461 def __GetRawExtensionKeys(self,extensionName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400462 """Return dict {configurable extensionName event : keybinding list}.
463
464 Events come from default config extension_cfgBindings section.
465 Keybindings list come from the splitting of GetOption, which
466 tries user config before default config.
Steven M. Gavac628a062002-01-19 10:33:21 +0000467 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400468 keysName = extensionName+'_cfgBindings'
469 extKeys = {}
Steven M. Gavac628a062002-01-19 10:33:21 +0000470 if self.defaultCfg['extensions'].has_section(keysName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400471 eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000472 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400473 binding = self.GetOption(
474 'extensions', keysName, eventName, default='').split()
475 event = '<<' + eventName + '>>'
476 extKeys[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000477 return extKeys
478
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400479 def GetExtensionBindings(self, extensionName):
480 """Return dict {extensionName event : active or defined keybinding}.
481
482 Augment self.GetExtensionKeys(extensionName) with mapping of non-
483 configurable events (from default config) to GetOption splits,
484 as in self.__GetRawExtensionKeys.
Steven M. Gavac628a062002-01-19 10:33:21 +0000485 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400486 bindsName = extensionName + '_bindings'
487 extBinds = self.GetExtensionKeys(extensionName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000488 #add the non-configurable bindings
489 if self.defaultCfg['extensions'].has_section(bindsName):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400490 eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
Steven M. Gavac628a062002-01-19 10:33:21 +0000491 for eventName in eventNames:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400492 binding = self.GetOption(
493 'extensions', bindsName, eventName, default='').split()
494 event = '<<' + eventName + '>>'
495 extBinds[event] = binding
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000496
497 return extBinds
498
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000499 def GetKeyBinding(self, keySetName, eventStr):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400500 """Return the keybinding list for keySetName eventStr.
501
502 keySetName - name of key binding set (config-keys section).
503 eventStr - virtual event, including brackets, as in '<<event>>'.
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000504 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400505 eventName = eventStr[2:-2] #trim off the angle brackets
506 binding = self.GetOption('keys', keySetName, eventName, default='').split()
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000507 return binding
508
Steven M. Gavac628a062002-01-19 10:33:21 +0000509 def GetCurrentKeySet(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400510 "Return CurrentKeys with 'darwin' modifications."
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000511 result = self.GetKeySet(self.CurrentKeys())
512
Ned Deilyb7601672014-03-27 20:49:14 -0700513 if sys.platform == "darwin":
514 # OS X Tk variants do not support the "Alt" keyboard modifier.
515 # So replace all keybingings that use "Alt" with ones that
516 # use the "Option" keyboard modifier.
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400517 # TODO (Ned?): the "Option" modifier does not work properly for
Ned Deilyb7601672014-03-27 20:49:14 -0700518 # Cocoa Tk and XQuartz Tk so we should not use it
519 # in default OS X KeySets.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000520 for k, v in result.items():
521 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
522 if v != v2:
523 result[k] = v2
524
525 return result
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000526
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400527 def GetKeySet(self, keySetName):
528 """Return event-key dict for keySetName core plus active extensions.
529
530 If a binding defined in an extension is already in use, the
531 extension binding is disabled by being set to ''
Steven M. Gava2a63a072001-10-26 06:50:54 +0000532 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400533 keySet = self.GetCoreKeys(keySetName)
534 activeExtns = self.GetExtensions(active_only=1)
Steven M. Gavac628a062002-01-19 10:33:21 +0000535 for extn in activeExtns:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400536 extKeys = self.__GetRawExtensionKeys(extn)
Steven M. Gavac628a062002-01-19 10:33:21 +0000537 if extKeys: #the extension defines keybindings
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000538 for event in extKeys:
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000539 if extKeys[event] in keySet.values():
Steven M. Gavac628a062002-01-19 10:33:21 +0000540 #the binding is already in use
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400541 extKeys[event] = '' #disable this binding
542 keySet[event] = extKeys[event] #add binding
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000543 return keySet
544
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400545 def IsCoreBinding(self, virtualEvent):
546 """Return True if the virtual event is one of the core idle key events.
547
548 virtualEvent - string, name of the virtual event to test for,
549 without the enclosing '<< >>'
Steven M. Gavaa498af22002-02-01 01:33:36 +0000550 """
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000551 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000552
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400553# TODO make keyBindins a file or class attribute used for test above
554# and copied in function below
555
Steven M. Gavac628a062002-01-19 10:33:21 +0000556 def GetCoreKeys(self, keySetName=None):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400557 """Return dict of core virtual-key keybindings for keySetName.
558
559 The default keySetName None corresponds to the keyBindings base
560 dict. If keySetName is not None, bindings from the config
561 file(s) are loaded _over_ these defaults, so if there is a
562 problem getting any core binding there will be an 'ultimate last
563 resort fallback' to the CUA-ish bindings defined here.
Steven M. Gava2a63a072001-10-26 06:50:54 +0000564 """
Steven M. Gava17d01542001-12-03 00:37:28 +0000565 keyBindings={
Steven M. Gavaa498af22002-02-01 01:33:36 +0000566 '<<copy>>': ['<Control-c>', '<Control-C>'],
567 '<<cut>>': ['<Control-x>', '<Control-X>'],
568 '<<paste>>': ['<Control-v>', '<Control-V>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000569 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
570 '<<center-insert>>': ['<Control-l>'],
571 '<<close-all-windows>>': ['<Control-q>'],
572 '<<close-window>>': ['<Alt-F4>'],
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000573 '<<do-nothing>>': ['<Control-x>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000574 '<<end-of-file>>': ['<Control-d>'],
575 '<<python-docs>>': ['<F1>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000576 '<<python-context-help>>': ['<Shift-F1>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000577 '<<history-next>>': ['<Alt-n>'],
578 '<<history-previous>>': ['<Alt-p>'],
579 '<<interrupt-execution>>': ['<Control-c>'],
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000580 '<<view-restart>>': ['<F6>'],
Kurt B. Kaiser4cc5ef52003-01-22 00:23:23 +0000581 '<<restart-shell>>': ['<Control-F6>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000582 '<<open-class-browser>>': ['<Alt-c>'],
583 '<<open-module>>': ['<Alt-m>'],
584 '<<open-new-window>>': ['<Control-n>'],
585 '<<open-window-from-file>>': ['<Control-o>'],
586 '<<plain-newline-and-indent>>': ['<Control-j>'],
Steven M. Gava7981ce52002-06-11 04:45:34 +0000587 '<<print-window>>': ['<Control-p>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000588 '<<redo>>': ['<Control-y>'],
589 '<<remove-selection>>': ['<Escape>'],
Kurt B. Kaiser2303b1c2003-11-24 05:26:16 +0000590 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
Steven M. Gava17d01542001-12-03 00:37:28 +0000591 '<<save-window-as-file>>': ['<Alt-s>'],
592 '<<save-window>>': ['<Control-s>'],
593 '<<select-all>>': ['<Alt-a>'],
594 '<<toggle-auto-coloring>>': ['<Control-slash>'],
Steven M. Gava0cae01c2002-01-04 07:53:06 +0000595 '<<undo>>': ['<Control-z>'],
596 '<<find-again>>': ['<Control-g>', '<F3>'],
597 '<<find-in-files>>': ['<Alt-F3>'],
598 '<<find-selection>>': ['<Control-F3>'],
599 '<<find>>': ['<Control-f>'],
600 '<<replace>>': ['<Control-h>'],
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000601 '<<goto-line>>': ['<Alt-g>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000602 '<<smart-backspace>>': ['<Key-BackSpace>'],
Andrew Svetlov67ac0792012-03-29 19:01:28 +0300603 '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000604 '<<smart-indent>>': ['<Key-Tab>'],
605 '<<indent-region>>': ['<Control-Key-bracketright>'],
606 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
607 '<<comment-region>>': ['<Alt-Key-3>'],
608 '<<uncomment-region>>': ['<Alt-Key-4>'],
609 '<<tabify-region>>': ['<Alt-Key-5>'],
610 '<<untabify-region>>': ['<Alt-Key-6>'],
611 '<<toggle-tabs>>': ['<Alt-Key-t>'],
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000612 '<<change-indentwidth>>': ['<Alt-Key-u>'],
613 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
614 '<<del-word-right>>': ['<Control-Key-Delete>']
Kurt B. Kaisera9f8cbc2002-09-14 03:17:01 +0000615 }
Steven M. Gava17d01542001-12-03 00:37:28 +0000616 if keySetName:
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000617 for event in keyBindings:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400618 binding = self.GetKeyBinding(keySetName, event)
Steven M. Gava49745752002-02-18 01:43:11 +0000619 if binding:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400620 keyBindings[event] = binding
Steven M. Gava49745752002-02-18 01:43:11 +0000621 else: #we are going to return a default, print warning
Walter Dörwald70a6b492004-02-12 17:35:32 +0000622 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
623 ' -\n problem retrieving key binding for event %r'
624 '\n from key set %r.\n'
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400625 ' returning default value: %r' %
Walter Dörwald70a6b492004-02-12 17:35:32 +0000626 (event, keySetName, keyBindings[event]))
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000627 try:
Terry Jan Reedy81b062f2014-09-19 22:38:41 -0400628 print(warning, file=sys.stderr)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200629 except OSError:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000630 pass
Steven M. Gava17d01542001-12-03 00:37:28 +0000631 return keyBindings
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000632
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400633 def GetExtraHelpSourceList(self, configSet):
634 """Return list of extra help sources from a given configSet.
Kurt B. Kaisere66675b2003-01-27 02:36:18 +0000635
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000636 Valid configSets are 'user' or 'default'. Return a list of tuples of
637 the form (menu_item , path_to_help_file , option), or return the empty
638 list. 'option' is the sequence number of the help resource. 'option'
639 values determine the position of the menu items on the Help menu,
640 therefore the returned list must be sorted by 'option'.
641
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000642 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400643 helpSources = []
644 if configSet == 'user':
645 cfgParser = self.userCfg['main']
646 elif configSet == 'default':
647 cfgParser = self.defaultCfg['main']
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000648 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000649 raise InvalidConfigSet('Invalid configSet specified')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000650 options=cfgParser.GetOptionList('HelpFiles')
651 for option in options:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400652 value=cfgParser.Get('HelpFiles', option, default=';')
653 if value.find(';') == -1: #malformed config entry with no ';'
654 menuItem = '' #make these empty
655 helpPath = '' #so value won't be added to list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000656 else: #config entry contains ';' as expected
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000657 value=value.split(';')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000658 menuItem=value[0].strip()
659 helpPath=value[1].strip()
660 if menuItem and helpPath: #neither are empty strings
661 helpSources.append( (menuItem,helpPath,option) )
Kurt B. Kaiser4718bf82008-02-12 21:34:12 +0000662 helpSources.sort(key=lambda x: x[2])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000663 return helpSources
664
665 def GetAllExtraHelpSourcesList(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400666 """Return a list of the details of all additional help sources.
667
668 Tuples in the list are those of GetExtraHelpSourceList.
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000669 """
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400670 allHelpSources = (self.GetExtraHelpSourceList('default') +
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000671 self.GetExtraHelpSourceList('user') )
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000672 return allHelpSources
673
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400674 def GetFont(self, root, configType, section):
675 """Retrieve a font from configuration (font, font-size, font-bold)
676 Intercept the special value 'TkFixedFont' and substitute
677 the actual font, factoring in some tweaks if needed for
678 appearance sakes.
679
680 The 'root' parameter can normally be any valid Tkinter widget.
681
682 Return a tuple (family, size, weight) suitable for passing
683 to tkinter.Font
684 """
685 family = self.GetOption(configType, section, 'font', default='courier')
686 size = self.GetOption(configType, section, 'font-size', type='int',
687 default='10')
688 bold = self.GetOption(configType, section, 'font-bold', default=0,
689 type='bool')
690 if (family == 'TkFixedFont'):
691 f = Font(name='TkFixedFont', exists=True, root=root)
692 actualFont = Font.actual(f)
693 family = actualFont['family']
694 size = actualFont['size']
695 if size < 0:
696 size = 10 # if font in pixels, ignore actual size
697 bold = actualFont['weight']=='bold'
698 return (family, size, 'bold' if bold else 'normal')
699
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000700 def LoadCfgFiles(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400701 "Load all configuration files."
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000702 for key in self.defaultCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000703 self.defaultCfg[key].Load()
704 self.userCfg[key].Load() #same keys
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000705
706 def SaveUserCfgFiles(self):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400707 "Write all loaded user configuration files to disk."
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000708 for key in self.userCfg:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000709 self.userCfg[key].Save()
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000710
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000711
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400712idleConf = IdleConf()
713
714# TODO Revise test output, write expanded unittest
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000715### module test
716if __name__ == '__main__':
717 def dumpCfg(cfg):
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400718 print('\n', cfg, '\n')
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000719 for key in cfg:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400720 sections = cfg[key].sections()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000721 print(key)
722 print(sections)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000723 for section in sections:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400724 options = cfg[key].options(section)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000725 print(section)
726 print(options)
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000727 for option in options:
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400728 print(option, '=', cfg[key].Get(section, option))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000729 dumpCfg(idleConf.defaultCfg)
730 dumpCfg(idleConf.userCfg)
Terry Jan Reedydeb7bf12014-10-06 23:26:26 -0400731 print(idleConf.userCfg['main'].Get('Theme', 'name'))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000732 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')