Skip to content

Commit

Permalink
Incubates #6558
Browse files Browse the repository at this point in the history
Re issue #6470
Merge remote-tracking branch 'origin/i6470-LimitCursorBlinkRate' into next

Conflicts:
	source/config/__init__.py

To resolve, a line added to the configSchema needed to be moved into the
configSpec.py file. The line was:
	readNumbersAs = integer(default=0,min=0,max=3)
  • Loading branch information
feerrenrut committed Jan 5, 2017
2 parents fc9ed0a + 820c02f commit 6157f43
Show file tree
Hide file tree
Showing 7 changed files with 389 additions and 202 deletions.
3 changes: 2 additions & 1 deletion source/braille.py
Original file line number Diff line number Diff line change
Expand Up @@ -1282,8 +1282,9 @@ def _updateDisplay(self):
self._displayWithCursor()
if self._cursorPos is None or not showCursor:
return
cursorShouldBlink = config.conf["braille"]["cursorBlink"]
blinkRate = config.conf["braille"]["cursorBlinkRate"]
if blinkRate:
if cursorShouldBlink and blinkRate:
self._cursorBlinkTimer = wx.PyTimer(self._blink)
self._cursorBlinkTimer.Start(blinkRate)

Expand Down
245 changes: 52 additions & 193 deletions source/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

"""Manages NVDA configuration.
"""

import globalVars
import _winreg
import ctypes
Expand All @@ -17,14 +16,18 @@
from cStringIO import StringIO
import itertools
import contextlib
from copy import deepcopy
from collections import OrderedDict
from configobj import ConfigObj, ConfigObjError
from validate import Validator
from logHandler import log
from logHandler import log, levelNames
from logging import DEBUG
import shlobj
import baseObject
import easeOfAccess
import winKernel
import profileUpgrader
from .configSpec import confspec

def validateConfig(configObj,validator,validationResult=None,keyList=None):
"""
Expand Down Expand Up @@ -59,188 +62,6 @@ def validateConfig(configObj,validator,validationResult=None,keyList=None):
#: @deprecated: Use C{conf.validator} instead.
val = Validator()

#: The configuration specification
#: @type: ConfigObj
confspec = ConfigObj(StringIO(
"""# NVDA Configuration File
[general]
language = string(default="Windows")
saveConfigurationOnExit = boolean(default=True)
askToExit = boolean(default=true)
playStartAndExitSounds = boolean(default=true)
#possible log levels are DEBUG, IO, DEBUGWARNING, INFO
loggingLevel = string(default="INFO")
showWelcomeDialogAtStartup = boolean(default=true)
# Speech settings
[speech]
# The synthesiser to use
synth = string(default=auto)
symbolLevel = integer(default=100)
trustVoiceLanguage = boolean(default=true)
beepSpeechModePitch = integer(default=10000,min=50,max=11025)
outputDevice = string(default=default)
autoLanguageSwitching = boolean(default=true)
autoDialectSwitching = boolean(default=false)
readNumbersAs = integer(default=0,min=0,max=3)
[[__many__]]
capPitchChange = integer(default=30,min=-100,max=100)
sayCapForCapitals = boolean(default=false)
beepForCapitals = boolean(default=false)
useSpellingFunctionality = boolean(default=true)
# Audio settings
[audio]
audioDuckingMode = integer(default=0)
# Braille settings
[braille]
display = string(default=noBraille)
translationTable = string(default=en-us-comp8.ctb)
inputTable = string(default=en-us-comp8.ctb)
expandAtCursor = boolean(default=true)
showCursor = boolean(default=true)
cursorBlinkRate = integer(default=500,min=0,max=2000)
cursorShape = integer(default=192,min=1,max=255)
messageTimeout = integer(default=4,min=0,max=20)
tetherTo = string(default="focus")
readByParagraph = boolean(default=false)
wordWrap = boolean(default=true)
# Braille display driver settings
[[__many__]]
port = string(default="")
# Presentation settings
[presentation]
reportKeyboardShortcuts = boolean(default=true)
reportObjectPositionInformation = boolean(default=true)
guessObjectPositionInformationWhenUnavailable = boolean(default=false)
reportTooltips = boolean(default=false)
reportHelpBalloons = boolean(default=true)
reportObjectDescriptions = boolean(default=True)
reportDynamicContentChanges = boolean(default=True)
[[progressBarUpdates]]
reportBackgroundProgressBars = boolean(default=false)
#output modes are beep, speak, both, or off
progressBarOutputMode = string(default="beep")
speechPercentageInterval = integer(default=10)
beepPercentageInterval = integer(default=1)
beepMinHZ = integer(default=110)
[mouse]
enableMouseTracking = boolean(default=True) #must be true for any of the other settings to work
mouseTextUnit = string(default="paragraph")
reportObjectRoleOnMouseEnter = boolean(default=False)
audioCoordinatesOnMouseMove = boolean(default=False)
audioCoordinates_detectBrightness = boolean(default=False)
audioCoordinates_blurFactor = integer(default=3)
audioCoordinates_minVolume = float(default=0.1)
audioCoordinates_maxVolume = float(default=1.0)
audioCoordinates_minPitch = integer(default=220)
audioCoordinates_maxPitch = integer(default=880)
reportMouseShapeChanges = boolean(default=false)
[speechViewer]
showSpeechViewerAtStartup = boolean(default=false)
autoPositionWindow = boolean(default=True)
# values for positioning the window. Defaults are not used. They should not be read if autoPositionWindow is True
x = integer()
y = integer()
width = integer()
height = integer()
displays = string_list()
#Keyboard settings
[keyboard]
useCapsLockAsNVDAModifierKey = boolean(default=false)
useNumpadInsertAsNVDAModifierKey = boolean(default=true)
useExtendedInsertAsNVDAModifierKey = boolean(default=true)
keyboardLayout = string(default="desktop")
speakTypedCharacters = boolean(default=true)
speakTypedWords = boolean(default=false)
beepForLowercaseWithCapslock = boolean(default=true)
speakCommandKeys = boolean(default=false)
speechInterruptForCharacters = boolean(default=true)
speechInterruptForEnter = boolean(default=true)
allowSkimReadingInSayAll = boolean(default=False)
alertForSpellingErrors = boolean(default=True)
handleInjectedKeys= boolean(default=true)
[virtualBuffers]
maxLineLength = integer(default=100)
linesPerPage = integer(default=25)
useScreenLayout = boolean(default=True)
autoPassThroughOnFocusChange = boolean(default=true)
autoPassThroughOnCaretMove = boolean(default=false)
passThroughAudioIndication = boolean(default=true)
autoSayAllOnPageLoad = boolean(default=true)
trapNonCommandGestures = boolean(default=true)
#Settings for document reading (such as MS Word and wordpad)
[documentFormatting]
#These settings affect what information is reported when you navigate to text where the formatting or placement has changed
detectFormatAfterCursor = boolean(default=false)
reportFontName = boolean(default=false)
reportFontSize = boolean(default=false)
reportFontAttributes = boolean(default=false)
reportRevisions = boolean(default=true)
reportEmphasis = boolean(default=false)
reportColor = boolean(default=False)
reportAlignment = boolean(default=false)
reportLineSpacing = boolean(default=false)
reportStyle = boolean(default=false)
reportSpellingErrors = boolean(default=true)
reportPage = boolean(default=true)
reportLineNumber = boolean(default=False)
reportLineIndentation = boolean(default=False)
reportLineIndentationWithTones = boolean(default=False)
reportParagraphIndentation = boolean(default=False)
reportTables = boolean(default=true)
includeLayoutTables = boolean(default=False)
reportTableHeaders = boolean(default=True)
reportTableCellCoords = boolean(default=True)
reportLinks = boolean(default=true)
reportComments = boolean(default=true)
reportLists = boolean(default=true)
reportHeadings = boolean(default=true)
reportBlockQuotes = boolean(default=true)
reportLandmarks = boolean(default=true)
reportFrames = boolean(default=true)
reportClickable = boolean(default=true)
[reviewCursor]
simpleReviewMode = boolean(default=True)
followFocus = boolean(default=True)
followCaret = boolean(default=True)
followMouse = boolean(default=False)
[UIA]
minWindowsVersion = float(default=6.1)
enabled = boolean(default=true)
[update]
autoCheck = boolean(default=true)
[inputComposition]
autoReportAllCandidates = boolean(default=True)
announceSelectedCandidate = boolean(default=True)
alwaysIncludeShortCharacterDescriptionInCandidateName = boolean(default=True)
reportReadingStringChanges = boolean(default=True)
reportCompositionStringChanges = boolean(default=True)
[debugLog]
hwIo = boolean(default=false)
audioDucking = boolean(default=false)
[upgrade]
newLaptopKeyboardLayout = boolean(default=false)
"""
), list_values=False, encoding="UTF-8")
confspec.newlines = "\r\n"

#: The active configuration, C{None} if it has not yet been loaded.
#: @type: ConfigObj
conf = None
Expand Down Expand Up @@ -548,18 +369,16 @@ def _handleProfileSwitch(self):
def _initBaseConf(self, factoryDefaults=False):
fn = os.path.join(globalVars.appArgs.configPath, "nvda.ini")
if factoryDefaults:
profile = ConfigObj(None, indent_type="\t", encoding="UTF-8")
profile = self._loadConfig(None)
profile.filename = fn
else:
try:
profile = ConfigObj(fn, indent_type="\t", encoding="UTF-8")
profile = self._loadConfig(fn) # a blank config returned if fn does not exist
self.baseConfigError = False
except:
log.error("Error loading base configuration", exc_info=True)
self.baseConfigError = True
return self._initBaseConf(factoryDefaults=True)
# Python converts \r\n to \n when reading files in Windows, so ConfigObj can't determine the true line ending.
profile.newlines = "\r\n"

for key in self.BASE_ONLY_SECTIONS:
# These sections are returned directly from the base config, so validate them here.
Expand All @@ -576,6 +395,29 @@ def _initBaseConf(self, factoryDefaults=False):
self.profiles.append(profile)
self._handleProfileSwitch()

def _loadConfig(self, fn, fileError=False):
log.info(u"Loading config: {0}".format(fn))
profile = ConfigObj(fn, indent_type="\t", encoding="UTF-8", file_error=fileError)
# Python converts \r\n to \n when reading files in Windows, so ConfigObj can't determine the true line ending.
profile.newlines = "\r\n"
profileCopy = deepcopy(profile)
try:
profileUpgrader.upgrade(profile, self.validator)
except Exception as e:
# Log at level info to ensure that the profile is logged.
log.info(u"Config before schema update:\n%s" % profileCopy, exc_info=False)
raise e
# since profile settings are not yet imported we have to "peek" to see
# if debug level logging is enabled.
try:
logLevelName = profile["general"]["loggingLevel"]
except KeyError as e:
logLevelName = None
if log.isEnabledFor(log.DEBUG) or (logLevelName and DEBUG >= levelNames.get(logLevelName)):
# Log at level info to ensure that the profile is logged.
log.info(u"Config loaded (after upgrade, and in the state it will be used by NVDA):\n{0}".format(profile))
return profile

def __getitem__(self, key):
if key in self.BASE_ONLY_SECTIONS:
# Return these directly from the base configuration.
Expand Down Expand Up @@ -609,9 +451,7 @@ def _getProfile(self, name, load=True):

# Load the profile.
fn = self._getProfileFn(name)
profile = ConfigObj(fn, indent_type="\t", encoding="UTF-8", file_error=True)
# Python converts \r\n to \n when reading files in Windows, so ConfigObj can't determine the true line ending.
profile.newlines = "\r\n"
profile = self._loadConfig(fn, fileError = True) # file must exist.
profile.name = name
profile.manual = False
profile.triggered = False
Expand Down Expand Up @@ -924,6 +764,21 @@ def saveProfileTriggers(self):
self.triggersToProfiles.parent.write()
log.info("Profile triggers saved")

def getConfigValidationParameter(self, keyPath, validationParameter):
"""Get a config validation parameter
This can be used to get the min, max, default, or other values for a config key.
@param keyPath: a sequence of the identifiers leading to the config key. EG ("braille", "messageTimeout")
@param validationParameter: the parameter to return the value for. EG "max"
@type validationParameter: string
"""
if not keyPath or len(keyPath) < 1:
raise ValueError("Key path not provided")

spec = conf.spec
for nextKey in keyPath:
spec = spec[nextKey]
return conf.validator._parse_with_caching(spec)[2][validationParameter]

class AggregatedSection(object):
"""A view of a section of configuration which aggregates settings from all active profiles.
"""
Expand All @@ -936,7 +791,7 @@ def __init__(self, manager, path, spec, profiles):
self.profiles = profiles
self._cache = {}

def __getitem__(self, key):
def __getitem__(self, key, checkValidity=True):
# Try the cache first.
try:
val = self._cache[key]
Expand Down Expand Up @@ -968,6 +823,8 @@ def __getitem__(self, key):
subProfiles.append(val)
else:
# This is a setting.
if not checkValidity:
spec = None
return self._cacheLeaf(key, spec, val)
subProfiles.reverse()

Expand Down Expand Up @@ -1083,7 +940,9 @@ def __setitem__(self, key, val):
val = self.manager.validator.check(spec, val)

try:
curVal = self[key]
# when setting the value we dont care if the existing value
# is not valid.
curVal = self.__getitem__(key, checkValidity=False)
except KeyError:
pass
else:
Expand Down
Loading

0 comments on commit 6157f43

Please sign in to comment.