Package config
[hide private]
[frames] | no frames]

Source Code for Package config

  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 #No errors 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 #If a key is invalid configObj does not record its default, thus we need to get and set the default manually 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 #: The configuration specification 42 #: @type: ConfigObj 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 #: The active configuration, C{None} if it has not yet been loaded. 171 #: @type: ConfigObj 172 conf = None 173 #: template config spec for concrete synthesizer's settings. It is used in SynthDriver.getConfigSpec() to build a real spec 174 #: @type: L{configobj.Section} 175 synthSpec=None 176
177 -def load():
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 # Python converts \r\n to \n when reading files in Windows, so ConfigObj can't determine the true line ending. 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
199 -def save():
200 """Saves the configuration to the config file. 201 """ 202 #We never want to save config if runing securely 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 # Copy default settings and formatting. 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
224 -def saveOnExit():
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
235 -def isInstalledCopy():
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
248 -def getUserDefaultConfigPath():
249 if isInstalledCopy(): 250 try: 251 return os.path.join(shlobj.SHGetFolderPath(0, shlobj.CSIDL_APPDATA), "nvda") 252 except WindowsError: 253 pass 254 return u'.\\userConfig\\'
255
256 -def getSystemConfigPath():
257 if isInstalledCopy(): 258 try: 259 return os.path.join(shlobj.SHGetFolderPath(0, shlobj.CSIDL_COMMON_APPDATA), "nvda") 260 except WindowsError: 261 pass 262 return None
263
264 -def initConfigPath(configPath=None):
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
281 -def getStartAfterLogon():
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
289 -def setStartAfterLogon(enable):
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
300 -def isServiceInstalled():
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
310 -def execElevated(path, params=None, wait=False):
311 import subprocess 312 import shellapi 313 import winKernel 314 import winUser 315 if params is not None: 316 params = subprocess.list2cmdline(params) 317 sei = shellapi.SHELLEXECUTEINFO(lpVerb=u"runas", lpFile=os.path.abspath(path), lpParameters=params, nShow=winUser.SW_HIDE) 318 if wait: 319 sei.fMask = shellapi.SEE_MASK_NOCLOSEPROCESS 320 shellapi.ShellExecuteEx(sei) 321 if wait: 322 try: 323 winKernel.waitForSingleObject(sei.hProcess, winKernel.INFINITE) 324 return winKernel.GetExitCodeProcess(sei.hProcess) 325 finally: 326 winKernel.closeHandle(sei.hProcess)
327 328 SLAVE_FILENAME = u"nvda_slave.exe" 329 330 NVDA_REGKEY = ur"SOFTWARE\NVDA" 331
332 -def getStartOnLogonScreen():
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
339 -def _setStartOnLogonScreen(enable):
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
343 -def setSystemConfigToCurrentConfig():
344 fromPath=os.path.abspath(globalVars.appArgs.configPath) 345 try: 346 _setSystemConfig(fromPath) 347 return True 348 except (OSError,WindowsError): 349 return execElevated(SLAVE_FILENAME, (u"setNvdaSystemConfig", fromPath), wait=True)==0
350
351 -def _setSystemConfig(fromPath):
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
358 -def setStartOnLogonScreen(enable):
359 if getStartOnLogonScreen() == enable: 360 return 361 try: 362 # Try setting it directly. 363 _setStartOnLogonScreen(enable) 364 except WindowsError: 365 # We probably don't have admin privs, so we need to elevate to do this using the slave. 366 if execElevated(SLAVE_FILENAME, (u"config_setStartOnLogonScreen", u"%d" % enable), wait=True) != 0: 367 raise RuntimeError("Slave failed to set startOnLogonScreen")
368
369 -def getConfigDirs(subpath=None):
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
381 -def addConfigDirsToPythonPackagePath(module, subdir=None):
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 # Python 2.x doesn't properly handle unicode import paths, so convert them. 392 dirs = [dir.encode("mbcs") for dir in getConfigDirs(subdir)] 393 dirs.extend(module.__path__ ) 394 module.__path__ = dirs
395