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.