1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-05 14:18:53 +02:00

Merge branch 'master' into scrubbing

This commit is contained in:
Paul Licameli 2016-05-02 10:10:23 -04:00
commit 260e8b0879
11 changed files with 627 additions and 314 deletions

View File

@ -4,7 +4,10 @@ to extract the 10.6 SDK from an earlier version of Xcode.
In the instructions below, Xcode 4.3.3 (for the 10.6 SDK) and
Xcode 5.1.1 will be used. Xcode 6.1.1, 6.3, 6.4.1, and 7.1
have been verified to work as well.
have also been verified to work. Xcode 7.2 and later require a
custom build setting to accept the 10.6 SDK, or to edit
src\MemoryX.h in the Audacity code. See this topic:
http://forum.audacityteam.org/viewtopic.php?p=303835#p303835 .
1) Download Xcode 5.1.1 or greater and install it to /Applications.
2) Download Xcode 4.3.3 (it includes the 10.6 and 10.7 SDK's despite

View File

@ -183,7 +183,7 @@ void AColor::DrawFocus(wxDC & dc, wxRect & rect)
dc.SetLogicalFunction(wxCOPY);
}
void AColor::Bevel(wxDC & dc, bool up, wxRect & r)
void AColor::Bevel(wxDC & dc, bool up, const wxRect & r)
{
if (up)
AColor::Light(&dc, false);
@ -211,7 +211,7 @@ wxColour AColor::Blend( const wxColour & c1, const wxColour & c2 )
return c3;
}
void AColor::BevelTrackInfo(wxDC & dc, bool up, wxRect & r)
void AColor::BevelTrackInfo(wxDC & dc, bool up, const wxRect & r)
{
#ifndef EXPERIMENTAL_THEMING
Bevel( dc, up, r );

View File

@ -38,8 +38,8 @@ class AColor {
static void Arrow(wxDC & dc, wxCoord x, wxCoord y, int width, bool down = true);
static void Line(wxDC & dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2);
static void DrawFocus(wxDC & dc, wxRect & r);
static void Bevel(wxDC & dc, bool up, wxRect & r);
static void BevelTrackInfo(wxDC & dc, bool up, wxRect & r);
static void Bevel(wxDC & dc, bool up, const wxRect & r);
static void BevelTrackInfo(wxDC & dc, bool up, const wxRect & r);
static wxColour Blend(const wxColour & c1, const wxColour & c2);
static void UseThemeColour( wxDC * dc, int iIndex );

View File

@ -185,15 +185,14 @@ static wxString HelpTextBuiltIn( const wxString & Key )
/// so that they are consistent
/* i18n-hint: Preserve [[file:quick_help.html as it's the name of a file.*/
return WrapText(
wxString(wxT("")) +
_("<center><h3>How to Get Help</h3></center>") +
_("Welcome to Audacity ") + AUDACITY_VERSION_STRING + wxT(".<p>") +
_("These are our support methods:") + wxT("</p>") + wxT("<ul><li>") +
_(" [[file:quick_help.html|Quick Help]] (should be installed locally, <a href=\"http://manual.audacityteam.org/o/quick_help.html\">Internet version if it isn't</a>)") + wxT("</li><li>") +
_(" [[file:index.html|Manual]] (should be installed locally, <a href=\"http://manual.audacityteam.org/o/\">Internet version if it isn't</a>)") + wxT("</li><li>") +
_(" [[http://wiki.audacityteam.org/index.php|Wiki]] (the latest tips, tricks and tutorials, on the Internet)") + wxT("</li><li>") +
_(" <a href=\"http://forum.audacityteam.org/\">Forum</a> (ask your question directly, on the Internet)") + wxT("</li></ul></p><p>") +
_(" For even quicker answers, all the online resources above are <b>searchable</b>.") + wxT("</p>")
wxString(wxT("")) +
wxT("<center><h3>Audacity ") + AUDACITY_VERSION_STRING + wxT("</h3><h3>") +
_("How to get help") + wxT("</h3></center>") +
_("These are our support methods:") + wxT("<p><ul><li>") +
_(" [[file:quick_help.html|Quick Help]] - if not installed locally, <a href=\"http://manual.audacityteam.org/quick_help.html\">view online</a>") + wxT("</li><li>") +
_(" [[file:index.html|Manual]] - if not installed locally, <a href=\"http://manual.audacityteam.org/\">view online</a>") + wxT("</li><li>") +
_(" <a href=\"http://forum.audacityteam.org/\">Forum</a> - ask your question directly, online.") + wxT("</li></ul></p><p>") + wxT("<b>") +
_("More:</b> Visit our [[http://wiki.audacityteam.org/index.php|Wiki]] for tips, tricks, extra tutorials and effects plug-ins.") + wxT("</p>")
);
}
if(Key==wxT("wma-proprietary"))

View File

@ -926,6 +926,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
&mViewInfo,
this,
mRuler);
mTrackPanel->UpdatePrefs();
mIndicatorOverlay = std::make_unique<PlayIndicatorOverlay>(this);
@ -1024,7 +1025,6 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
InitialState();
FixScrollbars();
mRuler->SetLeftOffset(mTrackPanel->GetLeftOffset()); // bevel on AdornedRuler
mRuler->SetProject(this);
//
// Set the Icon

View File

@ -460,7 +460,6 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
mLabelTrackStartXPos=-1;
mCircularTrackNavigation = false;
UpdatePrefs();
mRedrawAfterStop = false;
@ -797,6 +796,9 @@ void TrackPanel::UpdatePrefs()
// All vertical rulers must be recalculated since the minimum and maximum
// frequences may have been changed.
UpdateVRulers();
mTrackInfo.UpdatePrefs();
Refresh();
}
@ -8775,21 +8777,7 @@ TrackInfo::TrackInfo(TrackPanel * pParentIn)
PAN_SLIDER);
mPanCaptured->SetDefaultValue(0.0);
int fontSize = 10;
mFont.Create(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
int allowableWidth = GetTrackInfoWidth() - 2; // 2 to allow for left/right borders
int textWidth, textHeight;
do {
mFont.SetPointSize(fontSize);
pParent->GetTextExtent(_("Stereo, 999999Hz"),
&textWidth,
&textHeight,
NULL,
NULL,
&mFont);
fontSize--;
} while (textWidth >= allowableWidth);
UpdatePrefs();
}
TrackInfo::~TrackInfo()
@ -9170,6 +9158,28 @@ LWSlider * TrackInfo::PanSlider(WaveTrack *t, bool captured) const
return captured ? mPanCaptured : mPan;
}
void TrackInfo::UpdatePrefs()
{
// Calculation of best font size depends on language, so it should be redone in case
// the language preference changed.
int fontSize = 10;
mFont.Create(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
int allowableWidth = GetTrackInfoWidth() - 2; // 2 to allow for left/right borders
int textWidth, textHeight;
do {
mFont.SetPointSize(fontSize);
pParent->GetTextExtent(_("Stereo, 999999Hz"),
&textWidth,
&textHeight,
NULL,
NULL,
&mFont);
fontSize--;
} while (textWidth >= allowableWidth);
}
TrackPanelCellIterator::TrackPanelCellIterator(TrackPanel *trackPanel, bool begin)
: mPanel(trackPanel)
, mIter(trackPanel->GetProject())

View File

@ -121,6 +121,8 @@ public:
#endif
private:
void UpdatePrefs();
TrackPanel * pParent;
wxFont mFont;
LWSlider *mGainCaptured;

View File

@ -283,7 +283,7 @@ bool EffectTruncSilence::ProcessIndependently()
if (track2->GetKind() == Track::Wave &&
!(track2 == track || track2 == link) &&
track2->GetSelected()) {
::wxMessageBox(_("When truncating independently, there may only be one selected audio track in each sync-lock group."));
::wxMessageBox(_("When truncating independently, there may only be one selected audio track in each Sync-Locked Track Group."));
return false;
}
}

View File

@ -1774,9 +1774,6 @@ BEGIN_EVENT_TABLE(AdornedRulerPanel, wxPanel)
EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll)
EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion)
// Main menu commands
EVT_MENU(OnShowHideScrubbingID, AdornedRulerPanel::OnShowHideScrubbing)
END_EVENT_TABLE()
AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent,
@ -1785,6 +1782,7 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent,
const wxSize& size,
ViewInfo *viewinfo)
: wxPanel(parent, id, pos, size)
, mProject(parent)
, mViewInfo(viewinfo)
{
SetLabel( _("Timeline") );
@ -1829,6 +1827,8 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent,
mPlayRegionDragsSelection = (gPrefs->Read(wxT("/QuickPlay/DragSelection"), 0L) == 1)? true : false;
mQuickPlayEnabled = !!gPrefs->Read(wxT("/QuickPlay/QuickPlayEnabled"), 1L);
mButtonFont.Create(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
UpdatePrefs();
#if wxUSE_TOOLTIPS
@ -1896,6 +1896,33 @@ void AdornedRulerPanel::UpdatePrefs()
UpdateRects();
RegenerateTooltips();
mButtonFontSize = -1;
}
wxFont &AdornedRulerPanel::GetButtonFont() const
{
if (mButtonFontSize < 0) {
mButtonFontSize = 10;
bool done;
do {
done = true;
mButtonFont.SetPointSize(mButtonFontSize);
wxCoord width, height;
for (auto button = StatusChoice::FirstButton; done && IsButton(button); ++button) {
auto allowableWidth = GetButtonRect(button).GetWidth() - 2;
// 2 corresponds with the Inflate(-1, -1)
GetParent()->GetTextExtent(
wxGetTranslation(GetPushButtonStrings(button)->label),
&width, &height, NULL, NULL, &mButtonFont);
done = width < allowableWidth;
}
mButtonFontSize--;
} while (mButtonFontSize > 0 && !done);
}
return mButtonFont;
}
void AdornedRulerPanel::InvalidateRuler()
@ -1910,11 +1937,32 @@ void AdornedRulerPanel::RegenerateTooltips()
if (mIsRecording) {
this->SetToolTip(_("Timeline actions disabled during recording"));
}
else if (!mQuickPlayEnabled) {
this->SetToolTip(_("Quick-Play disabled"));
}
else {
this->SetToolTip(_("Quick-Play enabled"));
switch(mPrevZone) {
case StatusChoice::QuickPlayButton :
case StatusChoice::EnteringQP :
if (!mQuickPlayEnabled) {
this->SetToolTip(_("Quick-Play disabled"));
}
else {
this->SetToolTip(_("Quick-Play enabled"));
}
break;
case StatusChoice::ScrubBarButton :
if (!mShowScrubbing) {
this->SetToolTip(_("Scrub bar hidden"));
}
else {
this->SetToolTip(_("Scrub bar shown"));
}
break;
case StatusChoice::EnteringScrubZone :
this->SetToolTip(_("Scrub Bar"));
break;
default:
this->SetToolTip(NULL);
break;
}
}
}
else {
@ -1979,7 +2027,7 @@ int AdornedRulerPanel::IndicatorBigWidth()
enum {
ScrubHeight = 14,
RulerHeight = 28
ProperRulerHeight = 28
};
int AdornedRulerPanel::IndicatorBigHeight()
@ -2026,6 +2074,8 @@ void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
DoDrawPlayRegion(&mBackDC);
DoDrawPushbuttons(&mBackDC);
dc.Blit(0, 0, mBack->GetWidth(), mBack->GetHeight(), &mBackDC, 0, 0);
if (mQuickPlayInd)
@ -2109,69 +2159,49 @@ bool AdornedRulerPanel::IsWithinMarker(int mousePosX, double markerTime)
void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
{
// Disable mouse actions on Timeline while recording.
if (mIsRecording)
return;
const bool inScrubZone =
// only if scrubbing is allowed now
mProject->GetScrubber().CanScrub() &&
mShowScrubbing &&
mScrubZone.Contains(evt.GetPosition());
const bool changeInScrubZone = (inScrubZone != mPrevInScrubZone);
mPrevInScrubZone = inScrubZone;
double t0 = mTracks->GetStartTime();
double t1 = mTracks->GetEndTime();
double sel0 = mProject->GetSel0();
double sel1 = mProject->GetSel1();
wxCoord xx = evt.GetX();
wxCoord mousePosX = xx;
UpdateQuickPlayPos(mousePosX);
// If not looping, restrict selection to end of project
if (!inScrubZone && !evt.ShiftDown()) {
mQuickPlayPos = std::min(t1, mQuickPlayPos);
}
// If position was adjusted right, we are over menu
const bool overMenu = (xx < mousePosX);
auto &scrubber = mProject->GetScrubber();
// Handle status bar messages
UpdateStatusBar (
overMenu || evt.Leaving()
? StatusChoice::Leaving
: evt.Entering() || changeInScrubZone
? inScrubZone
? StatusChoice::EnteringScrubZone
: StatusChoice::EnteringQP
: StatusChoice::NoChange
);
if (overMenu && evt.Button(wxMOUSE_BTN_ANY)) {
if(evt.ButtonDown())
DoMainMenu();
return;
}
// Handle popup menus
if (evt.RightDown() && !(evt.LeftIsDown())) {
if(inScrubZone)
ShowScrubMenu(evt.GetPosition());
else
ShowMenu(evt.GetPosition());
// dismiss and clear Quick-Play indicator
HideQuickPlayIndicator();
if (mIsRecording) {
if (HasCapture())
ReleaseMouse();
return;
}
const bool overButtons = GetButtonAreaRect(true).Contains(evt.GetPosition());
const StatusChoice button = FindButton(evt.GetPosition());
const bool inScrubZone = !overButtons &&
// only if scrubbing is allowed now
mProject->GetScrubber().CanScrub() &&
mShowScrubbing &&
mScrubZone.Contains(evt.GetPosition());
const StatusChoice zone =
evt.Leaving()
? StatusChoice::Leaving
: overButtons
? button
: inScrubZone
? StatusChoice::EnteringScrubZone
: StatusChoice::EnteringQP;
const bool changeInZone = (zone != mPrevZone);
mPrevZone = zone;
wxCoord xx = evt.GetX();
wxCoord mousePosX = xx;
UpdateQuickPlayPos(mousePosX);
HandleSnapping();
// If not looping, restrict selection to end of project
if (zone == StatusChoice::EnteringQP && !evt.ShiftDown()) {
const double t1 = mTracks->GetEndTime();
mQuickPlayPos = std::min(t1, mQuickPlayPos);
}
// Handle status bar messages
UpdateStatusBarAndTooltips (
evt.Leaving() || evt.Entering() || changeInZone
? zone
: StatusChoice::NoChange
);
auto &scrubber = mProject->GetScrubber();
if (scrubber.HasStartedScrubbing()) {
// If already clicked for scrub, preempt the usual event handling,
// no matter what the y coordinate.
@ -2191,6 +2221,9 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
wxClientDC dc(this);
DrawQuickPlayIndicator(&dc);
if (HasCapture())
ReleaseMouse();
return;
}
@ -2201,14 +2234,9 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
mPlayRegionLock = mProject->IsPlayRegionLocked();
}
bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
bool isWithinClick = (mLeftDownClick >= 0) && IsWithinMarker(mousePosX, mLeftDownClick);
bool canDragSel = !mPlayRegionLock && mPlayRegionDragsSelection;
// Handle entering and leaving of the bar, or movement from
// one portion (quick play or scrub) to the other
if (evt.Leaving() || (changeInScrubZone && inScrubZone)) {
if (evt.Leaving() || (changeInZone && zone != StatusChoice::EnteringQP)) {
if (evt.Leaving()) {
// Erase the line
HideQuickPlayIndicator();
@ -2226,84 +2254,119 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
return;
// else, may detect a scrub click below
}
else if (evt.Entering() || (changeInScrubZone && !inScrubZone)) {
else if (evt.Entering() || (changeInZone && zone == StatusChoice::EnteringQP)) {
SetCursor(mCursorHand);
HideQuickPlayIndicator();
return;
}
if (inScrubZone) {
if (evt.LeftDown())
if (HasCapture() && mCaptureState != StatusChoice::NoButton)
HandlePushbuttonEvent(evt);
else if (!HasCapture() && overButtons)
HandlePushbuttonClick(evt);
// Handle popup menus
else if (!HasCapture() && evt.RightDown() && !(evt.LeftIsDown())) {
if(inScrubZone)
ShowScrubMenu(evt.GetPosition());
else
ShowMenu(evt.GetPosition());
// dismiss and clear Quick-Play indicator
HideQuickPlayIndicator();
if (HasCapture())
ReleaseMouse();
return;
}
else if (!HasCapture() && inScrubZone) {
if (evt.LeftDown()) {
scrubber.MarkScrubStart(evt.m_x, false, false);
UpdateStatusBar(StatusChoice::EnteringScrubZone);
UpdateStatusBarAndTooltips(StatusChoice::EnteringScrubZone);
}
wxClientDC dc(this);
DrawQuickPlayIndicator(&dc);
return;
}
if (!mQuickPlayEnabled)
return;
if (isWithinStart || isWithinEnd) {
if (!mIsWE) {
SetCursor(mCursorSizeWE);
mIsWE = true;
}
}
else {
if (mIsWE) {
SetCursor(mCursorHand);
mIsWE = false;
}
}
HandleSnapping();
mQuickPlayInd = true;
wxClientDC dc(this);
DrawQuickPlayIndicator(&dc);
if (evt.LeftDown())
{
// Temporarily unlock locked play region
if (mPlayRegionLock && evt.LeftDown()) {
//mPlayRegionLock = true;
mProject->OnUnlockPlayRegion();
}
mLeftDownClick = mQuickPlayPos;
isWithinClick = IsWithinMarker(mousePosX, mLeftDownClick);
else if ( mQuickPlayEnabled) {
bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
if (isWithinStart || isWithinEnd) {
// If Quick-Play is playing from a point, we need to treat it as a click
// not as dragging.
if (mOldPlayRegionStart == mOldPlayRegionEnd)
mMouseEventState = mesSelectingPlayRegionClick;
// otherwise check which marker is nearer
else {
// Don't compare times, compare positions.
//if (fabs(mQuickPlayPos - mPlayRegionStart) < fabs(mQuickPlayPos - mPlayRegionEnd))
if (abs(Time2Pos(mQuickPlayPos) - Time2Pos(mPlayRegionStart)) <
abs(Time2Pos(mQuickPlayPos) - Time2Pos(mPlayRegionEnd)))
mMouseEventState = mesDraggingPlayRegionStart;
else
mMouseEventState = mesDraggingPlayRegionEnd;
if (!mIsWE) {
SetCursor(mCursorSizeWE);
mIsWE = true;
}
}
else {
// Clicked but not yet dragging
mMouseEventState = mesSelectingPlayRegionClick;
if (mIsWE) {
SetCursor(mCursorHand);
mIsWE = false;
}
}
// Check if we are dragging BEFORE CaptureMouse.
if (mMouseEventState != mesNone)
SetCursor(mCursorSizeWE);
CaptureMouse();
if (evt.LeftDown()) {
HandleQPClick(evt, mousePosX);
HandleQPDrag(evt, mousePosX);
}
else if (evt.LeftIsDown())
HandleQPDrag(evt, mousePosX);
else if (evt.LeftUp())
HandleQPRelease(evt);
mQuickPlayInd = true;
wxClientDC dc(this);
DrawQuickPlayIndicator(&dc);
}
}
void AdornedRulerPanel::HandleQPClick(wxMouseEvent &evt, wxCoord mousePosX)
{
// Temporarily unlock locked play region
if (mPlayRegionLock && evt.LeftDown()) {
//mPlayRegionLock = true;
mProject->OnUnlockPlayRegion();
}
if (evt.LeftIsDown()) {
switch (mMouseEventState)
{
mLeftDownClick = mQuickPlayPos;
bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
if (isWithinStart || isWithinEnd) {
// If Quick-Play is playing from a point, we need to treat it as a click
// not as dragging.
if (mOldPlayRegionStart == mOldPlayRegionEnd)
mMouseEventState = mesSelectingPlayRegionClick;
// otherwise check which marker is nearer
else {
// Don't compare times, compare positions.
//if (fabs(mQuickPlayPos - mPlayRegionStart) < fabs(mQuickPlayPos - mPlayRegionEnd))
if (abs(Time2Pos(mQuickPlayPos) - Time2Pos(mPlayRegionStart)) <
abs(Time2Pos(mQuickPlayPos) - Time2Pos(mPlayRegionEnd)))
mMouseEventState = mesDraggingPlayRegionStart;
else
mMouseEventState = mesDraggingPlayRegionEnd;
}
}
else {
// Clicked but not yet dragging
mMouseEventState = mesSelectingPlayRegionClick;
}
// Check if we are dragging BEFORE CaptureMouse.
if (mMouseEventState != mesNone)
SetCursor(mCursorSizeWE);
CaptureMouse();
}
void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX)
{
bool isWithinClick = (mLeftDownClick >= 0) && IsWithinMarker(mousePosX, mLeftDownClick);
bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
bool canDragSel = !mPlayRegionLock && mPlayRegionDragsSelection;
switch (mMouseEventState)
{
case mesNone:
// If close to either end of play region, snap to closest
if (isWithinStart || isWithinEnd) {
@ -2351,7 +2414,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
break;
case mesSelectingPlayRegionClick:
// Don't start dragging until mouse is beyond tollerance of initial click.
// Don't start dragging until mouse is beyond tolerance of initial click.
if (isWithinClick || mLeftDownClick == -1) {
DrawQuickPlayIndicator(NULL);
@ -2382,167 +2445,168 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
DragSelection();
}
break;
}
Refresh();
Update();
}
Refresh();
Update();
}
void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
{
HideQuickPlayIndicator();
if (HasCapture())
ReleaseMouse();
mCaptureState = StatusChoice::NoButton;
if (mPlayRegionEnd < mPlayRegionStart) {
// Swap values to ensure mPlayRegionStart < mPlayRegionEnd
double tmp = mPlayRegionStart;
mPlayRegionStart = mPlayRegionEnd;
mPlayRegionEnd = tmp;
}
if (evt.LeftUp())
{
HideQuickPlayIndicator();
const double t0 = mTracks->GetStartTime();
const double t1 = mTracks->GetEndTime();
const double sel0 = mProject->GetSel0();
const double sel1 = mProject->GetSel1();
if (HasCapture())
ReleaseMouse();
if (mPlayRegionEnd < mPlayRegionStart) {
// Swap values to ensure mPlayRegionStart < mPlayRegionEnd
double tmp = mPlayRegionStart;
mPlayRegionStart = mPlayRegionEnd;
mPlayRegionEnd = tmp;
}
// We want some audio in the selection, but we allow a dragged
// region to include selected white-space and space before audio start.
if (evt.ShiftDown() && (mPlayRegionStart == mPlayRegionEnd)) {
// Looping the selection or project.
// Disable if track selection is in white-space beyond end of tracks and
// play position is outside of track contents.
if (((sel1 < t0) || (sel0 > t1)) &&
((mPlayRegionStart < t0) || (mPlayRegionStart > t1))) {
ClearPlayRegion();
}
}
// Disable if beyond end.
else if (mPlayRegionStart >= t1) {
ClearPlayRegion();
}
// Disable if empty selection before start.
// (allow Quick-Play region to include 'pre-roll' white space)
else if (((mPlayRegionEnd - mPlayRegionStart) > 0.0) && (mPlayRegionEnd < t0)) {
// We want some audio in the selection, but we allow a dragged
// region to include selected white-space and space before audio start.
if (evt.ShiftDown() && (mPlayRegionStart == mPlayRegionEnd)) {
// Looping the selection or project.
// Disable if track selection is in white-space beyond end of tracks and
// play position is outside of track contents.
if (((sel1 < t0) || (sel0 > t1)) &&
((mPlayRegionStart < t0) || (mPlayRegionStart > t1))) {
ClearPlayRegion();
}
}
// Disable if beyond end.
else if (mPlayRegionStart >= t1) {
ClearPlayRegion();
}
// Disable if empty selection before start.
// (allow Quick-Play region to include 'pre-roll' white space)
else if (((mPlayRegionEnd - mPlayRegionStart) > 0.0) && (mPlayRegionEnd < t0)) {
ClearPlayRegion();
}
// Start / Restart playback on left click.
bool startPlaying = (mPlayRegionStart >= 0);
// Start / Restart playback on left click.
bool startPlaying = (mPlayRegionStart >= 0);
if (startPlaying) {
ControlToolBar* ctb = mProject->GetControlToolBar();
ctb->StopPlaying();
if (startPlaying) {
ControlToolBar* ctb = mProject->GetControlToolBar();
ctb->StopPlaying();
bool loopEnabled = true;
double start, end;
bool loopEnabled = true;
double start, end;
if ((mPlayRegionEnd - mPlayRegionStart == 0.0) && evt.ShiftDown()) {
// Loop play a point will loop either a selection or the project.
if ((mPlayRegionStart > sel0) && (mPlayRegionStart < sel1)) {
// we are in a selection, so use the selection
start = sel0;
end = sel1;
} // not in a selection, so use the project
else {
start = t0;
end = t1;
}
}
if ((mPlayRegionEnd - mPlayRegionStart == 0.0) && evt.ShiftDown()) {
// Loop play a point will loop either a selection or the project.
if ((mPlayRegionStart > sel0) && (mPlayRegionStart < sel1)) {
// we are in a selection, so use the selection
start = sel0;
end = sel1;
} // not in a selection, so use the project
else {
start = mPlayRegionStart;
end = mPlayRegionEnd;
start = t0;
end = t1;
}
// Looping a tiny selection may freeze, so just play it once.
loopEnabled = ((end - start) > 0.001)? true : false;
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
options.playLooped = (loopEnabled && evt.ShiftDown());
if (!evt.ControlDown())
options.pStartTime = &mPlayRegionStart;
else
options.timeTrack = NULL;
ControlToolBar::PlayAppearance appearance =
evt.ControlDown() ? ControlToolBar::PlayAppearance::CutPreview
: options.playLooped ? ControlToolBar::PlayAppearance::Looped
: ControlToolBar::PlayAppearance::Straight;
ctb->PlayPlayRegion((SelectedRegion(start, end)),
options, PlayMode::normalPlay,
appearance,
false,
true);
mPlayRegionStart = start;
mPlayRegionEnd = end;
Refresh();
}
mMouseEventState = mesNone;
mIsDragging = false;
mLeftDownClick = -1;
if (mPlayRegionLock) {
// Restore Locked Play region
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
mProject->OnLockPlayRegion();
// and release local lock
mPlayRegionLock = false;
else {
start = mPlayRegionStart;
end = mPlayRegionEnd;
}
// Looping a tiny selection may freeze, so just play it once.
loopEnabled = ((end - start) > 0.001)? true : false;
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
options.playLooped = (loopEnabled && evt.ShiftDown());
if (!evt.ControlDown())
options.pStartTime = &mPlayRegionStart;
else
options.timeTrack = NULL;
ControlToolBar::PlayAppearance appearance =
evt.ControlDown() ? ControlToolBar::PlayAppearance::CutPreview
: options.playLooped ? ControlToolBar::PlayAppearance::Looped
: ControlToolBar::PlayAppearance::Straight;
ctb->PlayPlayRegion((SelectedRegion(start, end)),
options, PlayMode::normalPlay,
appearance,
false,
true);
mPlayRegionStart = start;
mPlayRegionEnd = end;
Refresh();
}
mMouseEventState = mesNone;
mIsDragging = false;
mLeftDownClick = -1;
if (mPlayRegionLock) {
// Restore Locked Play region
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
mProject->OnLockPlayRegion();
// and release local lock
mPlayRegionLock = false;
}
}
void AdornedRulerPanel::UpdateStatusBar(StatusChoice choice)
void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice)
{
if (choice == StatusChoice::NoChange)
return;
const auto &scrubber = mProject->GetScrubber();
const bool scrubbing = scrubber.HasStartedScrubbing();
if (scrubbing && choice != StatusChoice::Leaving)
// Don't distinguish zones
choice = StatusChoice::EnteringScrubZone;
wxString message{};
wxString message {};
switch (choice) {
case StatusChoice::EnteringQP:
{
// message = Insert timeline status bar message here
}
break;
if (IsButton(choice)) {
bool state = GetButtonState(choice);
const auto &strings = *GetPushButtonStrings(choice);
message += wxGetTranslation(state ? strings.disable : strings.enable);
message += wxT(" ") + _("(Right-Click for options)");
}
else {
const auto &scrubber = mProject->GetScrubber();
const bool scrubbing = scrubber.HasStartedScrubbing();
if (scrubbing && choice != StatusChoice::Leaving)
// Don't distinguish zones
choice = StatusChoice::EnteringScrubZone;
case StatusChoice::EnteringScrubZone:
{
if (scrubbing) {
if(!scrubber.IsAlwaysSeeking())
message = _("Click or drag to seek");
switch (choice) {
case StatusChoice::EnteringQP:
{
// message = Insert timeline status bar message here
}
else
message = _("Click to scrub, Double-Click to scroll, Drag to seek");
}
break;
break;
case StatusChoice::Leaving:
default:
break;
case StatusChoice::EnteringScrubZone:
{
if (scrubbing) {
if(!scrubber.IsAlwaysSeeking())
message = _("Click or drag to seek");
}
else
message = _("Click to scrub, Double-Click to scroll, Drag to seek");
}
break;
default:
break;
}
}
// Display a message, or empty message
mProject->TP_DisplayStatusMessage(message);
RegenerateTooltips();
}
void AdornedRulerPanel::DoMainMenu()
{
wxMenu menu;
menu.AppendCheckItem(OnShowHideScrubbingID, _("Scrub Bar"));
menu.Check(OnShowHideScrubbingID, mShowScrubbing);
// Position the popup similarly to the track control panel menus
wxPoint pos {
kLeftInset + kTrackInfoBtnSize + 1,
GetSize().GetHeight() + 1
};
PopupMenu(&menu, pos);
}
void AdornedRulerPanel::OnShowHideScrubbing(wxCommandEvent&)
void AdornedRulerPanel::OnToggleScrubbing()
{
mShowScrubbing = !mShowScrubbing;
WriteScrubEnabledPref(mShowScrubbing);
@ -2704,8 +2768,8 @@ void AdornedRulerPanel::DoDrawPlayRegion(wxDC * dc)
if (start >= 0)
{
int x1 = Time2Pos(start) + 1;
int x2 = Time2Pos(end);
const int x1 = Time2Pos(start) + 1;
const int x2 = Time2Pos(end);
int y = mInner.y - TopMargin + mInner.height/2;
bool isLocked = mProject->IsPlayRegionLocked();
@ -2746,13 +2810,195 @@ void AdornedRulerPanel::DoDrawPlayRegion(wxDC * dc)
r.x = x1 + PLAY_REGION_TRIANGLE_SIZE;
r.y = y - PLAY_REGION_RECT_HEIGHT/2 + PLAY_REGION_GLOBAL_OFFSET_Y;
r.width = x2-x1 - PLAY_REGION_TRIANGLE_SIZE*2;
r.width = std::max(0, x2-x1 - PLAY_REGION_TRIANGLE_SIZE*2);
r.height = PLAY_REGION_RECT_HEIGHT;
dc->DrawRectangle(r);
}
}
}
wxRect AdornedRulerPanel::GetButtonAreaRect(bool includeBorder) const
{
int x, y, bottomMargin;
if(includeBorder)
x = 0, y = 0, bottomMargin = 0;
else
x = LeftMargin, y = TopMargin, bottomMargin = BottomMargin;
wxRect rect {
x, y,
mProject->GetTrackPanel()->GetLeftOffset() - x,
GetRulerHeight() - y - bottomMargin
};
// Leave room for one digit on the ruler, so "0.0" is not obscured if you go to start.
// But the digit string at the left end may be longer if you are not at the start.
// Perhaps there should be room for more than one digit.
wxScreenDC dc;
dc.SetFont(*mRuler.GetFonts().major);
rect.width -= dc.GetTextExtent(wxT("0")).GetWidth();
return rect;
}
wxRect AdornedRulerPanel::GetButtonRect( StatusChoice button ) const
{
if (!IsButton(button))
return wxRect {};
wxRect rect { GetButtonAreaRect() };
// Reduce the height
rect.height -= (GetRulerHeight() - ProperRulerHeight);
auto num = static_cast<unsigned>(button);
auto denom = static_cast<unsigned>(StatusChoice::NumButtons);
rect.x += (num * rect.width) / denom;
rect.width = (((1 + num) * rect.width) / denom) - rect.x;
return rect;
}
bool AdornedRulerPanel::InButtonRect( StatusChoice button ) const
{
return GetButtonRect(button).Contains(ScreenToClient(::wxGetMousePosition()));
}
auto AdornedRulerPanel::FindButton( wxPoint position ) const -> StatusChoice
{
for (auto button = StatusChoice::FirstButton; IsButton(button); ++button) {
if(GetButtonRect(button).Contains(position))
return button;
}
return StatusChoice::NoButton;
}
bool AdornedRulerPanel::GetButtonState( StatusChoice button ) const
{
switch(button) {
case StatusChoice::QuickPlayButton:
return mQuickPlayEnabled;
case StatusChoice::ScrubBarButton:
return mShowScrubbing;
default:
wxASSERT(false);
return false;
}
}
void AdornedRulerPanel::ToggleButtonState( StatusChoice button )
{
switch(button) {
case StatusChoice::QuickPlayButton: {
wxCommandEvent dummy;
OnToggleQuickPlay(dummy);
}
break;
case StatusChoice::ScrubBarButton:
OnToggleScrubbing();
break;
default:
wxASSERT(false);
}
}
void AdornedRulerPanel::ShowButtonMenu( StatusChoice button, wxPoint position)
{
switch (button) {
case StatusChoice::QuickPlayButton:
return ShowMenu(position);
case StatusChoice::ScrubBarButton:
return ShowScrubMenu(position);
default:
return;
}
}
const AdornedRulerPanel::ButtonStrings AdornedRulerPanel::PushbuttonLabels
[static_cast<size_t>(StatusChoice::NumButtons)]
{
{ XO("Quick-Play"), XO("Enable Quick-Play"), XO("Disable Quick-Play") },
/* i18n-hint: A long screen area (bar) controlling variable speed play (scrubbing) */
{ XO("Scrub Bar"), XO("Show Scrub Bar"), XO("Hide Scrub Bar") },
};
void AdornedRulerPanel::DoDrawPushbutton(wxDC *dc, StatusChoice button, bool down) const
{
// Adapted from TrackInfo::DrawMuteSolo()
auto bev = GetButtonRect( button );
// This part corresponds to part of TrackInfo::DrawBordersWithin() :
AColor::Dark(dc, false);
dc->DrawRectangle(bev);
bev.Inflate(-1, -1);
if (down)
AColor::Solo(dc, true, false);
else
AColor::MediumTrackInfo(dc, false);
dc->SetPen( *wxTRANSPARENT_PEN );//No border!
dc->DrawRectangle(bev);
dc->SetTextForeground(theTheme.Colour(clrTrackPanelText));
wxCoord textWidth, textHeight;
wxString str = wxGetTranslation(GetPushButtonStrings(button)->label);
dc->SetFont(GetButtonFont());
dc->GetTextExtent(str, &textWidth, &textHeight);
dc->DrawText(str, bev.x + (bev.width - textWidth) / 2,
bev.y + (bev.height - textHeight) / 2);
AColor::BevelTrackInfo(*dc, !down, bev);
}
void AdornedRulerPanel::HandlePushbuttonClick(wxMouseEvent &evt)
{
auto button = FindButton(evt.GetPosition());
if (IsButton(button)) {
if (evt.LeftDown()) {
CaptureMouse();
mCaptureState = button;
Refresh();
}
else if (evt.RightDown()) {
auto rect = GetButtonRect(button);
ShowButtonMenu( button, wxPoint{ rect.GetX() + 1, rect.GetBottom() + 1 } );
}
}
}
void AdornedRulerPanel::HandlePushbuttonEvent(wxMouseEvent &evt)
{
if(evt.ButtonUp()) {
if(HasCapture())
ReleaseMouse();
if(InButtonRect(mCaptureState)) {
ToggleButtonState(mCaptureState);
UpdateStatusBarAndTooltips(mCaptureState);
}
mCaptureState = StatusChoice::NoButton;
}
Refresh();
}
void AdornedRulerPanel::DoDrawPushbuttons(wxDC *dc) const
{
// Paint the area behind the buttons
wxRect background = GetButtonAreaRect();
AColor::MediumTrackInfo(dc, false);
dc->DrawRectangle(background);
for (auto button = StatusChoice::FirstButton; IsButton(button); ++button) {
auto state = GetButtonState(button);
auto toggle = (button == mCaptureState && InButtonRect(button));
auto down = (state != toggle);
DoDrawPushbutton(dc, button, down);
}
}
void AdornedRulerPanel::DoDrawBorder(wxDC * dc)
{
// Draw AdornedRulerPanel border
@ -2762,8 +3008,8 @@ void AdornedRulerPanel::DoDrawBorder(wxDC * dc)
if (mShowScrubbing) {
// Let's distinguish the scrubbing area by using the same gray as for
// selected track control panel.
AColor::Medium(&mBackDC, true);
mBackDC.DrawRectangle(mScrubZone);
AColor::MediumTrackInfo(dc, true);
dc->DrawRectangle(mScrubZone);
}
wxRect r = mOuter;
@ -2819,7 +3065,7 @@ int AdornedRulerPanel::GetRulerHeight()
int AdornedRulerPanel::GetRulerHeight(bool showScrubBar)
{
return RulerHeight + (showScrubBar ? ScrubHeight : 0);
return ProperRulerHeight + (showScrubBar ? ScrubHeight : 0);
}
void AdornedRulerPanel::SetLeftOffset(int offset)
@ -2945,14 +3191,15 @@ void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc)
const int x = Time2Pos(mQuickPlayPos);
bool previewScrub =
mPrevInScrubZone &&
mPrevZone == StatusChoice::EnteringScrubZone &&
!mProject->GetScrubber().IsScrubbing();
GetOverlay()->Update(x, mIsSnapped, previewScrub);
DoEraseIndicator(dc, mLastQuickPlayX);
mLastQuickPlayX = x;
auto scrub = mPrevInScrubZone || mProject->GetScrubber().HasStartedScrubbing();
auto scrub = mPrevZone == StatusChoice::EnteringScrubZone ||
mProject->GetScrubber().HasStartedScrubbing();
auto width = scrub ? IndicatorBigWidth() : IndicatorSmallWidth;
DoDrawIndicator(dc, mQuickPlayPos, true, width, scrub);
}

View File

@ -102,6 +102,9 @@ class AUDACITY_DLL_API Ruler {
// Good defaults are provided, but you can override here
void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont);
struct Fonts { wxFont *major, *minor, *minorMinor; };
Fonts GetFonts() const
{ return { mMajorFont, mMinorFont, mMinorMinorFont }; }
// Copies *pScale if it is not NULL
void SetNumberScale(const NumberScale *pScale);
@ -304,7 +307,6 @@ public:
void ClearPlayRegion();
void GetPlayRegion(double* playRegionStart, double* playRegionEnd);
void SetProject(AudacityProject* project) {mProject = project;}
void GetMaxSize(wxCoord *width, wxCoord *height);
void InvalidateRuler();
@ -321,16 +323,37 @@ private:
void OnSize(wxSizeEvent &evt);
void UpdateRects();
void OnMouseEvents(wxMouseEvent &evt);
void HandleQPClick(wxMouseEvent &event, wxCoord mousePosX);
void HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX);
void HandleQPRelease(wxMouseEvent &event);
enum class StatusChoice {
EnteringQP,
FirstButton = 0,
QuickPlayButton = FirstButton,
ScrubBarButton,
NumButtons,
NoButton = -1,
EnteringQP = NumButtons,
EnteringScrubZone,
Leaving,
NoChange
};
void UpdateStatusBar(StatusChoice choice);
friend inline StatusChoice &operator++ (StatusChoice &choice) {
choice = static_cast<StatusChoice>(1 + static_cast<int>(choice));
return choice;
}
static inline bool IsButton(StatusChoice choice)
{
auto integer = static_cast<int>(choice);
return integer >= 0 &&
integer < static_cast<int>(StatusChoice::NumButtons);
}
static inline bool IsButton(int choice)
{ return IsButton(static_cast<StatusChoice>(choice)); }
void DoMainMenu();
void UpdateStatusBarAndTooltips(StatusChoice choice);
void OnCaptureLost(wxMouseCaptureLostEvent &evt);
@ -344,6 +367,32 @@ private:
void DrawQuickPlayIndicator(wxDC * dc /*NULL to DELETE old only*/);
void DoDrawPlayRegion(wxDC * dc);
wxRect GetButtonAreaRect(bool includeBorder = false) const;
struct ButtonStrings {
wxString label, enable, disable;
};
static const ButtonStrings PushbuttonLabels[];
static const ButtonStrings *GetPushButtonStrings(StatusChoice choice)
{
if(IsButton(choice))
return &PushbuttonLabels[static_cast<size_t>(choice)];
return nullptr;
}
wxRect GetButtonRect( StatusChoice button ) const;
bool InButtonRect( StatusChoice button ) const;
StatusChoice FindButton( wxPoint position ) const;
bool GetButtonState( StatusChoice button ) const;
void ToggleButtonState( StatusChoice button );
void ShowButtonMenu( StatusChoice button, wxPoint position);
void DoDrawPushbutton(wxDC *dc, StatusChoice button, bool down) const;
void DoDrawPushbuttons(wxDC *dc) const;
void HandlePushbuttonClick(wxMouseEvent &evt);
void HandlePushbuttonEvent(wxMouseEvent &evt);
wxFont &GetButtonFont() const;
double Pos2Time(int p, bool ignoreFisheye = false);
int Time2Pos(double t, bool ignoreFisheye = false);
@ -361,7 +410,7 @@ private:
Ruler mRuler;
ViewInfo *const mViewInfo;
AudacityProject *mProject;
AudacityProject *const mProject;
TrackList *mTracks;
wxBitmap *mBack;
@ -404,16 +453,15 @@ private:
void OnAutoScroll(wxCommandEvent &evt);
void OnLockPlayRegion(wxCommandEvent &evt);
//
// Main menu
//
void OnShowHideScrubbing(wxCommandEvent &evt);
void OnToggleScrubbing();
bool mPlayRegionDragsSelection;
bool mTimelineToolTip;
bool mQuickPlayEnabled;
StatusChoice mCaptureState { StatusChoice::NoButton };
enum MouseEventState {
mesNone,
mesDraggingPlayRegionStart,
@ -429,9 +477,12 @@ private:
std::unique_ptr<QuickPlayIndicatorOverlay> mOverlay;
bool mPrevInScrubZone{};
StatusChoice mPrevZone { StatusChoice::NoChange };
bool mShowScrubbing { true };
mutable int mButtonFontSize { -1 };
mutable wxFont mButtonFont;
DECLARE_EVENT_TABLE()
};

View File

@ -28,7 +28,7 @@ public:
void swap(wxFileNameWrapper &that)
{
if (this != &that) {
#if 1
#if 0
// Awful hack number 1 makes gcc 5 choke
enum : size_t { Size = sizeof(*this) };
// Do it bitwise.
@ -39,6 +39,7 @@ public:
memcpy(&that, &buffer, Size);
#else
// Awful hack number 2 relies on knowing the class layout
// This is the less evil one but watch out for redefinition of the base class
struct Contents
{
void swap(Contents &that) {