commit/StationPlaylist: 31 new changesets

  • From: commits-noreply@xxxxxxxxxxxxx
  • To: nvda-addons-commits@xxxxxxxxxxxxx
  • Date: Thu, 29 Dec 2016 20:08:11 -0000

31 new commits in StationPlaylist:

https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/e3643d11792f/
Changeset:   e3643d11792f
Branch:      None
User:        josephsl
Date:        2016-12-10 01:19:16+00:00
Summary:     Alarms Center (17.1-dev): Move SPLAlarmDialog code to config UI 
module.

As a preparation for Alarms Center, moved alarm dialog code to config UI module 
for consistency with other dialogs and metadata streaming dialog.

Affected #:  3 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 037b666..f72709d 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -962,12 +962,12 @@ class AppModule(appModuleHandler.AppModule):
                        wx.CallAfter(gui.messageBox, _("The add-on settings 
dialog is opened. Please close the settings dialog first."), _("Error"), 
wx.OK|wx.ICON_ERROR)
                        return
                try:
-                       d = splconfig.SPLAlarmDialog(gui.mainFrame, level)
+                       d = splconfui.AlarmsCenter(gui.mainFrame, level)
                        gui.mainFrame.prePopup()
                        d.Raise()
                        d.Show()
                        gui.mainFrame.postPopup()
-                       splconfig._alarmDialogOpened = True
+                       splconfui._alarmDialogOpened = True
                except RuntimeError:
                        wx.CallAfter(splconfig._alarmError)
 

diff --git a/addon/appModules/splstudio/splconfig.py 
b/addon/appModules/splstudio/splconfig.py
index b83144e..3351125 100755
--- a/addon/appModules/splstudio/splconfig.py
+++ b/addon/appModules/splstudio/splconfig.py
@@ -851,117 +851,9 @@ def _shouldBuildDescriptionPieces():
        or len(SPLConfig["ColumnAnnouncement"]["IncludedColumns"]) != 17))
 
 
-# Additional configuration dialogs
+# Additional configuration and miscellaneous dialogs
 # See splconfui module for basic configuration dialogs.
 
-# A common alarm dialog
-# Based on NVDA core's find dialog code (implemented by the author of this 
add-on).
-# Extended in 2016 to handle microphone alarms.
-# Only one instance can be active at a given moment (code borrowed from GUI's 
exit dialog routine).
-_alarmDialogOpened = False
-
-# A common alarm error dialog.
-def _alarmError():
-       # Translators: Text of the dialog when another alarm dialog is open.
-       gui.messageBox(_("Another alarm dialog is 
open."),_("Error"),style=wx.OK | wx.ICON_ERROR)
-
-class SPLAlarmDialog(wx.Dialog):
-       """A dialog providing common alarm settings.
-       This dialog contains a number entry field for alarm duration and a 
check box to enable or disable the alarm.
-       For one particular case, it consists of two number entry fields.
-       """
-
-       # The following comes from exit dialog class from GUI package (credit: 
NV Access and Zahari from Bulgaria).
-       _instance = None
-
-       def __new__(cls, parent, *args, **kwargs):
-               # Make this a singleton and prompt an error dialog if it isn't.
-               if _alarmDialogOpened:
-                       raise RuntimeError("An instance of alarm dialog is 
opened")
-               inst = cls._instance() if cls._instance else None
-               if not inst:
-                       return super(cls, cls).__new__(cls, parent, *args, 
**kwargs)
-               return inst
-
-       def __init__(self, parent, level=0):
-               inst = SPLAlarmDialog._instance() if SPLAlarmDialog._instance 
else None
-               if inst:
-                       return
-               # Use a weakref so the instance can die.
-               import weakref
-               SPLAlarmDialog._instance = weakref.ref(self)
-
-               # Now the actual alarm dialog code.
-               # 8.0: Apart from level 0 (all settings shown), levels change 
title.
-               titles = (_("Alarms Center"), _("End of track alarm"), _("Song 
intro alarm"), _("Microphone alarm"))
-               super(SPLAlarmDialog, self).__init__(parent, wx.ID_ANY, 
titles[level])
-               self.level = level
-               mainSizer = wx.BoxSizer(wx.VERTICAL)
-               # 17.1: Utilize various enhancements from GUI helper (added in 
NVDA 2016.4).
-               contentSizerHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
-
-               if level in (0, 1):
-                       timeVal = 
SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
-                       alarmLabel = _("Enter &end of track alarm time in 
seconds (currently {curAlarmSec})").format(curAlarmSec = timeVal)
-                       self.outroAlarmEntry = 
contentSizerHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=59, initial=timeVal)
-                       
self.outroToggleCheckBox=contentSizerHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of track is approaching")))
-                       
self.outroToggleCheckBox.SetValue(SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
-
-               if level in (0, 2):
-                       rampVal = SPLConfig["IntroOutroAlarms"]["SongRampTime"]
-                       alarmLabel = _("Enter song &intro alarm time in seconds 
(currently {curRampSec})").format(curRampSec = rampVal)
-                       self.introAlarmEntry = 
contentSizerHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=9, initial=rampVal)
-                       
self.introToggleCheckBox=contentSizerHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of introduction is approaching")))
-                       
self.introToggleCheckBox.SetValue(SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
-
-               if level in (0, 3):
-                       micAlarm = SPLConfig["MicrophoneAlarm"]["MicAlarm"]
-                       micAlarmInterval = 
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"]
-                       if micAlarm:
-                               # Translators: A dialog message to set 
microphone active alarm (curAlarmSec is the current mic monitoring alarm in 
seconds).
-                               timeMSG = _("Enter microphone alarm time in 
seconds (currently {curAlarmSec}, 0 disables the alarm)").format(curAlarmSec = 
micAlarm)
-                       else:
-                               # Translators: A dialog message when microphone 
alarm is disabled (set to 0).
-                               timeMSG = _("Enter microphone alarm time in 
seconds (currently disabled, 0 disables the alarm)")
-                       micIntervalMSG = _("Microphone alarm interval")
-                       self.micAlarmEntry = 
contentSizerHelper.addLabeledControl(timeMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, initial=micAlarm)
-                       self.micIntervalEntry = 
contentSizerHelper.addLabeledControl(micIntervalMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=60, initial=micAlarmInterval)
-
-               
contentSizerHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
-               self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
-               self.Bind(wx.EVT_BUTTON, self.onCancel, id=wx.ID_CANCEL)
-               mainSizer.Add(contentSizerHelper.sizer, 
border=gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
-               mainSizer.Fit(self)
-               self.SetSizer(mainSizer)
-               self.Center(wx.BOTH | wx.CENTER_ON_SCREEN)
-               if level in (0, 1): self.outroAlarmEntry.SetFocus()
-               elif level == 2: self.introAlarmEntry.SetFocus()
-               elif level == 3: self.micAlarmEntry.SetFocus()
-
-       def onOk(self, evt):
-               global SPLConfig, _alarmDialogOpened
-               # Optimization: don't bother if Studio is dead and if the same 
value has been entered.
-               import winUser
-               if winUser.user32.FindWindowA("SPLStudio", None):
-                       # Gather settings to be applied in section/key format.
-                       if self.level in (0, 1):
-                               SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] 
= self.outroAlarmEntry.GetValue()
-                               SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] 
= self.outroToggleCheckBox.GetValue()
-                       elif self.level in (0, 2):
-                               SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.introAlarmEntry.GetValue()
-                               SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.introToggleCheckBox.GetValue()
-                       elif self.level in (0, 3):
-                               SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarmEntry.GetValue()
-                               
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micIntervalEntry.GetValue()
-               self.Destroy()
-               _alarmDialogOpened = False
-
-       def onCancel(self, evt):
-               self.Destroy()
-               global _alarmDialogOpened
-               _alarmDialogOpened = False
-
-
 # Startup dialogs.
 
 # Audio ducking reminder (NVDA 2016.1 and later).

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index 32d41bb..c798efb 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -815,6 +815,114 @@ class TriggersDialog(wx.Dialog):
                        prompt.Enable() if self.timeSwitchCheckbox.IsChecked() 
else prompt.Disable()
                self.Fit()
 
+# A common alarm dialog (Alarms Center)
+# Based on NVDA core's find dialog code (implemented by the author of this 
add-on).
+# Extended in 2016 to handle microphone alarms.
+# Only one instance can be active at a given moment (code borrowed from GUI's 
exit dialog routine).
+_alarmDialogOpened = False
+
+# A common alarm error dialog.
+def _alarmError():
+       # Translators: Text of the dialog when another alarm dialog is open.
+       gui.messageBox(_("Another alarm dialog is 
open."),_("Error"),style=wx.OK | wx.ICON_ERROR)
+
+class AlarmsCenter(wx.Dialog):
+       """A dialog providing common alarm settings.
+       This dialog contains a number entry field for alarm duration and a 
check box to enable or disable the alarm.
+       For one particular case, it consists of two number entry fields.
+       """
+
+       # The following comes from exit dialog class from GUI package (credit: 
NV Access and Zahari from Bulgaria).
+       _instance = None
+
+       def __new__(cls, parent, *args, **kwargs):
+               # Make this a singleton and prompt an error dialog if it isn't.
+               if _alarmDialogOpened:
+                       raise RuntimeError("An instance of alarm dialog is 
opened")
+               inst = cls._instance() if cls._instance else None
+               if not inst:
+                       return super(cls, cls).__new__(cls, parent, *args, 
**kwargs)
+               return inst
+
+       def __init__(self, parent, level=0):
+               inst = AlarmsCenter._instance() if AlarmsCenter._instance else 
None
+               if inst:
+                       return
+               # Use a weakref so the instance can die.
+               import weakref
+               AlarmsCenter._instance = weakref.ref(self)
+
+               # Now the actual alarm dialog code.
+               # 8.0: Apart from level 0 (all settings shown), levels change 
title.
+               titles = (_("Alarms Center"), _("End of track alarm"), _("Song 
intro alarm"), _("Microphone alarm"))
+               super(AlarmsCenter, self).__init__(parent, wx.ID_ANY, 
titles[level])
+               self.level = level
+               mainSizer = wx.BoxSizer(wx.VERTICAL)
+               # 17.1: Utilize various enhancements from GUI helper (added in 
NVDA 2016.4).
+               contentSizerHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
+
+               if level in (0, 1):
+                       timeVal = 
SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
+                       alarmLabel = _("Enter &end of track alarm time in 
seconds (currently {curAlarmSec})").format(curAlarmSec = timeVal)
+                       self.outroAlarmEntry = 
contentSizerHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=59, initial=timeVal)
+                       
self.outroToggleCheckBox=contentSizerHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of track is approaching")))
+                       
self.outroToggleCheckBox.SetValue(SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
+
+               if level in (0, 2):
+                       rampVal = SPLConfig["IntroOutroAlarms"]["SongRampTime"]
+                       alarmLabel = _("Enter song &intro alarm time in seconds 
(currently {curRampSec})").format(curRampSec = rampVal)
+                       self.introAlarmEntry = 
contentSizerHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=9, initial=rampVal)
+                       
self.introToggleCheckBox=contentSizerHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of introduction is approaching")))
+                       
self.introToggleCheckBox.SetValue(SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
+
+               if level in (0, 3):
+                       micAlarm = SPLConfig["MicrophoneAlarm"]["MicAlarm"]
+                       micAlarmInterval = 
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"]
+                       if micAlarm:
+                               # Translators: A dialog message to set 
microphone active alarm (curAlarmSec is the current mic monitoring alarm in 
seconds).
+                               timeMSG = _("Enter microphone alarm time in 
seconds (currently {curAlarmSec}, 0 disables the alarm)").format(curAlarmSec = 
micAlarm)
+                       else:
+                               # Translators: A dialog message when microphone 
alarm is disabled (set to 0).
+                               timeMSG = _("Enter microphone alarm time in 
seconds (currently disabled, 0 disables the alarm)")
+                       micIntervalMSG = _("Microphone alarm interval")
+                       self.micAlarmEntry = 
contentSizerHelper.addLabeledControl(timeMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, initial=micAlarm)
+                       self.micIntervalEntry = 
contentSizerHelper.addLabeledControl(micIntervalMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=60, initial=micAlarmInterval)
+
+               
contentSizerHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
+               self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
+               self.Bind(wx.EVT_BUTTON, self.onCancel, id=wx.ID_CANCEL)
+               mainSizer.Add(contentSizerHelper.sizer, 
border=gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
+               mainSizer.Fit(self)
+               self.SetSizer(mainSizer)
+               self.Center(wx.BOTH | wx.CENTER_ON_SCREEN)
+               if level in (0, 1): self.outroAlarmEntry.SetFocus()
+               elif level == 2: self.introAlarmEntry.SetFocus()
+               elif level == 3: self.micAlarmEntry.SetFocus()
+
+       def onOk(self, evt):
+               global SPLConfig, _alarmDialogOpened
+               # Optimization: don't bother if Studio is dead and if the same 
value has been entered.
+               import winUser
+               if winUser.user32.FindWindowA("SPLStudio", None):
+                       # Gather settings to be applied in section/key format.
+                       if self.level in (0, 1):
+                               SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] 
= self.outroAlarmEntry.GetValue()
+                               SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] 
= self.outroToggleCheckBox.GetValue()
+                       elif self.level in (0, 2):
+                               SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.introAlarmEntry.GetValue()
+                               SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.introToggleCheckBox.GetValue()
+                       elif self.level in (0, 3):
+                               SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarmEntry.GetValue()
+                               
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micIntervalEntry.GetValue()
+               self.Destroy()
+               _alarmDialogOpened = False
+
+       def onCancel(self, evt):
+               self.Destroy()
+               global _alarmDialogOpened
+               _alarmDialogOpened = False
+
+
 # Metadata reminder controller.
 # Select notification/streaming URL's for metadata streaming.
 _metadataDialogOpened = False


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/ff156699b9db/
Changeset:   ff156699b9db
Branch:      None
User:        josephsl
Date:        2016-12-10 16:44:35+00:00
Summary:     Alarms Center (17.1-dev): Officialy launch Alarms Center UI.

Now that Alarms Center has moved to config UI module, make sure changes to 
where SPLConfig and others are stored are reflected.
The Alarms Center UI is here, and can be accessed from Alarms Center button in 
main add-on config dialog. For now, only the settings are displayed (OK and 
Cancel buttons virtually do nothing for now). The old alarms UI from main 
config dialog will be kept for a while.

Affected #:  1 file

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index c798efb..502cf86 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -127,6 +127,10 @@ class SPLConfigDialog(gui.SettingsDialog):
                self.onIntroCheck(None)
                SPLConfigHelper.addItem(self.introSizer)
 
+               # Translators: The label of a button to open a dialog to 
configure various alarms.
+               alarmsCenterButton = SPLConfigHelper.addItem(wx.Button(self, 
label=_("&Alarms Center...")))
+               alarmsCenterButton.Bind(wx.EVT_BUTTON, self.onAlarmsCenter)
+
                self.brailleTimerValues=[("off",_("Off")),
                # Translators: One of the braille timer settings.
                ("outro",_("Track ending")),
@@ -561,7 +565,12 @@ class SPLConfigDialog(gui.SettingsDialog):
                action(flag)
                self.profiles.SetString(index, profile if not len(flags) else 
"{0} <{1}>".format(profile, ", ".join(flags)))
 
-       # Manage metadata streaming.
+       # Alarms Center.
+       def onAlarmsCenter(self, evt):
+               self.Disable()
+               AlarmsCenter(self).Show()
+
+               # Manage metadata streaming.
        def onManageMetadata(self, evt):
                self.Disable()
                MetadataStreamingDialog(self).Show()
@@ -602,7 +611,7 @@ class SPLConfigDialog(gui.SettingsDialog):
 # Open the above dialog upon request.
 def onConfigDialog(evt):
        # 5.2: Guard against alarm dialogs.
-       if splconfig._alarmDialogOpened or _metadataDialogOpened:
+       if _alarmDialogOpened or _metadataDialogOpened:
                # Translators: Presented when an alarm dialog is opened.
                wx.CallAfter(gui.messageBox, _("Another add-on settings dialog 
is open. Please close the previously opened dialog first."), _("Error"), 
wx.OK|wx.ICON_ERROR)
        else: gui.mainFrame._popupSettingsDialog(SPLConfigDialog)
@@ -858,26 +867,25 @@ class AlarmsCenter(wx.Dialog):
                super(AlarmsCenter, self).__init__(parent, wx.ID_ANY, 
titles[level])
                self.level = level
                mainSizer = wx.BoxSizer(wx.VERTICAL)
-               # 17.1: Utilize various enhancements from GUI helper (added in 
NVDA 2016.4).
-               contentSizerHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
+               alarmsCenterHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
 
                if level in (0, 1):
-                       timeVal = 
SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
+                       timeVal = 
splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
                        alarmLabel = _("Enter &end of track alarm time in 
seconds (currently {curAlarmSec})").format(curAlarmSec = timeVal)
-                       self.outroAlarmEntry = 
contentSizerHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=59, initial=timeVal)
-                       
self.outroToggleCheckBox=contentSizerHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of track is approaching")))
-                       
self.outroToggleCheckBox.SetValue(SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
+                       self.outroAlarmEntry = 
alarmsCenterHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=59, initial=timeVal)
+                       
self.outroToggleCheckBox=alarmsCenterHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of track is approaching")))
+                       
self.outroToggleCheckBox.SetValue(splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
 
                if level in (0, 2):
-                       rampVal = SPLConfig["IntroOutroAlarms"]["SongRampTime"]
+                       rampVal = 
splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"]
                        alarmLabel = _("Enter song &intro alarm time in seconds 
(currently {curRampSec})").format(curRampSec = rampVal)
-                       self.introAlarmEntry = 
contentSizerHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=9, initial=rampVal)
-                       
self.introToggleCheckBox=contentSizerHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of introduction is approaching")))
-                       
self.introToggleCheckBox.SetValue(SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
+                       self.introAlarmEntry = 
alarmsCenterHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=9, initial=rampVal)
+                       
self.introToggleCheckBox=alarmsCenterHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of introduction is approaching")))
+                       
self.introToggleCheckBox.SetValue(splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
 
                if level in (0, 3):
-                       micAlarm = SPLConfig["MicrophoneAlarm"]["MicAlarm"]
-                       micAlarmInterval = 
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"]
+                       micAlarm = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"]
+                       micAlarmInterval = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"]
                        if micAlarm:
                                # Translators: A dialog message to set 
microphone active alarm (curAlarmSec is the current mic monitoring alarm in 
seconds).
                                timeMSG = _("Enter microphone alarm time in 
seconds (currently {curAlarmSec}, 0 disables the alarm)").format(curAlarmSec = 
micAlarm)
@@ -885,13 +893,13 @@ class AlarmsCenter(wx.Dialog):
                                # Translators: A dialog message when microphone 
alarm is disabled (set to 0).
                                timeMSG = _("Enter microphone alarm time in 
seconds (currently disabled, 0 disables the alarm)")
                        micIntervalMSG = _("Microphone alarm interval")
-                       self.micAlarmEntry = 
contentSizerHelper.addLabeledControl(timeMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, initial=micAlarm)
-                       self.micIntervalEntry = 
contentSizerHelper.addLabeledControl(micIntervalMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=60, initial=micAlarmInterval)
+                       self.micAlarmEntry = 
alarmsCenterHelper.addLabeledControl(timeMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, initial=micAlarm)
+                       self.micIntervalEntry = 
alarmsCenterHelper.addLabeledControl(micIntervalMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=60, initial=micAlarmInterval)
 
-               
contentSizerHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
+               
alarmsCenterHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
                self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
                self.Bind(wx.EVT_BUTTON, self.onCancel, id=wx.ID_CANCEL)
-               mainSizer.Add(contentSizerHelper.sizer, 
border=gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
+               mainSizer.Add(alarmsCenterHelper.sizer, 
border=gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
                mainSizer.Fit(self)
                self.SetSizer(mainSizer)
                self.Center(wx.BOTH | wx.CENTER_ON_SCREEN)
@@ -900,10 +908,9 @@ class AlarmsCenter(wx.Dialog):
                elif level == 3: self.micAlarmEntry.SetFocus()
 
        def onOk(self, evt):
-               global SPLConfig, _alarmDialogOpened
+               global _alarmDialogOpened
                # Optimization: don't bother if Studio is dead and if the same 
value has been entered.
-               import winUser
-               if winUser.user32.FindWindowA("SPLStudio", None):
+               """if user32.FindWindowA("SPLStudio", None):
                        # Gather settings to be applied in section/key format.
                        if self.level in (0, 1):
                                SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] 
= self.outroAlarmEntry.GetValue()
@@ -913,16 +920,20 @@ class AlarmsCenter(wx.Dialog):
                                SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.introToggleCheckBox.GetValue()
                        elif self.level in (0, 3):
                                SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarmEntry.GetValue()
-                               
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micIntervalEntry.GetValue()
+                               
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micIntervalEntry.GetValue()"""
+               if self.level == 0:
+                       self.Parent.profiles.SetFocus()
+                       self.Parent.Enable()
                self.Destroy()
                _alarmDialogOpened = False
 
        def onCancel(self, evt):
+               if self.level == 0:
+                       self.Parent.Enable()
                self.Destroy()
                global _alarmDialogOpened
                _alarmDialogOpened = False
 
-
 # Metadata reminder controller.
 # Select notification/streaming URL's for metadata streaming.
 _metadataDialogOpened = False


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/c75619470a30/
Changeset:   c75619470a30
Branch:      None
User:        josephsl
Date:        2016-12-10 16:56:16+00:00
Summary:     Merged stable

Affected #:  1 file

diff --git a/readme.md b/readme.md
index 69b4872..d9285a0 100755
--- a/readme.md
+++ b/readme.md
@@ -172,6 +172,11 @@ If you are using Studio on a touchscreen computer running 
Windows 8 or later and
 * Added a combo box in add-on settings dialog to set which column should be 
announced when moving through columns vertically.
 * Removed Track Dial (NVDA's version of enhanced arrow keys), replaced by 
Columns explorer and Column Navigator/table navigation commands). This affects 
Studio and Track Tool.
 
+## Version 16.12.1
+
+* Corrected user interface presentation for SPL add-on settings dialog.
+* Updated translations.
+
 ## Version 16.12/15.4-LTS
 
 * More work on supporting Studio 5.20, including announcing cart insert mode 
status (if turned on) from SPL Assistant layer (T).


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/df211b104de1/
Changeset:   df211b104de1
Branch:      None
User:        josephsl
Date:        2016-12-11 02:02:33+00:00
Summary:     GUI Helper services (17.1-dev): encoder config dialog is now 
powered by GUI Helper.

One of the last remaining dialogs to be converted: encoder config dialog (part 
of the global plugin) is now powered by GUI Helper.

Affected #:  1 file

diff --git a/addon/globalPlugins/SPLStudioUtils/encoders.py 
b/addon/globalPlugins/SPLStudioUtils/encoders.py
index fa1dda9..b24eb40 100755
--- a/addon/globalPlugins/SPLStudioUtils/encoders.py
+++ b/addon/globalPlugins/SPLStudioUtils/encoders.py
@@ -195,36 +195,29 @@ class EncoderConfigDialog(wx.Dialog):
                # Translators: The title of the encoder settings dialog 
(example: Encoder settings for SAM 1").
                super(EncoderConfigDialog, self).__init__(parent, wx.ID_ANY, 
_("Encoder settings for {name}").format(name = title))
                mainSizer = wx.BoxSizer(wx.VERTICAL)
+               encoderConfigHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
 
-               sizer = wx.BoxSizer(wx.HORIZONTAL)
                # Translators: An edit field in encoder settings to set stream 
label for this encoder.
-               streamLabelPrompt = wx.StaticText(self, wx.ID_ANY, 
label=_("Stream &label"))
-               sizer.Add(streamLabelPrompt)
-               self.streamLabel = wx.TextCtrl(self, wx.ID_ANY)
+               self.streamLabel = 
encoderConfigHelper.addLabeledControl(_("Stream &label"), wx.TextCtrl)
                self.streamLabel.SetValue(self.curStreamLabel if 
self.curStreamLabel is not None else "")
-               sizer.Add(self.streamLabel)
-               mainSizer.Add(sizer,border=20,flag=wx.LEFT|wx.RIGHT|wx.TOP)
 
                # Translators: A checkbox in encoder settings to set if NvDA 
should switch focus to Studio window when connected.
-               self.focusToStudio=wx.CheckBox(self,wx.NewId(),label=_("&Focus 
to Studio when connected"))
+               self.focusToStudio = 
encoderConfigHelper.addItem(wx.CheckBox(self, label=_("&Focus to Studio when 
connected")))
                self.focusToStudio.SetValue(obj.getEncoderId() in 
SPLFocusToStudio)
-               mainSizer.Add(self.focusToStudio,border=10,flag=wx.BOTTOM)
                # Translators: A checkbox in encoder settings to set if NvDA 
should play the next track when connected.
-               
self.playAfterConnecting=wx.CheckBox(self,wx.NewId(),label=_("&Play first track 
when connected"))
+               self.playAfterConnecting = 
encoderConfigHelper.addItem(wx.CheckBox(self, label=_("&Play first track when 
connected")))
                self.playAfterConnecting.SetValue(obj.getEncoderId() in 
SPLPlayAfterConnecting)
-               mainSizer.Add(self.playAfterConnecting,border=10,flag=wx.BOTTOM)
                # Translators: A checkbox in encoder settings to set if NvDA 
should monitor the status of this encoder in the background.
-               
self.backgroundMonitor=wx.CheckBox(self,wx.NewId(),label=_("Enable background 
connection &monitoring"))
+               self.backgroundMonitor = 
encoderConfigHelper.addItem(wx.CheckBox(self, label=_("Enable background 
connection &monitoring")))
                self.backgroundMonitor.SetValue(obj.getEncoderId() in 
SPLBackgroundMonitor)
-               mainSizer.Add(self.backgroundMonitor,border=10,flag=wx.BOTTOM)
                # Translators: A checkbox in encoder settings to set if NvDA 
should play connection progress tone.
-               self.noConnectionTone=wx.CheckBox(self,wx.NewId(),label=_("Play 
connection status &beep while connecting"))
+               self.noConnectionTone = 
encoderConfigHelper.addItem(wx.CheckBox(self, label=_("Play connection status 
&beep while connecting")))
                self.noConnectionTone.SetValue(obj.getEncoderId() not in 
SPLNoConnectionTone)
-               mainSizer.Add(self.noConnectionTone,border=10,flag=wx.BOTTOM)
 
-               mainSizer.AddSizer(self.CreateButtonSizer(wx.OK|wx.CANCEL))
+               
encoderConfigHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
                self.Bind(wx.EVT_BUTTON,self.onOk,id=wx.ID_OK)
                self.Bind(wx.EVT_BUTTON,self.onCancel,id=wx.ID_CANCEL)
+               mainSizer.Add(encoderConfigHelper.sizer, border = 
gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
                mainSizer.Fit(self)
                self.SetSizer(mainSizer)
                self.Center(wx.BOTH | wx.CENTER_ON_SCREEN)


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/75d4aad286d9/
Changeset:   75d4aad286d9
Branch:      None
User:        josephsl
Date:        2016-12-13 02:33:17+00:00
Summary:     SPL Creator: Initial foundation to provide support for Creator, 
starting with announcing dialog texts.

The registration and About dialogs in SPL Creator (a tool used for managing 
playlists to be broadcast via Studio) contents are now announced, a foundation 
commit for providing support for Creator (2017).

Affected #:  1 file

diff --git a/addon/appModules/splcreator.py b/addon/appModules/splcreator.py
new file mode 100755
index 0000000..860ecc7
--- /dev/null
+++ b/addon/appModules/splcreator.py
@@ -0,0 +1,17 @@
+# StationPlaylist Creator
+# An app module and global plugin package for NVDA
+# Copyright 2016, Joseph Lee and others, released under GPL.
+
+import controlTypes
+import appModuleHandler
+import api
+from NVDAObjects.behaviors import Dialog
+import addonHandler
+addonHandler.initTranslation()
+
+
+class AppModule(appModuleHandler.AppModule):
+
+       def chooseNVDAObjectOverlayClasses(self, obj, clsList):
+               if obj.windowClassName in ("TDemoRegForm", "TAboutForm"):
+                       clsList.insert(0, Dialog)


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/c78117c3aee8/
Changeset:   c78117c3aee8
Branch:      None
User:        josephsl
Date:        2016-12-15 16:32:37+00:00
Summary:     Alarms Center (17.1-dev): Alarms Center is operational, removed 
old alarm config controls from main settings dialog.

Now that Alarms Center is here, there's no need to keep old controls in 
settings dialog. Consequently, checkbox hiding routine and related routines are 
no more, all handled form new Alarms Center.
This is destined for 2017.

Affected #:  1 file

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index 502cf86..5d6d0df 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -92,41 +92,12 @@ class SPLConfigDialog(gui.SettingsDialog):
                except:
                        pass
 
-               self.outroSizer = wx.BoxSizer(wx.HORIZONTAL)
-               # Check box hiding method comes from Alberto Buffolino's 
Columns Review add-on.
-               # Translators: Label for a check box in SPL add-on settings to 
notify when end of track (outro) is approaching.
-               self.outroCheckBox=wx.CheckBox(self,wx.NewId(),label=_("&Notify 
when end of track is approaching"))
-               
self.outroCheckBox.SetValue(splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
-               self.outroCheckBox.Bind(wx.EVT_CHECKBOX, self.onOutroCheck)
-               self.outroSizer.Add(self.outroCheckBox, 
border=10,flag=wx.BOTTOM)
-
-               # Translators: The label for a setting in SPL Add-on settings 
to specify end of track (outro) alarm.
-               self.outroAlarmLabel = wx.StaticText(self, wx.ID_ANY, 
label=_("&End of track alarm in seconds"))
-               self.outroSizer.Add(self.outroAlarmLabel)
-               self.endOfTrackAlarm = wx.SpinCtrl(self, wx.ID_ANY, min=1, 
max=59)
-               
self.endOfTrackAlarm.SetValue(long(splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]))
-               self.endOfTrackAlarm.SetSelection(-1, -1)
-               self.outroSizer.Add(self.endOfTrackAlarm)
-               self.onOutroCheck(None)
-               SPLConfigHelper.addItem(self.outroSizer)
-
-               self.introSizer = wx.BoxSizer(wx.HORIZONTAL)
-               # Translators: Label for a check box in SPL add-on settings to 
notify when end of intro is approaching.
-               self.introCheckBox=wx.CheckBox(self,wx.NewId(),label=_("&Notify 
when end of introduction is approaching"))
-               
self.introCheckBox.SetValue(splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
-               self.introCheckBox.Bind(wx.EVT_CHECKBOX, self.onIntroCheck)
-               self.introSizer.Add(self.introCheckBox, 
border=10,flag=wx.BOTTOM)
-
-               # Translators: The label for a setting in SPL Add-on settings 
to specify track intro alarm.
-               self.introAlarmLabel = wx.StaticText(self, wx.ID_ANY, 
label=_("&Track intro alarm in seconds"))
-               self.introSizer.Add(self.introAlarmLabel)
-               self.songRampAlarm = wx.SpinCtrl(self, wx.ID_ANY, min=1, max=9)
-               
self.songRampAlarm.SetValue(long(splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"]))
-               self.songRampAlarm.SetSelection(-1, -1)
-               self.introSizer.Add(self.songRampAlarm)
-               self.onIntroCheck(None)
-               SPLConfigHelper.addItem(self.introSizer)
-
+               self.endOfTrackTime = 
splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
+               self.sayEndOfTrack = 
splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"]
+               self.songRampTime = 
splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"]
+               self.saySongRamp = 
splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"]
+               self.micAlarm = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"]
+               self.micAlarmInterval = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"]
                # Translators: The label of a button to open a dialog to 
configure various alarms.
                alarmsCenterButton = SPLConfigHelper.addItem(wx.Button(self, 
label=_("&Alarms Center...")))
                alarmsCenterButton.Bind(wx.EVT_BUTTON, self.onAlarmsCenter)
@@ -147,13 +118,6 @@ class SPLConfigDialog(gui.SettingsDialog):
                except:
                        pass
 
-               sizer = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.HORIZONTAL)
-               # Translators: The label for a setting in SPL Add-on settings 
to change microphone alarm setting.
-               self.micAlarm = sizer.addLabeledControl(_("&Microphone alarm in 
seconds"), gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, 
initial=splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"])
-               # Translators: The label for a setting in SPL Add-on settings 
to specify mic alarm interval.
-               self.micAlarmInterval = sizer.addLabeledControl(_("Microphone 
alarm &interval in seconds"), gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, 
max=60, initial=splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"])
-               SPLConfigHelper.addItem(sizer)
-
                # Translators: One of the alarm notification options.
                self.alarmAnnounceValues=[("beep",_("beep")),
                # Translators: One of the alarm notification options.
@@ -303,13 +267,13 @@ class SPLConfigDialog(gui.SettingsDialog):
                        
splconfig.SPLConfig.swapProfiles(splconfig.SPLConfig.activeProfile, 
selectedProfile)
                splconfig.SPLConfig["General"]["BeepAnnounce"] = 
self.beepAnnounceCheckbox.Value
                splconfig.SPLConfig["General"]["MessageVerbosity"] = 
self.verbosityLevels[self.verbosityList.GetSelection()][0]
-               splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] = 
self.outroCheckBox.Value
-               splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] = 
self.endOfTrackAlarm.Value
-               splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.introCheckBox.Value
-               splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.songRampAlarm.Value
+               splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] = 
self.endOfTrackTime
+               splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] = 
self.sayEndOfTrack
+               splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.songRampTime
+               splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.saySongRamp
+               splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarm
+               splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micAlarmInterval
                splconfig.SPLConfig["General"]["BrailleTimer"] = 
self.brailleTimerValues[self.brailleTimerList.GetSelection()][0]
-               splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarm.Value
-               splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micAlarmInterval.Value
                splconfig.SPLConfig["General"]["AlarmAnnounce"] = 
self.alarmAnnounceValues[self.alarmAnnounceList.GetSelection()][0]
                splconfig.SPLConfig["General"]["LibraryScanAnnounce"] = 
self.libScanValues[self.libScanList.GetSelection()][0]
                splconfig.SPLConfig["General"]["TimeHourAnnounce"] = 
self.hourAnnounceCheckbox.Value
@@ -395,25 +359,6 @@ class SPLConfigDialog(gui.SettingsDialog):
                else:
                        if splupdate._SPLUpdateT is None: splconfig.updateInit()
 
-       # Check events for outro and intro alarms, respectively.
-       def onOutroCheck(self, evt):
-               if not self.outroCheckBox.IsChecked():
-                       self.outroSizer.Hide(self.outroAlarmLabel)
-                       self.outroSizer.Hide(self.endOfTrackAlarm)
-               else:
-                       self.outroSizer.Show(self.outroAlarmLabel)
-                       self.outroSizer.Show(self.endOfTrackAlarm)
-               self.Fit()
-
-       def onIntroCheck(self, evt):
-               if not self.introCheckBox.IsChecked():
-                       self.introSizer.Hide(self.introAlarmLabel)
-                       self.introSizer.Hide(self.songRampAlarm)
-               else:
-                       self.introSizer.Show(self.introAlarmLabel)
-                       self.introSizer.Show(self.songRampAlarm)
-               self.Fit()
-
        # Include profile flags such as instant profile string for display 
purposes.
        def displayProfiles(self, profiles):
                for index in xrange(len(profiles)):
@@ -439,14 +384,12 @@ class SPLConfigDialog(gui.SettingsDialog):
                        self.deleteButton.Enable()
                        self.triggerButton.Enable()
                curProfile = splconfig.getProfileByName(selectedProfile)
-               
self.outroCheckBox.SetValue(curProfile["IntroOutroAlarms"]["SayEndOfTrack"])
-               
self.endOfTrackAlarm.SetValue(long(curProfile["IntroOutroAlarms"]["EndOfTrackTime"]))
-               self.onOutroCheck(None)
-               
self.introCheckBox.SetValue(curProfile["IntroOutroAlarms"]["SaySongRamp"])
-               
self.songRampAlarm.SetValue(long(curProfile["IntroOutroAlarms"]["SongRampTime"]))
-               self.onIntroCheck(None)
-               
self.micAlarm.SetValue(long(curProfile["MicrophoneAlarm"]["MicAlarm"]))
-               
self.micAlarmInterval.SetValue(long(curProfile["MicrophoneAlarm"]["MicAlarmInterval"]))
+               self.endOfTrackTime = 
curProfile["IntroOutroAlarms"]["EndOfTrackTime"]
+               self.sayEndOfTrack = 
curProfile["IntroOutroAlarms"]["SayEndOfTrack"]
+               self.songRampTime = 
curProfile["IntroOutroAlarms"]["SongRampTime"]
+               self.saySongRamp = curProfile["IntroOutroAlarms"]["SaySongRamp"]
+               self.micAlarm = curProfile["MicrophoneAlarm"]["MicAlarm"]
+               self.micAlarmInterval = 
curProfile["MicrophoneAlarm"]["MicAlarmInterval"]
                # 6.1: Take care of profile-specific column and metadata 
settings.
                self.metadataStreams = 
curProfile["MetadataStreaming"]["MetadataEnabled"]
                
self.columnOrderCheckbox.SetValue(curProfile["ColumnAnnouncement"]["UseScreenColumnOrder"])
@@ -870,31 +813,23 @@ class AlarmsCenter(wx.Dialog):
                alarmsCenterHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
 
                if level in (0, 1):
-                       timeVal = 
splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
-                       alarmLabel = _("Enter &end of track alarm time in 
seconds (currently {curAlarmSec})").format(curAlarmSec = timeVal)
-                       self.outroAlarmEntry = 
alarmsCenterHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=59, initial=timeVal)
+                       timeVal = parent.endOfTrackTime if level == 0 else 
splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
+                       self.outroAlarmEntry = 
alarmsCenterHelper.addLabeledControl(_("&End of track alarm in seconds"), 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=59, initial=timeVal)
                        
self.outroToggleCheckBox=alarmsCenterHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of track is approaching")))
-                       
self.outroToggleCheckBox.SetValue(splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
+                       self.outroToggleCheckBox.SetValue(parent.sayEndOfTrack 
if level == 0 else splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
 
                if level in (0, 2):
-                       rampVal = 
splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"]
-                       alarmLabel = _("Enter song &intro alarm time in seconds 
(currently {curRampSec})").format(curRampSec = rampVal)
-                       self.introAlarmEntry = 
alarmsCenterHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=9, initial=rampVal)
+                       rampVal = parent.songRampTime if level == 0 else 
splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"]
+                       self.introAlarmEntry = 
alarmsCenterHelper.addLabeledControl(_("&Track intro alarm in seconds"), 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=9, initial=rampVal)
                        
self.introToggleCheckBox=alarmsCenterHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of introduction is approaching")))
-                       
self.introToggleCheckBox.SetValue(splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
+                       self.introToggleCheckBox.SetValue(parent.saySongRamp if 
level == 0 else splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
 
                if level in (0, 3):
-                       micAlarm = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"]
-                       micAlarmInterval = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"]
-                       if micAlarm:
-                               # Translators: A dialog message to set 
microphone active alarm (curAlarmSec is the current mic monitoring alarm in 
seconds).
-                               timeMSG = _("Enter microphone alarm time in 
seconds (currently {curAlarmSec}, 0 disables the alarm)").format(curAlarmSec = 
micAlarm)
-                       else:
-                               # Translators: A dialog message when microphone 
alarm is disabled (set to 0).
-                               timeMSG = _("Enter microphone alarm time in 
seconds (currently disabled, 0 disables the alarm)")
-                       micIntervalMSG = _("Microphone alarm interval")
-                       self.micAlarmEntry = 
alarmsCenterHelper.addLabeledControl(timeMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, initial=micAlarm)
-                       self.micIntervalEntry = 
alarmsCenterHelper.addLabeledControl(micIntervalMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=60, initial=micAlarmInterval)
+                       micAlarm = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"] if level == 3 else 
parent.micAlarm
+                       micAlarmInterval = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] if level == 3 else 
parent.micAlarmInterval
+                       # Translators: A dialog message to set microphone 
active alarm.
+                       self.micAlarmEntry = 
alarmsCenterHelper.addLabeledControl(_("&Microphone alarm in seconds (0 
disables the alarm)"), gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, 
initial=micAlarm)
+                       self.micIntervalEntry = 
alarmsCenterHelper.addLabeledControl(_("Microphone alarm &interval in 
seconds"), gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=60, 
initial=micAlarmInterval)
 
                
alarmsCenterHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
                self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
@@ -909,19 +844,26 @@ class AlarmsCenter(wx.Dialog):
 
        def onOk(self, evt):
                global _alarmDialogOpened
-               # Optimization: don't bother if Studio is dead and if the same 
value has been entered.
-               """if user32.FindWindowA("SPLStudio", None):
+               # Optimization: don't bother if Studio is dead and if the same 
value has been entered (only when standalone versions are opened).
+               if self.level > 0 and user32.FindWindowA("SPLStudio", None):
                        # Gather settings to be applied in section/key format.
-                       if self.level in (0, 1):
+                       if self.level == 1:
                                SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] 
= self.outroAlarmEntry.GetValue()
                                SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] 
= self.outroToggleCheckBox.GetValue()
-                       elif self.level in (0, 2):
+                       elif self.level == 2:
                                SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.introAlarmEntry.GetValue()
                                SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.introToggleCheckBox.GetValue()
-                       elif self.level in (0, 3):
+                       elif self.level == 3:
                                SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarmEntry.GetValue()
-                               
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micIntervalEntry.GetValue()"""
-               if self.level == 0:
+                               
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micIntervalEntry.GetValue()
+               elif self.level == 0:
+                       parent = self.Parent
+                       parent.endOfTrackTime = self.outroAlarmEntry.GetValue()
+                       parent.sayEndOfTrack = 
self.outroToggleCheckBox.GetValue()
+                       parent.songRampTime = self.introAlarmEntry.GetValue()
+                       parent.saySongRamp = self.introToggleCheckBox.GetValue()
+                       parent.micAlarm = self.micAlarmEntry.GetValue()
+                       parent.micAlarmInterval = 
self.micIntervalEntry.GetValue()
                        self.Parent.profiles.SetFocus()
                        self.Parent.Enable()
                self.Destroy()


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/a75ddd1f1201/
Changeset:   a75ddd1f1201
Branch:      None
User:        josephsl
Date:        2016-12-17 04:47:03+00:00
Summary:     SPL Controller/5.20 support (17.1-dev): introduce a new command to 
obtain status information.

A new command (SPL Controller, Q) is now availalbe in SPL Controller layer to 
announce status information. This include playback status, automation, 
microphone, line-in, record to file, cart edit and insert modes, same info as 
SPL Studio's status bar. The first two are available for all versions, while 
the others are reserved for Studio 5.20 and later.
This is destined for add-on 17.1.

Affected #:  1 file

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index faa4f39..958f6a9 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -40,6 +40,7 @@ SPLWin = 0 # A handle to studio window.
 SPLMSG = winUser.WM_USER
 
 # Various SPL IPC tags.
+SPLVersion = 2
 SPLPlay = 12
 SPLStop = 13
 SPLPause = 15
@@ -48,6 +49,7 @@ SPLMic = 17
 SPLLineIn = 18
 SPLLibraryScanCount = 32
 SPLListenerCount = 35
+SPLStatusInfo = 39 #Studio 5.20 and later.
 SPL_TrackPlaybackStatus = 104
 SPLCurTrackPlaybackTime = 105
 
@@ -73,6 +75,7 @@ S: Stop with fade.
 T: Instant stop.
 E: Announce if any encoders are being monitored.
 I: Announce listener count.
+Q: Announce Studio status information.
 R: Remaining time for the playing track.
 Shift+R: Library scan progress.""")
 
@@ -264,6 +267,25 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                encoders.announceNumMonitoringEncoders()
                self.finish()
 
+       def script_statusInfo(self, gesture):
+               statusInfo = []
+               # 17.1: For Studio 5.10 and up, announce playback and 
automation status.
+               playingNow = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPL_TrackPlaybackStatus)
+               statusInfo.append("Play status: playing" if playingNow else 
"Play status: stopped")
+               statusInfo.append("Automation on" if playingNow == 2 else 
"Automation off")
+               # 5.20 and later.
+               if winUser.sendMessage(SPLWin, SPLMSG, 0, SPLVersion) >= 520:
+                       statusInfo.append("Microphone on" if 
winUser.sendMessage(SPLWin, SPLMSG, 2, SPLStatusInfo) else "Microphone off")
+                       statusInfo.append("Line-inon" if 
winUser.sendMessage(SPLWin, SPLMSG, 3, SPLStatusInfo) else "Line-in off")
+                       statusInfo.append("Record to file on" if 
winUser.sendMessage(SPLWin, SPLMSG, 4, SPLStatusInfo) else "Record to file off")
+                       cartEdit = winUser.sendMessage(SPLWin, SPLMSG, 5, 
SPLStatusInfo)
+                       cartInsert = winUser.sendMessage(SPLWin, SPLMSG, 6, 
SPLStatusInfo)
+                       if not cartEdit and not cartInsert: 
statusInfo.append("Cart edit off")
+                       elif cartEdit and not cartInsert: 
statusInfo.append("Cart edit on")
+                       elif not cartEdit and cartInsert: 
statusInfo.append("Cart insert on")
+               ui.message("; ".join(statusInfo))
+               self.finish()
+
        def script_conHelp(self, gesture):
                # Translators: The title for SPL Controller help dialog.
                wx.CallAfter(gui.messageBox, SPLConHelp, _("SPL Controller 
help"))
@@ -286,6 +308,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                "kb:r":"remainingTime",
                "kb:e":"announceNumMonitoringEncoders",
                "kb:i":"listenerCount",
+               "kb:q":"statusInfo",
                "kb:f1":"conHelp"
        }
 


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/5ca2a88d658f/
Changeset:   5ca2a88d658f
Branch:      None
User:        josephsl
Date:        2016-12-17 04:57:01+00:00
Summary:     Merge branch 'alarmsCenter'

Affected #:  3 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 037b666..f72709d 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -962,12 +962,12 @@ class AppModule(appModuleHandler.AppModule):
                        wx.CallAfter(gui.messageBox, _("The add-on settings 
dialog is opened. Please close the settings dialog first."), _("Error"), 
wx.OK|wx.ICON_ERROR)
                        return
                try:
-                       d = splconfig.SPLAlarmDialog(gui.mainFrame, level)
+                       d = splconfui.AlarmsCenter(gui.mainFrame, level)
                        gui.mainFrame.prePopup()
                        d.Raise()
                        d.Show()
                        gui.mainFrame.postPopup()
-                       splconfig._alarmDialogOpened = True
+                       splconfui._alarmDialogOpened = True
                except RuntimeError:
                        wx.CallAfter(splconfig._alarmError)
 

diff --git a/addon/appModules/splstudio/splconfig.py 
b/addon/appModules/splstudio/splconfig.py
index b83144e..3351125 100755
--- a/addon/appModules/splstudio/splconfig.py
+++ b/addon/appModules/splstudio/splconfig.py
@@ -851,117 +851,9 @@ def _shouldBuildDescriptionPieces():
        or len(SPLConfig["ColumnAnnouncement"]["IncludedColumns"]) != 17))
 
 
-# Additional configuration dialogs
+# Additional configuration and miscellaneous dialogs
 # See splconfui module for basic configuration dialogs.
 
-# A common alarm dialog
-# Based on NVDA core's find dialog code (implemented by the author of this 
add-on).
-# Extended in 2016 to handle microphone alarms.
-# Only one instance can be active at a given moment (code borrowed from GUI's 
exit dialog routine).
-_alarmDialogOpened = False
-
-# A common alarm error dialog.
-def _alarmError():
-       # Translators: Text of the dialog when another alarm dialog is open.
-       gui.messageBox(_("Another alarm dialog is 
open."),_("Error"),style=wx.OK | wx.ICON_ERROR)
-
-class SPLAlarmDialog(wx.Dialog):
-       """A dialog providing common alarm settings.
-       This dialog contains a number entry field for alarm duration and a 
check box to enable or disable the alarm.
-       For one particular case, it consists of two number entry fields.
-       """
-
-       # The following comes from exit dialog class from GUI package (credit: 
NV Access and Zahari from Bulgaria).
-       _instance = None
-
-       def __new__(cls, parent, *args, **kwargs):
-               # Make this a singleton and prompt an error dialog if it isn't.
-               if _alarmDialogOpened:
-                       raise RuntimeError("An instance of alarm dialog is 
opened")
-               inst = cls._instance() if cls._instance else None
-               if not inst:
-                       return super(cls, cls).__new__(cls, parent, *args, 
**kwargs)
-               return inst
-
-       def __init__(self, parent, level=0):
-               inst = SPLAlarmDialog._instance() if SPLAlarmDialog._instance 
else None
-               if inst:
-                       return
-               # Use a weakref so the instance can die.
-               import weakref
-               SPLAlarmDialog._instance = weakref.ref(self)
-
-               # Now the actual alarm dialog code.
-               # 8.0: Apart from level 0 (all settings shown), levels change 
title.
-               titles = (_("Alarms Center"), _("End of track alarm"), _("Song 
intro alarm"), _("Microphone alarm"))
-               super(SPLAlarmDialog, self).__init__(parent, wx.ID_ANY, 
titles[level])
-               self.level = level
-               mainSizer = wx.BoxSizer(wx.VERTICAL)
-               # 17.1: Utilize various enhancements from GUI helper (added in 
NVDA 2016.4).
-               contentSizerHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
-
-               if level in (0, 1):
-                       timeVal = 
SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
-                       alarmLabel = _("Enter &end of track alarm time in 
seconds (currently {curAlarmSec})").format(curAlarmSec = timeVal)
-                       self.outroAlarmEntry = 
contentSizerHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=59, initial=timeVal)
-                       
self.outroToggleCheckBox=contentSizerHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of track is approaching")))
-                       
self.outroToggleCheckBox.SetValue(SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
-
-               if level in (0, 2):
-                       rampVal = SPLConfig["IntroOutroAlarms"]["SongRampTime"]
-                       alarmLabel = _("Enter song &intro alarm time in seconds 
(currently {curRampSec})").format(curRampSec = rampVal)
-                       self.introAlarmEntry = 
contentSizerHelper.addLabeledControl(alarmLabel, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=9, initial=rampVal)
-                       
self.introToggleCheckBox=contentSizerHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of introduction is approaching")))
-                       
self.introToggleCheckBox.SetValue(SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
-
-               if level in (0, 3):
-                       micAlarm = SPLConfig["MicrophoneAlarm"]["MicAlarm"]
-                       micAlarmInterval = 
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"]
-                       if micAlarm:
-                               # Translators: A dialog message to set 
microphone active alarm (curAlarmSec is the current mic monitoring alarm in 
seconds).
-                               timeMSG = _("Enter microphone alarm time in 
seconds (currently {curAlarmSec}, 0 disables the alarm)").format(curAlarmSec = 
micAlarm)
-                       else:
-                               # Translators: A dialog message when microphone 
alarm is disabled (set to 0).
-                               timeMSG = _("Enter microphone alarm time in 
seconds (currently disabled, 0 disables the alarm)")
-                       micIntervalMSG = _("Microphone alarm interval")
-                       self.micAlarmEntry = 
contentSizerHelper.addLabeledControl(timeMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, initial=micAlarm)
-                       self.micIntervalEntry = 
contentSizerHelper.addLabeledControl(micIntervalMSG, 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=60, initial=micAlarmInterval)
-
-               
contentSizerHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
-               self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
-               self.Bind(wx.EVT_BUTTON, self.onCancel, id=wx.ID_CANCEL)
-               mainSizer.Add(contentSizerHelper.sizer, 
border=gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
-               mainSizer.Fit(self)
-               self.SetSizer(mainSizer)
-               self.Center(wx.BOTH | wx.CENTER_ON_SCREEN)
-               if level in (0, 1): self.outroAlarmEntry.SetFocus()
-               elif level == 2: self.introAlarmEntry.SetFocus()
-               elif level == 3: self.micAlarmEntry.SetFocus()
-
-       def onOk(self, evt):
-               global SPLConfig, _alarmDialogOpened
-               # Optimization: don't bother if Studio is dead and if the same 
value has been entered.
-               import winUser
-               if winUser.user32.FindWindowA("SPLStudio", None):
-                       # Gather settings to be applied in section/key format.
-                       if self.level in (0, 1):
-                               SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] 
= self.outroAlarmEntry.GetValue()
-                               SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] 
= self.outroToggleCheckBox.GetValue()
-                       elif self.level in (0, 2):
-                               SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.introAlarmEntry.GetValue()
-                               SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.introToggleCheckBox.GetValue()
-                       elif self.level in (0, 3):
-                               SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarmEntry.GetValue()
-                               
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micIntervalEntry.GetValue()
-               self.Destroy()
-               _alarmDialogOpened = False
-
-       def onCancel(self, evt):
-               self.Destroy()
-               global _alarmDialogOpened
-               _alarmDialogOpened = False
-
-
 # Startup dialogs.
 
 # Audio ducking reminder (NVDA 2016.1 and later).

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index 32d41bb..5d6d0df 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -92,40 +92,15 @@ class SPLConfigDialog(gui.SettingsDialog):
                except:
                        pass
 
-               self.outroSizer = wx.BoxSizer(wx.HORIZONTAL)
-               # Check box hiding method comes from Alberto Buffolino's 
Columns Review add-on.
-               # Translators: Label for a check box in SPL add-on settings to 
notify when end of track (outro) is approaching.
-               self.outroCheckBox=wx.CheckBox(self,wx.NewId(),label=_("&Notify 
when end of track is approaching"))
-               
self.outroCheckBox.SetValue(splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
-               self.outroCheckBox.Bind(wx.EVT_CHECKBOX, self.onOutroCheck)
-               self.outroSizer.Add(self.outroCheckBox, 
border=10,flag=wx.BOTTOM)
-
-               # Translators: The label for a setting in SPL Add-on settings 
to specify end of track (outro) alarm.
-               self.outroAlarmLabel = wx.StaticText(self, wx.ID_ANY, 
label=_("&End of track alarm in seconds"))
-               self.outroSizer.Add(self.outroAlarmLabel)
-               self.endOfTrackAlarm = wx.SpinCtrl(self, wx.ID_ANY, min=1, 
max=59)
-               
self.endOfTrackAlarm.SetValue(long(splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]))
-               self.endOfTrackAlarm.SetSelection(-1, -1)
-               self.outroSizer.Add(self.endOfTrackAlarm)
-               self.onOutroCheck(None)
-               SPLConfigHelper.addItem(self.outroSizer)
-
-               self.introSizer = wx.BoxSizer(wx.HORIZONTAL)
-               # Translators: Label for a check box in SPL add-on settings to 
notify when end of intro is approaching.
-               self.introCheckBox=wx.CheckBox(self,wx.NewId(),label=_("&Notify 
when end of introduction is approaching"))
-               
self.introCheckBox.SetValue(splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
-               self.introCheckBox.Bind(wx.EVT_CHECKBOX, self.onIntroCheck)
-               self.introSizer.Add(self.introCheckBox, 
border=10,flag=wx.BOTTOM)
-
-               # Translators: The label for a setting in SPL Add-on settings 
to specify track intro alarm.
-               self.introAlarmLabel = wx.StaticText(self, wx.ID_ANY, 
label=_("&Track intro alarm in seconds"))
-               self.introSizer.Add(self.introAlarmLabel)
-               self.songRampAlarm = wx.SpinCtrl(self, wx.ID_ANY, min=1, max=9)
-               
self.songRampAlarm.SetValue(long(splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"]))
-               self.songRampAlarm.SetSelection(-1, -1)
-               self.introSizer.Add(self.songRampAlarm)
-               self.onIntroCheck(None)
-               SPLConfigHelper.addItem(self.introSizer)
+               self.endOfTrackTime = 
splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
+               self.sayEndOfTrack = 
splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"]
+               self.songRampTime = 
splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"]
+               self.saySongRamp = 
splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"]
+               self.micAlarm = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"]
+               self.micAlarmInterval = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"]
+               # Translators: The label of a button to open a dialog to 
configure various alarms.
+               alarmsCenterButton = SPLConfigHelper.addItem(wx.Button(self, 
label=_("&Alarms Center...")))
+               alarmsCenterButton.Bind(wx.EVT_BUTTON, self.onAlarmsCenter)
 
                self.brailleTimerValues=[("off",_("Off")),
                # Translators: One of the braille timer settings.
@@ -143,13 +118,6 @@ class SPLConfigDialog(gui.SettingsDialog):
                except:
                        pass
 
-               sizer = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.HORIZONTAL)
-               # Translators: The label for a setting in SPL Add-on settings 
to change microphone alarm setting.
-               self.micAlarm = sizer.addLabeledControl(_("&Microphone alarm in 
seconds"), gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, 
initial=splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"])
-               # Translators: The label for a setting in SPL Add-on settings 
to specify mic alarm interval.
-               self.micAlarmInterval = sizer.addLabeledControl(_("Microphone 
alarm &interval in seconds"), gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, 
max=60, initial=splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"])
-               SPLConfigHelper.addItem(sizer)
-
                # Translators: One of the alarm notification options.
                self.alarmAnnounceValues=[("beep",_("beep")),
                # Translators: One of the alarm notification options.
@@ -299,13 +267,13 @@ class SPLConfigDialog(gui.SettingsDialog):
                        
splconfig.SPLConfig.swapProfiles(splconfig.SPLConfig.activeProfile, 
selectedProfile)
                splconfig.SPLConfig["General"]["BeepAnnounce"] = 
self.beepAnnounceCheckbox.Value
                splconfig.SPLConfig["General"]["MessageVerbosity"] = 
self.verbosityLevels[self.verbosityList.GetSelection()][0]
-               splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] = 
self.outroCheckBox.Value
-               splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] = 
self.endOfTrackAlarm.Value
-               splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.introCheckBox.Value
-               splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.songRampAlarm.Value
+               splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] = 
self.endOfTrackTime
+               splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] = 
self.sayEndOfTrack
+               splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.songRampTime
+               splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.saySongRamp
+               splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarm
+               splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micAlarmInterval
                splconfig.SPLConfig["General"]["BrailleTimer"] = 
self.brailleTimerValues[self.brailleTimerList.GetSelection()][0]
-               splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarm.Value
-               splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micAlarmInterval.Value
                splconfig.SPLConfig["General"]["AlarmAnnounce"] = 
self.alarmAnnounceValues[self.alarmAnnounceList.GetSelection()][0]
                splconfig.SPLConfig["General"]["LibraryScanAnnounce"] = 
self.libScanValues[self.libScanList.GetSelection()][0]
                splconfig.SPLConfig["General"]["TimeHourAnnounce"] = 
self.hourAnnounceCheckbox.Value
@@ -391,25 +359,6 @@ class SPLConfigDialog(gui.SettingsDialog):
                else:
                        if splupdate._SPLUpdateT is None: splconfig.updateInit()
 
-       # Check events for outro and intro alarms, respectively.
-       def onOutroCheck(self, evt):
-               if not self.outroCheckBox.IsChecked():
-                       self.outroSizer.Hide(self.outroAlarmLabel)
-                       self.outroSizer.Hide(self.endOfTrackAlarm)
-               else:
-                       self.outroSizer.Show(self.outroAlarmLabel)
-                       self.outroSizer.Show(self.endOfTrackAlarm)
-               self.Fit()
-
-       def onIntroCheck(self, evt):
-               if not self.introCheckBox.IsChecked():
-                       self.introSizer.Hide(self.introAlarmLabel)
-                       self.introSizer.Hide(self.songRampAlarm)
-               else:
-                       self.introSizer.Show(self.introAlarmLabel)
-                       self.introSizer.Show(self.songRampAlarm)
-               self.Fit()
-
        # Include profile flags such as instant profile string for display 
purposes.
        def displayProfiles(self, profiles):
                for index in xrange(len(profiles)):
@@ -435,14 +384,12 @@ class SPLConfigDialog(gui.SettingsDialog):
                        self.deleteButton.Enable()
                        self.triggerButton.Enable()
                curProfile = splconfig.getProfileByName(selectedProfile)
-               
self.outroCheckBox.SetValue(curProfile["IntroOutroAlarms"]["SayEndOfTrack"])
-               
self.endOfTrackAlarm.SetValue(long(curProfile["IntroOutroAlarms"]["EndOfTrackTime"]))
-               self.onOutroCheck(None)
-               
self.introCheckBox.SetValue(curProfile["IntroOutroAlarms"]["SaySongRamp"])
-               
self.songRampAlarm.SetValue(long(curProfile["IntroOutroAlarms"]["SongRampTime"]))
-               self.onIntroCheck(None)
-               
self.micAlarm.SetValue(long(curProfile["MicrophoneAlarm"]["MicAlarm"]))
-               
self.micAlarmInterval.SetValue(long(curProfile["MicrophoneAlarm"]["MicAlarmInterval"]))
+               self.endOfTrackTime = 
curProfile["IntroOutroAlarms"]["EndOfTrackTime"]
+               self.sayEndOfTrack = 
curProfile["IntroOutroAlarms"]["SayEndOfTrack"]
+               self.songRampTime = 
curProfile["IntroOutroAlarms"]["SongRampTime"]
+               self.saySongRamp = curProfile["IntroOutroAlarms"]["SaySongRamp"]
+               self.micAlarm = curProfile["MicrophoneAlarm"]["MicAlarm"]
+               self.micAlarmInterval = 
curProfile["MicrophoneAlarm"]["MicAlarmInterval"]
                # 6.1: Take care of profile-specific column and metadata 
settings.
                self.metadataStreams = 
curProfile["MetadataStreaming"]["MetadataEnabled"]
                
self.columnOrderCheckbox.SetValue(curProfile["ColumnAnnouncement"]["UseScreenColumnOrder"])
@@ -561,7 +508,12 @@ class SPLConfigDialog(gui.SettingsDialog):
                action(flag)
                self.profiles.SetString(index, profile if not len(flags) else 
"{0} <{1}>".format(profile, ", ".join(flags)))
 
-       # Manage metadata streaming.
+       # Alarms Center.
+       def onAlarmsCenter(self, evt):
+               self.Disable()
+               AlarmsCenter(self).Show()
+
+               # Manage metadata streaming.
        def onManageMetadata(self, evt):
                self.Disable()
                MetadataStreamingDialog(self).Show()
@@ -602,7 +554,7 @@ class SPLConfigDialog(gui.SettingsDialog):
 # Open the above dialog upon request.
 def onConfigDialog(evt):
        # 5.2: Guard against alarm dialogs.
-       if splconfig._alarmDialogOpened or _metadataDialogOpened:
+       if _alarmDialogOpened or _metadataDialogOpened:
                # Translators: Presented when an alarm dialog is opened.
                wx.CallAfter(gui.messageBox, _("Another add-on settings dialog 
is open. Please close the previously opened dialog first."), _("Error"), 
wx.OK|wx.ICON_ERROR)
        else: gui.mainFrame._popupSettingsDialog(SPLConfigDialog)
@@ -815,6 +767,115 @@ class TriggersDialog(wx.Dialog):
                        prompt.Enable() if self.timeSwitchCheckbox.IsChecked() 
else prompt.Disable()
                self.Fit()
 
+# A common alarm dialog (Alarms Center)
+# Based on NVDA core's find dialog code (implemented by the author of this 
add-on).
+# Extended in 2016 to handle microphone alarms.
+# Only one instance can be active at a given moment (code borrowed from GUI's 
exit dialog routine).
+_alarmDialogOpened = False
+
+# A common alarm error dialog.
+def _alarmError():
+       # Translators: Text of the dialog when another alarm dialog is open.
+       gui.messageBox(_("Another alarm dialog is 
open."),_("Error"),style=wx.OK | wx.ICON_ERROR)
+
+class AlarmsCenter(wx.Dialog):
+       """A dialog providing common alarm settings.
+       This dialog contains a number entry field for alarm duration and a 
check box to enable or disable the alarm.
+       For one particular case, it consists of two number entry fields.
+       """
+
+       # The following comes from exit dialog class from GUI package (credit: 
NV Access and Zahari from Bulgaria).
+       _instance = None
+
+       def __new__(cls, parent, *args, **kwargs):
+               # Make this a singleton and prompt an error dialog if it isn't.
+               if _alarmDialogOpened:
+                       raise RuntimeError("An instance of alarm dialog is 
opened")
+               inst = cls._instance() if cls._instance else None
+               if not inst:
+                       return super(cls, cls).__new__(cls, parent, *args, 
**kwargs)
+               return inst
+
+       def __init__(self, parent, level=0):
+               inst = AlarmsCenter._instance() if AlarmsCenter._instance else 
None
+               if inst:
+                       return
+               # Use a weakref so the instance can die.
+               import weakref
+               AlarmsCenter._instance = weakref.ref(self)
+
+               # Now the actual alarm dialog code.
+               # 8.0: Apart from level 0 (all settings shown), levels change 
title.
+               titles = (_("Alarms Center"), _("End of track alarm"), _("Song 
intro alarm"), _("Microphone alarm"))
+               super(AlarmsCenter, self).__init__(parent, wx.ID_ANY, 
titles[level])
+               self.level = level
+               mainSizer = wx.BoxSizer(wx.VERTICAL)
+               alarmsCenterHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
+
+               if level in (0, 1):
+                       timeVal = parent.endOfTrackTime if level == 0 else 
splconfig.SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"]
+                       self.outroAlarmEntry = 
alarmsCenterHelper.addLabeledControl(_("&End of track alarm in seconds"), 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=59, initial=timeVal)
+                       
self.outroToggleCheckBox=alarmsCenterHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of track is approaching")))
+                       self.outroToggleCheckBox.SetValue(parent.sayEndOfTrack 
if level == 0 else splconfig.SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"])
+
+               if level in (0, 2):
+                       rampVal = parent.songRampTime if level == 0 else 
splconfig.SPLConfig["IntroOutroAlarms"]["SongRampTime"]
+                       self.introAlarmEntry = 
alarmsCenterHelper.addLabeledControl(_("&Track intro alarm in seconds"), 
gui.nvdaControls.SelectOnFocusSpinCtrl, min=1, max=9, initial=rampVal)
+                       
self.introToggleCheckBox=alarmsCenterHelper.addItem(wx.CheckBox(self, 
label=_("&Notify when end of introduction is approaching")))
+                       self.introToggleCheckBox.SetValue(parent.saySongRamp if 
level == 0 else splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"])
+
+               if level in (0, 3):
+                       micAlarm = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"] if level == 3 else 
parent.micAlarm
+                       micAlarmInterval = 
splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] if level == 3 else 
parent.micAlarmInterval
+                       # Translators: A dialog message to set microphone 
active alarm.
+                       self.micAlarmEntry = 
alarmsCenterHelper.addLabeledControl(_("&Microphone alarm in seconds (0 
disables the alarm)"), gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=7200, 
initial=micAlarm)
+                       self.micIntervalEntry = 
alarmsCenterHelper.addLabeledControl(_("Microphone alarm &interval in 
seconds"), gui.nvdaControls.SelectOnFocusSpinCtrl, min=0, max=60, 
initial=micAlarmInterval)
+
+               
alarmsCenterHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
+               self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
+               self.Bind(wx.EVT_BUTTON, self.onCancel, id=wx.ID_CANCEL)
+               mainSizer.Add(alarmsCenterHelper.sizer, 
border=gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
+               mainSizer.Fit(self)
+               self.SetSizer(mainSizer)
+               self.Center(wx.BOTH | wx.CENTER_ON_SCREEN)
+               if level in (0, 1): self.outroAlarmEntry.SetFocus()
+               elif level == 2: self.introAlarmEntry.SetFocus()
+               elif level == 3: self.micAlarmEntry.SetFocus()
+
+       def onOk(self, evt):
+               global _alarmDialogOpened
+               # Optimization: don't bother if Studio is dead and if the same 
value has been entered (only when standalone versions are opened).
+               if self.level > 0 and user32.FindWindowA("SPLStudio", None):
+                       # Gather settings to be applied in section/key format.
+                       if self.level == 1:
+                               SPLConfig["IntroOutroAlarms"]["EndOfTrackTime"] 
= self.outroAlarmEntry.GetValue()
+                               SPLConfig["IntroOutroAlarms"]["SayEndOfTrack"] 
= self.outroToggleCheckBox.GetValue()
+                       elif self.level == 2:
+                               SPLConfig["IntroOutroAlarms"]["SongRampTime"] = 
self.introAlarmEntry.GetValue()
+                               SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.introToggleCheckBox.GetValue()
+                       elif self.level == 3:
+                               SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarmEntry.GetValue()
+                               
SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micIntervalEntry.GetValue()
+               elif self.level == 0:
+                       parent = self.Parent
+                       parent.endOfTrackTime = self.outroAlarmEntry.GetValue()
+                       parent.sayEndOfTrack = 
self.outroToggleCheckBox.GetValue()
+                       parent.songRampTime = self.introAlarmEntry.GetValue()
+                       parent.saySongRamp = self.introToggleCheckBox.GetValue()
+                       parent.micAlarm = self.micAlarmEntry.GetValue()
+                       parent.micAlarmInterval = 
self.micIntervalEntry.GetValue()
+                       self.Parent.profiles.SetFocus()
+                       self.Parent.Enable()
+               self.Destroy()
+               _alarmDialogOpened = False
+
+       def onCancel(self, evt):
+               if self.level == 0:
+                       self.Parent.Enable()
+               self.Destroy()
+               global _alarmDialogOpened
+               _alarmDialogOpened = False
+
 # Metadata reminder controller.
 # Select notification/streaming URL's for metadata streaming.
 _metadataDialogOpened = False


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/ce7f47fe74f2/
Changeset:   ce7f47fe74f2
Branch:      None
User:        josephsl
Date:        2016-12-17 05:47:13+00:00
Summary:     Readme entries for Alarms Center and Studio status info SPL 
Controller command

Affected #:  1 file

diff --git a/readme.md b/readme.md
index d9285a0..b652b8f 100755
--- a/readme.md
+++ b/readme.md
@@ -9,7 +9,7 @@ This add-on package provides improved usage of StationPlaylist 
Studio, as well a
 
 For more information about the add-on, read the [add-on guide][4]. For 
developers seeking to know how to build the add-on, see buildInstructions.txt 
located at the root of the add-on source code repository.
 
-IMPORTANT: This add-on requires NVDA 2016.4 or later and StationPlaylist 
Studio 5.10 or later. If you have installed NVDA 2016.1 or later on Windows 8 
and later, disable audio ducking mode. Also, add-on 8.0/16.10 requires Studio 
5.10 and later, and for broadcasters using Studio 5.0x, a long-term support 
version (15.x) is available.
+IMPORTANT: This add-on requires NVDA 2016.4 or later and StationPlaylist 
Studio 5.10 or later. If using Windows 8 or later, for best experience, disable 
audio ducking mode. Also, add-on 8.0/16.10 requires Studio 5.10 and later, and 
for broadcasters using Studio 5.0x, a long-term support version (15.x) is 
available.
 
 ## Shortcut keys
 
@@ -127,6 +127,7 @@ The available SPL Controller commands are:
 * Press Shift+R to get a report on library scan progress.
 * Press E to get count and labels for encoders being monitored.
 * Press I to obtain listener count.
+* Press Q to obtain various status information about Studio including whether 
a track is playing, microphone is on and others.
 * Press F1 to show a help dialog which lists available commands.
 
 ## Track alarms
@@ -170,7 +171,10 @@ If you are using Studio on a touchscreen computer running 
Windows 8 or later and
 * Improvements to presentation of various add-on dialogs thanks to NVDA 2016.4 
features.
 * Added ability to press Control+Alt+up or down arrow keys to move between 
tracks (specifically, track columns) vertically just as one is moving to next 
or previous row in a table.
 * Added a combo box in add-on settings dialog to set which column should be 
announced when moving through columns vertically.
+* Moved end of track , intro and microphone alarm controls from add-on 
settings to the new Alarms Center.
+* In Alarms Center, end of track and track intro edit fields are always shown 
regardless of state of alarm notification checkboxes.
 * Removed Track Dial (NVDA's version of enhanced arrow keys), replaced by 
Columns explorer and Column Navigator/table navigation commands). This affects 
Studio and Track Tool.
+* Added a new command in SPL Controller layer to announce Studio status such 
as track playback and microphone status (Q).
 
 ## Version 16.12.1
 


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/d9efb75bc839/
Changeset:   d9efb75bc839
Branch:      None
User:        josephsl
Date:        2016-12-17 06:01:41+00:00
Summary:     SPL Status info (17.1-dev): Allow SPL Controller, Q to be 
assignable.

Affected #:  2 files

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index 958f6a9..1ac7977 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -268,13 +268,17 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                self.finish()
 
        def script_statusInfo(self, gesture):
+               # For consistency reasons (because of the Studio status bar), 
messages in this method will remain in English.
                statusInfo = []
                # 17.1: For Studio 5.10 and up, announce playback and 
automation status.
                playingNow = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPL_TrackPlaybackStatus)
                statusInfo.append("Play status: playing" if playingNow else 
"Play status: stopped")
-               statusInfo.append("Automation on" if playingNow == 2 else 
"Automation off")
-               # 5.20 and later.
-               if winUser.sendMessage(SPLWin, SPLMSG, 0, SPLVersion) >= 520:
+               # For automation, Studio 5.11 and earlier does not have an easy 
way to detect this flag, thus resort to using playback status.
+               if winUser.sendMessage(SPLWin, SPLMSG, 0, SPLVersion) < 520:
+                       statusInfo.append("Automation on" if playingNow == 2 
else "Automation off")
+               else:
+                       statusInfo.append("Automation on" if 
winUser.sendMessage(SPLWin, SPLMSG, 1, SPLStatusInfo) else "Automation off")
+                       # 5.20 and later.
                        statusInfo.append("Microphone on" if 
winUser.sendMessage(SPLWin, SPLMSG, 2, SPLStatusInfo) else "Microphone off")
                        statusInfo.append("Line-inon" if 
winUser.sendMessage(SPLWin, SPLMSG, 3, SPLStatusInfo) else "Line-in off")
                        statusInfo.append("Record to file on" if 
winUser.sendMessage(SPLWin, SPLMSG, 4, SPLStatusInfo) else "Record to file off")
@@ -285,6 +289,8 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                        elif not cartEdit and cartInsert: 
statusInfo.append("Cart insert on")
                ui.message("; ".join(statusInfo))
                self.finish()
+       # Translators: Input help message for a SPL Controller command.
+       script_statusInfo.__doc__ = _("Announces Studio status such as track 
playback status from other programs")
 
        def script_conHelp(self, gesture):
                # Translators: The title for SPL Controller help dialog.

diff --git a/readme.md b/readme.md
index b652b8f..6038fd4 100755
--- a/readme.md
+++ b/readme.md
@@ -36,6 +36,7 @@ The following commands are not assigned by default; if you 
wish to assign them,
 
 * Switching to SPL Studio window from any program.
 * SPL Controller layer.
+* Announcing Studio status such as track playback from other programs.
 * SPL Assistant layer from SPL Studio.
 * Announce time including seconds from SPL Studio.
 * Announcing temperature.


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/9ff8455d754d/
Changeset:   9ff8455d754d
Branch:      None
User:        josephsl
Date:        2016-12-23 03:16:25+00:00
Summary:     SPL add-on dialog: reorder braille timer and alarm notification 
controls around.

After Alarms Center, the next logical control in tab order is alarm 
notification combo box, thus braille timer will be positioned after this.

Affected #:  1 file

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index 5d6d0df..5e0511e 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -102,6 +102,21 @@ class SPLConfigDialog(gui.SettingsDialog):
                alarmsCenterButton = SPLConfigHelper.addItem(wx.Button(self, 
label=_("&Alarms Center...")))
                alarmsCenterButton.Bind(wx.EVT_BUTTON, self.onAlarmsCenter)
 
+               # Translators: One of the alarm notification options.
+               self.alarmAnnounceValues=[("beep",_("beep")),
+               # Translators: One of the alarm notification options.
+               ("message",_("message")),
+               # Translators: One of the alarm notification options.
+               ("both",_("both beep and message"))]
+                               # Translators: The label for a setting in SPL 
add-on dialog to control alarm announcement type.
+               self.alarmAnnounceList = 
SPLConfigHelper.addLabeledControl(_("&Alarm notification:"), wx.Choice, 
choices=[x[1] for x in self.alarmAnnounceValues])
+               
alarmAnnounceCurValue=splconfig.SPLConfig["General"]["AlarmAnnounce"]
+               selection = (x for x,y in enumerate(self.alarmAnnounceValues) 
if y[0]==alarmAnnounceCurValue).next()
+               try:
+                       self.alarmAnnounceList.SetSelection(selection)
+               except:
+                       pass
+
                self.brailleTimerValues=[("off",_("Off")),
                # Translators: One of the braille timer settings.
                ("outro",_("Track ending")),
@@ -118,21 +133,6 @@ class SPLConfigDialog(gui.SettingsDialog):
                except:
                        pass
 
-               # Translators: One of the alarm notification options.
-               self.alarmAnnounceValues=[("beep",_("beep")),
-               # Translators: One of the alarm notification options.
-               ("message",_("message")),
-               # Translators: One of the alarm notification options.
-               ("both",_("both beep and message"))]
-                               # Translators: The label for a setting in SPL 
add-on dialog to control alarm announcement type.
-               self.alarmAnnounceList = 
SPLConfigHelper.addLabeledControl(_("&Alarm notification:"), wx.Choice, 
choices=[x[1] for x in self.alarmAnnounceValues])
-               
alarmAnnounceCurValue=splconfig.SPLConfig["General"]["AlarmAnnounce"]
-               selection = (x for x,y in enumerate(self.alarmAnnounceValues) 
if y[0]==alarmAnnounceCurValue).next()
-               try:
-                       self.alarmAnnounceList.SetSelection(selection)
-               except:
-                       pass
-
                self.libScanValues=[("off",_("Off")),
                # Translators: One of the library scan announcement settings.
                ("ending",_("Start and end only")),
@@ -273,8 +273,8 @@ class SPLConfigDialog(gui.SettingsDialog):
                splconfig.SPLConfig["IntroOutroAlarms"]["SaySongRamp"] = 
self.saySongRamp
                splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"] = 
self.micAlarm
                splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarmInterval"] = 
self.micAlarmInterval
-               splconfig.SPLConfig["General"]["BrailleTimer"] = 
self.brailleTimerValues[self.brailleTimerList.GetSelection()][0]
                splconfig.SPLConfig["General"]["AlarmAnnounce"] = 
self.alarmAnnounceValues[self.alarmAnnounceList.GetSelection()][0]
+               splconfig.SPLConfig["General"]["BrailleTimer"] = 
self.brailleTimerValues[self.brailleTimerList.GetSelection()][0]
                splconfig.SPLConfig["General"]["LibraryScanAnnounce"] = 
self.libScanValues[self.libScanList.GetSelection()][0]
                splconfig.SPLConfig["General"]["TimeHourAnnounce"] = 
self.hourAnnounceCheckbox.Value
                splconfig.SPLConfig["General"]["CategorySounds"] = 
self.categorySoundsCheckbox.Value


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/34685de12c3d/
Changeset:   34685de12c3d
Branch:      None
User:        josephsl
Date:        2016-12-23 18:10:16+00:00
Summary:     SPL Controller/status info (17.1-dev): Correct cart edit/insert 
mode detection logic.

No matter if insert mode is on or off, cart edit serves as a master switch, 
thus catch this. This fixes an issue where cart edit status will not be 
announced under some circumstances.

Affected #:  1 file

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index 1ac7977..9701601 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -284,9 +284,9 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                        statusInfo.append("Record to file on" if 
winUser.sendMessage(SPLWin, SPLMSG, 4, SPLStatusInfo) else "Record to file off")
                        cartEdit = winUser.sendMessage(SPLWin, SPLMSG, 5, 
SPLStatusInfo)
                        cartInsert = winUser.sendMessage(SPLWin, SPLMSG, 6, 
SPLStatusInfo)
-                       if not cartEdit and not cartInsert: 
statusInfo.append("Cart edit off")
-                       elif cartEdit and not cartInsert: 
statusInfo.append("Cart edit on")
+                       if cartEdit: statusInfo.append("Cart edit on")
                        elif not cartEdit and cartInsert: 
statusInfo.append("Cart insert on")
+                       else: statusInfo.append("Cart edit off")
                ui.message("; ".join(statusInfo))
                self.finish()
        # Translators: Input help message for a SPL Controller command.


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/3445582bf825/
Changeset:   3445582bf825
Branch:      None
User:        josephsl
Date:        2016-12-25 18:54:37+00:00
Summary:     Library scan monitor improvements, including use of Studio 5.10 
API to detect library scan completion. re #15

In the old days, when there was no easy way of telling when a library scan was 
finished, one assumed that temporary scan count will not change in the next few 
seconds. It turns out this is flawed:
* For scans that involve thousands of tracks (for example, when scanning an 
entire drive), scan count will remain the same for a while before further scans 
are done. This meant that one would terminate library scan monitor too early, 
giving potentially misleading information about items scanned.
* In Studio 5.10 and later, there is an easy way to tell when the library scan 
has completed, thus making reliance on scan count unappealing.
Thus changed the library scan monitor routine to use Studio 5.10 API (this 
means breaking backwards compatibility with Studio 5.0x, which means this fix 
is destined for add-on 17.1).

Affected #:  1 file

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index f72709d..fb11d3f 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -1256,40 +1256,35 @@ class AppModule(appModuleHandler.AppModule):
                global libScanT
                if libScanT and libScanT.isAlive() and 
api.getForegroundObject().windowClassName == "TTrackInsertForm":
                        return
-               countA = statusAPI(1, 32, ret=True)
-               if countA == 0:
+               if statusAPI(1, 32, ret=True) < 0:
                        self.libraryScanning = False
                        return
                time.sleep(0.1)
                if api.getForegroundObject().windowClassName == 
"TTrackInsertForm" and self.productVersion in noLibScanMonitor:
                        self.libraryScanning = False
                        return
-               # Sometimes, a second call is needed to obtain the real scan 
count in Studio 5.10 and later.
-               countB = statusAPI(1, 32, ret=True)
-               if countA == countB:
+               # 17.1: Library scan may have finished while this thread was 
sleeping.
+               if statusAPI(1, 32, ret=True) < 0:
                        self.libraryScanning = False
-                       countB = statusAPI(0, 32, ret=True)
                        # Translators: Presented when library scanning is 
finished.
-                       ui.message(_("{itemCount} items in the 
library").format(itemCount = countB))
+                       ui.message(_("{itemCount} items in the 
library").format(itemCount = statusAPI(0, 32, ret=True)))
                else:
-                       libScanT = 
threading.Thread(target=self.libraryScanReporter, args=(_SPLWin, countA, 
countB, 1))
+                       libScanT = 
threading.Thread(target=self.libraryScanReporter)
                        libScanT.daemon = True
                        libScanT.start()
 
-       def libraryScanReporter(self, _SPLWin, countA, countB, parem):
+       def libraryScanReporter(self):
                scanIter = 0
-               while countA != countB:
+               # 17.1: Use the constant directly, as 5.10 and later provides a 
convenient method to detect completion of library scans.
+               while statusAPI(1, 32, ret=True) >= 0:
                        if not self.libraryScanning: return
-                       countA = countB
                        time.sleep(1)
                        # Do not continue if we're back on insert tracks form 
or library scan is finished.
                        if api.getForegroundObject().windowClassName == 
"TTrackInsertForm" or not self.libraryScanning:
                                return
-                       countB, scanIter = statusAPI(parem, 32, ret=True), 
scanIter+1
-                       if countB < 0:
-                               break
+                       scanIter+=1
                        if scanIter%5 == 0 and 
splconfig.SPLConfig["General"]["LibraryScanAnnounce"] not in ("off", "ending"):
-                               self._libraryScanAnnouncer(countB, 
splconfig.SPLConfig["General"]["LibraryScanAnnounce"])
+                               self._libraryScanAnnouncer(statusAPI(1, 32, 
ret=True), splconfig.SPLConfig["General"]["LibraryScanAnnounce"])
                self.libraryScanning = False
                if self.backgroundStatusMonitor: return
                if splconfig.SPLConfig["General"]["LibraryScanAnnounce"] != 
"off":
@@ -1297,7 +1292,7 @@ class AppModule(appModuleHandler.AppModule):
                                tones.beep(370, 100)
                        else:
                                # Translators: Presented after library scan is 
done.
-                               ui.message(_("Scan complete with {itemCount} 
items").format(itemCount = countB))
+                               ui.message(_("Scan complete with {itemCount} 
items").format(itemCount = statusAPI(0, 32, ret=True)))
 
        # Take care of library scanning announcement.
        def _libraryScanAnnouncer(self, count, announcementType):


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/05b7bc687fbc/
Changeset:   05b7bc687fbc
Branch:      None
User:        josephsl
Date:        2016-12-25 20:45:46+00:00
Summary:     SPL Controller: Refine library scan status command to announce 
progress if needed.

Building from the fix from earlier: because Studio 5.10 API provides an easy 
way to detect library scan completion, use this so it can now announce scan 
progress and item count.
This is destined for add-on 17.1.

Affected #:  1 file

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index 9701601..2df4e25 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -228,9 +228,13 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                self.finish()
 
        def script_libraryScanProgress(self, gesture):
-               scanned = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLLibraryScanCount)
-               # Translators: Announces number of items in the Studio's track 
library (example: 1000 items scanned).
-               ui.message(_("{itemCount} items scanned").format(itemCount = 
scanned))
+               scanned = winUser.sendMessage(SPLWin, SPLMSG, 1, 
SPLLibraryScanCount)
+               if scanned >= 0:
+                       # Translators: Announces number of items in the 
Studio's track library (example: 1000 items scanned).
+                       ui.message(_("Scan in progress with {itemCount} items 
scanned").format(itemCount = scanned))
+               else:
+                       # Translators: Announces number of items in the 
Studio's track library (example: 1000 items scanned).
+                       ui.message(_("Scan complete with {itemCount} items 
scanned").format(itemCount = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLLibraryScanCount)))
                self.finish()
 
        def script_listenerCount(self, gesture):


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/395f4360f49d/
Changeset:   395f4360f49d
Branch:      None
User:        josephsl
Date:        2016-12-25 20:48:41+00:00
Summary:     Library scan reporter: do not announce when library scan API says 
-1.

In some cases, while the scan reporter thread is sleeping, library scan is 
completed, thus catch this and prevent scan status reporter from announcing 
this.

Affected #:  1 file

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index fb11d3f..95275e8 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -1276,15 +1276,20 @@ class AppModule(appModuleHandler.AppModule):
        def libraryScanReporter(self):
                scanIter = 0
                # 17.1: Use the constant directly, as 5.10 and later provides a 
convenient method to detect completion of library scans.
-               while statusAPI(1, 32, ret=True) >= 0:
+               scanCount = statusAPI(1, 32, ret=True)
+               while scanCount >= 0:
                        if not self.libraryScanning: return
                        time.sleep(1)
                        # Do not continue if we're back on insert tracks form 
or library scan is finished.
                        if api.getForegroundObject().windowClassName == 
"TTrackInsertForm" or not self.libraryScanning:
                                return
+                       # Scan count may have changed during sleep.
+                       scanCount = statusAPI(1, 32, ret=True)
+                       if scanCount < 0:
+                               break
                        scanIter+=1
                        if scanIter%5 == 0 and 
splconfig.SPLConfig["General"]["LibraryScanAnnounce"] not in ("off", "ending"):
-                               self._libraryScanAnnouncer(statusAPI(1, 32, 
ret=True), splconfig.SPLConfig["General"]["LibraryScanAnnounce"])
+                               self._libraryScanAnnouncer(scanCount, 
splconfig.SPLConfig["General"]["LibraryScanAnnounce"])
                self.libraryScanning = False
                if self.backgroundStatusMonitor: return
                if splconfig.SPLConfig["General"]["LibraryScanAnnounce"] != 
"off":


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/e5adad79697d/
Changeset:   e5adad79697d
Branch:      None
User:        josephsl
Date:        2016-12-26 03:28:48+00:00
Summary:     Merge branch '16.10.x'

Affected #:  15 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 95275e8..6c61b1e 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -1,6 +1,6 @@
 # StationPlaylist Studio
 # An app module and global plugin package for NVDA
-# Copyright 2011, 2013-2016, Geoff Shang, Joseph Lee and others, released 
under GPL.
+# Copyright 2011, 2013-2017, Geoff Shang, Joseph Lee and others, released 
under GPL.
 # The primary function of this appModule is to provide meaningful feedback to 
users of SplStudio
 # by allowing speaking of items which cannot be easily found.
 # Version 0.01 - 7 April 2011:

diff --git a/addon/appModules/splstudio/splconfig.py 
b/addon/appModules/splstudio/splconfig.py
index 3351125..c9c3475 100755
--- a/addon/appModules/splstudio/splconfig.py
+++ b/addon/appModules/splstudio/splconfig.py
@@ -1,6 +1,6 @@
 # SPL Studio Configuration Manager
 # An app module and global plugin package for NVDA
-# Copyright 2015-2016 Joseph Lee and others, released under GPL.
+# Copyright 2015-2017 Joseph Lee and others, released under GPL.
 # Provides the configuration management package for SPL Studio app module.
 # For miscellaneous dialogs and tool, see SPLMisc module.
 # For UI surrounding this module, see splconfui module.

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index 5e0511e..7b24654 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -1,6 +1,6 @@
 # SPL Studio Configuration user interfaces
 # An app module and global plugin package for NVDA
-# Copyright 2016 Joseph Lee and others, released under GPL.
+# Copyright 2016-2017 Joseph Lee and others, released under GPL.
 # Split from SPL config module in 2016.
 # Provides the configuration management UI package for SPL Studio app module.
 # For code which provides foundation for code in this module, see splconfig 
module.

diff --git a/addon/appModules/splstudio/splmisc.py 
b/addon/appModules/splstudio/splmisc.py
index d7a9940..a804668 100755
--- a/addon/appModules/splstudio/splmisc.py
+++ b/addon/appModules/splstudio/splmisc.py
@@ -1,6 +1,6 @@
 # SPL Studio Miscellaneous User Interfaces and internal services
 # An app module and global plugin package for NVDA
-# Copyright 2015-2016 Joseph Lee and others, released under GPL.
+# Copyright 2015-2017 Joseph Lee and others, released under GPL.
 # Miscellaneous functions and user interfaces
 # Split from config module in 2015.
 

diff --git a/addon/appModules/splstudio/splupdate.py 
b/addon/appModules/splstudio/splupdate.py
index c9e3a3a..8eb7bcf 100755
--- a/addon/appModules/splstudio/splupdate.py
+++ b/addon/appModules/splstudio/splupdate.py
@@ -1,6 +1,6 @@
 # StationPlaylist Studio update checker
 # A support module for SPL add-on
-# Copyright 2015-2016, Joseph Lee, released under GPL.
+# Copyright 2015-2017 Joseph Lee, released under GPL.
 
 # Provides update check facility, basics borrowed from NVDA Core's update 
checker class.
 

diff --git a/addon/appModules/tracktool.py b/addon/appModules/tracktool.py
index df79410..77cfd82 100755
--- a/addon/appModules/tracktool.py
+++ b/addon/appModules/tracktool.py
@@ -1,6 +1,6 @@
 # StationPlaylist Track Tool
 # An app module for NVDA
-# Copyright 2014-2016 Joseph Lee and contributors, released under gPL.
+# Copyright 2014-2017 Joseph Lee and contributors, released under gPL.
 # Functionality is based on JFW scripts for SPL Track Tool by Brian Hartgen.
 
 import appModuleHandler

diff --git a/addon/doc/ar/readme.md b/addon/doc/ar/readme.md
index d38cd83..83680a9 100644
--- a/addon/doc/ar/readme.md
+++ b/addon/doc/ar/readme.md
@@ -249,6 +249,11 @@ broadcast profiles.
 استخدم لمسة ب3 أصابع للانتقال لنمط اللمس, ثم استخدم أوامر اللمس المسرودة
 أعلاه لأداء المهام.
 
+## Version 16.12.1
+
+* Corrected user interface presentation for SPL add-on settings dialog.
+* ترجمة الإضافة لمزيد من اللغات
+
 ## Version 16.12/15.4-LTS
 
 * More work on supporting Studio 5.20, including announcing cart insert mode

diff --git a/addon/doc/es/readme.md b/addon/doc/es/readme.md
index 3e9db61..72e3e18 100644
--- a/addon/doc/es/readme.md
+++ b/addon/doc/es/readme.md
@@ -287,6 +287,11 @@ realizar algunas órdenes de Studio desde la pantalla 
táctil. Primero utiliza
 un toque con tres dedos para cambiar a modo SPL, entonces utiliza las
 órdenes táctiles listadas arriba para llevar a cabo tareas.
 
+## Version 16.12.1
+
+* Corrected user interface presentation for SPL add-on settings dialog.
+* Traducciones actualizadas.
+
 ## Versión 16.12/15.4-LTS
 
 * Más trabajo sobre el soporte de Studio 5.20, incluyendo el anunciado del

diff --git a/addon/doc/fr/readme.md b/addon/doc/fr/readme.md
index 2d68631..c496723 100644
--- a/addon/doc/fr/readme.md
+++ b/addon/doc/fr/readme.md
@@ -297,6 +297,11 @@ un écran tactile. Tout d'abord utiliser une tape à trois 
doigts pour
 basculer en mode SPL, puis utilisez les commandes tactile énumérées
 ci-dessus pour exécuter des commandes.
 
+## Version 16.12.1
+
+* Corrected user interface presentation for SPL add-on settings dialog.
+* Mises à jour des traductions.
+
 ## Version 16.12/15.4-LTS
 
 * More work on supporting Studio 5.20, including announcing cart insert mode

diff --git a/addon/doc/gl/readme.md b/addon/doc/gl/readme.md
index 8933f44..8ebec55 100644
--- a/addon/doc/gl/readme.md
+++ b/addon/doc/gl/readme.md
@@ -279,6 +279,11 @@ realizar algunhas ordes do Studio dende a pantalla tactil. 
Primeiro usa un
 toque con tgres dedos para cambiar a modo SPL, logo usa as ordes tactiles
 listadas arriba para realizar ordes.
 
+## Version 16.12.1
+
+* Corrected user interface presentation for SPL add-on settings dialog.
+* Traducións actualizadas.
+
 ## Versión 16.12/15.4-LTS
 
 * Máis traballo no soporte do Studio 5.20, incluindo o anunciado do estado

diff --git a/addon/doc/hu/readme.md b/addon/doc/hu/readme.md
index 5ddcfac..4e65bd7 100644
--- a/addon/doc/hu/readme.md
+++ b/addon/doc/hu/readme.md
@@ -260,6 +260,11 @@ Amennyiben érintőképernyős számítógépen használja a 
Studiot Windows 8,
 parancsokat végrehajthat az érintőképernyőn is. Először 3 ujjas koppintással
 váltson SPL módra, és utána már használhatók az alább felsorolt parancsok.
 
+## Version 16.12.1
+
+* Corrected user interface presentation for SPL add-on settings dialog.
+* Fordítások frissítése
+
 ## Version 16.12/15.4-LTS
 
 * More work on supporting Studio 5.20, including announcing cart insert mode

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index 2df4e25..61072f2 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -1,6 +1,6 @@
 # StationPlaylist Utilities
 # Author: Joseph Lee
-# Copyright 2013-2016, released under GPL.
+# Copyright 2013-2017, released under GPL.
 # Adds a few utility features such as switching focus to the SPL Studio window 
and some global scripts.
 # For encoder support, see the encoders package.
 

diff --git a/addon/globalPlugins/SPLStudioUtils/encoders.py 
b/addon/globalPlugins/SPLStudioUtils/encoders.py
index b24eb40..e3f81d4 100755
--- a/addon/globalPlugins/SPLStudioUtils/encoders.py
+++ b/addon/globalPlugins/SPLStudioUtils/encoders.py
@@ -1,6 +1,6 @@
 # StationPlaylist encoders support
 # Author: Joseph Lee
-# Copyright 2015-2016, released under GPL.
+# Copyright 2015-2017, released under GPL.
 # Split from main global plugin in 2015.
 
 import threading

diff --git a/addon/installTasks.py b/addon/installTasks.py
index e0f5bdc..a216d8c 100755
--- a/addon/installTasks.py
+++ b/addon/installTasks.py
@@ -1,5 +1,5 @@
 # StationPlaylist Studio add-on installation tasks
-# Copyright 2015-2016 Joseph Lee and others, released under GPL.
+# Copyright 2015-2017 Joseph Lee and others, released under GPL.
 
 # Provides needed routines during add-on installation and removal.
 # Routines are partly based on other add-ons, particularly Place Markers by 
Noelia Martinez (thanks add-on authors).

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/9ff4a7f7ce90/
Changeset:   9ff4a7f7ce90
Branch:      None
User:        josephsl
Date:        2016-12-26 03:29:43+00:00
Summary:     Merge branch 'splcreator'

Affected #:  1 file

diff --git a/addon/appModules/splcreator.py b/addon/appModules/splcreator.py
new file mode 100755
index 0000000..860ecc7
--- /dev/null
+++ b/addon/appModules/splcreator.py
@@ -0,0 +1,17 @@
+# StationPlaylist Creator
+# An app module and global plugin package for NVDA
+# Copyright 2016, Joseph Lee and others, released under GPL.
+
+import controlTypes
+import appModuleHandler
+import api
+from NVDAObjects.behaviors import Dialog
+import addonHandler
+addonHandler.initTranslation()
+
+
+class AppModule(appModuleHandler.AppModule):
+
+       def chooseNVDAObjectOverlayClasses(self, obj, clsList):
+               if obj.windowClassName in ("TDemoRegForm", "TAboutForm"):
+                       clsList.insert(0, Dialog)


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/d5f1fa20dcc9/
Changeset:   d5f1fa20dcc9
Branch:      None
User:        josephsl
Date:        2016-12-26 03:52:49+00:00
Summary:     Update copyright years for SPL Creator app module

Affected #:  1 file

diff --git a/addon/appModules/splcreator.py b/addon/appModules/splcreator.py
index 860ecc7..43066b4 100755
--- a/addon/appModules/splcreator.py
+++ b/addon/appModules/splcreator.py
@@ -1,6 +1,8 @@
 # StationPlaylist Creator
 # An app module and global plugin package for NVDA
-# Copyright 2016, Joseph Lee and others, released under GPL.
+# Copyright 2016-2017 Joseph Lee and others, released under GPL.
+
+# Basic support for StationPlaylist Creator.
 
 import controlTypes
 import appModuleHandler


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/bc58457d38d7/
Changeset:   bc58457d38d7
Branch:      None
User:        josephsl
Date:        2016-12-26 05:53:17+00:00
Summary:     Library scan: allow scanning flag to be set to true regardless of 
background status monitor flag, readme entries for library scan fixes.

One of the reasons why one had to do manual lib scan monitoring after closing 
Insert Tracks was that lib scan flag was not set. in this case, background 
status monitor was on the way - this isn't required at all when it comes to lib 
scan reporting, thus removed.
Added readme entries for library scan bug fixes.

Affected #:  2 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 6c61b1e..1f25727 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -694,8 +694,7 @@ class AppModule(appModuleHandler.AppModule):
                                                if self.scanCount%100 == 0:
                                                        
self._libraryScanAnnouncer(obj.name[1:obj.name.find("]")], 
splconfig.SPLConfig["General"]["LibraryScanAnnounce"])
                                        if not self.libraryScanning:
-                                               if self.productVersion not in 
noLibScanMonitor:
-                                                       if not 
self.backgroundStatusMonitor: self.libraryScanning = True
+                                               if self.productVersion not in 
noLibScanMonitor: self.libraryScanning = True
                                elif "match" in obj.name:
                                        if 
splconfig.SPLConfig["General"]["LibraryScanAnnounce"] != "off" and 
self.libraryScanning:
                                                if 
splconfig.SPLConfig["General"]["BeepAnnounce"]: tones.beep(370, 100)

diff --git a/readme.md b/readme.md
index 6038fd4..ed64ddf 100755
--- a/readme.md
+++ b/readme.md
@@ -175,6 +175,10 @@ If you are using Studio on a touchscreen computer running 
Windows 8 or later and
 * Moved end of track , intro and microphone alarm controls from add-on 
settings to the new Alarms Center.
 * In Alarms Center, end of track and track intro edit fields are always shown 
regardless of state of alarm notification checkboxes.
 * Removed Track Dial (NVDA's version of enhanced arrow keys), replaced by 
Columns explorer and Column Navigator/table navigation commands). This affects 
Studio and Track Tool.
+* After closing Insert Tracks dialog while a library scan is in progress, it 
is no longer required to press SPL Assistant, Shift+R to monitor scan progress.
+* Improved accuracy of detecting and reporting completion of library scans in 
Studio 5.10 and later. This fixes a problem where library scan monitor will end 
prematurely when there are more tracks to be scanned, necessitating restarting 
library scan monitor.
+* Improved library scan status reporting via SPL Controller (Shift+R) by 
announcing scan count if scan is indeed happening.
+* Initial support for StationPlaylist Creator.
 * Added a new command in SPL Controller layer to announce Studio status such 
as track playback and microphone status (Q).
 
 ## Version 16.12.1


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/626a381211f8/
Changeset:   626a381211f8
Branch:      None
User:        josephsl
Date:        2016-12-26 20:17:19+00:00
Summary:     Code cleanup/maintenance: remove unused imports, group imports 
that are used in a single function.

There are imports that are no longer in use, and some modules are invoked in a 
single function. Thus removed unused imports, and use inline import in a 
function for the latter ones.

Affected #:  10 files

diff --git a/addon/appModules/splcreator.py b/addon/appModules/splcreator.py
index 43066b4..31b99da 100755
--- a/addon/appModules/splcreator.py
+++ b/addon/appModules/splcreator.py
@@ -4,16 +4,12 @@
 
 # Basic support for StationPlaylist Creator.
 
-import controlTypes
 import appModuleHandler
-import api
-from NVDAObjects.behaviors import Dialog
-import addonHandler
-addonHandler.initTranslation()
 
 
 class AppModule(appModuleHandler.AppModule):
 
        def chooseNVDAObjectOverlayClasses(self, obj, clsList):
                if obj.windowClassName in ("TDemoRegForm", "TAboutForm"):
+                       from NVDAObjects.behaviors import Dialog
                        clsList.insert(0, Dialog)

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 1f25727..1bae61c 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -17,10 +17,7 @@ import threading
 import controlTypes
 import appModuleHandler
 import api
-import review
-import eventHandler
 import scriptHandler
-import queueHandler
 import ui
 import nvwave
 import speech
@@ -574,6 +571,7 @@ class AppModule(appModuleHandler.AppModule):
                # Announce status changes while using other programs.
                # This requires NVDA core support and will be available in 6.0 
and later (cannot be ported to earlier versions).
                # For now, handle all background events, but in the end, make 
this configurable.
+               import eventHandler
                if hasattr(eventHandler, "requestEvents"):
                        eventHandler.requestEvents(eventName="nameChange", 
processId=self.processID, windowClassName="TStatusBar")
                        eventHandler.requestEvents(eventName="nameChange", 
processId=self.processID, windowClassName="TStaticText")
@@ -591,6 +589,7 @@ class AppModule(appModuleHandler.AppModule):
                # LTS: Only do this if channel hasn't changed.
                if splconfig.SPLConfig["Update"]["AutoUpdateCheck"] or 
splupdate._updateNow:
                        # 7.0: Have a timer call the update function indirectly.
+                       import queueHandler
                        queueHandler.queueFunction(queueHandler.eventQueue, 
splconfig.updateInit)
                # Display startup dialogs if any.
                wx.CallAfter(splconfig.showStartupDialogs)
@@ -628,6 +627,7 @@ class AppModule(appModuleHandler.AppModule):
                        obj.role = controlTypes.ROLE_GROUPING
                # In certain edit fields and combo boxes, the field name is 
written to the screen, and there's no way to fetch the object for this text. 
Thus use review position text.
                elif obj.windowClassName in ("TEdit", "TComboBox") and not 
obj.name:
+                       import review
                        fieldName, fieldObj  = review.getScreenPosition(obj)
                        fieldName.expand(textInfos.UNIT_LINE)
                        if obj.windowClassName == "TComboBox":

diff --git a/addon/appModules/splstudio/splconfig.py 
b/addon/appModules/splstudio/splconfig.py
index c9c3475..5cd8800 100755
--- a/addon/appModules/splstudio/splconfig.py
+++ b/addon/appModules/splstudio/splconfig.py
@@ -12,10 +12,8 @@ from validate import Validator
 import time
 import datetime
 import cPickle
-import copy
 import globalVars
 import ui
-import api
 import gui
 import wx
 import splupdate
@@ -236,6 +234,7 @@ class ConfigHub(ChainMap):
 
        def _cacheConfig(self, conf):
                global _SPLCache
+               import copy
                if _SPLCache is None: _SPLCache = {}
                key = None if conf.filename == SPLIni else conf.name
                _SPLCache[key] = {}

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index 7b24654..47f35d6 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -7,9 +7,6 @@
 
 import os
 import weakref
-import datetime
-import calendar
-import ui
 import api
 import gui
 import wx
@@ -666,6 +663,7 @@ class TriggersDialog(wx.Dialog):
 
                daysSizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, 
_("Day")), wx.HORIZONTAL)
                self.triggerDays = []
+               import calendar
                for day in xrange(len(calendar.day_name)):
                        triggerDay=wx.CheckBox(self, 
wx.NewId(),label=calendar.day_name[day])
                        triggerDay.SetValue((64 >> day & 
self.Parent._profileTriggersConfig[profile][0]) if profile in 
self.Parent._profileTriggersConfig else 0)
@@ -738,6 +736,7 @@ class TriggersDialog(wx.Dialog):
                                # Otherwise trigger flag will be added each 
time this is called (either this handler or the add-on settings' flags 
retriever must retrieve the flags set).
                                if not self.profile in 
parent._profileTriggersConfig:
                                        parent.setProfileFlags(self.selection, 
"add", _("time-based"))
+                               import datetime
                                parent._profileTriggersConfig[self.profile] = 
splconfig.setNextTimedProfile(self.profile, bit, datetime.time(hour, min))
                                parent._profileTriggersConfig[self.profile][6] 
= duration
                        else:

diff --git a/addon/appModules/splstudio/splmisc.py 
b/addon/appModules/splstudio/splmisc.py
index a804668..4e8bec3 100755
--- a/addon/appModules/splstudio/splmisc.py
+++ b/addon/appModules/splstudio/splmisc.py
@@ -13,7 +13,6 @@ from csv import reader # For cart explorer.
 import gui
 import wx
 import ui
-from NVDAObjects.IAccessible import sysListView32
 from winUser import user32, sendMessage
 
 # Locate column content.
@@ -23,6 +22,7 @@ from winUser import user32, sendMessage
 # In track finder, this is used when encountering the track item but NVDA says 
otherwise.
 def _getColumnContent(obj, col):
        import winKernel
+       from NVDAObjects.IAccessible import sysListView32
        # Borrowed from SysListView32 implementation.
        buffer=None
        processHandle=obj.processHandle

diff --git a/addon/appModules/splstudio/splupdate.py 
b/addon/appModules/splstudio/splupdate.py
index 8eb7bcf..218d98a 100755
--- a/addon/appModules/splstudio/splupdate.py
+++ b/addon/appModules/splstudio/splupdate.py
@@ -4,14 +4,10 @@
 
 # Provides update check facility, basics borrowed from NVDA Core's update 
checker class.
 
-import urllib
 import os # Essentially, update download is no different than file downloads.
 import cPickle
-import threading
 import gui
 import wx
-import tones
-import time
 import addonHandler
 import globalVars
 
@@ -101,6 +97,7 @@ def updateCheck(auto=False, continuous=False, 
confUpdateInterval=1):
                return
        global _SPLUpdateT, SPLAddonCheck, _retryAfterFailure, _progressDialog, 
_updateNow
        if _updateNow: _updateNow = False
+       import time
        # Regardless of whether it is an auto check, update the check time.
        # However, this shouldnt' be done if this is a retry after a failed 
attempt.
        if not _retryAfterFailure: SPLAddonCheck = time.time()
@@ -112,6 +109,7 @@ def updateCheck(auto=False, continuous=False, 
confUpdateInterval=1):
        updateCandidate = False
        updateURL = SPLUpdateURL if SPLUpdateChannel not in channels else 
channels[SPLUpdateChannel]
        try:
+               import urllib
                # Look up the channel if different from the default.
                url = urllib.urlopen(updateURL)
                url.close()

diff --git a/addon/appModules/tracktool.py b/addon/appModules/tracktool.py
index 77cfd82..d112a5f 100755
--- a/addon/appModules/tracktool.py
+++ b/addon/appModules/tracktool.py
@@ -5,11 +5,7 @@
 
 import appModuleHandler
 import addonHandler
-import api
 import tones
-import speech
-import braille
-from controlTypes import ROLE_LISTITEM
 import ui
 from NVDAObjects.IAccessible import IAccessible
 from splstudio import splconfig
@@ -60,6 +56,7 @@ class TrackToolItem(IAccessible):
                                # Translators: Presented when some info is not 
defined for a track in Track Tool (example: cue not found)
                                ui.message(_("{header} not 
found").format(header = columnHeader))
                        else:
+                               import speech, braille
                                speech.speakMessage(_("{header}: 
blank").format(header = columnHeader))
                                braille.handler.message(_("{header}: 
()").format(header = columnHeader))
 
@@ -119,6 +116,7 @@ class AppModule(appModuleHandler.AppModule):
        SPLColNumber = 0
 
        def chooseNVDAObjectOverlayClasses(self, obj, clsList):
-               if obj.windowClassName in ("TListView", 
"TTntListView.UnicodeClass") and obj.role == ROLE_LISTITEM:
+               import controlTypes
+               if obj.windowClassName in ("TListView", 
"TTntListView.UnicodeClass") and obj.role == controlTypes.ROLE_LISTITEM:
                        clsList.insert(0, TrackToolItem)
 

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index 61072f2..c527a84 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -8,15 +8,11 @@ from functools import wraps
 import os
 import globalPluginHandler
 import api
-from controlTypes import ROLE_LISTITEM
 import ui
 import globalVars
 from NVDAObjects.IAccessible import getNVDAObjectFromEvent
 import winUser
-import tones
 import nvwave
-import gui
-import wx
 import addonHandler
 addonHandler.initTranslation()
 
@@ -118,6 +114,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                self.bindGestures(self.__gestures)
 
        def script_error(self, gesture):
+               import tones
                tones.beep(120, 100)
 
        # Switch focus to SPL Studio window from anywhere.
@@ -297,6 +294,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
        script_statusInfo.__doc__ = _("Announces Studio status such as track 
playback status from other programs")
 
        def script_conHelp(self, gesture):
+               import gui, wx
                # Translators: The title for SPL Controller help dialog.
                wx.CallAfter(gui.messageBox, SPLConHelp, _("SPL Controller 
help"))
                self.finish()
@@ -334,10 +332,8 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
 
        def chooseNVDAObjectOverlayClasses(self, obj, clsList):
                if obj.appModule.appName in ("splengine", "splstreamer"):
-                       import encoders
+                       import controlTypes, encoders
                        if obj.windowClassName == "TListView":
                                clsList.insert(0, encoders.SAMEncoder)
-                       elif obj.windowClassName == "SysListView32":
-                               if obj.role == ROLE_LISTITEM:
+                       elif obj.windowClassName == "SysListView32" and 
obj.role == controlTypes.ROLE_LISTITEM:
                                        clsList.insert(0, encoders.SPLEncoder)
-

diff --git a/addon/globalPlugins/SPLStudioUtils/encoders.py 
b/addon/globalPlugins/SPLStudioUtils/encoders.py
index e3f81d4..0607f2d 100755
--- a/addon/globalPlugins/SPLStudioUtils/encoders.py
+++ b/addon/globalPlugins/SPLStudioUtils/encoders.py
@@ -4,18 +4,13 @@
 # Split from main global plugin in 2015.
 
 import threading
-import os
 import time
-import weakref
-from configobj import ConfigObj
 import api
 import ui
 import speech
-import globalVars
 import scriptHandler
 from NVDAObjects.IAccessible import IAccessible, getNVDAObjectFromEvent
 import winUser
-import winKernel
 import tones
 import gui
 import wx
@@ -49,7 +44,8 @@ streamLabels = None
 # Load stream labels (and possibly other future goodies) from a file-based 
database.
 def loadStreamLabels():
        global streamLabels, SAMStreamLabels, SPLStreamLabels, 
SPLFocusToStudio, SPLPlayAfterConnecting, SPLBackgroundMonitor, 
SPLNoConnectionTone
-       streamLabels = ConfigObj(os.path.join(globalVars.appArgs.configPath, 
"splStreamLabels.ini"))
+       import os, configobj, globalVars
+       streamLabels = 
configobj.ConfigObj(os.path.join(globalVars.appArgs.configPath, 
"splStreamLabels.ini"))
        # Read stream labels.
        try:
                SAMStreamLabels = dict(streamLabels["SAMEncoders"])
@@ -188,6 +184,7 @@ class EncoderConfigDialog(wx.Dialog):
                if inst:
                        return
                # Use a weakref so the instance can die.
+               import weakref
                EncoderConfigDialog._instance = weakref.ref(self)
 
                self.obj = obj
@@ -403,6 +400,7 @@ class Encoder(IAccessible):
 
        # Announce complete time including seconds (slight change from global 
commands version).
        def script_encoderDateTime(self, gesture):
+               import winKernel
                if scriptHandler.getLastScriptRepeatCount()==0:
                        
text=winKernel.GetTimeFormat(winKernel.LOCALE_USER_DEFAULT, 0, None, None)
                else:

diff --git a/addon/installTasks.py b/addon/installTasks.py
index a216d8c..6258417 100755
--- a/addon/installTasks.py
+++ b/addon/installTasks.py
@@ -4,10 +4,8 @@
 # Provides needed routines during add-on installation and removal.
 # Routines are partly based on other add-ons, particularly Place Markers by 
Noelia Martinez (thanks add-on authors).
 
-import os
-import shutil
-
 def onInstall():
+       import os, shutil
        profiles = os.path.join(os.path.dirname(__file__), "..", 
"stationPlaylist", "profiles")
        # Import old profiles.
        if os.path.exists(profiles):


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/f41ece41f24b/
Changeset:   f41ece41f24b
Branch:      None
User:        josephsl
Date:        2016-12-27 04:41:30+00:00
Summary:     Code cleanup: variables that are really constants or generate 
constants and then used in a subsequent function clals are now truly constants.

In some cases, expressions such as:
someConst = constantGenExpression
someFunc(someConst)
isn't quite necessary. This is a bit better:
someFunc(constGenExpression)
results in less bytecode and improves fetching variables that are really 
constants (this is the case when the generated constant is used in one place 
only). This observation has been applied to SPL Controller, library scan 
initiator in SPL Assistant and in Track Tool app module.

Affected #:  3 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 1bae61c..d30844c 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -1688,10 +1688,8 @@ class AppModule(appModuleHandler.AppModule):
 
        def script_libraryScanMonitor(self, gesture):
                if not self.libraryScanning:
-                       scanning = statusAPI(1, 32, ret=True)
-                       if scanning < 0:
-                               items = statusAPI(0, 32, ret=True)
-                               ui.message(_("{itemCount} items in the 
library").format(itemCount = items))
+                       if statusAPI(1, 32, ret=True) < 0:
+                               ui.message(_("{itemCount} items in the 
library").format(itemCount = statusAPI(0, 32, ret=True)))
                                return
                        self.libraryScanning = True
                        # Translators: Presented when attempting to start 
library scan.

diff --git a/addon/appModules/tracktool.py b/addon/appModules/tracktool.py
index d112a5f..ce45e94 100755
--- a/addon/appModules/tracktool.py
+++ b/addon/appModules/tracktool.py
@@ -82,8 +82,7 @@ class TrackToolItem(IAccessible):
 
        def script_columnExplorer(self, gesture):
                # Just like the main app module, due to the below formula, 
columns explorer will be restricted to number commands.
-               columnPos = int(gesture.displayName.split("+")[-1])-1
-               header = 
splconfig.SPLConfig["General"]["ExploreColumnsTT"][columnPos]
+               header = 
splconfig.SPLConfig["General"]["ExploreColumnsTT"][int(gesture.displayName.split("+")[-1])-1]
                # Several corner cases.
                # Look up track name if artist is the header name.
                if header == "Artist":
@@ -99,8 +98,7 @@ class TrackToolItem(IAccessible):
                        ui.message(_("Introduction not set"))
                else:
                        try:
-                               pos = 
indexOf(self.appModule.productVersion).index(header)
-                               self.announceColumnContent(pos, 
columnHeader=header)
+                               
self.announceColumnContent(indexOf(self.appModule.productVersion).index(header),
 columnHeader=header)
                        except ValueError:
                                # Translators: Presented when some info is not 
defined for a track in Track Tool (example: cue not found)
                                ui.message(_("{headerText} not 
found").format(headerText = header))
@@ -119,4 +117,3 @@ class AppModule(appModuleHandler.AppModule):
                import controlTypes
                if obj.windowClassName in ("TListView", 
"TTntListView.UnicodeClass") and obj.role == controlTypes.ROLE_LISTITEM:
                        clsList.insert(0, TrackToolItem)
-

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index c527a84..4a0838f 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -49,11 +49,6 @@ SPLStatusInfo = 39 #Studio 5.20 and later.
 SPL_TrackPlaybackStatus = 104
 SPLCurTrackPlaybackTime = 105
 
-
-# On/off toggle wave files.
-onFile = os.path.join(os.path.dirname(__file__), "..", "..", "appModules", 
"splstudio", "SPL_on.wav")
-offFile = os.path.join(os.path.dirname(__file__), "..", "..", "appModules", 
"splstudio", "SPL_off.wav")
-
 # Help message for SPL Controller
 # Translators: the dialog text for SPL Controller help.
 SPLConHelp=_("""
@@ -184,12 +179,12 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
 
        def script_micOn(self, gesture):
                winUser.sendMessage(SPLWin,SPLMSG,1,SPLMic)
-               nvwave.playWaveFile(onFile)
+               nvwave.playWaveFile(os.path.join(os.path.dirname(__file__), 
"..", "..", "appModules", "splstudio", "SPL_on.wav"))
                self.finish()
 
        def script_micOff(self, gesture):
                winUser.sendMessage(SPLWin,SPLMSG,0,SPLMic)
-               nvwave.playWaveFile(offFile)
+               nvwave.playWaveFile(os.path.join(os.path.dirname(__file__), 
"..", "..", "appModules", "splstudio", "SPL_off.wav"))
                self.finish()
 
        def script_micNoFade(self, gesture):
@@ -235,9 +230,8 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                self.finish()
 
        def script_listenerCount(self, gesture):
-               count = winUser.sendMessage(SPLWin, SPLMSG, 0, SPLListenerCount)
                # Translators: Announces number of stream listeners.
-               ui.message(_("Listener count: 
{listenerCount}").format(listenerCount = count))
+               ui.message(_("Listener count: 
{listenerCount}").format(listenerCount = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLListenerCount)))
                self.finish()
 
        def script_remainingTime(self, gesture):


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/a2ca2ba1c84d/
Changeset:   a2ca2ba1c84d
Branch:      None
User:        josephsl
Date:        2016-12-27 06:10:36+00:00
Summary:     Merged 16.10.x

Affected #:  0 files



https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/953d67d6804b/
Changeset:   953d67d6804b
Branch:      None
User:        josephsl
Date:        2016-12-27 06:44:03+00:00
Summary:     Merge branch '16.10.x'

Affected #:  2 files

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index 4a0838f..142ed4c 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -70,22 +70,6 @@ Q: Announce Studio status information.
 R: Remaining time for the playing track.
 Shift+R: Library scan progress.""")
 
-# Try to see if SPL foreground object can be fetched. This is used for 
switching to SPL Studio window from anywhere and to switch to Studio window 
from SAM encoder window.
-
-def fetchSPLForegroundWindow():
-       # Turns out NVDA core does have a method to fetch desktop objects, so 
use this to find SPL window from among its children.
-       dt = api.getDesktopObject()
-       fg = None
-       fgCount = 0
-       for possibleFG in dt.children:
-               if "splstudio" in possibleFG.appModule.appModuleName:
-                       fg = possibleFG
-                       fgCount+=1
-       # Just in case the window is really minimized (not to the system tray)
-       if fgCount == 1:
-               fg = getNVDAObjectFromEvent(user32.FindWindowA("TStudioForm", 
None), winUser.OBJID_CLIENT, 0)
-       return fg
-
 
 class GlobalPlugin(globalPluginHandler.GlobalPlugin):
 
@@ -120,13 +104,10 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
                if "splstudio" in 
api.getForegroundObject().appModule.appModuleName: return
                else:
                        SPLHwnd = user32.FindWindowA("SPLStudio", None) # Used 
ANSI version, as Wide char version always returns 0.
-                       if SPLHwnd == 0: ui.message(_("SPL Studio is not 
running."))
+                       if not SPLHwnd: ui.message(_("SPL Studio is not 
running."))
                        else:
-                               SPLFG = fetchSPLForegroundWindow()
-                               if SPLFG == None:
-                                       # Translators: Presented when Studio is 
minimized to system tray (notification area).
-                                       ui.message(_("SPL minimized to system 
tray."))
-                               else: SPLFG.setFocus()
+                               # 17.01: SetForegroundWindow function is 
better, as there's no need to traverse top-level windows and allows users to 
"switch" to SPL window if the window is minimized.
+                               
user32.SetForegroundWindow(user32.FindWindowA("TStudioForm", None))
        # Translators: Input help mode message for a command to switch to 
Station Playlist Studio from any program.
        script_focusToSPLWindow.__doc__=_("Moves to SPL Studio window from 
other programs.")
 

diff --git a/addon/globalPlugins/SPLStudioUtils/encoders.py 
b/addon/globalPlugins/SPLStudioUtils/encoders.py
index 0607f2d..b25486a 100755
--- a/addon/globalPlugins/SPLStudioUtils/encoders.py
+++ b/addon/globalPlugins/SPLStudioUtils/encoders.py
@@ -139,24 +139,6 @@ def cleanup():
        # 7.0: Destroy threads also.
        encoderMonCount = {"SAM":0, "SPL":0}
 
-
-# Try to see if SPL foreground object can be fetched. This is used for 
switching to SPL Studio window from anywhere and to switch to Studio window 
from SAM encoder window.
-
-def fetchSPLForegroundWindow():
-       # Turns out NVDA core does have a method to fetch desktop objects, so 
use this to find SPL window from among its children.
-       dt = api.getDesktopObject()
-       fg = None
-       fgCount = 0
-       for possibleFG in dt.children:
-               if "splstudio" in possibleFG.appModule.appModuleName:
-                       fg = possibleFG
-                       fgCount+=1
-       # Just in case the window is really minimized (not to the system tray)
-       if fgCount == 1:
-               fg = getNVDAObjectFromEvent(user32.FindWindowA("TStudioForm", 
None), winUser.OBJID_CLIENT, 0)
-       return fg
-
-
 # Encoder configuration dialog.
 _configDialogOpened = False
 
@@ -541,10 +523,7 @@ class SAMEncoder(Encoder):
                                if self.focusToStudio and not encoding:
                                        if api.getFocusObject().appModule == 
"splstudio":
                                                continue
-                                       try:
-                                               
fetchSPLForegroundWindow().setFocus()
-                                       except AttributeError:
-                                               pass
+                                       
user32.SetForegroundWindow(user32.FindWindowA("TStudioForm", None))
                                if self.playAfterConnecting and not encoding:
                                        # Do not interupt the currently playing 
track.
                                        if winUser.sendMessage(SPLWin, SPLMSG, 
0, SPL_TrackPlaybackStatus) == 0:
@@ -740,10 +719,7 @@ class SPLEncoder(Encoder):
                                # We're on air, so exit.
                                if not connected: tones.beep(1000, 150)
                                if self.focusToStudio and not connected:
-                                       try:
-                                               
fetchSPLForegroundWindow().setFocus()
-                                       except AttributeError:
-                                               pass
+                                       
user32.SetForegroundWindow(user32.FindWindowA("TStudioForm", None))
                                if self.playAfterConnecting and not connected:
                                        if winUser.sendMessage(SPLWin, SPLMSG, 
0, SPL_TrackPlaybackStatus) == 0:
                                                winUser.sendMessage(SPLWin, 
SPLMSG, 0, SPLPlay)


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/c337500fb5fd/
Changeset:   c337500fb5fd
Branch:      None
User:        josephsl
Date:        2016-12-27 09:22:53+00:00
Summary:     Merge branch '16.10.x'

Affected #:  1 file

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index d30844c..fb3e177 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -772,14 +772,21 @@ class AppModule(appModuleHandler.AppModule):
 
        # Perform extra action in specific situations (mic alarm, for example).
        def doExtraAction(self, status):
-               micAlarm = splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"]
-               if self.cartExplorer:
-                       if status == "Cart Edit On":
-                               # Translators: Presented when cart edit mode is 
toggled on while cart explorer is on.
-                               ui.message(_("Cart explorer is active"))
-                       elif status == "Cart Edit Off":
+               # Be sure to only deal with cart mode changes if Cart Explorer 
is on.
+               # Optimization: Return early if the below condition is true.
+               if self.cartExplorer and status.startswith("Cart"):
+                       # 17.01: Cart Insert mode flag should also be covered.
+                       # Workaround: Check if the previous status was "Cart 
Edit On".
+                       if self.cartEdit:
                                # Translators: Presented when cart edit mode is 
toggled off while cart explorer is on.
                                ui.message(_("Please reenter cart explorer to 
view updated cart assignments"))
+                               self.cartEdit = False
+                       else:
+                       # Translators: Presented when cart edit mode is toggled 
on while cart explorer is on.
+                               ui.message(_("Cart explorer is active"))
+                       return
+               # Microphone alarm and alarm interval if defined.
+               micAlarm = splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"]
                if micAlarm:
                        # Play an alarm sound (courtesy of Jerry Mader from 
Mader Radio).
                        global micAlarmT, micAlarmT2
@@ -1409,6 +1416,18 @@ class AppModule(appModuleHandler.AppModule):
                        if not libScanT or (libScanT and not 
libScanT.isAlive()):
                                self.monitorLibraryScan()
 
+       # Have a handler for Cart Edit toggle command (Control+T) handy.
+       # 17.01: This is needed in order to announce cart edit toggle correctly 
while Cart Explorer is on (if so, one should reenter this mode).
+       cartEdit = False
+       def script_toggleCartEdit(self, gesture):
+               gesture.send()
+               if api.getForegroundObject().windowClassName == "TStudioForm":
+                       # For Studio 5.11 and earlier, the only reliable way is 
looking at previous status flag.
+                       if self.productVersion < "5.2":
+                               self.cartEdit = 
self.status(self.SPLPlayStatus).getChild(5).name != "Cart Edit On"
+                       else:
+                               self.cartEdit = not statusAPI(5, 39, ret=True)
+
        # The developer would like to get feedback from you.
        def script_sendFeedbackEmail(self, gesture):
                os.startfile("mailto:joseph.lee22590@xxxxxxxxx";)
@@ -1960,6 +1979,7 @@ class AppModule(appModuleHandler.AppModule):
                "kb:Shift+delete":"deleteTrack",
                "kb:Shift+numpadDelete":"deleteTrack",
                "kb:escape":"escape",
+               "kb:Control+T":"toggleCartEdit",
                "kb:control+nvda+-":"sendFeedbackEmail",
                #"kb:control+nvda+`":"SPLAssistantToggle"
        }


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/76ea7641bcdf/
Changeset:   76ea7641bcdf
Branch:      None
User:        josephsl
Date:        2016-12-27 17:19:55+00:00
Summary:     Merge branch '16.10.x'

Affected #:  2 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index fb3e177..78ab627 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -776,11 +776,10 @@ class AppModule(appModuleHandler.AppModule):
                # Optimization: Return early if the below condition is true.
                if self.cartExplorer and status.startswith("Cart"):
                        # 17.01: Cart Insert mode flag should also be covered.
-                       # Workaround: Check if the previous status was "Cart 
Edit On".
-                       if self.cartEdit:
+                       # The best way to detect Cart Edit off is consulting 
file modification time.
+                       if 
splmisc.shouldCartExplorerRefresh(api.getForegroundObject().name):
                                # Translators: Presented when cart edit mode is 
toggled off while cart explorer is on.
                                ui.message(_("Please reenter cart explorer to 
view updated cart assignments"))
-                               self.cartEdit = False
                        else:
                        # Translators: Presented when cart edit mode is toggled 
on while cart explorer is on.
                                ui.message(_("Cart explorer is active"))
@@ -1416,18 +1415,6 @@ class AppModule(appModuleHandler.AppModule):
                        if not libScanT or (libScanT and not 
libScanT.isAlive()):
                                self.monitorLibraryScan()
 
-       # Have a handler for Cart Edit toggle command (Control+T) handy.
-       # 17.01: This is needed in order to announce cart edit toggle correctly 
while Cart Explorer is on (if so, one should reenter this mode).
-       cartEdit = False
-       def script_toggleCartEdit(self, gesture):
-               gesture.send()
-               if api.getForegroundObject().windowClassName == "TStudioForm":
-                       # For Studio 5.11 and earlier, the only reliable way is 
looking at previous status flag.
-                       if self.productVersion < "5.2":
-                               self.cartEdit = 
self.status(self.SPLPlayStatus).getChild(5).name != "Cart Edit On"
-                       else:
-                               self.cartEdit = not statusAPI(5, 39, ret=True)
-
        # The developer would like to get feedback from you.
        def script_sendFeedbackEmail(self, gesture):
                os.startfile("mailto:joseph.lee22590@xxxxxxxxx";)
@@ -1979,7 +1966,5 @@ class AppModule(appModuleHandler.AppModule):
                "kb:Shift+delete":"deleteTrack",
                "kb:Shift+numpadDelete":"deleteTrack",
                "kb:escape":"escape",
-               "kb:Control+T":"toggleCartEdit",
                "kb:control+nvda+-":"sendFeedbackEmail",
-               #"kb:control+nvda+`":"SPLAssistantToggle"
        }

diff --git a/addon/appModules/splstudio/splmisc.py 
b/addon/appModules/splstudio/splmisc.py
index 4e8bec3..a403b99 100755
--- a/addon/appModules/splstudio/splmisc.py
+++ b/addon/appModules/splstudio/splmisc.py
@@ -262,9 +262,12 @@ def _populateCarts(carts, cartlst, modifier, 
standardEdition=False):
                else: cart = "%s+%s"%(modifier, identifier)
                carts[cart] = cartName
 
-# Initialize Cart Explorer i.e. fetch carts.
+# Cart file timestamps.
+_cartEditTimestamps = [0, 0, 0, 0]
+               # Initialize Cart Explorer i.e. fetch carts.
 # Cart files list is for future use when custom cart names are used.
 def cartExplorerInit(StudioTitle, cartFiles=None):
+       global _cartEditTimestamps
        # Use cart files in SPL's data folder to build carts dictionary.
        # use a combination of SPL user name and static cart location to locate 
cart bank files.
        # Once the cart banks are located, use the routines in the populate 
method above to assign carts.
@@ -294,10 +297,31 @@ def cartExplorerInit(StudioTitle, cartFiles=None):
                        continue
                with open(cartFile) as cartInfo:
                        cl = [row for row in reader(cartInfo)]
+                       # 17.01: Look up file modification date to signal the 
app module that Cart Explorer reentry should occur.
+                       _cartEditTimestamps[cartFiles.index(f)] = 
os.path.getmtime(cartFile)
                _populateCarts(carts, cl[1], mod, 
standardEdition=carts["standardLicense"]) # See the comment for _populate 
method above.
        carts["faultyCarts"] = faultyCarts
        return carts
 
+# See if cart files were modified.
+# This is needed in order to announce Cart Explorer reentry command.
+def shouldCartExplorerRefresh(StudioTitle):
+       global _cartEditTimestamps
+       cartsDataPath = 
os.path.join(os.environ["PROGRAMFILES"],"StationPlaylist","Data") # Provided 
that Studio was installed using default path.
+       userNameIndex = StudioTitle.find("-")
+       # Until NVDA core moves to Python 3, assume that file names aren't 
unicode.
+       cartFiles = [u"main carts.cart", u"shift carts.cart", u"ctrl 
carts.cart", u"alt carts.cart"]
+       if userNameIndex >= 0:
+               cartFiles = [StudioTitle[userNameIndex+2:]+" "+cartFile for 
cartFile in cartFiles]
+       for f in cartFiles:
+               # No need to check for faulty carts here, as Cart Explorer 
activation checked it already.
+               timestamp = os.path.getmtime(os.path.join(cartsDataPath,f))
+               # 17.01: Look up file modification date to signal the app 
module that Cart Explorer reentry should occur.
+               # Optimization: Short-circuit if even one cart file has been 
modified.
+               if _cartEditTimestamps[cartFiles.index(f)] != timestamp:
+                       return True
+       return False
+
 
 # Countdown timer.
 # This is utilized by many services, chiefly profile triggers routine.


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/3dd9e76b599c/
Changeset:   3dd9e76b599c
Branch:      None
User:        josephsl
Date:        2016-12-27 21:57:35+00:00
Summary:     Merge branch 'stable'

Affected #:  1 file

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 78ab627..0680824 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -775,14 +775,13 @@ class AppModule(appModuleHandler.AppModule):
                # Be sure to only deal with cart mode changes if Cart Explorer 
is on.
                # Optimization: Return early if the below condition is true.
                if self.cartExplorer and status.startswith("Cart"):
-                       # 17.01: Cart Insert mode flag should also be covered.
-                       # The best way to detect Cart Edit off is consulting 
file modification time.
-                       if 
splmisc.shouldCartExplorerRefresh(api.getForegroundObject().name):
-                               # Translators: Presented when cart edit mode is 
toggled off while cart explorer is on.
-                               ui.message(_("Please reenter cart explorer to 
view updated cart assignments"))
-                       else:
+                       # 17.01: The best way to detect Cart Edit off is 
consulting file modification time.
+                       # Automatically reload cart information if this is the 
case.
+                       studioTitle = api.getForegroundObject().name
+                       if splmisc.shouldCartExplorerRefresh(studioTitle):
+                               self.carts = 
splmisc.cartExplorerInit(studioTitle)
                        # Translators: Presented when cart edit mode is toggled 
on while cart explorer is on.
-                               ui.message(_("Cart explorer is active"))
+                       ui.message(_("Cart explorer is active"))
                        return
                # Microphone alarm and alarm interval if defined.
                micAlarm = splconfig.SPLConfig["MicrophoneAlarm"]["MicAlarm"]


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/fa83964a0ab7/
Changeset:   fa83964a0ab7
Branch:      None
User:        josephsl
Date:        2016-12-27 22:10:48+00:00
Summary:     Bump to version 17.04, updated comments to reflect the new version.

Affected #:  7 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index 0680824..d250a6b 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -154,7 +154,7 @@ class SPLTrackItem(IAccessible):
                if splconfig.SPLConfig["General"]["TrackCommentAnnounce"] != 
"off":
                        self.announceTrackComment(0)
                # 6.3: Catch an unusual case where screen order is off yet 
column order is same as screen order and NvDA is told to announce all columns.
-               # 17.1: Even if vertical column commands are performed, build 
description pieces for consistency.
+               # 17.04: Even if vertical column commands are performed, build 
description pieces for consistency.
                if splconfig._shouldBuildDescriptionPieces():
                        descriptionPieces = []
                        columnsToInclude = 
splconfig.SPLConfig["ColumnAnnouncement"]["IncludedColumns"]
@@ -199,7 +199,7 @@ class SPLTrackItem(IAccessible):
 
        # Announce column content if any.
        # 7.0: Add an optional header in order to announce correct header 
information in columns explorer.
-       # 17.1: Allow checked status in 5.1x and later to be announced if this 
is such a case (vertical column navigation).)
+       # 17.04: Allow checked status in 5.1x and later to be announced if this 
is such a case (vertical column navigation).)
        def announceColumnContent(self, colNumber, header=None, 
reportStatus=False):
                columnHeader = header if header is not None else 
self.appModule._columnHeaderNames[colNumber]
                columnContent = 
self._getColumnContent(self.indexOf(columnHeader))
@@ -1267,7 +1267,7 @@ class AppModule(appModuleHandler.AppModule):
                if api.getForegroundObject().windowClassName == 
"TTrackInsertForm" and self.productVersion in noLibScanMonitor:
                        self.libraryScanning = False
                        return
-               # 17.1: Library scan may have finished while this thread was 
sleeping.
+               # 17.04: Library scan may have finished while this thread was 
sleeping.
                if statusAPI(1, 32, ret=True) < 0:
                        self.libraryScanning = False
                        # Translators: Presented when library scanning is 
finished.
@@ -1279,7 +1279,7 @@ class AppModule(appModuleHandler.AppModule):
 
        def libraryScanReporter(self):
                scanIter = 0
-               # 17.1: Use the constant directly, as 5.10 and later provides a 
convenient method to detect completion of library scans.
+               # 17.04: Use the constant directly, as 5.10 and later provides 
a convenient method to detect completion of library scans.
                scanCount = statusAPI(1, 32, ret=True)
                while scanCount >= 0:
                        if not self.libraryScanning: return

diff --git a/addon/appModules/splstudio/splconfig.py 
b/addon/appModules/splstudio/splconfig.py
index 5cd8800..24f4e7a 100755
--- a/addon/appModules/splstudio/splconfig.py
+++ b/addon/appModules/splstudio/splconfig.py
@@ -474,7 +474,7 @@ def _extraInitSteps(conf, profileName=None):
                else:
                        _configLoadStatus[profileName] = "metadataReset"
                conf["MetadataStreaming"]["MetadataEnabled"] = [False, False, 
False, False, False]
-       # 17.1: If vertical column announcement value is "None", transform this 
to NULL.
+       # 17.04: If vertical column announcement value is "None", transform 
this to NULL.
        if conf["General"]["VerticalColumnAnnounce"] == "None":
                conf["General"]["VerticalColumnAnnounce"] = None
 

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index 47f35d6..80673f7 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -912,7 +912,7 @@ class MetadataStreamingDialog(wx.Dialog):
 
                # WX's CheckListBox isn't user friendly.
                # Therefore use checkboxes laid out across the top.
-               # 17.1: instead of two loops, just use one loop, with labels 
deriving from the below tuple.
+               # 17.04: instead of two loops, just use one loop, with labels 
deriving from the below tuple.
                # Only one loop is needed as helper.addLabelControl returns the 
checkbox itself and that can be appended.
                streamLabels = ("DSP encoder", "URL 1", "URL 2", "URL 3", "URL 
4")
                self.checkedStreams = []
@@ -982,7 +982,7 @@ class ColumnAnnouncementsDialog(wx.Dialog):
                # Same as metadata dialog (wx.CheckListBox isn't user friendly).
                # Gather values for checkboxes except artist and title.
                # 6.1: Split these columns into rows.
-               # 17.1: Gather items into a single list instead of three.
+               # 17.04: Gather items into a single list instead of three.
                self.checkedColumns = []
                sizer = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.HORIZONTAL)
                for column in ("Duration", "Intro", "Category", "Filename"):
@@ -1002,7 +1002,7 @@ class ColumnAnnouncementsDialog(wx.Dialog):
 
                # WXPython Phoenix contains RearrangeList to allow item orders 
to be changed automatically.
                # Because WXPython Classic doesn't include this, work around by 
using a variant of list box and move up/down buttons.
-               # 17.1: The label for the list below is above the list, so move 
move up/down buttons to the right of the list box.
+               # 17.04: The label for the list below is above the list, so 
move move up/down buttons to the right of the list box.
                # Translators: The label for a setting in SPL add-on dialog to 
select column announcement order.
                self.trackColumns = 
colAnnouncementsHelper.addLabeledControl(_("Column &order:"), wx.ListBox, 
choices=parent.columnOrder)
                self.trackColumns.Bind(wx.EVT_LISTBOX,self.onColumnSelection)
@@ -1100,7 +1100,7 @@ class ColumnsExplorerDialog(wx.Dialog):
                colExplorerHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
 
                # 7.0: Studio 5.0x columns.
-               # 17.1: Five by two grid layout as 5.0x is no longer supported.
+               # 17.04: Five by two grid layout as 5.0x is no longer supported.
                sizer = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.HORIZONTAL)
                for slot in xrange(5):
                        # Translators: The label for a setting in SPL add-on 
dialog to select column for this column slot.

diff --git a/addon/appModules/splstudio/splmisc.py 
b/addon/appModules/splstudio/splmisc.py
index a403b99..e3355bc 100755
--- a/addon/appModules/splstudio/splmisc.py
+++ b/addon/appModules/splstudio/splmisc.py
@@ -373,7 +373,7 @@ def _metadataAnnouncer(reminder=False, handle=None):
        # DSP is treated specially.
        dsp = sendMessage(handle, 1024, 0, 36)
        # For others, a simple list.append will do.
-       # 17.1: Use a conditional list comprehension.
+       # 17.04: Use a conditional list comprehension.
        streamCount = [str(pos) for pos in xrange(1, 5) if sendMessage(handle, 
1024, pos, 36)]
        # Announce streaming status when told to do so.
        status = None

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
index 142ed4c..cb63d5a 100755
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ b/addon/globalPlugins/SPLStudioUtils/__init__.py
@@ -246,7 +246,7 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
        def script_statusInfo(self, gesture):
                # For consistency reasons (because of the Studio status bar), 
messages in this method will remain in English.
                statusInfo = []
-               # 17.1: For Studio 5.10 and up, announce playback and 
automation status.
+               # 17.04: For Studio 5.10 and up, announce playback and 
automation status.
                playingNow = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPL_TrackPlaybackStatus)
                statusInfo.append("Play status: playing" if playingNow else 
"Play status: stopped")
                # For automation, Studio 5.11 and earlier does not have an easy 
way to detect this flag, thus resort to using playback status.

diff --git a/buildVars.py b/buildVars.py
index 04f4768..26e6739 100755
--- a/buildVars.py
+++ b/buildVars.py
@@ -20,7 +20,7 @@ addon_info = {
        "addon_description" : _("""Enhances support for StationPlaylist Studio.
 In addition, adds global commands for the studio from everywhere."""),
        # version
-       "addon_version" : "17.1-dev",
+       "addon_version" : "17.04-dev",
        # Author(s)
        "addon_author" : u"Geoff Shang, Joseph Lee and other contributors",
        # URL for the add-on documentation support

diff --git a/readme.md b/readme.md
index ed64ddf..abc6f63 100755
--- a/readme.md
+++ b/readme.md
@@ -167,7 +167,7 @@ From studio window, you can press Alt+NVDA+0 to open the 
add-on configuration di
 
 If you are using Studio on a touchscreen computer running Windows 8 or later 
and have NVDA 2012.3 or later installed, you can perform some Studio commands 
from the touchscreen. First use three finger tap to switch to SPL mode, then 
use the touch commands listed above to perform commands.
 
-## Version 17.1-dev
+## Version 17.04-dev
 
 * Improvements to presentation of various add-on dialogs thanks to NVDA 2016.4 
features.
 * Added ability to press Control+Alt+up or down arrow keys to move between 
tracks (specifically, track columns) vertically just as one is moving to next 
or previous row in a table.


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/25f9d66e1b1e/
Changeset:   25f9d66e1b1e
Branch:      None
User:        josephsl
Date:        2016-12-27 22:16:34+00:00
Summary:     Renamed 'SPLStudioUtils' to 'splUtils' to comply with community 
add-on naming guidelines.

Affected #:  6 files

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index d250a6b..bbdb87c 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -857,9 +857,9 @@ class AppModule(appModuleHandler.AppModule):
                # 6.3: Memory leak results if encoder flag sets and other 
encoder support maps aren't cleaned up.
                # This also could have allowed a hacker to modify the flags set 
(highly unlikely) so NvDA could get confused next time Studio loads.
                import sys
-               if "globalPlugins.SPLStudioUtils.encoders" in sys.modules:
-                       import globalPlugins.SPLStudioUtils.encoders
-                       globalPlugins.SPLStudioUtils.encoders.cleanup()
+               if "globalPlugins.splUtils.encoders" in sys.modules:
+                       import globalPlugins.splUtils.encoders
+                       globalPlugins.splUtils.encoders.cleanup()
                splconfig.saveConfig()
                # Delete focused track reference.
                self._focusedTrack = None

diff --git a/addon/appModules/splstudio/splconfui.py 
b/addon/appModules/splstudio/splconfui.py
index 80673f7..743aab1 100755
--- a/addon/appModules/splstudio/splconfui.py
+++ b/addon/appModules/splstudio/splconfui.py
@@ -1328,9 +1328,9 @@ class ResetDialog(wx.Dialog):
                        if self.resetEncodersCheckbox.Value:
                                if 
os.path.exists(os.path.join(globalVars.appArgs.configPath, 
"splStreamLabels.ini")):
                                        
os.remove(os.path.join(globalVars.appArgs.configPath, "splStreamLabels.ini"))
-                               if "globalPlugins.SPLStudioUtils.encoders" in 
sys.modules:
-                                       import 
globalPlugins.SPLStudioUtils.encoders
-                                       
globalPlugins.SPLStudioUtils.encoders.cleanup()
+                               if "globalPlugins.splUtils.encoders" in 
sys.modules:
+                                       import globalPlugins.splUtils.encoders
+                                       
globalPlugins.splUtils.encoders.cleanup()
                        _configDialogOpened = False
                        # Translators: A dialog message shown when settings 
were reset to defaults.
                        wx.CallAfter(gui.messageBox, _("Successfully applied 
default add-on settings."),

diff --git a/addon/globalPlugins/SPLStudioUtils/__init__.py 
b/addon/globalPlugins/SPLStudioUtils/__init__.py
deleted file mode 100755
index cb63d5a..0000000
--- a/addon/globalPlugins/SPLStudioUtils/__init__.py
+++ /dev/null
@@ -1,314 +0,0 @@
-# StationPlaylist Utilities
-# Author: Joseph Lee
-# Copyright 2013-2017, released under GPL.
-# Adds a few utility features such as switching focus to the SPL Studio window 
and some global scripts.
-# For encoder support, see the encoders package.
-
-from functools import wraps
-import os
-import globalPluginHandler
-import api
-import ui
-import globalVars
-from NVDAObjects.IAccessible import getNVDAObjectFromEvent
-import winUser
-import nvwave
-import addonHandler
-addonHandler.initTranslation()
-
-# Layer environment: same as the app module counterpart.
-
-def finally_(func, final):
-       """Calls final after func, even if it fails."""
-       def wrap(f):
-               @wraps(f)
-               def new(*args, **kwargs):
-                       try:
-                               func(*args, **kwargs)
-                       finally:
-                               final()
-               return new
-       return wrap(final)
-
-# SPL Studio uses WM messages to send and receive data, similar to Winamp (see 
NVDA sources/appModules/winamp.py for more information).
-user32 = winUser.user32 # user32.dll.
-SPLWin = 0 # A handle to studio window.
-SPLMSG = winUser.WM_USER
-
-# Various SPL IPC tags.
-SPLVersion = 2
-SPLPlay = 12
-SPLStop = 13
-SPLPause = 15
-SPLAutomate = 16
-SPLMic = 17
-SPLLineIn = 18
-SPLLibraryScanCount = 32
-SPLListenerCount = 35
-SPLStatusInfo = 39 #Studio 5.20 and later.
-SPL_TrackPlaybackStatus = 104
-SPLCurTrackPlaybackTime = 105
-
-# Help message for SPL Controller
-# Translators: the dialog text for SPL Controller help.
-SPLConHelp=_("""
-After entering SPL Controller, press:
-A: Turn automation on.
-Shift+A: Turn automation off.
-M: Turn microphone on.
-Shift+M: Turn microphone off.
-N: Turn microphone on without fade.
-L: Turn line in on.
-Shift+L: Turn line in off.
-P: Play.
-U: Pause.
-S: Stop with fade.
-T: Instant stop.
-E: Announce if any encoders are being monitored.
-I: Announce listener count.
-Q: Announce Studio status information.
-R: Remaining time for the playing track.
-Shift+R: Library scan progress.""")
-
-
-class GlobalPlugin(globalPluginHandler.GlobalPlugin):
-
-       # Translators: Script category for Station Playlist commands in input 
gestures dialog.
-       scriptCategory = _("StationPlaylist Studio")
-
-       #Global layer environment (see the app module for more information).
-       SPLController = False # Control SPL from anywhere.
-
-       def getScript(self, gesture):
-               if not self.SPLController:
-                       return globalPluginHandler.GlobalPlugin.getScript(self, 
gesture)
-               script = globalPluginHandler.GlobalPlugin.getScript(self, 
gesture)
-               if not script:
-                       script = finally_(self.script_error, self.finish)
-               return finally_(script, self.finish)
-
-       def finish(self):
-               self.SPLController = False
-               self.clearGestureBindings()
-               self.bindGestures(self.__gestures)
-
-       def script_error(self, gesture):
-               import tones
-               tones.beep(120, 100)
-
-       # Switch focus to SPL Studio window from anywhere.
-       def script_focusToSPLWindow(self, gesture):
-               # 7.4: Forget it if this is the case like the following.
-               if globalVars.appArgs.secure: return
-               # Don't do anything if we're already focus on SPL Studio.
-               if "splstudio" in 
api.getForegroundObject().appModule.appModuleName: return
-               else:
-                       SPLHwnd = user32.FindWindowA("SPLStudio", None) # Used 
ANSI version, as Wide char version always returns 0.
-                       if not SPLHwnd: ui.message(_("SPL Studio is not 
running."))
-                       else:
-                               # 17.01: SetForegroundWindow function is 
better, as there's no need to traverse top-level windows and allows users to 
"switch" to SPL window if the window is minimized.
-                               
user32.SetForegroundWindow(user32.FindWindowA("TStudioForm", None))
-       # Translators: Input help mode message for a command to switch to 
Station Playlist Studio from any program.
-       script_focusToSPLWindow.__doc__=_("Moves to SPL Studio window from 
other programs.")
-
-       # The SPL Controller:
-       # This layer set allows the user to control various aspects of SPL 
Studio from anywhere.
-       def script_SPLControllerPrefix(self, gesture):
-               # 7.4: Red flag...
-               if globalVars.appArgs.secure: return
-               global SPLWin
-               # Error checks:
-               # 1. If SPL Studio is not running, print an error message.
-               # 2. If we're already  in SPL, ask the app module if SPL 
Assistant can be invoked with this command.
-               if "splstudio" in 
api.getForegroundObject().appModule.appModuleName:
-                       if not 
api.getForegroundObject().appModule.SPLConPassthrough():
-                               # Translators: Presented when NVDA cannot enter 
SPL Controller layer since SPL Studio is focused.
-                               ui.message(_("You are already in SPL Studio 
window. For status commands, use SPL Assistant commands."))
-                               self.finish()
-                               return
-                       else:
-                               
api.getForegroundObject().appModule.script_SPLAssistantToggle(gesture)
-                               return
-               SPLWin = user32.FindWindowA("SPLStudio", None)
-               if SPLWin == 0:
-                       # Translators: Presented when Station Playlist Studio 
is not running.
-                       ui.message(_("SPL Studio is not running."))
-                       self.finish()
-                       return
-               # No errors, so continue.
-               if not self.SPLController:
-                       self.bindGestures(self.__SPLControllerGestures)
-                       self.SPLController = True
-                       # Translators: The name of a layer command set for 
Station Playlist Studio.
-                       # Hint: it is better to translate it as "SPL Control 
Panel."
-                       ui.message(_("SPL Controller"))
-               else:
-                       self.script_error(gesture)
-                       self.finish()
-       # Translators: Input help mode message for a layer command in Station 
Playlist Studio.
-       script_SPLControllerPrefix.__doc__=_("SPl Controller layer command. See 
add-on guide for available commands.")
-
-       # The layer commands themselves. Calls user32.SendMessage method for 
each script.
-
-       def script_automateOn(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,1,SPLAutomate)
-               self.finish()
-
-       def script_automateOff(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,0,SPLAutomate)
-               self.finish()
-
-       def script_micOn(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,1,SPLMic)
-               nvwave.playWaveFile(os.path.join(os.path.dirname(__file__), 
"..", "..", "appModules", "splstudio", "SPL_on.wav"))
-               self.finish()
-
-       def script_micOff(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,0,SPLMic)
-               nvwave.playWaveFile(os.path.join(os.path.dirname(__file__), 
"..", "..", "appModules", "splstudio", "SPL_off.wav"))
-               self.finish()
-
-       def script_micNoFade(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,2,SPLMic)
-               self.finish()
-
-       def script_lineInOn(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,1,SPLLineIn)
-               self.finish()
-
-       def script_lineInOff(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,0,SPLLineIn)
-               self.finish()
-
-       def script_stopFade(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,0,SPLStop)
-               self.finish()
-
-       def script_stopInstant(self, gesture):
-               winUser.sendMessage(SPLWin,SPLMSG,1,SPLStop)
-               self.finish()
-
-       def script_play(self, gesture):
-               winUser.sendMessage(SPLWin, SPLMSG, 0, SPLPlay)
-               self.finish()
-
-       def script_pause(self, gesture):
-               playingNow = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPL_TrackPlaybackStatus)
-               # Translators: Presented when no track is playing in Station 
Playlist Studio.
-               if not playingNow: ui.message(_("There is no track playing. Try 
pausing while a track is playing."))
-               elif playingNow == 3: winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLPause)
-               else: winUser.sendMessage(SPLWin, SPLMSG, 1, SPLPause)
-               self.finish()
-
-       def script_libraryScanProgress(self, gesture):
-               scanned = winUser.sendMessage(SPLWin, SPLMSG, 1, 
SPLLibraryScanCount)
-               if scanned >= 0:
-                       # Translators: Announces number of items in the 
Studio's track library (example: 1000 items scanned).
-                       ui.message(_("Scan in progress with {itemCount} items 
scanned").format(itemCount = scanned))
-               else:
-                       # Translators: Announces number of items in the 
Studio's track library (example: 1000 items scanned).
-                       ui.message(_("Scan complete with {itemCount} items 
scanned").format(itemCount = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLLibraryScanCount)))
-               self.finish()
-
-       def script_listenerCount(self, gesture):
-               # Translators: Announces number of stream listeners.
-               ui.message(_("Listener count: 
{listenerCount}").format(listenerCount = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLListenerCount)))
-               self.finish()
-
-       def script_remainingTime(self, gesture):
-               remainingTime = winUser.sendMessage(SPLWin, SPLMSG, 3, 
SPLCurTrackPlaybackTime)
-               # Translators: Presented when no track is playing in Station 
Playlist Studio.
-               if remainingTime < 0: ui.message(_("There is no track 
playing."))
-               else:
-                       # 7.0: Present remaining time in hh:mm:ss format for 
enhanced experience (borrowed from the app module).
-                       remainingTime = (remainingTime/1000)+1
-                       if remainingTime == 0: ui.message("00:00")
-                       elif 1 <= remainingTime <= 59: 
ui.message("00:{0}".format(str(remainingTime).zfill(2)))
-                       else:
-                               mm, ss = divmod(remainingTime, 60)
-                               if mm > 59:
-                                       hh, mm = divmod(mm, 60)
-                                       t0 = str(hh).zfill(2)
-                                       t1 = str(mm).zfill(2)
-                                       t2 = str(ss).zfill(2)
-                                       ui.message(":".join([t0, t1, t2]))
-                               else:
-                                       t1 = str(mm).zfill(2)
-                                       t2 = str(ss).zfill(2)
-                                       ui.message(":".join([t1, t2]))
-               self.finish()
-
-       def script_announceNumMonitoringEncoders(self, gesture):
-               import encoders
-               encoders.announceNumMonitoringEncoders()
-               self.finish()
-
-       def script_statusInfo(self, gesture):
-               # For consistency reasons (because of the Studio status bar), 
messages in this method will remain in English.
-               statusInfo = []
-               # 17.04: For Studio 5.10 and up, announce playback and 
automation status.
-               playingNow = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPL_TrackPlaybackStatus)
-               statusInfo.append("Play status: playing" if playingNow else 
"Play status: stopped")
-               # For automation, Studio 5.11 and earlier does not have an easy 
way to detect this flag, thus resort to using playback status.
-               if winUser.sendMessage(SPLWin, SPLMSG, 0, SPLVersion) < 520:
-                       statusInfo.append("Automation on" if playingNow == 2 
else "Automation off")
-               else:
-                       statusInfo.append("Automation on" if 
winUser.sendMessage(SPLWin, SPLMSG, 1, SPLStatusInfo) else "Automation off")
-                       # 5.20 and later.
-                       statusInfo.append("Microphone on" if 
winUser.sendMessage(SPLWin, SPLMSG, 2, SPLStatusInfo) else "Microphone off")
-                       statusInfo.append("Line-inon" if 
winUser.sendMessage(SPLWin, SPLMSG, 3, SPLStatusInfo) else "Line-in off")
-                       statusInfo.append("Record to file on" if 
winUser.sendMessage(SPLWin, SPLMSG, 4, SPLStatusInfo) else "Record to file off")
-                       cartEdit = winUser.sendMessage(SPLWin, SPLMSG, 5, 
SPLStatusInfo)
-                       cartInsert = winUser.sendMessage(SPLWin, SPLMSG, 6, 
SPLStatusInfo)
-                       if cartEdit: statusInfo.append("Cart edit on")
-                       elif not cartEdit and cartInsert: 
statusInfo.append("Cart insert on")
-                       else: statusInfo.append("Cart edit off")
-               ui.message("; ".join(statusInfo))
-               self.finish()
-       # Translators: Input help message for a SPL Controller command.
-       script_statusInfo.__doc__ = _("Announces Studio status such as track 
playback status from other programs")
-
-       def script_conHelp(self, gesture):
-               import gui, wx
-               # Translators: The title for SPL Controller help dialog.
-               wx.CallAfter(gui.messageBox, SPLConHelp, _("SPL Controller 
help"))
-               self.finish()
-
-
-       __SPLControllerGestures={
-               "kb:p":"play",
-               "kb:a":"automateOn",
-               "kb:shift+a":"automateOff",
-               "kb:m":"micOn",
-               "kb:shift+m":"micOff",
-               "kb:n":"micNoFade",
-               "kb:l":"lineInOn",
-               "kb:shift+l":"lineInOff",
-               "kb:shift+r":"libraryScanProgress",
-               "kb:s":"stopFade",
-               "kb:t":"stopInstant",
-               "kb:u":"pause",
-               "kb:r":"remainingTime",
-               "kb:e":"announceNumMonitoringEncoders",
-               "kb:i":"listenerCount",
-               "kb:q":"statusInfo",
-               "kb:f1":"conHelp"
-       }
-
-
-       __gestures={
-               #"kb:nvda+shift+`":"focusToSPLWindow",
-               #"kb:nvda+`":"SPLControllerPrefix"
-       }
-
-       # Support for Encoders
-       # Each encoder is an overlay class, thus makes it easier to add 
encoders in the future by implementing overlay objects.
-       # Each encoder, at a minimum, must support connection monitoring 
routines.
-
-       def chooseNVDAObjectOverlayClasses(self, obj, clsList):
-               if obj.appModule.appName in ("splengine", "splstreamer"):
-                       import controlTypes, encoders
-                       if obj.windowClassName == "TListView":
-                               clsList.insert(0, encoders.SAMEncoder)
-                       elif obj.windowClassName == "SysListView32" and 
obj.role == controlTypes.ROLE_LISTITEM:
-                                       clsList.insert(0, encoders.SPLEncoder)

diff --git a/addon/globalPlugins/SPLStudioUtils/encoders.py 
b/addon/globalPlugins/SPLStudioUtils/encoders.py
deleted file mode 100755
index b25486a..0000000
--- a/addon/globalPlugins/SPLStudioUtils/encoders.py
+++ /dev/null
@@ -1,830 +0,0 @@
-# StationPlaylist encoders support
-# Author: Joseph Lee
-# Copyright 2015-2017, released under GPL.
-# Split from main global plugin in 2015.
-
-import threading
-import time
-import api
-import ui
-import speech
-import scriptHandler
-from NVDAObjects.IAccessible import IAccessible, getNVDAObjectFromEvent
-import winUser
-import tones
-import gui
-import wx
-
-
-# SPL Studio uses WM messages to send and receive data, similar to Winamp (see 
NVDA sources/appModules/winamp.py for more information).
-user32 = winUser.user32 # user32.dll.
-SPLWin = 0 # A handle to studio window.
-SPLMSG = winUser.WM_USER
-
-# Various SPL IPC tags.
-SPLPlay = 12
-SPL_TrackPlaybackStatus = 104
-
-# Needed in Encoder support:
-SPLFocusToStudio = set() # Whether to focus to Studio or not.
-SPLPlayAfterConnecting = set()
-SPLBackgroundMonitor = set()
-SPLNoConnectionTone = set()
-
-# Customized for each encoder type.
-SAMStreamLabels= {} # A dictionary to store custom labels for each stream.
-SPLStreamLabels= {} # Same as above but optimized for SPL encoders (Studio 
5.00 and later).
-SAMMonitorThreads = {}
-SPLMonitorThreads = {}
-encoderMonCount = {"SAM":0, "SPL":0}
-
-# Configuration management.
-streamLabels = None
-
-# Load stream labels (and possibly other future goodies) from a file-based 
database.
-def loadStreamLabels():
-       global streamLabels, SAMStreamLabels, SPLStreamLabels, 
SPLFocusToStudio, SPLPlayAfterConnecting, SPLBackgroundMonitor, 
SPLNoConnectionTone
-       import os, configobj, globalVars
-       streamLabels = 
configobj.ConfigObj(os.path.join(globalVars.appArgs.configPath, 
"splStreamLabels.ini"))
-       # Read stream labels.
-       try:
-               SAMStreamLabels = dict(streamLabels["SAMEncoders"])
-       except KeyError:
-               SAMStreamLabels = {}
-       try:
-               SPLStreamLabels = dict(streamLabels["SPLEncoders"])
-       except KeyError:
-               SPLStreamLabels = {}
-       # Read other settings.
-       if "FocusToStudio" in streamLabels:
-               SPLFocusToStudio = set(streamLabels["FocusToStudio"])
-       if "PlayAfterConnecting" in streamLabels:
-               SPLPlayAfterConnecting = 
set(streamLabels["PlayAfterConnecting"])
-       if "BackgroundMonitor" in streamLabels:
-               SPLBackgroundMonitor = set(streamLabels["BackgroundMonitor"])
-       if "ConnectionTone" in streamLabels:
-               SPLNoConnectionTone = set(streamLabels["NoConnectionTone"])
-
-# Report number of encoders being monitored.
-# 6.0: Refactor the below function to use the newer encoder config format.
-def getStreamLabel(identifier):
-       encoderType, id = identifier.split()
-       # 5.2: Use a static map.
-       # 6.0: Look up the encoder type.
-       if encoderType == "SAM": labels = SAMStreamLabels
-       elif encoderType == "SPL": labels = SPLStreamLabels
-       if id in labels: return labels[id]
-       return None
-
-def announceNumMonitoringEncoders():
-       monitorCount = len(SPLBackgroundMonitor)
-       if not monitorCount:
-               # Translators: Message presented when there are no encoders 
being monitored.
-               ui.message(_("No encoders are being monitored"))
-       else:
-               # Locate stream labels if any.
-               labels = []
-               for identifier in SPLBackgroundMonitor:
-                       label = getStreamLabel(identifier)
-                       if label is None: labels.append(identifier)
-                       else: labels.append("{encoderID} 
({streamLabel})".format(encoderID = identifier, streamLabel=label))
-               # Translators: Announces number of encoders being monitored in 
the background.
-               ui.message(_("Number of encoders monitored: {numberOfEncoders}: 
{streamLabels}").format(numberOfEncoders = monitorCount, streamLabels=", 
".join(labels)))
-
-# Remove encoder ID from various settings maps.
-# This is a private module level function in order for it to be invoked by 
humans alone.
-_encoderConfigRemoved = None
-def _removeEncoderID(encoderType, pos):
-       global _encoderConfigRemoved
-       # For now, store the key to map.
-       # This might become a module-level constant if other functions require 
this dictionary.
-       key2map = {"FocusToStudio":SPLFocusToStudio, 
"PlayAfterConnecting":SPLPlayAfterConnecting, 
"BackgroundMonitor":SPLBackgroundMonitor, 
"NoConnectionTone":SPLNoConnectionTone}
-       encoderID = " ".join([encoderType, pos])
-       # Go through each feature map, remove the encoder ID and manipulate 
encoder positions in these sets.
-       # For each set, have a list of set items handy, otherwise set 
cardinality error (RuntimeError) will occur if items are removed on the fly.
-       for key in key2map:
-               map = key2map[key]
-               if encoderID in map:
-                       map.remove(encoderID)
-                       _encoderConfigRemoved = True
-               # If not sorted, encoders will appear in random order (a 
downside of using sets, as their ordering is quite unpredictable).
-               currentEncoders = sorted(filter(lambda x: 
x.startswith(encoderType), map))
-               if len(currentEncoders) and encoderID < currentEncoders[-1]:
-                       # Same algorithm as stream label remover.
-                       start = 0
-                       if encoderID > currentEncoders[0]:
-                               for candidate in currentEncoders:
-                                       if encoderID < candidate:
-                                               start = 
currentEncoders.index(candidate)
-                       # Do set entry manipulations (remove first, then add).
-                       for item in currentEncoders[start:]:
-                               map.remove(item)
-                               map.add(" ".join([encoderType, 
"%s"%(int(item.split()[-1])-1)]))
-               _encoderConfigRemoved = True
-               if len(map): streamLabels[key] = list(map)
-               else:
-                       try:
-                               del streamLabels[key]
-                       except KeyError:
-                               pass
-
-# Nullify various flag sets, otherwise memory leak occurs.
-def cleanup():
-       global streamLabels, SAMStreamLabels, SPLStreamLabels, 
SPLFocusToStudio, SPLPlayAfterConnecting, SPLBackgroundMonitor, 
SPLNoConnectionTone, encoderMonCount, SAMMonitorThreads, SPLMonitorThreads
-       for map in [streamLabels, SAMStreamLabels, SPLStreamLabels, 
SPLFocusToStudio, SPLPlayAfterConnecting, SPLBackgroundMonitor, 
SPLNoConnectionTone, SAMMonitorThreads, SPLMonitorThreads]:
-               if map is not None: map.clear()
-       # Nullify stream labels.
-       streamLabels = None
-       # Without resetting monitor count, we end up with higher and higher 
value for this.
-       # 7.0: Destroy threads also.
-       encoderMonCount = {"SAM":0, "SPL":0}
-
-# Encoder configuration dialog.
-_configDialogOpened = False
-
-# Presented if the config dialog for another encoder is opened.
-def _configDialogError():
-       # Translators: Text of the dialog when another alarm dialog is open.
-       gui.messageBox(_("Another encoder settings dialog is 
open."),_("Error"),style=wx.OK | wx.ICON_ERROR)
-
-class EncoderConfigDialog(wx.Dialog):
-
-       # The following comes from exit dialog class from GUI package (credit: 
NV Access and Zahari from Bulgaria).
-       _instance = None
-
-       def __new__(cls, parent, *args, **kwargs):
-               # Make this a singleton and prompt an error dialog if it isn't.
-               if _configDialogOpened:
-                       raise RuntimeError("An instance of encoder settings 
dialog is opened")
-               inst = cls._instance() if cls._instance else None
-               if not inst:
-                       return super(cls, cls).__new__(cls, parent, *args, 
**kwargs)
-               return inst
-
-       def __init__(self, parent, obj):
-               inst = EncoderConfigDialog._instance() if 
EncoderConfigDialog._instance else None
-               if inst:
-                       return
-               # Use a weakref so the instance can die.
-               import weakref
-               EncoderConfigDialog._instance = weakref.ref(self)
-
-               self.obj = obj
-               self.curStreamLabel, title = obj.getStreamLabel(getTitle=True)
-               # Translators: The title of the encoder settings dialog 
(example: Encoder settings for SAM 1").
-               super(EncoderConfigDialog, self).__init__(parent, wx.ID_ANY, 
_("Encoder settings for {name}").format(name = title))
-               mainSizer = wx.BoxSizer(wx.VERTICAL)
-               encoderConfigHelper = gui.guiHelper.BoxSizerHelper(self, 
orientation=wx.VERTICAL)
-
-               # Translators: An edit field in encoder settings to set stream 
label for this encoder.
-               self.streamLabel = 
encoderConfigHelper.addLabeledControl(_("Stream &label"), wx.TextCtrl)
-               self.streamLabel.SetValue(self.curStreamLabel if 
self.curStreamLabel is not None else "")
-
-               # Translators: A checkbox in encoder settings to set if NvDA 
should switch focus to Studio window when connected.
-               self.focusToStudio = 
encoderConfigHelper.addItem(wx.CheckBox(self, label=_("&Focus to Studio when 
connected")))
-               self.focusToStudio.SetValue(obj.getEncoderId() in 
SPLFocusToStudio)
-               # Translators: A checkbox in encoder settings to set if NvDA 
should play the next track when connected.
-               self.playAfterConnecting = 
encoderConfigHelper.addItem(wx.CheckBox(self, label=_("&Play first track when 
connected")))
-               self.playAfterConnecting.SetValue(obj.getEncoderId() in 
SPLPlayAfterConnecting)
-               # Translators: A checkbox in encoder settings to set if NvDA 
should monitor the status of this encoder in the background.
-               self.backgroundMonitor = 
encoderConfigHelper.addItem(wx.CheckBox(self, label=_("Enable background 
connection &monitoring")))
-               self.backgroundMonitor.SetValue(obj.getEncoderId() in 
SPLBackgroundMonitor)
-               # Translators: A checkbox in encoder settings to set if NvDA 
should play connection progress tone.
-               self.noConnectionTone = 
encoderConfigHelper.addItem(wx.CheckBox(self, label=_("Play connection status 
&beep while connecting")))
-               self.noConnectionTone.SetValue(obj.getEncoderId() not in 
SPLNoConnectionTone)
-
-               
encoderConfigHelper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | 
wx.CANCEL))
-               self.Bind(wx.EVT_BUTTON,self.onOk,id=wx.ID_OK)
-               self.Bind(wx.EVT_BUTTON,self.onCancel,id=wx.ID_CANCEL)
-               mainSizer.Add(encoderConfigHelper.sizer, border = 
gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
-               mainSizer.Fit(self)
-               self.SetSizer(mainSizer)
-               self.Center(wx.BOTH | wx.CENTER_ON_SCREEN)
-               self.streamLabel.SetFocus()
-
-       def onOk(self, evt):
-               self.obj._set_Flags(self.obj.getEncoderId(), 
self.focusToStudio.Value, SPLFocusToStudio, "FocusToStudio", save=False)
-               self.obj._set_Flags(self.obj.getEncoderId(), 
self.playAfterConnecting.Value, SPLPlayAfterConnecting, "PlayAfterConnecting", 
save=False)
-               self.obj._set_Flags(self.obj.getEncoderId(), 
self.backgroundMonitor.Value, SPLBackgroundMonitor, "BackgroundMonitor", 
save=False)
-               # Invert the following only.
-               self.obj._set_Flags(self.obj.getEncoderId(), not 
self.noConnectionTone.Value, SPLNoConnectionTone, "NoConnectionTone", 
save=False)
-               newStreamLabel = self.streamLabel.Value
-               if newStreamLabel is None: newStreamLabel = ""
-               if newStreamLabel == self.curStreamLabel:
-                       streamLabels.write() # Only flag(s) have changed.
-               else: self.obj.setStreamLabel(newStreamLabel)
-               self.Destroy()
-
-       def onCancel(self, evt):
-               self.Destroy()
-
-
-# Support for various encoders.
-# Each encoder must support connection routines.
-
-class Encoder(IAccessible):
-       """Represents an encoder from within StationPlaylist Studio or Streamer.
-       This base encoder provides scripts for all encoders such as stream 
labeler and toggling focusing to Studio when connected.
-       Subclasses must provide scripts to handle encoder connection and 
connection announcement routines.
-       In addition, they must implement the required actions to set options 
such as focusing to Studio, storing stream labels and so on, as each subclass 
relies on a feature map.
-       For example, for SAM encoder class, the feature map is SAM* where * 
denotes the feature in question.
-       Lastly, each encoder class must provide a unique identifying string to 
identify the type of the encoder (e.g. SAM for SAM encoder).
-       """
-
-       # Few useful variables for encoder list:
-       focusToStudio = False # If true, Studio will gain focus after encoder 
connects.
-       playAfterConnecting = False # When connected, the first track will be 
played.
-       backgroundMonitor = False # Monitor this encoder for connection status 
changes.
-       connectionTone = True # Play connection tone while connecting.
-
-
-       # Some helper functions
-
-       # Get the encoder identifier.
-       # This consists of two or three letter abbreviations for the encoder 
and the child ID.
-       def getEncoderId(self):
-               return " ".join([self.encoderType, 
str(self.IAccessibleChildID)])
-
-       # Format the status message to prepare for monitoring multiple encoders.
-       def encoderStatusMessage(self, message, id):
-               if encoderMonCount[self.encoderType] > 1:
-                       # Translators: Status message for encoder monitoring.
-                       ui.message(_("{encoder} {encoderNumber}: 
{status}").format(encoder = self.encoderType, encoderNumber = id, status = 
message))
-               else:
-                       ui.message(message)
-
-       # A master flag setter.
-       # Set or clear a given flag for the encoder given its ID, flag and flag 
container (currently a feature set).
-       # Also take in the flag key for storing it into the settings file.
-       # The flag will then be written to the configuration file.
-       # 7.0: Don't dump flags to disk unless told.
-       def _set_Flags(self, encoderId, flag, flagMap, flagKey, save=True):
-               if flag and not encoderId in flagMap:
-                       flagMap.add(encoderId)
-               elif not flag and encoderId in flagMap:
-                       flagMap.remove(encoderId)
-               # No need to store an empty flag map.
-               if len(flagMap): streamLabels[flagKey] = list(flagMap)
-               else:
-                       try:
-                               del streamLabels[flagKey]
-                       except KeyError:
-                               pass
-               if save: streamLabels.write()
-
-       # Now the flag configuration scripts.
-       # Project Rainbow: a new way to configure these will be created.
-
-       def script_toggleFocusToStudio(self, gesture):
-               if not self.focusToStudio:
-                       self.focusToStudio = True
-                       # Translators: Presented when toggling the setting to 
switch to Studio when connected to a streaming server.
-                       ui.message(_("Switch to Studio after connecting"))
-               else:
-                       self.focusToStudio = False
-                       # Translators: Presented when toggling the setting to 
switch to Studio when connected to a streaming server.
-                       ui.message(_("Do not switch to Studio after 
connecting"))
-               self._set_Flags(self.getEncoderId(), self.focusToStudio, 
SPLFocusToStudio, "FocusToStudio")
-       # Translators: Input help mode message in SAM Encoder window.
-       script_toggleFocusToStudio.__doc__=_("Toggles whether NVDA will switch 
to Studio when connected to a streaming server.")
-
-       def script_togglePlay(self, gesture):
-               if not self.playAfterConnecting:
-                       self.playAfterConnecting = True
-                       # Translators: Presented when toggling the setting to 
play selected song when connected to a streaming server.
-                       ui.message(_("Play first track after connecting"))
-               else:
-                       self.playAfterConnecting = False
-                       # Translators: Presented when toggling the setting to 
switch to Studio when connected to a streaming server.
-                       ui.message(_("Do not play first track after 
connecting"))
-               self._set_Flags(self.getEncoderId(), self.playAfterConnecting, 
SPLPlayAfterConnecting, "PlayAfterConnecting")
-       # Translators: Input help mode message in SAM Encoder window.
-       script_togglePlay.__doc__=_("Toggles whether Studio will play the first 
song when connected to a streaming server.")
-
-       def script_toggleBackgroundEncoderMonitor(self, gesture):
-               if scriptHandler.getLastScriptRepeatCount()==0:
-                       if not self.backgroundMonitor:
-                               self.backgroundMonitor = True
-                               encoderMonCount[self.encoderType] += 1 # 
Multiple encoders.
-                               # Translators: Presented when toggling the 
setting to monitor the selected encoder.
-                               ui.message(_("Monitoring encoder 
{encoderNumber}").format(encoderNumber = self.IAccessibleChildID))
-                       else:
-                               self.backgroundMonitor = False
-                               encoderMonCount[self.encoderType] -= 1
-                               # Translators: Presented when toggling the 
setting to monitor the selected encoder.
-                               ui.message(_("Encoder {encoderNumber} will not 
be monitored").format(encoderNumber = self.IAccessibleChildID))
-                       threadPool = self.setBackgroundMonitor()
-                       if self.backgroundMonitor:
-                               try:
-                                       monitoring = 
threadPool[self.IAccessibleChildID].isAlive()
-                               except KeyError:
-                                       monitoring = False
-                               if not monitoring:
-                                       statusThread = 
threading.Thread(target=self.reportConnectionStatus)
-                                       statusThread.name = "Connection Status 
Reporter " + str(self.IAccessibleChildID)
-                                       statusThread.start()
-                                       threadPool[self.IAccessibleChildID] = 
statusThread
-               else:
-                       for encoderType in encoderMonCount:
-                               encoderMonCount[encoderType] = 0
-                       SPLBackgroundMonitor.clear()
-                       # Translators: Announced when background encoder 
monitoring is canceled.
-                       ui.message(_("Encoder monitoring canceled"))
-       # Translators: Input help mode message in SAM Encoder window.
-       script_toggleBackgroundEncoderMonitor.__doc__=_("Toggles whether NVDA 
will monitor the selected encoder in the background.")
-
-       def script_streamLabeler(self, gesture):
-               curStreamLabel, title = self.getStreamLabel(getTitle=True)
-               if not curStreamLabel: curStreamLabel = ""
-               # Translators: The title of the stream labeler dialog (example: 
stream labeler for 1).
-               streamTitle = _("Stream labeler for 
{streamEntry}").format(streamEntry = title)
-               # Translators: The text of the stream labeler dialog.
-               streamText = _("Enter the label for this stream")
-               dlg = wx.TextEntryDialog(gui.mainFrame,
-               streamText, streamTitle, defaultValue=curStreamLabel)
-               def callback(result):
-                       if result == wx.ID_OK:
-                               newStreamLabel = dlg.GetValue()
-                               if newStreamLabel == curStreamLabel:
-                                       return # No need to write to disk.
-                               else: self.setStreamLabel(newStreamLabel)
-               gui.runScriptModalDialog(dlg, callback)
-       # Translators: Input help mode message in SAM Encoder window.
-       script_streamLabeler.__doc__=_("Opens a dialog to label the selected 
encoder.")
-
-       def script_streamLabelEraser(self, gesture):
-               # Translators: The title of the stream configuration eraser 
dialog.
-               streamEraserTitle = _("Stream label and settings eraser")
-               # Translators: The text of the stream configuration eraser 
dialog.
-               streamEraserText = _("Enter the position of the encoder you 
wish to delete or will delete")
-               dlg = wx.NumberEntryDialog(gui.mainFrame,
-               streamEraserText, "", streamEraserTitle, 
self.IAccessibleChildID, 1, self.simpleParent.childCount)
-               def callback(result):
-                       if result == wx.ID_OK:
-                               self.removeStreamConfig(str(dlg.GetValue()))
-               gui.runScriptModalDialog(dlg, callback)
-       # Translators: Input help mode message in SAM Encoder window.
-       script_streamLabelEraser.__doc__=_("Opens a dialog to erase stream 
labels and settings from an encoder that was deleted.")
-
-       # stream settings.
-       def script_encoderSettings(self, gesture):
-               try:
-                       d = EncoderConfigDialog(gui.mainFrame, self)
-                       gui.mainFrame.prePopup()
-                       d.Raise()
-                       d.Show()
-                       gui.mainFrame.postPopup()
-               except RuntimeError:
-                       wx.CallAfter(ui.message, "A settings dialog is opened")
-       # Translators: Input help mode message for a command in Station 
Playlist Studio.
-       script_encoderSettings.__doc__=_("Shows encoder configuration dialog to 
configure various encoder settings such as stream label.")
-       script_encoderSettings.category=_("Station Playlist Studio")
-
-       # Announce complete time including seconds (slight change from global 
commands version).
-       def script_encoderDateTime(self, gesture):
-               import winKernel
-               if scriptHandler.getLastScriptRepeatCount()==0:
-                       
text=winKernel.GetTimeFormat(winKernel.LOCALE_USER_DEFAULT, 0, None, None)
-               else:
-                       
text=winKernel.GetDateFormat(winKernel.LOCALE_USER_DEFAULT, 
winKernel.DATE_LONGDATE, None, None)
-               ui.message(text)
-       # Translators: Input help mode message for report date and time command.
-       script_encoderDateTime.__doc__=_("If pressed once, reports the current 
time including seconds. If pressed twice, reports the current date")
-       script_encoderDateTime.category=_("Station Playlist Studio")
-
-       # Various column announcement scripts.
-       # This base class implements encoder position and stream labels.
-       def script_announceEncoderPosition(self, gesture):
-               ui.message(_("Position: {pos}").format(pos = 
self.IAccessibleChildID))
-
-       def script_announceEncoderLabel(self, gesture):
-               try:
-                       streamLabel = self.getStreamLabel()[0]
-               except TypeError:
-                       streamLabel = None
-               if streamLabel:
-                       ui.message(_("Label: {label}").format(label = 
streamLabel))
-               else:
-                       ui.message(_("No stream label"))
-
-
-       def initOverlayClass(self):
-               global encoderMonCount
-               # Load stream labels upon request.
-               if streamLabels is None: loadStreamLabels()
-               encoderIdentifier = self.getEncoderId()
-               # Can I switch to Studio when connected to a streaming server?
-               try:
-                       self.focusToStudio = encoderIdentifier in 
SPLFocusToStudio
-               except KeyError:
-                       pass
-               # Can I play tracks when connected?
-               try:
-                       self.playAfterConnecting = encoderIdentifier in 
SPLPlayAfterConnecting
-               except KeyError:
-                       pass
-               # Am I being monitored for connection changes?
-               try:
-                       self.backgroundMonitor = encoderIdentifier in 
SPLBackgroundMonitor
-               except KeyError:
-                       pass
-               # 6.2: Make sure background monitor threads are started if the 
flag is set.
-               if self.backgroundMonitor:
-                       if self.encoderType == "SAM": threadPool = 
SAMMonitorThreads
-                       elif self.encoderType == "SPL": threadPool = 
SPLMonitorThreads
-                       if self.IAccessibleChildID in threadPool:
-                               if not 
threadPool[self.IAccessibleChildID].is_alive():
-                                       del threadPool[self.IAccessibleChildID]
-                               # If it is indeed alive... Otherwise another 
thread will be created to keep an eye on this encoder (undesirable).
-                               else: return
-                       statusThread = 
threading.Thread(target=self.reportConnectionStatus)
-                       statusThread.name = "Connection Status Reporter " + 
str(self.IAccessibleChildID)
-                       statusThread.start()
-                       threadPool[self.IAccessibleChildID] = statusThread
-                       encoderMonCount[self.encoderType] += 1
-               # Can I play connection beeps?
-               try:
-                       self.connectionTone = encoderIdentifier not in 
SPLNoConnectionTone
-               except KeyError:
-                       pass
-
-       def reportFocus(self):
-               try:
-                       streamLabel = self.getStreamLabel()[0]
-               except TypeError:
-                       streamLabel = None
-               # Announce stream label if it exists.
-               if streamLabel is not None:
-                       try:
-                               self.name = "(" + streamLabel + ") " + self.name
-                       except TypeError:
-                               pass
-               super(Encoder, self).reportFocus()
-
-
-       __gestures={
-               "kb:f11":"toggleFocusToStudio",
-               "kb:shift+f11":"togglePlay",
-               "kb:control+f11":"toggleBackgroundEncoderMonitor",
-               "kb:f12":"streamLabeler",
-               "kb:control+f12":"streamLabelEraser",
-               "kb:NVDA+F12":"encoderDateTime",
-               "kb:alt+NVDA+0":"encoderSettings",
-               "kb:control+NVDA+1":"announceEncoderPosition",
-               "kb:control+NVDA+2":"announceEncoderLabel",
-       }
-
-
-class SAMEncoder(Encoder):
-       # Support for Sam Encoders.
-
-       encoderType = "SAM"
-
-       def reportConnectionStatus(self, connecting=False):
-               # Keep an eye on the stream's description field for connection 
changes.
-               # In order to not block NVDA commands, this will be done using 
a different thread.
-               SPLWin = user32.FindWindowA("SPLStudio", None)
-               toneCounter = 0
-               messageCache = ""
-               # Status message flags.
-               idle = False
-               error = False
-               encoding = False
-               alreadyEncoding = False
-               while True:
-                       time.sleep(0.001)
-                       try:
-                               if messageCache != 
self.description[self.description.find("Status")+8:]:
-                                       messageCache = 
self.description[self.description.find("Status")+8:]
-                                       if not 
messageCache.startswith("Encoding"):
-                                               
self.encoderStatusMessage(messageCache, self.IAccessibleChildID)
-                       except AttributeError:
-                               return
-                       if messageCache.startswith("Idle"):
-                               if alreadyEncoding: alreadyEncoding = False
-                               if encoding: encoding = False
-                               if not idle:
-                                       tones.beep(250, 250)
-                                       idle = True
-                                       toneCounter = 0
-                       elif messageCache.startswith("Error"):
-                               # Announce the description of the error.
-                               if connecting: connecting= False
-                               if not error:
-                                       error = True
-                                       toneCounter = 0
-                               if alreadyEncoding: alreadyEncoding = False
-                       elif messageCache.startswith("Encoding"):
-                               if connecting: connecting = False
-                               # We're on air, so exit unless told to monitor 
for connection changes.
-                               if not encoding:
-                                       tones.beep(1000, 150)
-                                       self.encoderStatusMessage(messageCache, 
self.IAccessibleChildID)
-                               if self.focusToStudio and not encoding:
-                                       if api.getFocusObject().appModule == 
"splstudio":
-                                               continue
-                                       
user32.SetForegroundWindow(user32.FindWindowA("TStudioForm", None))
-                               if self.playAfterConnecting and not encoding:
-                                       # Do not interupt the currently playing 
track.
-                                       if winUser.sendMessage(SPLWin, SPLMSG, 
0, SPL_TrackPlaybackStatus) == 0:
-                                               winUser.sendMessage(SPLWin, 
SPLMSG, 0, SPLPlay)
-                               if not encoding: encoding = True
-                       else:
-                               if alreadyEncoding: alreadyEncoding = False
-                               if encoding: encoding = False
-                               elif "Error" not in self.description and error: 
error = False
-                               toneCounter+=1
-                               if toneCounter%250 == 0 and self.connectionTone:
-                                       tones.beep(500, 50)
-                       if connecting: continue
-                       if not " ".join([self.encoderType, 
str(self.IAccessibleChildID)]) in SPLBackgroundMonitor: return
-
-       def script_connect(self, gesture):
-               gesture.send()
-               # Translators: Presented when an Encoder is trying to connect 
to a streaming server.
-               ui.message(_("Connecting..."))
-               # Oi, status thread, can you keep an eye on the connection 
status for me?
-               # To be packaged into a new function in 7.0.
-               if not self.backgroundMonitor:
-                       statusThread = 
threading.Thread(target=self.reportConnectionStatus, 
kwargs=dict(connecting=True))
-                       statusThread.name = "Connection Status Reporter " + 
str(self.IAccessibleChildID)
-                       statusThread.start()
-                       SAMMonitorThreads[self.IAccessibleChildID] = 
statusThread
-
-       def script_disconnect(self, gesture):
-               gesture.send()
-               # Translators: Presented when an Encoder is disconnecting from 
a streaming server.
-               ui.message(_("Disconnecting..."))
-
-       # Connecting/disconnecting all encoders at once.
-       # Control+F9/Control+F10 hotkeys are broken. Thankfully, context menu 
retains these commands.
-       # Use object navigation and key press emulation hack.
-
-       def _samContextMenu(self, pos):
-               def _samContextMenuActivate(pos):
-                       speech.cancelSpeech()
-                       focus =api.getFocusObject()
-                       focus.children[pos].doAction()
-               import keyboardHandler
-               contextMenu = 
keyboardHandler.KeyboardInputGesture.fromName("applications")
-               contextMenu.send()
-               wx.CallLater(100, _samContextMenuActivate, pos)
-               time.sleep(0.2)
-
-       def script_connectAll(self, gesture):
-               ui.message(_("Connecting..."))
-               speechMode = speech.speechMode
-               speech.speechMode = 0
-               wx.CallAfter(self._samContextMenu, 7)
-               # Oi, status thread, can you keep an eye on the connection 
status for me?
-               if not self.backgroundMonitor:
-                       statusThread = 
threading.Thread(target=self.reportConnectionStatus, 
kwargs=dict(connecting=True))
-                       statusThread.name = "Connection Status Reporter " + 
str(self.IAccessibleChildID)
-                       statusThread.start()
-                       SAMMonitorThreads[self.IAccessibleChildID] = 
statusThread
-               speech.speechMode = speechMode
-
-       def script_disconnectAll(self, gesture):
-               ui.message(_("Disconnecting..."))
-               speechMode = speech.speechMode
-               speech.speechMode = 0
-               wx.CallAfter(self._samContextMenu, 8)
-               time.sleep(0.5)
-               speech.speechMode = speechMode
-               speech.cancelSpeech()
-
-
-       # Announce SAM columns: encoder name/type, status and description.
-       def script_announceEncoderFormat(self, gesture):
-               typeIndex = self.description.find(", Status: ")
-               ui.message(self.description[:typeIndex])
-
-       def script_announceEncoderStatus(self, gesture):
-               typeIndex = self.description.find(", Status: ")
-               statusIndex = self.description.find(", Description: ")
-               ui.message(self.description[typeIndex+2:statusIndex])
-
-       def script_announceEncoderStatusDesc(self, gesture):
-               statusIndex = self.description.find(", Description: ")
-               ui.message(self.description[statusIndex+2:])
-
-
-       def setBackgroundMonitor(self):
-               self._set_Flags(self.getEncoderId(), self.backgroundMonitor, 
SPLBackgroundMonitor, "BackgroundMonitor")
-               return SAMMonitorThreads
-
-
-       def getStreamLabel(self, getTitle=False):
-               if str(self.IAccessibleChildID) in SAMStreamLabels:
-                       streamLabel = 
SAMStreamLabels[str(self.IAccessibleChildID)]
-                       return streamLabel, self.IAccessibleChildID if getTitle 
else streamLabel
-               return None, self.IAccessibleChildID if getTitle else None
-
-       def setStreamLabel(self, newStreamLabel):
-               if len(newStreamLabel):
-                       SAMStreamLabels[str(self.IAccessibleChildID)] = 
newStreamLabel
-               else:
-                       try:
-                               del 
SAMStreamLabels[str(self.IAccessibleChildID)]
-                       except KeyError:
-                               pass
-               streamLabels["SAMEncoders"] = SAMStreamLabels
-               streamLabels.write()
-
-       def removeStreamConfig(self, pos):
-               # An application of map successor algorithm.
-               global _encoderConfigRemoved
-               # Manipulate SAM encoder settings and labels.
-               _removeEncoderID("SAM", pos)
-               labelLength = len(SAMStreamLabels)
-               if not labelLength or pos > max(SAMStreamLabels.keys()):
-                       if _encoderConfigRemoved is not None:
-                               streamLabels.write()
-                               _encoderConfigRemoved = None
-                       return
-               elif labelLength  == 1:
-                       if not pos in SAMStreamLabels:
-                               pos = SAMStreamLabels.keys()[0]
-                               oldPosition = int(pos)
-                               SAMStreamLabels[str(oldPosition-1)] = 
SAMStreamLabels[pos]
-                       del SAMStreamLabels[pos]
-               else:
-                       encoderPositions = sorted(SAMStreamLabels.keys())
-                       # What if the position happens to be the last stream 
label position?
-                       if pos == max(encoderPositions): del 
SAMStreamLabels[pos]
-                       else:
-                               # Find the exact or closest successor.
-                               startPosition = 0
-                               if pos == min(encoderPositions):
-                                       del SAMStreamLabels[pos]
-                                       startPosition = 1
-                               elif pos > min(encoderPositions):
-                                       for candidate in encoderPositions:
-                                               if candidate >= pos:
-                                                       startPositionCandidate 
= encoderPositions.index(candidate)
-                                                       startPosition = 
startPositionCandidate+1 if candidate == pos else startPositionCandidate
-                                                       break
-                               # Now move them forward.
-                               for position in 
encoderPositions[startPosition:]:
-                                       oldPosition = int(position)
-                                       SAMStreamLabels[str(oldPosition-1)] = 
SAMStreamLabels[position]
-                                       del SAMStreamLabels[position]
-               streamLabels["SAMEncoders"] = SAMStreamLabels
-               streamLabels.write()
-
-
-       __gestures={
-               "kb:f9":"connect",
-               "kb:control+f9":"connectAll",
-               "kb:f10":"disconnect",
-               "kb:control+f10":"disconnectAll",
-               "kb:control+NVDA+3":"announceEncoderFormat",
-               "kb:control+NVDA+4":"announceEncoderStatus",
-               "kb:control+NVDA+5":"announceEncoderStatusDesc"
-       }
-
-
-class SPLEncoder(Encoder):
-       # Support for SPL Encoder window.
-
-       encoderType = "SPL"
-
-       def reportConnectionStatus(self, connecting=False):
-               # Same routine as SAM encoder: use a thread to prevent blocking 
NVDA commands.
-               SPLWin = user32.FindWindowA("SPLStudio", None)
-               attempt = 0
-               messageCache = ""
-               # Status flags.
-               connected = False
-               while True:
-                       time.sleep(0.001)
-                       try:
-                               # An inner try block is required because 
statChild may say the base class is gone.
-                               try:
-                                       statChild = self.children[1]
-                               except NotImplementedError:
-                                       return # Only seen when the encoder 
dies.
-                       except IndexError:
-                               return # Don't leave zombie objects around.
-                       if messageCache != statChild.name:
-                               messageCache = statChild.name
-                               if not messageCache: return
-                               if "Kbps" not in messageCache:
-                                       self.encoderStatusMessage(messageCache, 
self.IAccessibleChildID)
-                       if messageCache == "Disconnected":
-                               connected = False
-                               if connecting: continue
-                       elif messageCache == "Connected":
-                               connecting = False
-                               # We're on air, so exit.
-                               if not connected: tones.beep(1000, 150)
-                               if self.focusToStudio and not connected:
-                                       
user32.SetForegroundWindow(user32.FindWindowA("TStudioForm", None))
-                               if self.playAfterConnecting and not connected:
-                                       if winUser.sendMessage(SPLWin, SPLMSG, 
0, SPL_TrackPlaybackStatus) == 0:
-                                               winUser.sendMessage(SPLWin, 
SPLMSG, 0, SPLPlay)
-                               if not connected: connected = True
-                       elif "Unable to connect" in messageCache or "Failed" in 
messageCache or statChild.name == "AutoConnect stopped.":
-                               if connected: connected = False
-                       else:
-                               if connected: connected = False
-                               if not "Kbps" in messageCache:
-                                       attempt += 1
-                                       if attempt%250 == 0 and 
self.connectionTone:
-                                               tones.beep(500, 50)
-                                               if attempt>= 500 and 
statChild.name == "Disconnected":
-                                                       tones.beep(250, 250)
-                               if connecting: continue
-                       if not " ".join([self.encoderType, 
str(self.IAccessibleChildID)]) in SPLBackgroundMonitor: return
-
-       def script_connect(self, gesture):
-               # Same as SAM's connection routine, but this time, keep an eye 
on self.name and a different connection flag.
-               connectButton = api.getForegroundObject().children[2]
-               if connectButton.name == "Disconnect": return
-               ui.message(_("Connecting..."))
-               # Juggle the focus around.
-               connectButton.doAction()
-               self.setFocus()
-               # Same as SAM encoders.
-               if not self.backgroundMonitor:
-                       statusThread = 
threading.Thread(target=self.reportConnectionStatus, 
kwargs=dict(connecting=True))
-                       statusThread.name = "Connection Status Reporter"
-                       statusThread.start()
-                       SPLMonitorThreads[self.IAccessibleChildID] = 
statusThread
-       script_connect.__doc__=_("Connects to a streaming server.")
-
-       # Announce SPL Encoder columns: encoder settings and transfer rate.
-       def script_announceEncoderSettings(self, gesture):
-               ui.message(_("Encoder Settings: {setting}").format(setting = 
self.children[0].name))
-
-       def script_announceEncoderTransfer(self, gesture):
-               ui.message(_("Transfer Rate: 
{transferRate}").format(transferRate = self.children[1].name))
-
-       def setBackgroundMonitor(self):
-               self._set_Flags(self.getEncoderId(), self.backgroundMonitor, 
SPLBackgroundMonitor, "BackgroundMonitor")
-               return SPLMonitorThreads
-
-       def getStreamLabel(self, getTitle=False):
-               if str(self.IAccessibleChildID) in SPLStreamLabels:
-                       streamLabel = 
SPLStreamLabels[str(self.IAccessibleChildID)]
-                       return streamLabel, self.firstChild.name if getTitle 
else streamLabel
-               return (None, self.firstChild.name) if getTitle else None
-
-       def setStreamLabel(self, newStreamLabel):
-               if len(newStreamLabel):
-                       SPLStreamLabels[str(self.IAccessibleChildID)] = 
newStreamLabel
-               else:
-                       try:
-                               del 
SPLStreamLabels[str(self.IAccessibleChildID)]
-                       except KeyError:
-                               pass
-               streamLabels["SPLEncoders"] = SPLStreamLabels
-               streamLabels.write()
-
-       def removeStreamConfig(self, pos):
-               global _encoderConfigRemoved
-               # This time, manipulate SPL ID entries.
-               _removeEncoderID("SPL", pos)
-               labelLength = len(SPLStreamLabels)
-               if not labelLength or pos > max(SPLStreamLabels.keys()):
-                       if _encoderConfigRemoved is not None:
-                               streamLabels.write()
-                               _encoderConfigRemoved = None
-                       return
-               elif labelLength  == 1:
-                       if not pos in SPLStreamLabels:
-                               pos = SPLStreamLabels.keys()[0]
-                               oldPosition = int(pos)
-                               SPLStreamLabels[str(oldPosition-1)] = 
SPLStreamLabels[pos]
-                       del SPLStreamLabels[pos]
-               else:
-                       encoderPositions = sorted(SPLStreamLabels.keys())
-                       if pos == max(encoderPositions): del 
SPLStreamLabels[pos]
-                       else:
-                               # Find the exact or closest successor.
-                               startPosition = 0
-                               if pos == min(encoderPositions):
-                                       del SPLStreamLabels[pos]
-                                       startPosition = 1
-                               elif pos > min(encoderPositions):
-                                       for candidate in encoderPositions:
-                                               if candidate >= pos:
-                                                       startPositionCandidate 
= encoderPositions.index(candidate)
-                                                       startPosition = 
startPositionCandidate+1 if candidate == pos else startPositionCandidate
-                                                       break
-                               # Now move them forward.
-                               for position in 
encoderPositions[startPosition:]:
-                                       oldPosition = int(position)
-                                       SPLStreamLabels[str(oldPosition-1)] = 
SPLStreamLabels[position]
-                                       del SPLStreamLabels[position]
-               streamLabels["SPLEncoders"] = SPLStreamLabels
-               streamLabels.write()
-
-
-       __gestures={
-               "kb:f9":"connect",
-               "kb:control+NVDA+3":"announceEncoderSettings",
-               "kb:control+NVDA+4":"announceEncoderTransfer"
-       }
-
-

diff --git a/addon/globalPlugins/splUtils/__init__.py 
b/addon/globalPlugins/splUtils/__init__.py
new file mode 100755
index 0000000..cb63d5a
--- /dev/null
+++ b/addon/globalPlugins/splUtils/__init__.py
@@ -0,0 +1,314 @@
+# StationPlaylist Utilities
+# Author: Joseph Lee
+# Copyright 2013-2017, released under GPL.
+# Adds a few utility features such as switching focus to the SPL Studio window 
and some global scripts.
+# For encoder support, see the encoders package.
+
+from functools import wraps
+import os
+import globalPluginHandler
+import api
+import ui
+import globalVars
+from NVDAObjects.IAccessible import getNVDAObjectFromEvent
+import winUser
+import nvwave
+import addonHandler
+addonHandler.initTranslation()
+
+# Layer environment: same as the app module counterpart.
+
+def finally_(func, final):
+       """Calls final after func, even if it fails."""
+       def wrap(f):
+               @wraps(f)
+               def new(*args, **kwargs):
+                       try:
+                               func(*args, **kwargs)
+                       finally:
+                               final()
+               return new
+       return wrap(final)
+
+# SPL Studio uses WM messages to send and receive data, similar to Winamp (see 
NVDA sources/appModules/winamp.py for more information).
+user32 = winUser.user32 # user32.dll.
+SPLWin = 0 # A handle to studio window.
+SPLMSG = winUser.WM_USER
+
+# Various SPL IPC tags.
+SPLVersion = 2
+SPLPlay = 12
+SPLStop = 13
+SPLPause = 15
+SPLAutomate = 16
+SPLMic = 17
+SPLLineIn = 18
+SPLLibraryScanCount = 32
+SPLListenerCount = 35
+SPLStatusInfo = 39 #Studio 5.20 and later.
+SPL_TrackPlaybackStatus = 104
+SPLCurTrackPlaybackTime = 105
+
+# Help message for SPL Controller
+# Translators: the dialog text for SPL Controller help.
+SPLConHelp=_("""
+After entering SPL Controller, press:
+A: Turn automation on.
+Shift+A: Turn automation off.
+M: Turn microphone on.
+Shift+M: Turn microphone off.
+N: Turn microphone on without fade.
+L: Turn line in on.
+Shift+L: Turn line in off.
+P: Play.
+U: Pause.
+S: Stop with fade.
+T: Instant stop.
+E: Announce if any encoders are being monitored.
+I: Announce listener count.
+Q: Announce Studio status information.
+R: Remaining time for the playing track.
+Shift+R: Library scan progress.""")
+
+
+class GlobalPlugin(globalPluginHandler.GlobalPlugin):
+
+       # Translators: Script category for Station Playlist commands in input 
gestures dialog.
+       scriptCategory = _("StationPlaylist Studio")
+
+       #Global layer environment (see the app module for more information).
+       SPLController = False # Control SPL from anywhere.
+
+       def getScript(self, gesture):
+               if not self.SPLController:
+                       return globalPluginHandler.GlobalPlugin.getScript(self, 
gesture)
+               script = globalPluginHandler.GlobalPlugin.getScript(self, 
gesture)
+               if not script:
+                       script = finally_(self.script_error, self.finish)
+               return finally_(script, self.finish)
+
+       def finish(self):
+               self.SPLController = False
+               self.clearGestureBindings()
+               self.bindGestures(self.__gestures)
+
+       def script_error(self, gesture):
+               import tones
+               tones.beep(120, 100)
+
+       # Switch focus to SPL Studio window from anywhere.
+       def script_focusToSPLWindow(self, gesture):
+               # 7.4: Forget it if this is the case like the following.
+               if globalVars.appArgs.secure: return
+               # Don't do anything if we're already focus on SPL Studio.
+               if "splstudio" in 
api.getForegroundObject().appModule.appModuleName: return
+               else:
+                       SPLHwnd = user32.FindWindowA("SPLStudio", None) # Used 
ANSI version, as Wide char version always returns 0.
+                       if not SPLHwnd: ui.message(_("SPL Studio is not 
running."))
+                       else:
+                               # 17.01: SetForegroundWindow function is 
better, as there's no need to traverse top-level windows and allows users to 
"switch" to SPL window if the window is minimized.
+                               
user32.SetForegroundWindow(user32.FindWindowA("TStudioForm", None))
+       # Translators: Input help mode message for a command to switch to 
Station Playlist Studio from any program.
+       script_focusToSPLWindow.__doc__=_("Moves to SPL Studio window from 
other programs.")
+
+       # The SPL Controller:
+       # This layer set allows the user to control various aspects of SPL 
Studio from anywhere.
+       def script_SPLControllerPrefix(self, gesture):
+               # 7.4: Red flag...
+               if globalVars.appArgs.secure: return
+               global SPLWin
+               # Error checks:
+               # 1. If SPL Studio is not running, print an error message.
+               # 2. If we're already  in SPL, ask the app module if SPL 
Assistant can be invoked with this command.
+               if "splstudio" in 
api.getForegroundObject().appModule.appModuleName:
+                       if not 
api.getForegroundObject().appModule.SPLConPassthrough():
+                               # Translators: Presented when NVDA cannot enter 
SPL Controller layer since SPL Studio is focused.
+                               ui.message(_("You are already in SPL Studio 
window. For status commands, use SPL Assistant commands."))
+                               self.finish()
+                               return
+                       else:
+                               
api.getForegroundObject().appModule.script_SPLAssistantToggle(gesture)
+                               return
+               SPLWin = user32.FindWindowA("SPLStudio", None)
+               if SPLWin == 0:
+                       # Translators: Presented when Station Playlist Studio 
is not running.
+                       ui.message(_("SPL Studio is not running."))
+                       self.finish()
+                       return
+               # No errors, so continue.
+               if not self.SPLController:
+                       self.bindGestures(self.__SPLControllerGestures)
+                       self.SPLController = True
+                       # Translators: The name of a layer command set for 
Station Playlist Studio.
+                       # Hint: it is better to translate it as "SPL Control 
Panel."
+                       ui.message(_("SPL Controller"))
+               else:
+                       self.script_error(gesture)
+                       self.finish()
+       # Translators: Input help mode message for a layer command in Station 
Playlist Studio.
+       script_SPLControllerPrefix.__doc__=_("SPl Controller layer command. See 
add-on guide for available commands.")
+
+       # The layer commands themselves. Calls user32.SendMessage method for 
each script.
+
+       def script_automateOn(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,1,SPLAutomate)
+               self.finish()
+
+       def script_automateOff(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,0,SPLAutomate)
+               self.finish()
+
+       def script_micOn(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,1,SPLMic)
+               nvwave.playWaveFile(os.path.join(os.path.dirname(__file__), 
"..", "..", "appModules", "splstudio", "SPL_on.wav"))
+               self.finish()
+
+       def script_micOff(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,0,SPLMic)
+               nvwave.playWaveFile(os.path.join(os.path.dirname(__file__), 
"..", "..", "appModules", "splstudio", "SPL_off.wav"))
+               self.finish()
+
+       def script_micNoFade(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,2,SPLMic)
+               self.finish()
+
+       def script_lineInOn(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,1,SPLLineIn)
+               self.finish()
+
+       def script_lineInOff(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,0,SPLLineIn)
+               self.finish()
+
+       def script_stopFade(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,0,SPLStop)
+               self.finish()
+
+       def script_stopInstant(self, gesture):
+               winUser.sendMessage(SPLWin,SPLMSG,1,SPLStop)
+               self.finish()
+
+       def script_play(self, gesture):
+               winUser.sendMessage(SPLWin, SPLMSG, 0, SPLPlay)
+               self.finish()
+
+       def script_pause(self, gesture):
+               playingNow = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPL_TrackPlaybackStatus)
+               # Translators: Presented when no track is playing in Station 
Playlist Studio.
+               if not playingNow: ui.message(_("There is no track playing. Try 
pausing while a track is playing."))
+               elif playingNow == 3: winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLPause)
+               else: winUser.sendMessage(SPLWin, SPLMSG, 1, SPLPause)
+               self.finish()
+
+       def script_libraryScanProgress(self, gesture):
+               scanned = winUser.sendMessage(SPLWin, SPLMSG, 1, 
SPLLibraryScanCount)
+               if scanned >= 0:
+                       # Translators: Announces number of items in the 
Studio's track library (example: 1000 items scanned).
+                       ui.message(_("Scan in progress with {itemCount} items 
scanned").format(itemCount = scanned))
+               else:
+                       # Translators: Announces number of items in the 
Studio's track library (example: 1000 items scanned).
+                       ui.message(_("Scan complete with {itemCount} items 
scanned").format(itemCount = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLLibraryScanCount)))
+               self.finish()
+
+       def script_listenerCount(self, gesture):
+               # Translators: Announces number of stream listeners.
+               ui.message(_("Listener count: 
{listenerCount}").format(listenerCount = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPLListenerCount)))
+               self.finish()
+
+       def script_remainingTime(self, gesture):
+               remainingTime = winUser.sendMessage(SPLWin, SPLMSG, 3, 
SPLCurTrackPlaybackTime)
+               # Translators: Presented when no track is playing in Station 
Playlist Studio.
+               if remainingTime < 0: ui.message(_("There is no track 
playing."))
+               else:
+                       # 7.0: Present remaining time in hh:mm:ss format for 
enhanced experience (borrowed from the app module).
+                       remainingTime = (remainingTime/1000)+1
+                       if remainingTime == 0: ui.message("00:00")
+                       elif 1 <= remainingTime <= 59: 
ui.message("00:{0}".format(str(remainingTime).zfill(2)))
+                       else:
+                               mm, ss = divmod(remainingTime, 60)
+                               if mm > 59:
+                                       hh, mm = divmod(mm, 60)
+                                       t0 = str(hh).zfill(2)
+                                       t1 = str(mm).zfill(2)
+                                       t2 = str(ss).zfill(2)
+                                       ui.message(":".join([t0, t1, t2]))
+                               else:
+                                       t1 = str(mm).zfill(2)
+                                       t2 = str(ss).zfill(2)
+                                       ui.message(":".join([t1, t2]))
+               self.finish()
+
+       def script_announceNumMonitoringEncoders(self, gesture):
+               import encoders
+               encoders.announceNumMonitoringEncoders()
+               self.finish()
+
+       def script_statusInfo(self, gesture):
+               # For consistency reasons (because of the Studio status bar), 
messages in this method will remain in English.
+               statusInfo = []
+               # 17.04: For Studio 5.10 and up, announce playback and 
automation status.
+               playingNow = winUser.sendMessage(SPLWin, SPLMSG, 0, 
SPL_TrackPlaybackStatus)
+               statusInfo.append("Play status: playing" if playingNow else 
"Play status: stopped")
+               # For automation, Studio 5.11 and earlier does not have an easy 
way to detect this flag, thus resort to using playback status.
+               if winUser.sendMessage(SPLWin, SPLMSG, 0, SPLVersion) < 520:
+                       statusInfo.append("Automation on" if playingNow == 2 
else "Automation off")
+               else:
+                       statusInfo.append("Automation on" if 
winUser.sendMessage(SPLWin, SPLMSG, 1, SPLStatusInfo) else "Automation off")
+                       # 5.20 and later.
+                       statusInfo.append("Microphone on" if 
winUser.sendMessage(SPLWin, SPLMSG, 2, SPLStatusInfo) else "Microphone off")
+                       statusInfo.append("Line-inon" if 
winUser.sendMessage(SPLWin, SPLMSG, 3, SPLStatusInfo) else "Line-in off")
+                       statusInfo.append("Record to file on" if 
winUser.sendMessage(SPLWin, SPLMSG, 4, SPLStatusInfo) else "Record to file off")
+                       cartEdit = winUser.sendMessage(SPLWin, SPLMSG, 5, 
SPLStatusInfo)
+                       cartInsert = winUser.sendMessage(SPLWin, SPLMSG, 6, 
SPLStatusInfo)
+                       if cartEdit: statusInfo.append("Cart edit on")
+                       elif not cartEdit and cartInsert: 
statusInfo.append("Cart insert on")
+                       else: statusInfo.append("Cart edit off")
+               ui.message("; ".join(statusInfo))
+               self.finish()
+       # Translators: Input help message for a SPL Controller command.
+       script_statusInfo.__doc__ = _("Announces Studio status such as track 
playback status from other programs")
+
+       def script_conHelp(self, gesture):
+               import gui, wx
+               # Translators: The title for SPL Controller help dialog.
+               wx.CallAfter(gui.messageBox, SPLConHelp, _("SPL Controller 
help"))
+               self.finish()
+
+
+       __SPLControllerGestures={
+               "kb:p":"play",
+               "kb:a":"automateOn",
+               "kb:shift+a":"automateOff",
+               "kb:m":"micOn",
+               "kb:shift+m":"micOff",
+               "kb:n":"micNoFade",
+               "kb:l":"lineInOn",
+               "kb:shift+l":"lineInOff",
+               "kb:shift+r":"libraryScanProgress",
+               "kb:s":"stopFade",
+               "kb:t":"stopInstant",
+               "kb:u":"pause",
+               "kb:r":"remainingTime",
+               "kb:e":"announceNumMonitoringEncoders",
+               "kb:i":"listenerCount",
+               "kb:q":"statusInfo",
+               "kb:f1":"conHelp"
+       }
+
+
+       __gestures={
+               #"kb:nvda+shift+`":"focusToSPLWindow",
+               #"kb:nvda+`":"SPLControllerPrefix"
+       }
+
+       # Support for Encoders
+       # Each encoder is an overlay class, thus makes it easier to add 
encoders in the future by implementing overlay objects.
+       # Each encoder, at a minimum, must support connection monitoring 
routines.
+
+       def chooseNVDAObjectOverlayClasses(self, obj, clsList):
+               if obj.appModule.appName in ("splengine", "splstreamer"):
+                       import controlTypes, encoders
+                       if obj.windowClassName == "TListView":
+                               clsList.insert(0, encoders.SAMEncoder)
+                       elif obj.windowClassName == "SysListView32" and 
obj.role == controlTypes.ROLE_LISTITEM:
+                                       clsList.insert(0, encoders.SPLEncoder)

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/776bf9e573b2/
Changeset:   776bf9e573b2
Branch:      None
User:        josephsl
Date:        2016-12-28 02:43:18+00:00
Summary:     Provide a transition routine to move gestures from SPLStudioUtils 
to splUtils.

In order to provide a seamless transition to splUtils global plugin, a 
transition algorithm has been implement that retrieves current gesture and adds 
new gestures under globalPlugins.splUtils module entry in gestures.ini (user 
gestures). This means breaking backwards compatibility and downgrades from 
17.04 onwards.

Affected #:  1 file

diff --git a/addon/installTasks.py b/addon/installTasks.py
index 6258417..a239ed3 100755
--- a/addon/installTasks.py
+++ b/addon/installTasks.py
@@ -5,7 +5,7 @@
 # Routines are partly based on other add-ons, particularly Place Markers by 
Noelia Martinez (thanks add-on authors).
 
 def onInstall():
-       import os, shutil
+       import os, shutil, inputCore
        profiles = os.path.join(os.path.dirname(__file__), "..", 
"stationPlaylist", "profiles")
        # Import old profiles.
        if os.path.exists(profiles):
@@ -16,3 +16,16 @@ def onInstall():
                        pass
        # 7.4 only: prepare LTS presentation file (an empty text file)
        #open(os.path.join(os.path.dirname(__file__), "ltsprep"), "w").close()
+       # 17.04 only: Global Plugin module name has changed, so pull in the old 
gestures if defined.
+       userGestures = inputCore.manager.userGestureMap._map
+       # Is the new module in there?
+       if "globalPlugins.splUtils" not in [script[0][0] for script in 
userGestures.values()]:
+               for gesture, script in userGestures.iteritems():
+                       if script[0][0] == "globalPlugins.SPLStudioUtils":
+                               # Prevent repeated gesture assignment.
+                               try:
+                                       
inputCore.manager.userGestureMap.remove(gesture, "globalPlugins.splUtils", 
script[0][1], script[0][2])
+                               except ValueError:
+                                       pass
+                               inputCore.manager.userGestureMap.add(gesture, 
"globalPlugins.splUtils", script[0][1], script[0][2])
+               inputCore.manager.userGestureMap.save()


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/4c26c3e8d7cf/
Changeset:   4c26c3e8d7cf
Branch:      None
User:        josephsl
Date:        2016-12-28 14:18:19+00:00
Summary:     Remove deprecated keys, audio ducking import is no longer 
conditional.

Removed Track Dial and Studio 5.00 reminder, which are deprecated as of 17.04. 
Also, audio ducking import is required (this means NvDA 2016.1 or later should 
be used from now on).
Specifically:
* Deprecated keys: remove old keys after normal profile has been cached, 
otherwise the add-on will say the profile should not be saved.
* Studio500 key is now OldSPLVersionReminder (to be used again later when a 
version to end support for Studio 5.1x comes out).
These modifications are destined for add-on 17.04 and later.

Affected #:  1 file

diff --git a/addon/appModules/splstudio/splconfig.py 
b/addon/appModules/splstudio/splconfig.py
index 24f4e7a..a5b448e 100755
--- a/addon/appModules/splstudio/splconfig.py
+++ b/addon/appModules/splstudio/splconfig.py
@@ -75,7 +75,6 @@ UpdateInterval = integer(min=1, max=30, default=7)
 [Startup]
 AudioDuckingReminder = boolean(default=true)
 WelcomeDialog = boolean(default=true)
-Studio500 = boolean(default=true)
 """), encoding="UTF-8", list_values=False)
 confspec7.newlines = "\r\n"
 SPLConfig = None
@@ -109,6 +108,12 @@ class ConfigHub(ChainMap):
                self.profileNames.append(None) # Signifying normal profile.
                # Always cache normal profile upon startup.
                self._cacheConfig(self.maps[0])
+               # Remove deprecated keys.
+               # This action must be performed after caching, otherwise the 
newly modified profile will not be saved.
+               deprecatedKeys = {"General":"TrackDial", "Startup":"Studio500"}
+               for section, key in deprecatedKeys.iteritems():
+                       if key in section: del self.maps[0][section][key]
+               # Moving onto broadcast profiles if any.
                try:
                        profiles = filter(lambda fn: os.path.splitext(fn)[-1] 
== ".ini", os.listdir(SPLProfiles))
                        for profile in profiles:
@@ -943,9 +948,10 @@ Thank you.""")
                self.Destroy()
 
 # Old version reminder.
-class OldVersionReminder(wx.Dialog):
-       """A dialog shown when using add-on 8.x under Studio 5.0x.
-       """
+# Only used when there is a LTS version.
+"""class OldVersionReminder(wx.Dialog):
+       #A dialog shown when using add-on 8.x under Studio 5.0x.
+       #
 
        def __init__(self, parent):
                # Translators: Title of a dialog displayed when the add-on 
starts reminding broadcasters about old Studio releases.
@@ -960,7 +966,7 @@ class OldVersionReminder(wx.Dialog):
                sizer = wx.BoxSizer(wx.HORIZONTAL)
                # Translators: A checkbox to turn off old version reminder 
message.
                self.oldVersionReminder=wx.CheckBox(self,wx.NewId(),label=_("Do 
not show this message again"))
-               self.oldVersionReminder.SetValue(not 
SPLConfig["Startup"]["Studio500"])
+               self.oldVersionReminder.SetValue(not 
SPLConfig["Startup"]["OldSPLVersionReminder"])
                sizer.Add(self.oldVersionReminder, border=10,flag=wx.TOP)
                mainSizer.Add(sizer, border=10, flag=wx.BOTTOM)
 
@@ -974,15 +980,16 @@ class OldVersionReminder(wx.Dialog):
        def onOk(self, evt):
                global SPLConfig
                if self.oldVersionReminder.Value:
-                       SPLConfig["Startup"]["Studio500"] = not 
self.oldVersionReminder.Value
-               self.Destroy()
+                       SPLConfig["Startup"]["OldSPLVersionReminder"] = not 
self.oldVersionReminder.Value
+               self.Destroy()"""
 
 # And to open the above dialog and any other dialogs.
 def showStartupDialogs(oldVer=False):
-       if oldVer and SPLConfig["Startup"]["Studio500"]:
-               gui.mainFrame.prePopup()
-               OldVersionReminder(gui.mainFrame).Show()
-               gui.mainFrame.postPopup()
+       # Old version reminder if this is such a case.
+       #if oldVer and SPLConfig["Startup"]["OldSPLVersionReminder"]:
+               #gui.mainFrame.prePopup()
+               #OldVersionReminder(gui.mainFrame).Show()
+               #gui.mainFrame.postPopup()
        if SPLConfig["Startup"]["WelcomeDialog"]:
                gui.mainFrame.prePopup()
                WelcomeDialog(gui.mainFrame).Show()
@@ -992,14 +999,11 @@ def showStartupDialogs(oldVer=False):
                #if gui.messageBox("The next major version of the add-on (15.x) 
will be the last version to support Studio versions earlier than 5.10, with 
add-on 15.x being designated as a long-term support version. Would you like to 
switch to long-term support release?", "Long-Term Support version", wx.YES | 
wx.NO | wx.CANCEL | wx.CENTER | wx.ICON_QUESTION) == wx.YES:
                        #splupdate.SPLUpdateChannel = "lts"
                        #os.remove(os.path.join(globalVars.appArgs.configPath, 
"addons", "stationPlaylist", "ltsprep"))
-       try:
-               import audioDucking
-               if SPLConfig["Startup"]["AudioDuckingReminder"] and 
audioDucking.isAudioDuckingSupported():
-                       gui.mainFrame.prePopup()
-                       AudioDuckingReminder(gui.mainFrame).Show()
-                       gui.mainFrame.postPopup()
-       except ImportError:
-               pass
+       import audioDucking
+       if SPLConfig["Startup"]["AudioDuckingReminder"] and 
audioDucking.isAudioDuckingSupported():
+               gui.mainFrame.prePopup()
+               AudioDuckingReminder(gui.mainFrame).Show()
+               gui.mainFrame.postPopup()
 
 
 # Message verbosity pool.


https://bitbucket.org/nvdaaddonteam/stationplaylist/commits/4fc928224428/
Changeset:   4fc928224428
Branch:      master
User:        josephsl
Date:        2016-12-29 17:41:32+00:00
Summary:     Merge branch 'stable'

Affected #:  1 file

diff --git a/addon/appModules/splstudio/__init__.py 
b/addon/appModules/splstudio/__init__.py
index bbdb87c..218bebb 100755
--- a/addon/appModules/splstudio/__init__.py
+++ b/addon/appModules/splstudio/__init__.py
@@ -872,6 +872,8 @@ class AppModule(appModuleHandler.AppModule):
                # Manually clear the following dictionaries.
                self.carts.clear()
                self._cachedStatusObjs.clear()
+               # Don't forget to reset timestamps for cart files.
+               splmisc._cartEditTimestamps = [0, 0, 0, 0]
                # Just to make sure:
                global _SPLWin
                if _SPLWin: _SPLWin = None

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: