1 """Manages NVDA configuration.
2 """
3
4 import globalVars
5 import _winreg
6 from copy import deepcopy
7 import os
8 import sys
9 from cStringIO import StringIO
10 from configobj import ConfigObj, ConfigObjError
11 from validate import Validator
12 from logHandler import log
13 import shlobj
14
15 -def validateConfig(configObj,validator,validationResult=None,keyList=None):
16 if validationResult is None:
17 validationResult=configObj.validate(validator,preserve_errors=True)
18 if validationResult is True:
19 return None
20 if validationResult is False:
21 return _("Badly formed configuration file")
22 errorStrings=[]
23 for k,v in validationResult.iteritems():
24 if v is True:
25 continue
26 newKeyList=list(keyList) if keyList is not None else []
27 newKeyList.append(k)
28 if isinstance(v,dict):
29 errorStrings.extend(validateConfig(configObj[k],validator,v,newKeyList))
30 else:
31
32 defaultValue=validator.get_default_value(configObj.configspec[k])
33 configObj[k]=defaultValue
34 if k not in configObj.defaults:
35 configObj.defaults.append(k)
36 errorStrings.append(_("%s: %s, defaulting to %s")%(k,v,defaultValue))
37 return errorStrings
38
39 val = Validator()
40
41
42
43 confspec = ConfigObj(StringIO(
44 """# NVDA Configuration File
45
46 [general]
47 language = string(default="Windows")
48 saveConfigurationOnExit = boolean(default=False)
49 askToExit = boolean(default=true)
50 #possible log levels are DEBUG, IO, DEBUGWARNING, INFO
51 loggingLevel = string(default="INFO")
52 showWelcomeDialogAtStartup = boolean(default=true)
53
54 # Speech settings
55 [speech]
56 # The synthesiser to use
57 synth = string(default=auto)
58 symbolLevel = integer(default=100)
59 beepSpeechModePitch = integer(default=10000,min=50,max=11025)
60 outputDevice = string(default=default)
61 autoLanguageSwitching = boolean(default=true)
62 autoDialectSwitching = boolean(default=false)
63
64 [[__many__]]
65 capPitchChange = integer(default=30,min=-100,max=100)
66 sayCapForCapitals = boolean(default=false)
67 beepForCapitals = boolean(default=false)
68 useSpellingFunctionality = boolean(default=true)
69
70 # Braille settings
71 [braille]
72 display = string(default=noBraille)
73 translationTable = string(default=en-us-comp8.ctb)
74 expandAtCursor = boolean(default=true)
75 cursorBlinkRate = integer(default=500,min=0,max=2000)
76 messageTimeout = integer(default=4,min=1,max=20)
77 tetherTo = string(default="focus")
78
79
80
81 # Presentation settings
82 [presentation]
83 reportClassOfClientObjects = boolean(default=false)
84 reportKeyboardShortcuts = boolean(default=true)
85 reportObjectPositionInformation = boolean(default=true)
86 guessObjectPositionInformationWhenUnavailable = boolean(default=false)
87 reportTooltips = boolean(default=false)
88 reportHelpBalloons = boolean(default=true)
89 reportObjectDescriptions = boolean(default=True)
90 reportDynamicContentChanges = boolean(default=True)
91 [[progressBarUpdates]]
92 reportBackgroundProgressBars = boolean(default=false)
93 #output modes are beep, speak, both, or off
94 progressBarOutputMode = string(default="beep")
95 speechPercentageInterval = integer(default=10)
96 beepPercentageInterval = integer(default=1)
97 beepMinHZ = integer(default=110)
98
99 [mouse]
100 enableMouseTracking = boolean(default=True) #must be true for any of the other settings to work
101 mouseTextUnit = string(default="paragraph")
102 reportObjectRoleOnMouseEnter = boolean(default=False)
103 audioCoordinatesOnMouseMove = boolean(default=False)
104 audioCoordinates_detectBrightness = boolean(default=False)
105 audioCoordinates_blurFactor = integer(default=3)
106 audioCoordinates_minVolume = float(default=0.1)
107 audioCoordinates_maxVolume = float(default=1.0)
108 audioCoordinates_minPitch = integer(default=220)
109 audioCoordinates_maxPitch = integer(default=880)
110 reportMouseShapeChanges = boolean(default=false)
111
112 #Keyboard settings
113 [keyboard]
114 useCapsLockAsNVDAModifierKey = boolean(default=false)
115 useNumpadInsertAsNVDAModifierKey = boolean(default=true)
116 useExtendedInsertAsNVDAModifierKey = boolean(default=true)
117 keyboardLayout = string(default="desktop")
118 speakTypedCharacters = boolean(default=true)
119 speakTypedWords = boolean(default=false)
120 beepForLowercaseWithCapslock = boolean(default=true)
121 speakCommandKeys = boolean(default=false)
122
123 [virtualBuffers]
124 maxLineLength = integer(default=100)
125 linesPerPage = integer(default=25)
126 useScreenLayout = boolean(default=True)
127 autoPassThroughOnFocusChange = boolean(default=true)
128 autoPassThroughOnCaretMove = boolean(default=false)
129 passThroughAudioIndication = boolean(default=true)
130 autoSayAllOnPageLoad = boolean(default=true)
131
132 #Settings for document reading (such as MS Word and wordpad)
133 [documentFormatting]
134 #These settings affect what information is reported when you navigate to text where the formatting or placement has changed
135 detectFormatAfterCursor = boolean(default=false)
136 reportFontName = boolean(default=false)
137 reportFontSize = boolean(default=false)
138 reportFontAttributes = boolean(default=false)
139 reportColor = boolean(default=False)
140 reportAlignment = boolean(default=false)
141 reportStyle = boolean(default=false)
142 reportSpellingErrors = boolean(default=true)
143 reportPage = boolean(default=true)
144 reportLineNumber = boolean(default=False)
145 reportLineIndentation = boolean(default=False)
146 reportTables = boolean(default=true)
147 includeLayoutTables = boolean(default=False)
148 reportTableHeaders = boolean(default=True)
149 reportTableCellCoords = boolean(default=True)
150 reportLinks = boolean(default=true)
151 reportLists = boolean(default=true)
152 reportHeadings = boolean(default=true)
153 reportBlockQuotes = boolean(default=true)
154 reportLandmarks = boolean(default=true)
155
156 [reviewCursor]
157 simpleReviewMode = boolean(default=True)
158 followFocus = boolean(default=True)
159 followCaret = boolean(default=True)
160 followMouse = boolean(default=False)
161
162 [UIA]
163 minWindowsVersion = float(default=6.1)
164 enabled = boolean(default=true)
165
166 """
167 ), list_values=False, encoding="UTF-8")
168 confspec.newlines = "\r\n"
169
170
171
172 conf = None
173
174
175 synthSpec=None
176
178 """Loads the configuration from the configFile.
179 It also takes note of the file's modification time so that L{save} won't lose any changes made to the file while NVDA is running.
180 """
181 global conf,synthSpec
182 configFileName=os.path.join(globalVars.appArgs.configPath,"nvda.ini")
183 try:
184 conf = ConfigObj(configFileName, configspec = confspec, indent_type = "\t", encoding="UTF-8")
185 except ConfigObjError as e:
186 conf = ConfigObj(None, configspec = confspec, indent_type = "\t", encoding="UTF-8")
187 conf.filename=configFileName
188 globalVars.configFileError=_("Error parsing configuration file: %s")%e
189
190 conf.newlines = "\r\n"
191 errorList=validateConfig(conf,val)
192 if synthSpec is None:
193 synthSpec=deepcopy(conf["speech"].configspec["__many__"])
194 if errorList:
195 globalVars.configFileError=_("Errors in configuration file '%s':\n%s")%(conf.filename,"\n".join(errorList))
196 if globalVars.configFileError:
197 log.warn(globalVars.configFileError)
198
200 """Saves the configuration to the config file.
201 """
202
203 if globalVars.appArgs.secure: return
204 global conf
205 if globalVars.configFileError:
206 raise RuntimeError("config file errors still exist")
207 if not os.path.isdir(globalVars.appArgs.configPath):
208 try:
209 os.makedirs(globalVars.appArgs.configPath)
210 except OSError, e:
211 log.warning("Could not create configuration directory")
212 log.debugWarning("", exc_info=True)
213 raise e
214 try:
215
216 conf.validate(val, copy = True)
217 conf.write()
218 log.info("Configuration saved")
219 except Exception, e:
220 log.warning("Could not save configuration - probably read only file system")
221 log.debugWarning("", exc_info=True)
222 raise e
223
225 """Save the configuration if configured to save on exit.
226 This should only be called if NVDA is about to exit.
227 Errors are ignored.
228 """
229 if conf["general"]["saveConfigurationOnExit"]:
230 try:
231 save()
232 except:
233 pass
234
236 """Checks to see if this running copy of NVDA is installed on the system"""
237 try:
238 k=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NVDA")
239 instDir=_winreg.QueryValueEx(k,"UninstallDirectory")[0]
240 except WindowsError:
241 return False
242 _winreg.CloseKey(k)
243 try:
244 return os.stat(instDir)==os.stat(os.getcwdu())
245 except WindowsError:
246 return False
247
255
263
265 """
266 Creates the current configuration path if it doesn't exist. Also makes sure that various sub directories also exist.
267 @param configPath: an optional path which should be used instead (only useful when being called from outside of NVDA)
268 @type configPath: basestring
269 """
270 if not configPath:
271 configPath=globalVars.appArgs.configPath
272 if not os.path.isdir(configPath):
273 os.makedirs(configPath)
274 for subdir in ("appModules","brailleDisplayDrivers","speechDicts","synthDrivers","globalPlugins"):
275 subdir=os.path.join(configPath,subdir)
276 if not os.path.isdir(subdir):
277 os.makedirs(subdir)
278
279 RUN_REGKEY = ur"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
280
282 try:
283 k = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, RUN_REGKEY)
284 val = _winreg.QueryValueEx(k, u"nvda")[0]
285 return os.stat(val) == os.stat(sys.argv[0])
286 except (WindowsError, OSError):
287 return False
288
290 if getStartAfterLogon() == enable:
291 return
292 k = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, RUN_REGKEY, 0, _winreg.KEY_WRITE)
293 if enable:
294 _winreg.SetValueEx(k, u"nvda", None, _winreg.REG_SZ, sys.argv[0])
295 else:
296 _winreg.DeleteValue(k, u"nvda")
297
298 SERVICE_FILENAME = u"nvda_service.exe"
299
301 if not os.path.isfile(SERVICE_FILENAME):
302 return False
303 try:
304 k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, ur"SYSTEM\CurrentControlSet\Services\nvda")
305 val = _winreg.QueryValueEx(k, u"ImagePath")[0].replace(u'"', u'')
306 return os.stat(val) == os.stat(SERVICE_FILENAME)
307 except (WindowsError, OSError):
308 return False
309
327
328 SLAVE_FILENAME = u"nvda_slave.exe"
329
330 NVDA_REGKEY = ur"SOFTWARE\NVDA"
331
333 try:
334 k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, NVDA_REGKEY)
335 return bool(_winreg.QueryValueEx(k, u"startOnLogonScreen")[0])
336 except WindowsError:
337 return False
338
340 k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, NVDA_REGKEY, 0, _winreg.KEY_WRITE)
341 _winreg.SetValueEx(k, u"startOnLogonScreen", None, _winreg.REG_DWORD, int(enable))
342
350
352 toPath=os.path.join(sys.prefix,'systemConfig')
353 import shutil
354 if os.path.isdir(toPath):
355 shutil.rmtree(toPath)
356 shutil.copytree(fromPath,toPath)
357
368
370 """Retrieve all directories that should be used when searching for configuration.
371 IF C{subpath} is provided, it will be added to each directory returned.
372 @param subpath: The path to be added to each directory, C{None} for none.
373 @type subpath: str
374 @return: The configuration directories in the order in which they should be searched.
375 @rtype: list of str
376 """
377 return [os.path.join(dir, subpath) if subpath else dir
378 for dir in (globalVars.appArgs.configPath,)
379 ]
380
382 """Add the configuration directories to the module search path (__path__) of a Python package.
383 C{subdir} is added to each configuration directory. It defaults to the name of the Python package.
384 @param module: The root module of the package.
385 @type module: module
386 @param subdir: The subdirectory to be used, C{None} for the name of C{module}.
387 @type subdir: str
388 """
389 if not subdir:
390 subdir = module.__name__
391
392 dirs = [dir.encode("mbcs") for dir in getConfigDirs(subdir)]
393 dirs.extend(module.__path__ )
394 module.__path__ = dirs
395