commit/StationPlaylist: josephsl: Merge branch 'master' into 7.0/orangeTrackInfoBoard

  • From: commits-noreply@xxxxxxxxxxxxx
  • To: nvda-addons-commits@xxxxxxxxxxxxx
  • Date: Sat, 16 Jan 2016 23:13:55 -0000

1 new commit in StationPlaylist:

https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/c1203d22b90f/
Changeset:   c1203d22b90f
Branch:      7.0/orangeTrackInfoBoard
User:        josephsl
Date:        2016-01-16 23:09:59+00:00
Summary:     Merge branch 'master' into 7.0/orangeTrackInfoBoard

Affected #:  3 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 5e1c037..d55ddac 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -393,6 +393,8 @@ class AppModule(appModuleHandler.AppModule):
                if splconfig.SPLConfig["Update"]["AutoUpdateCheck"]:
                        # 7.0: Have a timer call the update function indirectly.
                        queueHandler.queueFunction(queueHandler.eventQueue, 
splconfig.updateInit)
+               # Display startup dialogs if any.
+               wx.CallAfter(splconfig.showStartupDialogs)
 
        # Locate the handle for main window for caching purposes.
        def _locateSPLHwnd(self):

diff --git a/addon/appModules/splstudio/splconfig.py 
b/addon/appModules/splstudio/splconfig.py
index 65635dd..13a30d6 100755
--- a/addon/appModules/splstudio/splconfig.py
+++ b/addon/appModules/splstudio/splconfig.py
@@ -28,6 +28,7 @@ from splmisc import SPLCountdownTimer
 SPLIni = os.path.join(globalVars.appArgs.configPath, "splstudio.ini")
 SPLProfiles = os.path.join(globalVars.appArgs.configPath, "addons", 
"stationPlaylist", "profiles")
 # Old (5.0) style config.
+# To be superseeded by confspec7 in 8.0.
 confspec = ConfigObj(StringIO("""
 BeepAnnounce = boolean(default=false)
 MessageVerbosity = option("beginner", "advanced", default="beginner")
@@ -53,7 +54,7 @@ SayPlayingCartName = boolean(default=true)
 SayPlayingTrackName = string(default="True")
 SPLConPassthrough = boolean(default=false)
 CompatibilityLayer = option("off", "jfw", "wineyes", default="off")
-AutoUpdateCheck = boolean(default=true)
+AudioDuckingReminder = boolean(default=true)
 """), encoding="UTF-8", list_values=False)
 confspec.newlines = "\r\n"
 # New (7.0) style config.
@@ -91,12 +92,22 @@ SPLConPassthrough = boolean(default=false)
 CompatibilityLayer = option("off", "jfw", "wineyes", default="off")
 [Update]
 AutoUpdateCheck = boolean(default=true)
+[Startup]
+AudioDuckingReminder = boolean(default=true)
 """), encoding="UTF-8", list_values=False)
 confspec7.newlines = "\r\n"
 SPLConfig = None
 # A pool of broadcast profiles.
 SPLConfigPool = []
+# The following settings can be changed in profiles:
+_mutatableSettings=("SayEndOfTrack","EndOfTrackTime","SaySongRamp","SongRampTime","MicAlarm","MicAlarmInterval","MetadataEnabled","UseScreenColumnOrder","ColumnOrder","IncludedColumns")
+_mutatableSettings7=("IntroOutroAlarms", "MicrophoneAlarm", 
"MetadataStreaming", "ColumnAnnouncement")
+# 7.0: Profile-specific confspec (might be removed once a more optimal way to 
validate sections is found).
+# Dictionary comprehension is better here.
+confspecprofiles = {sect:key for sect, key in confspec7.iteritems() if sect in 
_mutatableSettings7}
+
 # Default config spec container.
+# To be removed in add-on 8.0.
 _SPLDefaults = ConfigObj(None, configspec = confspec, encoding="UTF-8")
 # And version 7 equivalent.
 _SPLDefaults7 = ConfigObj(None, configspec = confspec7, encoding="UTF-8")
@@ -104,13 +115,6 @@ _val = Validator()
 _SPLDefaults.validate(_val, copy=True)
 _SPLDefaults7.validate(_val, copy=True)
 
-# The following settings can be changed in profiles:
-_mutatableSettings=("SayEndOfTrack","EndOfTrackTime","SaySongRamp","SongRampTime","MicAlarm","MicAlarmInterval","MetadataEnabled","UseScreenColumnOrder","ColumnOrder","IncludedColumns")
-_mutatableSettings7=("IntroOutroAlarms", "MicrophoneAlarm", 
"MetadataStreaming", "ColumnAnnouncement")
-# 7.0: Profile-specific confspec (might be removed once a more optimal way to 
validate sections is found).
-# Dictionary comprehension is better here.
-confspecprofiles = {sect:key for sect, key in confspec7.iteritems() if sect in 
_mutatableSettings7}
-
 # Display an error dialog when configuration validation fails.
 def runConfigErrorDialog(errorText, errorType):
        wx.CallAfter(gui.messageBox, errorText, errorType, wx.OK|wx.ICON_ERROR)
@@ -118,16 +122,10 @@ def runConfigErrorDialog(errorText, errorType):
 # Reset settings to defaults.
 # This will be called when validation fails or when the user asks for it.
 # 6.0: The below function resets a single profile. A sister function will 
reset all of them.
-# 7.0: This will be split into several functions, with one of them being the 
master copy/settings transfer routine.
-def resetConfig(defaults, activeConfig, intentional=False):
-       for setting in activeConfig:
-               activeConfig[setting] = defaults[setting]
-       activeConfig.write()
-       if intentional:
-               # Translators: A dialog message shown when settings were reset 
to defaults.
-               wx.CallAfter(gui.messageBox, _("Successfully applied default 
add-on settings."),
-               # Translators: Title of the reset config dialog.
-               _("Reset configuration"), wx.OK|wx.ICON_INFORMATION)
+# 7.0: This calls copy profile function with default dictionary as the source 
profile.
+def resetConfig(defaults, activeConfig):
+       # The only time everything should be copied is when resetting normal 
profile.
+       copyProfile(defaults, activeConfig, complete=activeConfig.filename == 
SPLIni)
 
 # Reset all profiles upon request.
 def resetAllConfig():
@@ -136,8 +134,9 @@ def resetAllConfig():
                profilePath = profile.filename
                profile.reset()
                profile.filename = profilePath
-               for setting in _SPLDefaults7:
-                       profile[setting] = _SPLDefaults7[setting]
+               # 7.0: Without writing the profile, we end up with 
inconsistencies between profile cache and actual profile.
+               profile.write()
+               resetConfig(_SPLDefaults7, profile)
                # Convert certain settings to a different format.
                profile["ColumnAnnouncement"]["IncludedColumns"] = 
set(_SPLDefaults7["ColumnAnnouncement"]["IncludedColumns"])
        # Translators: A dialog message shown when settings were reset to 
defaults.
@@ -220,7 +219,6 @@ def initConfig():
 
 # Unlock (load) profiles from files.
 def unlockConfig(path, profileName=None, prefill=False):
-       t = time.time()
        global _configLoadStatus # To be mutated only during unlock routine.
        # Optimization: Profiles other than normal profile contains 
profile-specific sections only.
        # This speeds up profile loading routine significantly as there is no 
need to call a function to strip global settings.
@@ -234,8 +232,7 @@ def unlockConfig(path, profileName=None, prefill=False):
                if not configTest:
                        # Case 1: restore settings to defaults when 5.x config 
validation has failed on all values.
                        # 6.0: In case this is a user profile, apply base 
configuration.
-                       baseProfile = _SPLDefaults7 if prefill else 
SPLConfigPool[0]
-                       resetConfig(baseProfile, SPLConfigCheckpoint)
+                       resetConfig(_SPLDefaults7, SPLConfigCheckpoint)
                        _configLoadStatus[profileName] = "completeReset"
                elif isinstance(configTest, dict):
                        # Case 2: For 5.x and later, attempt to reconstruct the 
failed values.
@@ -265,7 +262,6 @@ def unlockConfig(path, profileName=None, prefill=False):
        # 7.0 optimization: Store an online backup.
        # This online backup is used to prolong SSD life (no need to save a 
config if it is same as this copy).
        _cacheConfig(SPLConfigCheckpoint)
-       print time.time()-t
        return SPLConfigCheckpoint
 
 # Extra initialization steps such as converting value types.
@@ -304,7 +300,7 @@ _SPLCache = {}
 def _cacheConfig(conf):
        global _SPLCache
        if _SPLCache is None: _SPLCache = {}
-       key = None if conf.name == SPLActiveProfile else conf.name
+       key = None if conf.filename == SPLIni else conf.name
        _SPLCache[key] = {}
        # Optimization: For broadcast profiles, copy profile-specific keys only.
        for setting in conf.keys():
@@ -331,12 +327,11 @@ def initProfileTriggers():
        except IOError:
                pass
        # Cache profile triggers, used to compare the runtime dictionary 
against the cache.
-       profileTriggers2 = profileTriggers
+       profileTriggers2 = dict(profileTriggers)
        triggerStart()
 
 # Locate time-based profiles if any.
 # A 3-tuple will be returned, containing the next trigger time (for time delta 
calculation), the profile name for this trigger time and whether an immediate 
switch is necessary.
-# For now, the third field will be ignored (always set to False).
 def nextTimedProfile(current=None):
        if current is None: current = datetime.datetime.now()
        # No need to proceed if no timed profiles are defined.
@@ -353,8 +348,6 @@ def nextTimedProfile(current=None):
                        if (current-triggerTime).seconds < entry[6]*60:
                                shouldBeSwitched = True
                possibleTriggers.append((triggerTime, profile, 
shouldBeSwitched))
-       if len(possibleTriggers):
-               d = min(possibleTriggers)[0] - current
        return min(possibleTriggers) if len(possibleTriggers) else None
 
 # Some helpers used in locating next air date/time.
@@ -401,7 +394,7 @@ def setNextTimedProfile(profile, bits, switchTime, 
date=None):
 # Find if another profile is occupying the specified time slot.
 def duplicateExists(map, profile, bits, hour, min, duration):
        if len(map) == 0 or (len(map) == 1 and profile in map): return False
-       # Convdrt hours and minutes to an integer for faster comparison.
+       # Convert hours and minutes to an integer for faster comparison.
        start1 = (hour*60) + min
        end1 = start1+duration
        # A possible duplicate may exist simply because of bits.
@@ -462,22 +455,26 @@ def getProfileIndexByName(name):
 def getProfileByName(name):
        return SPLConfigPool[getProfileIndexByName(name)]
 
+# Copy settings across profiles.
+# Setting complete flag controls whether profile-specific settings are applied 
(true otherwise, only set when resetting profiles).
+def copyProfile(sourceProfile, targetProfile, complete=False):
+       for section in sourceProfile.keys() if complete else 
_mutatableSettings7:
+               targetProfile[section] = dict(sourceProfile[section])
+
 # Merge sections when switching profiles.
 # This is also employed by the routine which saves changes to a profile when 
user selects a different profile from add-on settings dialog.
 # Profiles refer to indecies.
 # Active refers to whether this is a runtime switch (false if saving profiles).
 def mergeSections(profile, active=True):
        global SPLConfig, SPLConfigPool
-       for section in _mutatableSettings7:
-               SPLConfig[section] = dict(SPLConfigPool[profile][section])
+       copyProfile(SPLConfigPool[profile], SPLConfig)
        if active: SPLConfig["ActiveIndex"] = profile
 
 # A reverse of the above.
 def applySections(profile, key=None):
        global SPLConfig, SPLConfigPool
        if key is None:
-               for section in _mutatableSettings7:
-                       SPLConfigPool[profile][section] = 
dict(SPLConfig[section])
+               copyProfile(SPLConfig, SPLConfigPool[profile])
        else:
                # A slash (/) will denote section/key hierarchy.
                tree, leaf = key.split("/")
@@ -2004,6 +2001,54 @@ class SPLAlarmDialog(wx.Dialog):
                global _alarmDialogOpened
                _alarmDialogOpened = False
 
+
+# Startup dialogs.
+
+# Audio ducking reminder (NVDA 2016.1 and later).
+class AudioDuckingReminder(wx.Dialog):
+       """A dialog to remind users to turn off audio ducking (NVDA 2016.1 and 
later).
+       """
+
+       def __init__(self, parent):
+               super(AudioDuckingReminder, self).__init__(parent, title=_("SPL 
Studio and audio ducking"))
+
+               mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+               # Translators: A message displayed if audio ducking should be 
disabled.
+               label = wx.StaticText(self, wx.ID_ANY, label=_("NVDA 2016.1 and 
later allows NVDA to decrease volume of background audio including that of 
Studio. In order to not disrupt the listening experience of your listeners, 
please disable audio ducking by opening synthesizer dialog in NVDA and 
selecting 'no ducking' from audio ducking mode combo box or press 
NVDA+Shift+D."))
+               mainSizer.Add(label,border=20,flag=wx.LEFT|wx.RIGHT|wx.TOP)
+
+               sizer = wx.BoxSizer(wx.HORIZONTAL)
+               # Translators: A checkbox to turn off audio ducking reminder 
message.
+               
self.audioDuckingReminder=wx.CheckBox(self,wx.NewId(),label=_("Do not show this 
message again"))
+               self.audioDuckingReminder.SetValue(not 
SPLConfig["Startup"]["AudioDuckingReminder"])
+               sizer.Add(self.audioDuckingReminder, border=10,flag=wx.TOP)
+               mainSizer.Add(sizer, border=10, flag=wx.BOTTOM)
+
+               mainSizer.Add(self.CreateButtonSizer(wx.OK))
+               self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
+               mainSizer.Fit(self)
+               self.Sizer = mainSizer
+               self.audioDuckingReminder.SetFocus()
+               self.Center(wx.BOTH | wx.CENTER_ON_SCREEN)
+
+       def onOk(self, evt):
+               if self.audioDuckingReminder.Value:
+                       SPLConfig["Startup"]["AudioDuckingReminder"] = not 
self.audioDuckingReminder.Value
+               self.Destroy()
+
+# And to open the above dialog and any other dialogs.
+def showStartupDialogs():
+       try:
+               import audioDucking
+               if SPLConfig["Startup"]["AudioDuckingReminder"] and 
audioDucking.isAudioDuckingSupported():
+                       gui.mainFrame.prePopup()
+                       AudioDuckingReminder(gui.mainFrame).Show()
+                       gui.mainFrame.postPopup()
+       except ImportError:
+               pass
+
+
 # Message verbosity pool.
 # To be moved to its own module in add-on 7.0.
 # This is a multimap, consisting of category, value and message.

diff --git a/addon/installTasks.py b/addon/installTasks.py
index a6775ca..84cf354 100755
--- a/addon/installTasks.py
+++ b/addon/installTasks.py
@@ -39,8 +39,8 @@ SayListenerCount = boolean(default=true)
 SayPlayingCartName = boolean(default=true)
 SayPlayingTrackName = string(default="True")
 SPLConPassthrough = boolean(default=false)
-CompatibilityLayer = option("off", "jfw", "wineyes", default="off")
-AutoUpdateCheck = boolean(default=true)
+CompatibilityLayer = option("off", "jfw", default="off")
+AudioDuckingReminder = boolean(default=true)
 """), encoding="UTF-8", list_values=False)
 confspec.newlines = "\r\n"
 
@@ -70,14 +70,14 @@ _conversionConfig = {
        "SayPlayingTrackName":"SayStatus",
        "SPLConPassthrough":"Advanced",
        "CompatibilityLayer":"Advanced",
-       "AutoUpdateCheck":"Update",
+       "AudioDuckingReminder":"Startup",
 }
 
 # Invoked when upgrading to 7.0 (to be removed in 7.2).
 def config6to7(path):
        profile = ConfigObj(path, configspec = confspec, encoding="UTF-8")
        # Optimization: no need to convert if sectionized.
-       for section in ["General", "IntroOutroAlarms", "MicrophoneAlarm", 
"ColumnAnnouncement", "MetadataStreaming", "SayStatus", "Advanced", "Update"]:
+       for section in ["General", "IntroOutroAlarms", "MicrophoneAlarm", 
"ColumnAnnouncement", "MetadataStreaming", "SayStatus", "Advanced", "Startup"]:
                if section in profile:
                        return
        # For now, manually convert.

Repository URL: https://bitbucket.org/nvdaaddonteam/stationplaylist/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.

Other related posts: