mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-24 16:20:05 +02:00
Merge branch 'master' into scrubbing
This commit is contained in:
commit
f7765a5068
@ -17,16 +17,32 @@ It is also a place to document colour usage policy in Audacity
|
||||
|
||||
*//********************************************************************/
|
||||
|
||||
#include "Audacity.h"
|
||||
#include "AColor.h"
|
||||
|
||||
#include <wx/colour.h>
|
||||
#include <wx/dc.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
#include "AColor.h"
|
||||
#include "Theme.h"
|
||||
#include "Experimental.h"
|
||||
#include "AllThemeResources.h"
|
||||
|
||||
void DCUnchanger::operator () (wxDC *pDC) const
|
||||
{
|
||||
if (pDC) {
|
||||
pDC->SetPen(pen);
|
||||
pDC->SetBrush(brush);
|
||||
pDC->SetLogicalFunction(wxRasterOperationMode(logicalOperation));
|
||||
}
|
||||
}
|
||||
|
||||
ADCChanger::ADCChanger(wxDC *pDC)
|
||||
: Base{ pDC, ::DCUnchanger{ pDC->GetBrush(), pDC->GetPen(),
|
||||
long(pDC->GetLogicalFunction()) } }
|
||||
{}
|
||||
|
||||
bool AColor::inited = false;
|
||||
wxBrush AColor::lightBrush[2];
|
||||
wxBrush AColor::mediumBrush[2];
|
||||
|
27
src/AColor.h
27
src/AColor.h
@ -14,12 +14,39 @@
|
||||
#ifndef __AUDACITY_COLOR__
|
||||
#define __AUDACITY_COLOR__
|
||||
|
||||
#include "MemoryX.h"
|
||||
#include <wx/brush.h>
|
||||
#include <wx/pen.h>
|
||||
|
||||
class wxDC;
|
||||
class wxRect;
|
||||
|
||||
struct DCUnchanger {
|
||||
public:
|
||||
DCUnchanger() {}
|
||||
|
||||
DCUnchanger(const wxBrush &brush_, const wxPen &pen_, long logicalOperation_)
|
||||
: brush(brush_), pen(pen_), logicalOperation(logicalOperation_)
|
||||
{}
|
||||
|
||||
void operator () (wxDC *pDC) const;
|
||||
|
||||
wxBrush brush {};
|
||||
wxPen pen {};
|
||||
long logicalOperation {};
|
||||
};
|
||||
|
||||
// Like wxDCPenChanger, etc., but simple and general
|
||||
// Make temporary drawing context changes that you back out of, RAII style
|
||||
|
||||
class ADCChanger : public std::unique_ptr<wxDC, ::DCUnchanger>
|
||||
{
|
||||
using Base = std::unique_ptr<wxDC, ::DCUnchanger>;
|
||||
public:
|
||||
ADCChanger() : Base{} {}
|
||||
ADCChanger(wxDC *pDC);
|
||||
};
|
||||
|
||||
class AColor {
|
||||
public:
|
||||
|
||||
|
@ -61,7 +61,7 @@ void AboutDialog::CreateCreditsList()
|
||||
AddCredit(wxString(wxT("Greg Kozikowski, ")) + _("documentation and support"), roleTeamMember);
|
||||
AddCredit(wxString(wxT("Paul Licameli, ")) + _("developer"), roleTeamMember);
|
||||
AddCredit(wxString(wxT("Leland Lucius, ")) + _("developer"), roleTeamMember);
|
||||
AddCredit(wxString(wxT("Peter Sampson, ")) + _("documentation and support"), roleTeamMember);
|
||||
AddCredit(wxString(wxT("Peter Sampson")), roleTeamMember);
|
||||
AddCredit(wxString(wxT("Martyn Shaw, ")) + _("developer"), roleTeamMember);
|
||||
AddCredit(wxString(wxT("Bill Wharrie, ")) + _("documentation and support"), roleTeamMember);
|
||||
|
||||
|
@ -56,7 +56,7 @@ DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_OPEN_AUDIO_FILE, -1);
|
||||
|
||||
// These flags represent the majority of the states that affect
|
||||
// whether or not items in menus are enabled or disabled.
|
||||
enum
|
||||
enum CommandFlag : unsigned long long
|
||||
{
|
||||
AlwaysEnabledFlag = 0x00000000,
|
||||
|
||||
@ -93,11 +93,89 @@ enum
|
||||
CaptureNotBusyFlag = 0x20000000,
|
||||
CanStopAudioStreamFlag = 0x40000000,
|
||||
AudioStreamNotScrubbingFlag
|
||||
= 0x80000000,
|
||||
= 0x80000000ULL, // prl
|
||||
|
||||
NoFlagsSpecifed = 0xffffffff
|
||||
NoFlagsSpecifed = ~0ULL
|
||||
};
|
||||
|
||||
// Prevent accidental misuse with narrower types
|
||||
|
||||
bool operator == (CommandFlag, unsigned long) PROHIBITED;
|
||||
bool operator == (CommandFlag, long) PROHIBITED;
|
||||
bool operator == (unsigned long, CommandFlag) PROHIBITED;
|
||||
bool operator == (long, CommandFlag) PROHIBITED;
|
||||
|
||||
bool operator != (CommandFlag, unsigned long) PROHIBITED;
|
||||
bool operator != (CommandFlag, long) PROHIBITED;
|
||||
bool operator != (unsigned long, CommandFlag) PROHIBITED;
|
||||
bool operator != (long, CommandFlag) PROHIBITED;
|
||||
|
||||
CommandFlag operator & (CommandFlag, unsigned long) PROHIBITED;
|
||||
CommandFlag operator & (CommandFlag, long) PROHIBITED;
|
||||
CommandFlag operator & (unsigned long, CommandFlag) PROHIBITED;
|
||||
CommandFlag operator & (long, CommandFlag) PROHIBITED;
|
||||
|
||||
CommandFlag operator | (CommandFlag, unsigned long) PROHIBITED;
|
||||
CommandFlag operator | (CommandFlag, long) PROHIBITED;
|
||||
CommandFlag operator | (unsigned long, CommandFlag) PROHIBITED;
|
||||
CommandFlag operator | (long, CommandFlag) PROHIBITED;
|
||||
|
||||
CommandFlag operator ^ (CommandFlag, unsigned long) PROHIBITED;
|
||||
CommandFlag operator ^ (CommandFlag, long) PROHIBITED;
|
||||
CommandFlag operator ^ (unsigned long, CommandFlag) PROHIBITED;
|
||||
CommandFlag operator ^ (long, CommandFlag) PROHIBITED;
|
||||
|
||||
bool operator == (CommandFlag, unsigned int) PROHIBITED;
|
||||
bool operator == (CommandFlag, int) PROHIBITED;
|
||||
bool operator == (unsigned int, CommandFlag) PROHIBITED;
|
||||
bool operator == (int, CommandFlag) PROHIBITED;
|
||||
|
||||
bool operator != (CommandFlag, unsigned int) PROHIBITED;
|
||||
bool operator != (CommandFlag, int) PROHIBITED;
|
||||
bool operator != (unsigned int, CommandFlag) PROHIBITED;
|
||||
bool operator != (int, CommandFlag) PROHIBITED;
|
||||
|
||||
CommandFlag operator & (CommandFlag, unsigned int) PROHIBITED;
|
||||
CommandFlag operator & (CommandFlag, int) PROHIBITED;
|
||||
CommandFlag operator & (unsigned int, CommandFlag) PROHIBITED;
|
||||
CommandFlag operator & (int, CommandFlag) PROHIBITED;
|
||||
|
||||
CommandFlag operator | (CommandFlag, unsigned int) PROHIBITED;
|
||||
CommandFlag operator | (CommandFlag, int) PROHIBITED;
|
||||
CommandFlag operator | (unsigned int, CommandFlag) PROHIBITED;
|
||||
CommandFlag operator | (int, CommandFlag) PROHIBITED;
|
||||
|
||||
CommandFlag operator ^ (CommandFlag, unsigned int) PROHIBITED;
|
||||
CommandFlag operator ^ (CommandFlag, int) PROHIBITED;
|
||||
CommandFlag operator ^ (unsigned int, CommandFlag) PROHIBITED;
|
||||
CommandFlag operator ^ (int, CommandFlag) PROHIBITED;
|
||||
|
||||
// Supply the bitwise operations
|
||||
|
||||
inline CommandFlag operator ~ (CommandFlag flag)
|
||||
{
|
||||
return static_cast<CommandFlag>( ~ static_cast<unsigned long long> (flag) );
|
||||
}
|
||||
inline CommandFlag operator & (CommandFlag lhs, CommandFlag rhs)
|
||||
{
|
||||
return static_cast<CommandFlag> (
|
||||
static_cast<unsigned long long>(lhs) & static_cast<unsigned long long>(rhs)
|
||||
);
|
||||
}
|
||||
inline CommandFlag operator | (CommandFlag lhs, CommandFlag rhs)
|
||||
{
|
||||
return static_cast<CommandFlag> (
|
||||
static_cast<unsigned long long>(lhs) | static_cast<unsigned long long>(rhs)
|
||||
);
|
||||
}
|
||||
inline CommandFlag & operator |= (CommandFlag &lhs, CommandFlag rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
using CommandMask = CommandFlag;
|
||||
|
||||
class BlockFile;
|
||||
|
||||
class AudacityApp final : public wxApp {
|
||||
|
@ -2258,7 +2258,9 @@ void AudioIO::StopStream()
|
||||
while( mAudioThreadShouldCallFillBuffersOnce == true )
|
||||
{
|
||||
// LLL: Experienced recursive yield here...once.
|
||||
wxGetApp().Yield(true); // Pass true for onlyIfNeeded to avoid recursive call error.
|
||||
// PRL: Made it safe yield to avoid a certain recursive event processing in the
|
||||
// time ruler when switching from scrub to quick play.
|
||||
wxGetApp().SafeYield(nullptr, true); // Pass true for onlyIfNeeded to avoid recursive call error.
|
||||
wxMilliSleep( 50 );
|
||||
}
|
||||
|
||||
|
@ -907,7 +907,8 @@ void AudacityProject::CreateMenusAndCommands()
|
||||
#else
|
||||
wxT("Ctrl+M"),
|
||||
#endif
|
||||
0, AudioIONotBusyFlag);
|
||||
AlwaysEnabledFlag, // is this correct??
|
||||
AudioIONotBusyFlag);
|
||||
c->AddItem(wxT("EditLabels"), _("&Edit Labels..."), FN(OnEditLabels));
|
||||
|
||||
c->AddSeparator();
|
||||
@ -1214,7 +1215,7 @@ void AudacityProject::CreateMenusAndCommands()
|
||||
c->AddCommand(wxT("PlaySpeedInc"), _("Increase playback speed"), FN(OnPlaySpeedInc));
|
||||
c->AddCommand(wxT("PlaySpeedDec"), _("Decrease playback speed"), FN(OnPlaySpeedDec));
|
||||
|
||||
mLastFlags = 0;
|
||||
mLastFlags = AlwaysEnabledFlag;
|
||||
|
||||
#if defined(__WXDEBUG__)
|
||||
// c->CheckDups();
|
||||
@ -1223,8 +1224,8 @@ void AudacityProject::CreateMenusAndCommands()
|
||||
|
||||
void AudacityProject::PopulateEffectsMenu(CommandManager* c,
|
||||
EffectType type,
|
||||
int batchflags,
|
||||
int realflags)
|
||||
CommandFlag batchflags,
|
||||
CommandFlag realflags)
|
||||
{
|
||||
PluginManager & pm = PluginManager::Get();
|
||||
|
||||
@ -1295,8 +1296,8 @@ void AudacityProject::PopulateEffectsMenu(CommandManager* c,
|
||||
|
||||
void AudacityProject::AddEffectMenuItems(CommandManager *c,
|
||||
EffectPlugs & plugs,
|
||||
int batchflags,
|
||||
int realflags,
|
||||
CommandFlag batchflags,
|
||||
CommandFlag realflags,
|
||||
bool isDefault)
|
||||
{
|
||||
size_t pluginCnt = plugs.GetCount();
|
||||
@ -1311,7 +1312,7 @@ void AudacityProject::AddEffectMenuItems(CommandManager *c,
|
||||
|
||||
wxArrayString groupNames;
|
||||
PluginIDList groupPlugs;
|
||||
wxArrayInt groupFlags;
|
||||
std::vector<CommandFlag> groupFlags;
|
||||
if (grouped)
|
||||
{
|
||||
wxString last;
|
||||
@ -1361,13 +1362,13 @@ void AudacityProject::AddEffectMenuItems(CommandManager *c,
|
||||
|
||||
groupNames.Clear();
|
||||
groupPlugs.Clear();
|
||||
groupFlags.Clear();
|
||||
groupFlags.clear();
|
||||
last = current;
|
||||
}
|
||||
|
||||
groupNames.Add(name);
|
||||
groupPlugs.Add(plug->GetID());
|
||||
groupFlags.Add(plug->IsEffectRealtime() ? realflags : batchflags);
|
||||
groupFlags.push_back(plug->IsEffectRealtime() ? realflags : batchflags);
|
||||
}
|
||||
|
||||
if (groupNames.GetCount() > 0)
|
||||
@ -1414,7 +1415,7 @@ void AudacityProject::AddEffectMenuItems(CommandManager *c,
|
||||
|
||||
groupNames.Add(group + name);
|
||||
groupPlugs.Add(plug->GetID());
|
||||
groupFlags.Add(plug->IsEffectRealtime() ? realflags : batchflags);
|
||||
groupFlags.push_back(plug->IsEffectRealtime() ? realflags : batchflags);
|
||||
}
|
||||
|
||||
if (groupNames.GetCount() > 0)
|
||||
@ -1430,7 +1431,7 @@ void AudacityProject::AddEffectMenuItems(CommandManager *c,
|
||||
void AudacityProject::AddEffectMenuItemGroup(CommandManager *c,
|
||||
const wxArrayString & names,
|
||||
const PluginIDList & plugs,
|
||||
const wxArrayInt & flags,
|
||||
const std::vector<CommandFlag> & flags,
|
||||
bool isDefault)
|
||||
{
|
||||
int namesCnt = (int) names.GetCount();
|
||||
@ -1609,7 +1610,7 @@ void AudacityProject::RebuildOtherMenus()
|
||||
}
|
||||
}
|
||||
|
||||
int AudacityProject::GetFocusedFrame()
|
||||
CommandFlag AudacityProject::GetFocusedFrame()
|
||||
{
|
||||
wxWindow *w = FindFocus();
|
||||
|
||||
@ -1629,16 +1630,16 @@ int AudacityProject::GetFocusedFrame()
|
||||
w = w->GetParent();
|
||||
}
|
||||
|
||||
return 0;
|
||||
return AlwaysEnabledFlag;
|
||||
}
|
||||
|
||||
wxUint32 AudacityProject::GetUpdateFlags()
|
||||
CommandFlag AudacityProject::GetUpdateFlags()
|
||||
{
|
||||
// This method determines all of the flags that determine whether
|
||||
// certain menu items and commands should be enabled or disabled,
|
||||
// and returns them in a bitfield. Note that if none of the flags
|
||||
// have changed, it's not necessary to even check for updates.
|
||||
wxUint32 flags = 0;
|
||||
auto flags = AlwaysEnabledFlag;
|
||||
|
||||
if (!gAudioIO->IsAudioTokenActive(GetAudioIOToken()))
|
||||
flags |= AudioIONotBusyFlag;
|
||||
@ -1767,8 +1768,8 @@ wxUint32 AudacityProject::GetUpdateFlags()
|
||||
|
||||
void AudacityProject::SelectAllIfNone()
|
||||
{
|
||||
wxUint32 flags = GetUpdateFlags();
|
||||
if(((flags & TracksSelectedFlag) ==0) ||
|
||||
auto flags = GetUpdateFlags();
|
||||
if(!(flags & TracksSelectedFlag) ||
|
||||
(mViewInfo.selectedRegion.isPoint()))
|
||||
OnSelectAll();
|
||||
}
|
||||
@ -1850,17 +1851,17 @@ void AudacityProject::UpdateMenus(bool checkActive)
|
||||
if (checkActive && !IsActive())
|
||||
return;
|
||||
|
||||
wxUint32 flags = GetUpdateFlags();
|
||||
wxUint32 flags2 = flags;
|
||||
auto flags = GetUpdateFlags();
|
||||
auto flags2 = flags;
|
||||
|
||||
// We can enable some extra items if we have select-all-on-none.
|
||||
//EXPLAIN-ME: Why is this here rather than in GetUpdateFlags()?
|
||||
if (mSelectAllOnNone)
|
||||
{
|
||||
if ((flags & TracksExistFlag) != 0)
|
||||
if ((flags & TracksExistFlag))
|
||||
{
|
||||
flags2 |= TracksSelectedFlag;
|
||||
if ((flags & WaveTracksExistFlag) != 0 )
|
||||
if ((flags & WaveTracksExistFlag))
|
||||
{
|
||||
flags2 |= TimeSelectedFlag
|
||||
| WaveTracksSelectedFlag
|
||||
@ -1875,21 +1876,21 @@ void AudacityProject::UpdateMenus(bool checkActive)
|
||||
return;
|
||||
mLastFlags = flags;
|
||||
|
||||
mCommandManager.EnableUsingFlags(flags2 , 0xFFFFFFFF);
|
||||
mCommandManager.EnableUsingFlags(flags2 , NoFlagsSpecifed);
|
||||
|
||||
// With select-all-on-none, some items that we don't want enabled may have
|
||||
// been enabled, since we changed the flags. Here we manually disable them.
|
||||
if (mSelectAllOnNone)
|
||||
{
|
||||
if ((flags & TracksSelectedFlag) == 0)
|
||||
if (!(flags & TracksSelectedFlag))
|
||||
{
|
||||
mCommandManager.Enable(wxT("SplitCut"), false);
|
||||
|
||||
if ((flags & WaveTracksSelectedFlag) == 0)
|
||||
if (!(flags & WaveTracksSelectedFlag))
|
||||
{
|
||||
mCommandManager.Enable(wxT("Split"), false);
|
||||
}
|
||||
if ((flags & TimeSelectedFlag) == 0)
|
||||
if (!(flags & TimeSelectedFlag))
|
||||
{
|
||||
mCommandManager.Enable(wxT("ExportSel"), false);
|
||||
mCommandManager.Enable(wxT("SplitNew"), false);
|
||||
@ -2690,6 +2691,9 @@ void AudacityProject::NextFrame()
|
||||
case BotDockHasFocus:
|
||||
mToolManager->GetTopDock()->SetFocus();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2708,6 +2712,9 @@ void AudacityProject::PrevFrame()
|
||||
case BotDockHasFocus:
|
||||
mTrackPanel->SetFocus();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
14
src/Menus.h
14
src/Menus.h
@ -24,17 +24,21 @@
|
||||
private:
|
||||
void CreateMenusAndCommands();
|
||||
|
||||
void PopulateEffectsMenu(CommandManager *c, EffectType type, int batchflags, int realflags);
|
||||
void AddEffectMenuItems(CommandManager *c, EffectPlugs & plugs, int batchflags, int realflags, bool isDefault);
|
||||
void AddEffectMenuItemGroup(CommandManager *c, const wxArrayString & names, const PluginIDList & plugs, const wxArrayInt & flags, bool isDefault);
|
||||
void PopulateEffectsMenu(CommandManager *c, EffectType type,
|
||||
CommandFlag batchflags, CommandFlag realflags);
|
||||
void AddEffectMenuItems(CommandManager *c, EffectPlugs & plugs,
|
||||
CommandFlag batchflags, CommandFlag realflags, bool isDefault);
|
||||
void AddEffectMenuItemGroup(CommandManager *c, const wxArrayString & names,
|
||||
const PluginIDList & plugs,
|
||||
const std::vector<CommandFlag> & flags, bool isDefault);
|
||||
void CreateRecentFilesMenu(CommandManager *c);
|
||||
void ModifyUndoMenuItems();
|
||||
void ModifyToolbarMenus();
|
||||
// Calls ModifyToolbarMenus() on all projects
|
||||
void ModifyAllProjectToolbarMenus();
|
||||
|
||||
int GetFocusedFrame();
|
||||
wxUint32 GetUpdateFlags();
|
||||
CommandFlag GetFocusedFrame();
|
||||
CommandFlag GetUpdateFlags();
|
||||
|
||||
double NearestZeroCrossing(double t0);
|
||||
|
||||
|
@ -243,10 +243,31 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void SetScrollbar(int position, int thumbSize,
|
||||
int range, int pageSize,
|
||||
bool refresh = true) override;
|
||||
|
||||
private:
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
void ScrollBar::SetScrollbar(int position, int thumbSize,
|
||||
int range, int pageSize,
|
||||
bool refresh)
|
||||
{
|
||||
// Mitigate flashing of scrollbars by refreshing only when something really changes.
|
||||
|
||||
auto changed =
|
||||
position != GetThumbPosition() ||
|
||||
thumbSize != GetThumbSize() ||
|
||||
range != GetRange() ||
|
||||
pageSize != GetPageSize();
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
wxScrollBar::SetScrollbar(position, thumbSize, range, pageSize, refresh);
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(ScrollBar, wxScrollBar)
|
||||
EVT_SET_FOCUS(ScrollBar::OnSetFocus)
|
||||
END_EVENT_TABLE()
|
||||
@ -933,14 +954,18 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
|
||||
mCursorOverlay = std::make_unique<EditCursorOverlay>(this);
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
// This must follow construction of *mIndicatorOverlay, because it must
|
||||
// attach its timer event handler later (so that its handler is invoked
|
||||
// earlier)
|
||||
mScrubOverlay = std::make_unique<ScrubbingOverlay>(this);
|
||||
mScrubber = std::make_unique<Scrubber>(this);
|
||||
#endif
|
||||
|
||||
// This must follow construction of *mScrubOverlay, because it must
|
||||
// More order dependencies here...
|
||||
// This must follow construction of *mIndicatorOverlay, because it must
|
||||
// attach its timer event handler later (so that its handler is invoked
|
||||
// earlier)
|
||||
mPlaybackScroller = std::make_unique<PlaybackScroller>(this);
|
||||
|
||||
// This must follow construction of *mPlaybackScroller,
|
||||
// because it must
|
||||
// attach its timer event handler later (so that its handler is invoked
|
||||
// earlier)
|
||||
this->Connect(EVT_TRACK_PANEL_TIMER,
|
||||
@ -1771,7 +1796,6 @@ void AudacityProject::FixScrollbars()
|
||||
|
||||
mHsbar->SetScrollbar(scaledSbarH + offset, scaledSbarScreen, scaledSbarTotal,
|
||||
scaledSbarScreen, TRUE);
|
||||
mHsbar->Refresh();
|
||||
}
|
||||
|
||||
// Vertical scrollbar
|
||||
@ -1779,7 +1803,6 @@ void AudacityProject::FixScrollbars()
|
||||
panelHeight / mViewInfo.scrollStep,
|
||||
totalHeight / mViewInfo.scrollStep,
|
||||
panelHeight / mViewInfo.scrollStep, TRUE);
|
||||
mVsbar->Refresh();
|
||||
|
||||
if (refresh || (rescroll &&
|
||||
(GetScreenEndTime() - mViewInfo.h) < mViewInfo.total)) {
|
||||
@ -2049,11 +2072,12 @@ void AudacityProject::OnScroll(wxScrollEvent & WXUNUSED(event))
|
||||
/// Determines if flags for command are compatible with current state.
|
||||
/// If not, then try some recovery action to make it so.
|
||||
/// @return whether compatible or not after any actions taken.
|
||||
bool AudacityProject::TryToMakeActionAllowed( wxUint32 & flags, wxUint32 flagsRqd, wxUint32 mask )
|
||||
bool AudacityProject::TryToMakeActionAllowed
|
||||
( CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask )
|
||||
{
|
||||
bool bAllowed;
|
||||
|
||||
if( flags == 0 )
|
||||
if( !flags )
|
||||
flags = GetUpdateFlags();
|
||||
|
||||
bAllowed = ((flags & mask) == (flagsRqd & mask));
|
||||
@ -2065,12 +2089,12 @@ bool AudacityProject::TryToMakeActionAllowed( wxUint32 & flags, wxUint32 flagsRq
|
||||
if( !mSelectAllOnNone )
|
||||
return false;
|
||||
|
||||
wxUint32 MissingFlags = (flags & ~flagsRqd) & mask;
|
||||
auto MissingFlags = (flags & ~flagsRqd) & mask;
|
||||
|
||||
// IF selecting all audio won't do any good, THEN return with failure.
|
||||
if( (flags & WaveTracksExistFlag) == 0 )
|
||||
if( !(flags & WaveTracksExistFlag) )
|
||||
return false;
|
||||
if( (MissingFlags & ~( TimeSelectedFlag | WaveTracksSelectedFlag))!=0)
|
||||
if( (MissingFlags & ~( TimeSelectedFlag | WaveTracksSelectedFlag)) )
|
||||
return false;
|
||||
|
||||
OnSelectAll();
|
||||
@ -2084,7 +2108,7 @@ void AudacityProject::OnMenu(wxCommandEvent & event)
|
||||
|
||||
bool handled = mCommandManager.HandleMenuID(event.GetId(),
|
||||
GetUpdateFlags(),
|
||||
0xFFFFFFFF);
|
||||
NoFlagsSpecifed);
|
||||
|
||||
if (handled)
|
||||
event.Skip(false);
|
||||
@ -5315,3 +5339,46 @@ int AudacityProject::GetEstimatedRecordingMinsLeftOnDisk() {
|
||||
int iRecMins = (int)(dRecTime / 60.0);
|
||||
return iRecMins;
|
||||
}
|
||||
|
||||
AudacityProject::PlaybackScroller::PlaybackScroller(AudacityProject *project)
|
||||
: mProject(project)
|
||||
{
|
||||
mProject->Connect(EVT_TRACK_PANEL_TIMER,
|
||||
wxCommandEventHandler(PlaybackScroller::OnTimer),
|
||||
NULL,
|
||||
this);
|
||||
}
|
||||
|
||||
AudacityProject::PlaybackScroller::~PlaybackScroller()
|
||||
{
|
||||
mProject->Disconnect(EVT_TRACK_PANEL_TIMER,
|
||||
wxCommandEventHandler(PlaybackScroller::OnTimer),
|
||||
NULL,
|
||||
this);
|
||||
}
|
||||
|
||||
void AudacityProject::PlaybackScroller::OnTimer(wxCommandEvent &event)
|
||||
{
|
||||
// Let other listeners get the notification
|
||||
event.Skip();
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
if (mActive && mProject->IsAudioActive())
|
||||
{
|
||||
// Pan the view, so that we center the play indicator.
|
||||
|
||||
ViewInfo &viewInfo = mProject->GetViewInfo();
|
||||
TrackPanel *const trackPanel = mProject->GetTrackPanel();
|
||||
const int posX = viewInfo.TimeToPosition(viewInfo.mRecentStreamTime);
|
||||
int width;
|
||||
trackPanel->GetTracksUsableArea(&width, NULL);
|
||||
const int deltaX = posX - width / 2;
|
||||
viewInfo.h =
|
||||
viewInfo.OffsetTimeByPixels(viewInfo.h, deltaX, true);
|
||||
if (!viewInfo.bScrollBeyondZero)
|
||||
// Can't scroll too far left
|
||||
viewInfo.h = std::max(0.0, viewInfo.h);
|
||||
trackPanel->Refresh(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -504,7 +504,8 @@ public:
|
||||
void OnAudioIONewBlockFiles(const AutoSaveFile & blockFileLog) override;
|
||||
|
||||
// Command Handling
|
||||
bool TryToMakeActionAllowed( wxUint32 & flags, wxUint32 flagsRqd, wxUint32 mask );
|
||||
bool TryToMakeActionAllowed
|
||||
( CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask );
|
||||
|
||||
///Prevents DELETE from external thread - for e.g. use of GetActiveProject
|
||||
static void AllProjectsDeleteLock();
|
||||
@ -580,7 +581,7 @@ public:
|
||||
|
||||
CommandManager mCommandManager;
|
||||
|
||||
wxUint32 mLastFlags;
|
||||
CommandFlag mLastFlags;
|
||||
|
||||
// Window elements
|
||||
|
||||
@ -720,6 +721,28 @@ public:
|
||||
const Scrubber &GetScrubber() const { return *mScrubber; }
|
||||
#endif
|
||||
|
||||
class PlaybackScroller final : public wxEvtHandler
|
||||
{
|
||||
public:
|
||||
explicit PlaybackScroller(AudacityProject *project);
|
||||
~PlaybackScroller();
|
||||
|
||||
void Activate(bool active)
|
||||
{
|
||||
mActive = active;
|
||||
}
|
||||
|
||||
private:
|
||||
void OnTimer(wxCommandEvent &event);
|
||||
|
||||
AudacityProject *mProject;
|
||||
bool mActive { false };
|
||||
};
|
||||
std::unique_ptr<PlaybackScroller> mPlaybackScroller;
|
||||
|
||||
public:
|
||||
PlaybackScroller &GetPlaybackScroller() { return *mPlaybackScroller; }
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
8321
src/ThemeAsCeeCode.h
8321
src/ThemeAsCeeCode.h
File diff suppressed because it is too large
Load Diff
@ -1015,6 +1015,25 @@ void TrackPanel::ScrollDuringDrag()
|
||||
mAutoScrolling = true;
|
||||
mListener->TP_ScrollLeft();
|
||||
}
|
||||
else {
|
||||
// Bug1387: enable autoscroll during drag, if the pointer is at either extreme x
|
||||
// coordinate of the screen, even if that is still within the track area.
|
||||
|
||||
int xx = mMouseMostRecentX, yy = 0;
|
||||
this->ClientToScreen(&xx, &yy);
|
||||
if (xx == 0) {
|
||||
mAutoScrolling = true;
|
||||
mListener->TP_ScrollLeft();
|
||||
}
|
||||
else {
|
||||
int width, height;
|
||||
::wxDisplaySize(&width, &height);
|
||||
if (xx == width - 1) {
|
||||
mAutoScrolling = true;
|
||||
mListener->TP_ScrollRight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mAutoScrolling) {
|
||||
// AS: To keep the selection working properly as we scroll,
|
||||
@ -5901,16 +5920,26 @@ void TrackPanel::OnMouseEvent(wxMouseEvent & event)
|
||||
ReleaseMouse();
|
||||
}
|
||||
|
||||
if (event.Leaving() && !event.ButtonIsDown(wxMOUSE_BTN_ANY))
|
||||
if (event.Leaving())
|
||||
{
|
||||
|
||||
// PRL: was this test really needed? It interfered with my refactoring
|
||||
// that tried to eliminate those enum values.
|
||||
// I think it was never true, that mouse capture was pan or gain sliding,
|
||||
// but no mouse button was down.
|
||||
// if (mMouseCapture != IsPanSliding && mMouseCapture != IsGainSliding)
|
||||
{
|
||||
|
||||
auto buttons =
|
||||
// Bug 1325: button state in Leaving events is unreliable on Mac.
|
||||
// Poll the global state instead.
|
||||
// event.ButtonIsDown(wxMOUSE_BTN_ANY);
|
||||
::wxGetMouseState().ButtonIsDown(wxMOUSE_BTN_ANY);
|
||||
|
||||
if(!buttons) {
|
||||
SetCapturedTrack(NULL);
|
||||
|
||||
#if defined(__WXMAC__)
|
||||
|
||||
// We must install the cursor ourselves since the window under
|
||||
// the mouse is no longer this one and wx2.8.12 makes that check.
|
||||
// Should re-evaluate with wx3.
|
||||
|
@ -395,8 +395,8 @@ CommandManager::CommandManager():
|
||||
mCurrentID(17000),
|
||||
mCurrentMenuName(COMMAND),
|
||||
mCurrentMenu(NULL),
|
||||
mDefaultFlags(0),
|
||||
mDefaultMask(0)
|
||||
mDefaultFlags(AlwaysEnabledFlag),
|
||||
mDefaultMask(AlwaysEnabledFlag)
|
||||
{
|
||||
mbSeparatorAllowed = false;
|
||||
}
|
||||
@ -647,15 +647,15 @@ void CommandManager::AddCheck(const wxChar *name,
|
||||
const CommandFunctorPointer &callback,
|
||||
int checkmark)
|
||||
{
|
||||
AddItem(name, label, callback, wxT(""), (unsigned int)NoFlagsSpecifed, (unsigned int)NoFlagsSpecifed, checkmark);
|
||||
AddItem(name, label, callback, wxT(""), NoFlagsSpecifed, NoFlagsSpecifed, checkmark);
|
||||
}
|
||||
|
||||
void CommandManager::AddCheck(const wxChar *name,
|
||||
const wxChar *label,
|
||||
const CommandFunctorPointer &callback,
|
||||
int checkmark,
|
||||
unsigned int flags,
|
||||
unsigned int mask)
|
||||
CommandFlag flags,
|
||||
CommandMask mask)
|
||||
{
|
||||
AddItem(name, label, callback, wxT(""), flags, mask, checkmark);
|
||||
}
|
||||
@ -663,8 +663,8 @@ void CommandManager::AddCheck(const wxChar *name,
|
||||
void CommandManager::AddItem(const wxChar *name,
|
||||
const wxChar *label,
|
||||
const CommandFunctorPointer &callback,
|
||||
unsigned int flags,
|
||||
unsigned int mask)
|
||||
CommandFlag flags,
|
||||
CommandMask mask)
|
||||
{
|
||||
AddItem(name, label, callback, wxT(""), flags, mask);
|
||||
}
|
||||
@ -673,8 +673,8 @@ void CommandManager::AddItem(const wxChar *name,
|
||||
const wxChar *label_in,
|
||||
const CommandFunctorPointer &callback,
|
||||
const wxChar *accel,
|
||||
unsigned int flags,
|
||||
unsigned int mask,
|
||||
CommandFlag flags,
|
||||
CommandMask mask,
|
||||
int checkmark)
|
||||
{
|
||||
CommandListEntry *entry = NewIdentifier(name, label_in, accel, CurrentMenu(), callback, false, 0, 0);
|
||||
@ -726,8 +726,8 @@ void CommandManager::AddItemList(const wxString & name,
|
||||
void CommandManager::AddCommand(const wxChar *name,
|
||||
const wxChar *label,
|
||||
const CommandFunctorPointer &callback,
|
||||
unsigned int flags,
|
||||
unsigned int mask)
|
||||
CommandFlag flags,
|
||||
CommandMask mask)
|
||||
{
|
||||
AddCommand(name, label, callback, wxT(""), flags, mask);
|
||||
}
|
||||
@ -736,8 +736,8 @@ void CommandManager::AddCommand(const wxChar *name,
|
||||
const wxChar *label_in,
|
||||
const CommandFunctorPointer &callback,
|
||||
const wxChar *accel,
|
||||
unsigned int flags,
|
||||
unsigned int mask)
|
||||
CommandFlag flags,
|
||||
CommandMask mask)
|
||||
{
|
||||
NewIdentifier(name, label_in, accel, NULL, callback, false, 0, 0);
|
||||
|
||||
@ -755,8 +755,8 @@ void CommandManager::AddGlobalCommand(const wxChar *name,
|
||||
|
||||
entry->enabled = false;
|
||||
entry->isGlobal = true;
|
||||
entry->flags = 0;
|
||||
entry->mask = 0;
|
||||
entry->flags = AlwaysEnabledFlag;
|
||||
entry->mask = AlwaysEnabledFlag;
|
||||
}
|
||||
|
||||
void CommandManager::AddSeparator()
|
||||
@ -978,13 +978,13 @@ void CommandManager::Enable(const wxString &name, bool enabled)
|
||||
Enable(entry, enabled);
|
||||
}
|
||||
|
||||
void CommandManager::EnableUsingFlags(wxUint32 flags, wxUint32 mask)
|
||||
void CommandManager::EnableUsingFlags(CommandFlag flags, CommandMask mask)
|
||||
{
|
||||
for(const auto &entry : mCommandList) {
|
||||
if (entry->multi && entry->index != 0)
|
||||
continue;
|
||||
|
||||
wxUint32 combinedMask = (mask & entry->mask);
|
||||
auto combinedMask = (mask & entry->mask);
|
||||
if (combinedMask) {
|
||||
bool enable = ((flags & combinedMask) ==
|
||||
(entry->flags & combinedMask));
|
||||
@ -1038,15 +1038,15 @@ void CommandManager::SetKeyFromIndex(int i, const wxString &key)
|
||||
entry->key = KeyStringNormalize(key);
|
||||
}
|
||||
|
||||
void CommandManager::TellUserWhyDisallowed( wxUint32 flagsGot, wxUint32 flagsRequired )
|
||||
void CommandManager::TellUserWhyDisallowed( CommandFlag flagsGot, CommandMask flagsRequired )
|
||||
{
|
||||
// The default string for 'reason' is a catch all. I hope it won't ever be seen
|
||||
// and that we will get something more specific.
|
||||
wxString reason = _("There was a problem with your last action. If you think\nthis is a bug, please tell us exactly where it occurred.");
|
||||
|
||||
wxUint32 missingFlags = flagsRequired & (~flagsGot );
|
||||
auto missingFlags = flagsRequired & (~flagsGot );
|
||||
if( missingFlags & AudioIONotBusyFlag )
|
||||
reason= _("You can only do this when playing and recording are\n stopped. (Pausing is not sufficient.)");
|
||||
reason = _("You can only do this when playing and recording are\n stopped. (Pausing is not sufficient.)");
|
||||
else if( missingFlags & StereoRequiredFlag )
|
||||
reason = _("You must first select some stereo audio for this\n to use. (You cannot use this with mono.)");
|
||||
else if( missingFlags & TimeSelectedFlag )
|
||||
@ -1081,7 +1081,7 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent &
|
||||
// enable them temporarily and then disable them again after handling.
|
||||
// LL: Why do they need to be disabled???
|
||||
entry->enabled = true;
|
||||
bool ret = HandleCommandEntry(entry, 0xffffffff, 0xffffffff, &evt);
|
||||
bool ret = HandleCommandEntry(entry, NoFlagsSpecifed, NoFlagsSpecifed, &evt);
|
||||
entry->enabled = false;
|
||||
return ret;
|
||||
}
|
||||
@ -1094,7 +1094,7 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent &
|
||||
return false;
|
||||
}
|
||||
|
||||
wxUint32 flags = project->GetUpdateFlags();
|
||||
auto flags = project->GetUpdateFlags();
|
||||
|
||||
wxKeyEvent temp = evt;
|
||||
|
||||
@ -1105,12 +1105,12 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent &
|
||||
return true;
|
||||
}
|
||||
|
||||
return HandleCommandEntry(entry, flags, 0xffffffff, &temp);
|
||||
return HandleCommandEntry(entry, flags, NoFlagsSpecifed, &temp);
|
||||
}
|
||||
|
||||
if (type == wxEVT_KEY_UP && entry->wantKeyup)
|
||||
{
|
||||
return HandleCommandEntry(entry, flags, 0xffffffff, &temp);
|
||||
return HandleCommandEntry(entry, flags, NoFlagsSpecifed, &temp);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -1120,12 +1120,13 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent &
|
||||
/// returning true iff successful. If you pass any flags,
|
||||
///the command won't be executed unless the flags are compatible
|
||||
///with the command's flags.
|
||||
bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, wxUint32 flags, wxUint32 mask, const wxEvent * evt)
|
||||
bool CommandManager::HandleCommandEntry(const CommandListEntry * entry,
|
||||
CommandFlag flags, CommandMask mask, const wxEvent * evt)
|
||||
{
|
||||
if (!entry || !entry->enabled)
|
||||
return false;
|
||||
|
||||
wxUint32 combinedMask = (mask & entry->mask);
|
||||
auto combinedMask = (mask & entry->mask);
|
||||
if (combinedMask) {
|
||||
|
||||
AudacityProject * proj;
|
||||
@ -1154,7 +1155,7 @@ bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, wxUint32
|
||||
///CommandManagerListener function. If you pass any flags,
|
||||
///the command won't be executed unless the flags are compatible
|
||||
///with the command's flags.
|
||||
bool CommandManager::HandleMenuID(int id, wxUint32 flags, wxUint32 mask)
|
||||
bool CommandManager::HandleMenuID(int id, CommandFlag flags, CommandMask mask)
|
||||
{
|
||||
CommandListEntry *entry = mCommandIDHash[id];
|
||||
return HandleCommandEntry( entry, flags, mask );
|
||||
@ -1163,7 +1164,7 @@ bool CommandManager::HandleMenuID(int id, wxUint32 flags, wxUint32 mask)
|
||||
/// HandleTextualCommand() allows us a limitted version of script/batch
|
||||
/// behavior, since we can get from a string command name to the actual
|
||||
/// code to run.
|
||||
bool CommandManager::HandleTextualCommand(wxString & Str, wxUint32 flags, wxUint32 mask)
|
||||
bool CommandManager::HandleTextualCommand(wxString & Str, CommandFlag flags, CommandMask mask)
|
||||
{
|
||||
// Linear search for now...
|
||||
for (const auto &entry : mCommandList)
|
||||
@ -1410,14 +1411,14 @@ void CommandManager::WriteXML(XMLWriter &xmlFile)
|
||||
xmlFile.EndTag(wxT("audacitykeyboard"));
|
||||
}
|
||||
|
||||
void CommandManager::SetDefaultFlags(wxUint32 flags, wxUint32 mask)
|
||||
void CommandManager::SetDefaultFlags(CommandFlag flags, CommandMask mask)
|
||||
{
|
||||
mDefaultFlags = flags;
|
||||
mDefaultMask = mask;
|
||||
}
|
||||
|
||||
void CommandManager::SetCommandFlags(const wxString &name,
|
||||
wxUint32 flags, wxUint32 mask)
|
||||
CommandFlag flags, CommandMask mask)
|
||||
{
|
||||
CommandListEntry *entry = mCommandNameHash[name];
|
||||
if (entry) {
|
||||
@ -1427,7 +1428,7 @@ void CommandManager::SetCommandFlags(const wxString &name,
|
||||
}
|
||||
|
||||
void CommandManager::SetCommandFlags(const wxChar **names,
|
||||
wxUint32 flags, wxUint32 mask)
|
||||
CommandFlag flags, CommandMask mask)
|
||||
{
|
||||
const wxChar **nptr = names;
|
||||
while(*nptr) {
|
||||
@ -1436,7 +1437,7 @@ void CommandManager::SetCommandFlags(const wxChar **names,
|
||||
}
|
||||
}
|
||||
|
||||
void CommandManager::SetCommandFlags(wxUint32 flags, wxUint32 mask, ...)
|
||||
void CommandManager::SetCommandFlags(CommandFlag flags, CommandMask mask, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, mask);
|
||||
|
@ -66,8 +66,8 @@ struct CommandListEntry
|
||||
bool skipKeydown;
|
||||
bool wantKeyup;
|
||||
bool isGlobal;
|
||||
wxUint32 flags;
|
||||
wxUint32 mask;
|
||||
CommandFlag flags;
|
||||
CommandMask mask;
|
||||
};
|
||||
|
||||
using MenuBarList = std::vector < MenuBarListEntry >;
|
||||
@ -132,21 +132,21 @@ class AUDACITY_DLL_API CommandManager final : public XMLTagHandler
|
||||
const wxChar *label,
|
||||
const CommandFunctorPointer &callback,
|
||||
int checkmark,
|
||||
unsigned int flags,
|
||||
unsigned int mask);
|
||||
CommandFlag flags,
|
||||
CommandMask mask);
|
||||
|
||||
void AddItem(const wxChar *name,
|
||||
const wxChar *label,
|
||||
const CommandFunctorPointer &callback,
|
||||
unsigned int flags = NoFlagsSpecifed,
|
||||
unsigned int mask = NoFlagsSpecifed);
|
||||
CommandFlag flags = NoFlagsSpecifed,
|
||||
CommandMask mask = NoFlagsSpecifed);
|
||||
|
||||
void AddItem(const wxChar *name,
|
||||
const wxChar *label_in,
|
||||
const CommandFunctorPointer &callback,
|
||||
const wxChar *accel,
|
||||
unsigned int flags = NoFlagsSpecifed,
|
||||
unsigned int mask = NoFlagsSpecifed,
|
||||
CommandFlag flags = NoFlagsSpecifed,
|
||||
CommandMask mask = NoFlagsSpecifed,
|
||||
int checkmark = -1);
|
||||
|
||||
void AddSeparator();
|
||||
@ -156,15 +156,15 @@ class AUDACITY_DLL_API CommandManager final : public XMLTagHandler
|
||||
void AddCommand(const wxChar *name,
|
||||
const wxChar *label,
|
||||
const CommandFunctorPointer &callback,
|
||||
unsigned int flags = NoFlagsSpecifed,
|
||||
unsigned int mask = NoFlagsSpecifed);
|
||||
CommandFlag flags = NoFlagsSpecifed,
|
||||
CommandMask mask = NoFlagsSpecifed);
|
||||
|
||||
void AddCommand(const wxChar *name,
|
||||
const wxChar *label,
|
||||
const CommandFunctorPointer &callback,
|
||||
const wxChar *accel,
|
||||
unsigned int flags = NoFlagsSpecifed,
|
||||
unsigned int mask = NoFlagsSpecifed);
|
||||
CommandFlag flags = NoFlagsSpecifed,
|
||||
CommandMask mask = NoFlagsSpecifed);
|
||||
|
||||
void AddGlobalCommand(const wxChar *name,
|
||||
const wxChar *label,
|
||||
@ -175,21 +175,21 @@ class AUDACITY_DLL_API CommandManager final : public XMLTagHandler
|
||||
//
|
||||
|
||||
// For NEW items/commands
|
||||
void SetDefaultFlags(wxUint32 flags, wxUint32 mask);
|
||||
wxUint32 GetDefaultFlags() const { return mDefaultFlags; }
|
||||
wxUint32 GetDefaultMask() const { return mDefaultMask; }
|
||||
void SetDefaultFlags(CommandFlag flags, CommandMask mask);
|
||||
CommandFlag GetDefaultFlags() const { return mDefaultFlags; }
|
||||
CommandMask GetDefaultMask() const { return mDefaultMask; }
|
||||
|
||||
void SetCommandFlags(const wxString &name, wxUint32 flags, wxUint32 mask);
|
||||
void SetCommandFlags(const wxString &name, CommandFlag flags, CommandMask mask);
|
||||
void SetCommandFlags(const wxChar **names,
|
||||
wxUint32 flags, wxUint32 mask);
|
||||
CommandFlag flags, CommandMask mask);
|
||||
// Pass multiple command names as const wxChar *, terminated by NULL
|
||||
void SetCommandFlags(wxUint32 flags, wxUint32 mask, ...);
|
||||
void SetCommandFlags(CommandFlag flags, CommandMask mask, ...);
|
||||
|
||||
//
|
||||
// Modifying menus
|
||||
//
|
||||
|
||||
void EnableUsingFlags(wxUint32 flags, wxUint32 mask);
|
||||
void EnableUsingFlags(CommandFlag flags, CommandMask mask);
|
||||
void Enable(const wxString &name, bool enabled);
|
||||
void Check(const wxString &name, bool checked);
|
||||
void Modify(const wxString &name, const wxString &newLabel);
|
||||
@ -208,8 +208,8 @@ class AUDACITY_DLL_API CommandManager final : public XMLTagHandler
|
||||
// "permit" allows filtering even if the active window isn't a child of the project.
|
||||
// Lyrics and MixerTrackCluster classes use it.
|
||||
bool FilterKeyEvent(AudacityProject *project, const wxKeyEvent & evt, bool permit = false);
|
||||
bool HandleMenuID(int id, wxUint32 flags, wxUint32 mask);
|
||||
bool HandleTextualCommand(wxString & Str, wxUint32 flags, wxUint32 mask);
|
||||
bool HandleMenuID(int id, CommandFlag flags, CommandMask mask);
|
||||
bool HandleTextualCommand(wxString & Str, CommandFlag flags, CommandMask mask);
|
||||
|
||||
//
|
||||
// Accessing
|
||||
@ -271,8 +271,8 @@ protected:
|
||||
// Executing commands
|
||||
//
|
||||
|
||||
bool HandleCommandEntry(const CommandListEntry * entry, wxUint32 flags, wxUint32 mask, const wxEvent * evt = NULL);
|
||||
void TellUserWhyDisallowed(wxUint32 flagsGot, wxUint32 flagsRequired);
|
||||
bool HandleCommandEntry(const CommandListEntry * entry, CommandFlag flags, CommandMask mask, const wxEvent * evt = NULL);
|
||||
void TellUserWhyDisallowed(CommandFlag flagsGot, CommandFlag flagsRequired);
|
||||
|
||||
//
|
||||
// Modifying
|
||||
@ -313,8 +313,8 @@ private:
|
||||
wxString mCurrentMenuName;
|
||||
wxMenu * mCurrentMenu;
|
||||
|
||||
wxUint32 mDefaultFlags;
|
||||
wxUint32 mDefaultMask;
|
||||
CommandFlag mDefaultFlags;
|
||||
CommandMask mDefaultMask;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -38,7 +38,7 @@ bool ExecMenuCommand::Apply(CommandExecutionContext context)
|
||||
CommandManager *cmdManager = context.GetProject()->GetCommandManager();
|
||||
|
||||
wxString cmdName = GetString(wxT("CommandName"));
|
||||
wxUint32 cmdFlags = 0; // TODO ?
|
||||
wxUint32 cmdMask = 0;
|
||||
auto cmdFlags = AlwaysEnabledFlag; // TODO ?
|
||||
auto cmdMask = AlwaysEnabledFlag;
|
||||
return cmdManager->HandleTextualCommand(cmdName, cmdFlags, cmdMask);
|
||||
}
|
||||
|
@ -3219,7 +3219,7 @@ void EffectUIHost::OnApply(wxCommandEvent & evt)
|
||||
// Honor the "select all if none" preference...a little hackish, but whatcha gonna do...
|
||||
if (!mIsBatch && mEffect->GetType() != EffectTypeGenerate && mProject->mViewInfo.selectedRegion.isPoint())
|
||||
{
|
||||
wxUint32 flags = 0;
|
||||
auto flags = AlwaysEnabledFlag;
|
||||
bool allowed = mProject->TryToMakeActionAllowed(flags,
|
||||
WaveTracksSelectedFlag | TimeSelectedFlag,
|
||||
WaveTracksSelectedFlag | TimeSelectedFlag);
|
||||
|
@ -738,16 +738,24 @@ void ControlToolBar::OnKeyEvent(wxKeyEvent & event)
|
||||
|
||||
void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt))
|
||||
{
|
||||
if (!CanStopAudioStream())
|
||||
return;
|
||||
auto doubleClicked = mPlay->IsDoubleClicked();
|
||||
mPlay->ClearDoubleClicked();
|
||||
|
||||
StopPlaying();
|
||||
auto p = GetActiveProject();
|
||||
|
||||
AudacityProject *p = GetActiveProject();
|
||||
if (p) p->TP_DisplaySelection();
|
||||
if (doubleClicked)
|
||||
p->GetPlaybackScroller().Activate(true);
|
||||
else {
|
||||
if (!CanStopAudioStream())
|
||||
return;
|
||||
|
||||
PlayDefault();
|
||||
UpdateStatusBar(GetActiveProject());
|
||||
StopPlaying();
|
||||
|
||||
if (p) p->TP_DisplaySelection();
|
||||
|
||||
PlayDefault();
|
||||
UpdateStatusBar(p);
|
||||
}
|
||||
}
|
||||
|
||||
void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
|
||||
@ -778,9 +786,11 @@ void ControlToolBar::StopPlaying(bool stopStream /* = true*/)
|
||||
{
|
||||
AudacityProject *project = GetActiveProject();
|
||||
|
||||
if(project)
|
||||
if(project) {
|
||||
project->GetPlaybackScroller().Activate(false);
|
||||
// Let scrubbing code do some appearance change
|
||||
project->GetScrubber().StopScrubbing();
|
||||
}
|
||||
|
||||
if (!CanStopAudioStream())
|
||||
return;
|
||||
|
@ -474,11 +474,24 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview)
|
||||
// Come here from button clicks only
|
||||
void TranscriptionToolBar::OnPlaySpeed(wxCommandEvent & WXUNUSED(event))
|
||||
{
|
||||
// Let control have precedence over shift
|
||||
const bool cutPreview = mButtons[TTB_PlaySpeed]->WasControlDown();
|
||||
const bool looped = !cutPreview &&
|
||||
mButtons[TTB_PlaySpeed]->WasShiftDown();
|
||||
PlayAtSpeed(looped, cutPreview);
|
||||
auto button = mButtons[TTB_PlaySpeed];
|
||||
|
||||
auto doubleClicked = button->IsDoubleClicked();
|
||||
button->ClearDoubleClicked();
|
||||
|
||||
if (doubleClicked) {
|
||||
GetActiveProject()->GetPlaybackScroller().Activate(true);
|
||||
|
||||
// Pop up the button
|
||||
SetButton(false, button);
|
||||
}
|
||||
else {
|
||||
// Let control have precedence over shift
|
||||
const bool cutPreview = mButtons[TTB_PlaySpeed]->WasControlDown();
|
||||
const bool looped = !cutPreview &&
|
||||
button->WasShiftDown();
|
||||
PlayAtSpeed(looped, cutPreview);
|
||||
}
|
||||
}
|
||||
|
||||
void TranscriptionToolBar::OnSpeedSlider(wxCommandEvent& WXUNUSED(event))
|
||||
|
@ -8,6 +8,7 @@ Paul Licameli split from TrackPanel.cpp
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "../../Audacity.h"
|
||||
#include "PlayIndicatorOverlay.h"
|
||||
|
||||
#include "../../AColor.h"
|
||||
|
@ -200,7 +200,7 @@ void Scrubber::MarkScrubStart(
|
||||
// needed for the decision to start scrubbing later when handling
|
||||
// drag events.
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
mSmoothScrollingScrub = smoothScrolling;
|
||||
SetScrollScrubbing (smoothScrolling);
|
||||
#endif
|
||||
mAlwaysSeeking = alwaysSeeking;
|
||||
mScrubStartPosition = xx;
|
||||
@ -356,25 +356,6 @@ void Scrubber::ContinueScrubbing()
|
||||
if (mScrubSpeedDisplayCountdown > 0)
|
||||
--mScrubSpeedDisplayCountdown;
|
||||
}
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
if (mSmoothScrollingScrub) {
|
||||
// Pan the view, so that we center the play indicator.
|
||||
|
||||
ViewInfo &viewInfo = mProject->GetViewInfo();
|
||||
TrackPanel *const trackPanel = mProject->GetTrackPanel();
|
||||
const int posX = viewInfo.TimeToPosition(viewInfo.mRecentStreamTime);
|
||||
int width;
|
||||
trackPanel->GetTracksUsableArea(&width, NULL);
|
||||
const int deltaX = posX - width / 2;
|
||||
viewInfo.h =
|
||||
viewInfo.OffsetTimeByPixels(viewInfo.h, deltaX, true);
|
||||
if (!viewInfo.bScrollBeyondZero)
|
||||
// Can't scroll too far left
|
||||
viewInfo.h = std::max(0.0, viewInfo.h);
|
||||
trackPanel->Refresh(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Scrubber::StopScrubbing()
|
||||
@ -382,7 +363,7 @@ void Scrubber::StopScrubbing()
|
||||
UncheckAllMenuItems();
|
||||
|
||||
mScrubStartPosition = -1;
|
||||
mSmoothScrollingScrub = false;
|
||||
SetScrollScrubbing (false);
|
||||
|
||||
if (!IsScrubbing())
|
||||
{
|
||||
@ -393,6 +374,12 @@ void Scrubber::StopScrubbing()
|
||||
}
|
||||
}
|
||||
|
||||
void Scrubber::SetScrollScrubbing(bool scrollScrubbing)
|
||||
{
|
||||
mSmoothScrollingScrub = scrollScrubbing;
|
||||
mProject->GetPlaybackScroller().Activate(scrollScrubbing);
|
||||
}
|
||||
|
||||
bool Scrubber::IsScrubbing() const
|
||||
{
|
||||
if (mScrubToken <= 0)
|
||||
@ -403,6 +390,7 @@ bool Scrubber::IsScrubbing() const
|
||||
const_cast<Scrubber&>(*this).mScrubToken = -1;
|
||||
const_cast<Scrubber&>(*this).mScrubStartPosition = -1;
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
// Don't call SetScrollScrubbing
|
||||
const_cast<Scrubber&>(*this).mSmoothScrollingScrub = false;
|
||||
#endif
|
||||
return false;
|
||||
@ -557,7 +545,6 @@ void ScrubbingOverlay::Draw
|
||||
dc.DrawText(mLastScrubSpeedText, mLastScrubRect.GetX(), mLastScrubRect.GetY());
|
||||
}
|
||||
|
||||
|
||||
void ScrubbingOverlay::OnTimer(wxCommandEvent &event)
|
||||
{
|
||||
// Let other listeners get the notification
|
||||
@ -666,7 +653,7 @@ void Scrubber::DoScrub(bool scroll, bool seek)
|
||||
MarkScrubStart(xx, scroll, seek);
|
||||
}
|
||||
else if(!match) {
|
||||
mSmoothScrollingScrub = scroll;
|
||||
SetScrollScrubbing(scroll);
|
||||
mAlwaysSeeking = seek;
|
||||
UncheckAllMenuItems();
|
||||
CheckMenuItem();
|
||||
|
@ -54,8 +54,11 @@ public:
|
||||
bool HasStartedScrubbing() const
|
||||
{ return GetScrubStartPosition() >= 0; }
|
||||
bool IsScrubbing() const;
|
||||
|
||||
bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing()
|
||||
{ return mSmoothScrollingScrub; }
|
||||
void SetScrollScrubbing(bool scrollScrubbing);
|
||||
|
||||
bool IsAlwaysSeeking() const
|
||||
{ return mAlwaysSeeking; }
|
||||
|
||||
|
@ -407,6 +407,8 @@ void AButton::OnMouseEvent(wxMouseEvent & event)
|
||||
if (mEnabled && event.IsButton()) {
|
||||
if (event.ButtonIsDown(wxMOUSE_BTN_ANY)) {
|
||||
mIsClicking = true;
|
||||
if (event.ButtonDClick())
|
||||
mIsDoubleClicked = true;
|
||||
if( !HasCapture() )
|
||||
CaptureMouse();
|
||||
}
|
||||
|
@ -109,6 +109,11 @@ class AButton final : public wxWindow {
|
||||
bool WasControlDown(); // returns true if control was held down
|
||||
// the last time the button was clicked
|
||||
bool IsDown(){ return mButtonIsDown;}
|
||||
|
||||
// Double click is detected, but not automatically cleared.
|
||||
bool IsDoubleClicked() const { return mIsDoubleClicked; }
|
||||
void ClearDoubleClicked() { mIsDoubleClicked = false; }
|
||||
|
||||
void SetButtonToggles( bool toggler ){ mToggle = toggler;}
|
||||
void Toggle(){ mButtonIsDown ? PopUp() : PushDown();}
|
||||
void Click();
|
||||
@ -157,6 +162,7 @@ class AButton final : public wxWindow {
|
||||
bool mIsClicking;
|
||||
bool mEnabled;
|
||||
bool mUseDisabledAsDownHiliteImage;
|
||||
bool mIsDoubleClicked {};
|
||||
|
||||
struct ImageArr { ImageRoll mArr[4]; };
|
||||
std::vector<ImageArr> mImages;
|
||||
|
@ -467,6 +467,15 @@ void Meter::OnPaint(wxPaintEvent & WXUNUSED(event))
|
||||
// MixerTrackCluster style has no icon or L/R labels
|
||||
if (mStyle != MixerTrackCluster)
|
||||
{
|
||||
bool highlight = InIcon();
|
||||
if (highlight) {
|
||||
auto rect = mIconRect;
|
||||
rect.Inflate(gap, gap);
|
||||
wxColour colour(247, 247, 247);
|
||||
dc.SetBrush(colour);
|
||||
dc.SetPen(colour );
|
||||
dc.DrawRectangle(rect);
|
||||
}
|
||||
dc.DrawBitmap(*mIcon, mIconRect.GetPosition(), true);
|
||||
dc.SetFont(GetFont());
|
||||
dc.DrawText(mLeftText, mLeftTextPos.x, mLeftTextPos.y);
|
||||
@ -668,8 +677,22 @@ void Meter::OnSize(wxSizeEvent & WXUNUSED(event))
|
||||
mLayoutValid = false;
|
||||
}
|
||||
|
||||
bool Meter::InIcon(wxMouseEvent *pEvent) const
|
||||
{
|
||||
auto point = pEvent ? pEvent->GetPosition() : ScreenToClient(::wxGetMousePosition());
|
||||
return mIconRect.Contains(point);
|
||||
}
|
||||
|
||||
void Meter::OnMouse(wxMouseEvent &evt)
|
||||
{
|
||||
bool shouldHighlight = InIcon(&evt);
|
||||
if ((evt.GetEventType() == wxEVT_MOTION || evt.Entering() || evt.Leaving()) &&
|
||||
(mHighlighted != shouldHighlight)) {
|
||||
mHighlighted = shouldHighlight;
|
||||
mLayoutValid = false;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
if (mStyle == MixerTrackCluster) // MixerTrackCluster style has no menu.
|
||||
return;
|
||||
|
||||
@ -688,7 +711,7 @@ void Meter::OnMouse(wxMouseEvent &evt)
|
||||
#endif
|
||||
|
||||
if (evt.RightDown() ||
|
||||
(evt.ButtonDown() && mIconRect.Contains(evt.m_x, evt.m_y)))
|
||||
(evt.ButtonDown() && InIcon(&evt)))
|
||||
{
|
||||
wxMenu menu;
|
||||
// Note: these should be kept in the same order as the enum
|
||||
|
@ -186,6 +186,7 @@ class Meter final : public wxPanel
|
||||
void OnErase(wxEraseEvent &evt);
|
||||
void OnPaint(wxPaintEvent &evt);
|
||||
void OnSize(wxSizeEvent &evt);
|
||||
bool InIcon(wxMouseEvent *pEvent = nullptr) const;
|
||||
void OnMouse(wxMouseEvent &evt);
|
||||
void OnKeyDown(wxKeyEvent &evt);
|
||||
void OnKeyUp(wxKeyEvent &evt);
|
||||
@ -280,6 +281,8 @@ class Meter final : public wxPanel
|
||||
|
||||
friend class MeterAx;
|
||||
|
||||
bool mHighlighted {};
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
@ -84,6 +84,9 @@ array of Ruler::Label.
|
||||
#include "../Snap.h"
|
||||
#include "../tracks/ui/Scrubbing.h"
|
||||
|
||||
//#define SCRUB_ABOVE
|
||||
#define RULER_DOUBLE_CLICK
|
||||
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
@ -1774,6 +1777,9 @@ BEGIN_EVENT_TABLE(AdornedRulerPanel, wxPanel)
|
||||
EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll)
|
||||
EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion)
|
||||
|
||||
// Scrub bar menu commands
|
||||
EVT_MENU(OnShowHideScrubbingID, AdornedRulerPanel::OnToggleScrubbing)
|
||||
|
||||
END_EVENT_TABLE()
|
||||
|
||||
AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent,
|
||||
@ -1869,7 +1875,7 @@ namespace {
|
||||
bool ReadScrubEnabledPref()
|
||||
{
|
||||
bool result {};
|
||||
gPrefs->Read(scrubEnabledPrefName, &result, true);
|
||||
gPrefs->Read(scrubEnabledPrefName, &result, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1895,11 +1901,56 @@ void AdornedRulerPanel::UpdatePrefs()
|
||||
// Affected by the last
|
||||
UpdateRects();
|
||||
|
||||
RegenerateTooltips();
|
||||
RegenerateTooltips(mPrevZone);
|
||||
|
||||
mButtonFontSize = -1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
enum { ArrowWidth = 8, ArrowSpacing = 1, ArrowHeight = ArrowWidth / 2 };
|
||||
|
||||
// Find the part of the button rectangle in which you can click the arrow.
|
||||
// It includes the lower right corner.
|
||||
wxRect GetArrowRect(const wxRect &buttonRect)
|
||||
{
|
||||
// Change the following lines to change the size of the hot zone.
|
||||
// Make the hot zone as tall as the button
|
||||
auto width = std::min(
|
||||
std::max(1, buttonRect.GetWidth()) - 1,
|
||||
ArrowWidth + 2 * ArrowSpacing
|
||||
+ 2 // bevel around arrow
|
||||
+ 2 // outline around the bevel
|
||||
);
|
||||
auto height = buttonRect.GetHeight();
|
||||
|
||||
return wxRect {
|
||||
buttonRect.GetRight() + 1 - width,
|
||||
buttonRect.GetBottom() + 1 - height,
|
||||
width, height
|
||||
};
|
||||
}
|
||||
|
||||
wxRect GetTextRect(const wxRect &buttonRect)
|
||||
{
|
||||
auto result = buttonRect;
|
||||
result.width -= GetArrowRect(buttonRect).width;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Compensate for off-by-one problem in the bevel-drawing functions
|
||||
struct Deflator {
|
||||
Deflator(wxRect &rect) : mRect(rect) {
|
||||
--mRect.width;
|
||||
--mRect.height;
|
||||
}
|
||||
~Deflator() {
|
||||
++mRect.width;
|
||||
++mRect.height;
|
||||
}
|
||||
wxRect mRect;
|
||||
};
|
||||
}
|
||||
|
||||
wxFont &AdornedRulerPanel::GetButtonFont() const
|
||||
{
|
||||
if (mButtonFontSize < 0) {
|
||||
@ -1911,12 +1962,21 @@ wxFont &AdornedRulerPanel::GetButtonFont() const
|
||||
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)
|
||||
auto rect = GetTextRect(GetButtonRect(button));
|
||||
auto availableWidth = rect.GetWidth();
|
||||
auto availableHeight = rect.GetHeight();
|
||||
|
||||
// Deduct for outlines, and room to move text
|
||||
// I might deduct 2 more for bevel, but that made the text too small.
|
||||
availableWidth -= 2 + 1;
|
||||
availableHeight -= 2 + 1;
|
||||
|
||||
GetParent()->GetTextExtent(
|
||||
wxGetTranslation(GetPushButtonStrings(button)->label),
|
||||
&width, &height, NULL, NULL, &mButtonFont);
|
||||
done = width < allowableWidth;
|
||||
|
||||
// Yes, < not <= ! Leave at least some room.
|
||||
done = width < availableWidth && height < availableHeight;
|
||||
}
|
||||
mButtonFontSize--;
|
||||
} while (mButtonFontSize > 0 && !done);
|
||||
@ -1930,7 +1990,7 @@ void AdornedRulerPanel::InvalidateRuler()
|
||||
mRuler.Invalidate();
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::RegenerateTooltips()
|
||||
void AdornedRulerPanel::RegenerateTooltips(StatusChoice choice)
|
||||
{
|
||||
#if wxUSE_TOOLTIPS
|
||||
if (mTimelineToolTip) {
|
||||
@ -1938,7 +1998,7 @@ void AdornedRulerPanel::RegenerateTooltips()
|
||||
this->SetToolTip(_("Timeline actions disabled during recording"));
|
||||
}
|
||||
else {
|
||||
switch(mPrevZone) {
|
||||
switch(choice) {
|
||||
case StatusChoice::QuickPlayButton :
|
||||
case StatusChoice::EnteringQP :
|
||||
if (!mQuickPlayEnabled) {
|
||||
@ -1988,12 +2048,15 @@ void AdornedRulerPanel::OnCapture(wxCommandEvent & evt)
|
||||
// if recording is initiated by a modal window (Timer Record).
|
||||
SetCursor(mCursorDefault);
|
||||
mIsRecording = true;
|
||||
|
||||
// The quick play indicator is useless during recording
|
||||
HideQuickPlayIndicator();
|
||||
}
|
||||
else {
|
||||
SetCursor(mCursorHand);
|
||||
mIsRecording = false;
|
||||
}
|
||||
RegenerateTooltips();
|
||||
RegenerateTooltips(mPrevZone);
|
||||
}
|
||||
|
||||
enum : int {
|
||||
@ -2051,7 +2114,7 @@ void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
|
||||
mBack->Create(sz.x, sz.y, dc);
|
||||
mBackDC.SelectObject(*mBack);
|
||||
|
||||
DoDrawBorder(&mBackDC);
|
||||
DoDrawBackground(&mBackDC);
|
||||
|
||||
if (!mViewInfo->selectedRegion.isPoint())
|
||||
{
|
||||
@ -2063,8 +2126,7 @@ void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
|
||||
if (mIndType >= 0)
|
||||
{
|
||||
const bool scrub = mProject->GetScrubber().HasStartedScrubbing();
|
||||
auto width = scrub ? IndicatorBigWidth() : IndicatorMediumWidth;
|
||||
DoDrawIndicator(&mBackDC, mIndTime, mIndType != 0, width, scrub);
|
||||
DoDrawIndicator(&mBackDC, mIndTime, mIndType != 0, IndicatorMediumWidth, false);
|
||||
}
|
||||
|
||||
if (mViewInfo->selectedRegion.isPoint())
|
||||
@ -2076,11 +2138,13 @@ void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
|
||||
|
||||
DoDrawPushbuttons(&mBackDC);
|
||||
|
||||
DoDrawEdge(&mBackDC);
|
||||
|
||||
dc.Blit(0, 0, mBack->GetWidth(), mBack->GetHeight(), &mBackDC, 0, 0);
|
||||
|
||||
if (mQuickPlayInd)
|
||||
{
|
||||
DrawQuickPlayIndicator(&dc);
|
||||
DrawQuickPlayIndicator(&dc, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2103,21 +2167,30 @@ void AdornedRulerPanel::UpdateRects()
|
||||
mInner.x += LeftMargin;
|
||||
mInner.width -= (LeftMargin + RightMargin);
|
||||
|
||||
wxRect *top = &mInner;
|
||||
auto top = &mInner;
|
||||
auto bottom = &mInner;
|
||||
|
||||
if (mShowScrubbing) {
|
||||
mScrubZone = mInner;
|
||||
auto scrubHeight = std::min(mScrubZone.height, int(ScrubHeight));
|
||||
mScrubZone.height = scrubHeight;
|
||||
mInner.height -= scrubHeight;
|
||||
mInner.y += scrubHeight;
|
||||
top = &mScrubZone;
|
||||
|
||||
int topHeight;
|
||||
#ifdef SCRUB_ABOVE
|
||||
top = &mScrubZone, topHeight = scrubHeight;
|
||||
#else
|
||||
auto qpHeight = mScrubZone.height - scrubHeight;
|
||||
bottom = &mScrubZone, topHeight = qpHeight;
|
||||
#endif
|
||||
|
||||
top->height = topHeight;
|
||||
bottom->height -= topHeight;
|
||||
bottom->y += topHeight;
|
||||
}
|
||||
|
||||
top->y += TopMargin;
|
||||
top->height -= TopMargin;
|
||||
|
||||
mInner.height -= BottomMargin;
|
||||
bottom->height -= BottomMargin;
|
||||
|
||||
if (!mShowScrubbing)
|
||||
mScrubZone = mInner;
|
||||
@ -2166,7 +2239,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
||||
}
|
||||
|
||||
const bool overButtons = GetButtonAreaRect(true).Contains(evt.GetPosition());
|
||||
const StatusChoice button = FindButton(evt.GetPosition());
|
||||
auto button = FindButton(evt).button;
|
||||
const bool inScrubZone = !overButtons &&
|
||||
// only if scrubbing is allowed now
|
||||
mProject->GetScrubber().CanScrub() &&
|
||||
@ -2179,9 +2252,11 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
||||
? button
|
||||
: inScrubZone
|
||||
? StatusChoice::EnteringScrubZone
|
||||
: StatusChoice::EnteringQP;
|
||||
: mInner.Contains(evt.GetPosition())
|
||||
? StatusChoice::EnteringQP
|
||||
: StatusChoice::NoChange;
|
||||
const bool changeInZone = (zone != mPrevZone);
|
||||
mPrevZone = zone;
|
||||
const bool changing = evt.Leaving() || evt.Entering() || changeInZone;
|
||||
|
||||
wxCoord xx = evt.GetX();
|
||||
wxCoord mousePosX = xx;
|
||||
@ -2195,36 +2270,49 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
||||
}
|
||||
|
||||
// Handle status bar messages
|
||||
UpdateStatusBarAndTooltips (
|
||||
evt.Leaving() || evt.Entering() || changeInZone
|
||||
? zone
|
||||
: StatusChoice::NoChange
|
||||
);
|
||||
UpdateStatusBarAndTooltips (changing ? zone : StatusChoice::NoChange);
|
||||
|
||||
if ((IsButton(zone) || IsButton(mPrevZone)) &&
|
||||
(changing || evt.Moving() || evt.Dragging()))
|
||||
// So that the highlights in pushbuttons can update
|
||||
Refresh(false);
|
||||
|
||||
mPrevZone = zone;
|
||||
|
||||
auto &scrubber = mProject->GetScrubber();
|
||||
if (scrubber.HasStartedScrubbing()) {
|
||||
// If already clicked for scrub, preempt the usual event handling,
|
||||
// no matter what the y coordinate.
|
||||
if (zone == StatusChoice::EnteringQP &&
|
||||
evt.LeftDown()) {
|
||||
// Stop scrubbing
|
||||
if (HasCapture())
|
||||
ReleaseMouse();
|
||||
mProject->OnStop();
|
||||
// Continue to quick play event handling
|
||||
}
|
||||
else {
|
||||
// If already clicked for scrub, preempt the usual event handling,
|
||||
// no matter what the y coordinate.
|
||||
|
||||
// Do this hack so scrubber can detect mouse drags anywhere
|
||||
evt.ResumePropagation(wxEVENT_PROPAGATE_MAX);
|
||||
// Do this hack so scrubber can detect mouse drags anywhere
|
||||
evt.ResumePropagation(wxEVENT_PROPAGATE_MAX);
|
||||
|
||||
if (scrubber.IsScrubbing())
|
||||
evt.Skip();
|
||||
else if (evt.LeftDClick())
|
||||
// On the second button down, switch the pending scrub to scrolling
|
||||
scrubber.MarkScrubStart(evt.m_x, true, false);
|
||||
else
|
||||
evt.Skip();
|
||||
if (scrubber.IsScrubbing())
|
||||
evt.Skip();
|
||||
else if (evt.LeftDClick())
|
||||
// On the second button down, switch the pending scrub to scrolling
|
||||
scrubber.MarkScrubStart(evt.m_x, true, false);
|
||||
else
|
||||
evt.Skip();
|
||||
|
||||
mQuickPlayInd = true;
|
||||
wxClientDC dc(this);
|
||||
DrawQuickPlayIndicator(&dc);
|
||||
mQuickPlayInd = true;
|
||||
wxClientDC dc(this);
|
||||
DrawQuickPlayIndicator(&dc);
|
||||
|
||||
if (HasCapture())
|
||||
ReleaseMouse();
|
||||
if (HasCapture())
|
||||
ReleaseMouse();
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the initial play region state
|
||||
@ -2260,7 +2348,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasCapture() && mCaptureState != StatusChoice::NoButton)
|
||||
if (HasCapture() && mCaptureState.button != StatusChoice::NoButton)
|
||||
HandlePushbuttonEvent(evt);
|
||||
else if (!HasCapture() && overButtons)
|
||||
HandlePushbuttonClick(evt);
|
||||
@ -2304,7 +2392,15 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RULER_DOUBLE_CLICK
|
||||
if (evt.LeftDClick()) {
|
||||
mDoubleClick = true;
|
||||
HandleQPDoubleClick(evt, mousePosX);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (evt.LeftDown()) {
|
||||
mDoubleClick = false;
|
||||
HandleQPClick(evt, mousePosX);
|
||||
HandleQPDrag(evt, mousePosX);
|
||||
}
|
||||
@ -2319,6 +2415,11 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
||||
}
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::HandleQPDoubleClick(wxMouseEvent &evt, wxCoord mousePosX)
|
||||
{
|
||||
mProject->GetPlaybackScroller().Activate(true);
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::HandleQPClick(wxMouseEvent &evt, wxCoord mousePosX)
|
||||
{
|
||||
// Temporarily unlock locked play region
|
||||
@ -2452,12 +2553,15 @@ void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX)
|
||||
|
||||
void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
|
||||
{
|
||||
if (mDoubleClick)
|
||||
return;
|
||||
|
||||
HideQuickPlayIndicator();
|
||||
|
||||
if (HasCapture())
|
||||
ReleaseMouse();
|
||||
|
||||
mCaptureState = StatusChoice::NoButton;
|
||||
mCaptureState = CaptureState{};
|
||||
|
||||
if (mPlayRegionEnd < mPlayRegionStart) {
|
||||
// Swap values to ensure mPlayRegionStart < mPlayRegionEnd
|
||||
@ -2492,6 +2596,28 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
|
||||
ClearPlayRegion();
|
||||
}
|
||||
|
||||
StartQPPlay(evt.ShiftDown(), evt.ControlDown());
|
||||
|
||||
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::StartQPPlay(bool looped, bool cutPreview)
|
||||
{
|
||||
const double t0 = mTracks->GetStartTime();
|
||||
const double t1 = mTracks->GetEndTime();
|
||||
const double sel0 = mProject->GetSel0();
|
||||
const double sel1 = mProject->GetSel1();
|
||||
|
||||
// Start / Restart playback on left click.
|
||||
bool startPlaying = (mPlayRegionStart >= 0);
|
||||
|
||||
@ -2502,7 +2628,7 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
|
||||
bool loopEnabled = true;
|
||||
double start, end;
|
||||
|
||||
if ((mPlayRegionEnd - mPlayRegionStart == 0.0) && evt.ShiftDown()) {
|
||||
if ((mPlayRegionEnd - mPlayRegionStart == 0.0) && looped) {
|
||||
// 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
|
||||
@ -2522,15 +2648,15 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
|
||||
loopEnabled = ((end - start) > 0.001)? true : false;
|
||||
|
||||
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
|
||||
options.playLooped = (loopEnabled && evt.ShiftDown());
|
||||
options.playLooped = (loopEnabled && looped);
|
||||
|
||||
if (!evt.ControlDown())
|
||||
if (!cutPreview)
|
||||
options.pStartTime = &mPlayRegionStart;
|
||||
else
|
||||
options.timeTrack = NULL;
|
||||
|
||||
ControlToolBar::PlayAppearance appearance =
|
||||
evt.ControlDown() ? ControlToolBar::PlayAppearance::CutPreview
|
||||
cutPreview ? ControlToolBar::PlayAppearance::CutPreview
|
||||
: options.playLooped ? ControlToolBar::PlayAppearance::Looped
|
||||
: ControlToolBar::PlayAppearance::Straight;
|
||||
ctb->PlayPlayRegion((SelectedRegion(start, end)),
|
||||
@ -2543,18 +2669,6 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
|
||||
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::UpdateStatusBarAndTooltips(StatusChoice choice)
|
||||
@ -2567,8 +2681,7 @@ void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice)
|
||||
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)");
|
||||
message = wxGetTranslation(state ? strings.disable : strings.enable);
|
||||
}
|
||||
else {
|
||||
const auto &scrubber = mProject->GetScrubber();
|
||||
@ -2603,10 +2716,10 @@ void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice)
|
||||
// Display a message, or empty message
|
||||
mProject->TP_DisplayStatusMessage(message);
|
||||
|
||||
RegenerateTooltips();
|
||||
RegenerateTooltips(choice);
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::OnToggleScrubbing()
|
||||
void AdornedRulerPanel::OnToggleScrubbing(wxCommandEvent&)
|
||||
{
|
||||
mShowScrubbing = !mShowScrubbing;
|
||||
WriteScrubEnabledPref(mShowScrubbing);
|
||||
@ -2686,6 +2799,15 @@ void AdornedRulerPanel::ShowScrubMenu(const wxPoint & pos)
|
||||
auto cleanup = finally([this]{ PopEventHandler(); });
|
||||
|
||||
wxMenu rulerMenu;
|
||||
auto label = wxGetTranslation(
|
||||
AdornedRulerPanel::PushbuttonLabels
|
||||
[static_cast<int>(StatusChoice::ScrubBarButton)].label);
|
||||
rulerMenu.AppendCheckItem(OnShowHideScrubbingID, _("Scrub Bar"));
|
||||
if(GetButtonState(StatusChoice::ScrubBarButton))
|
||||
rulerMenu.FindItem(OnShowHideScrubbingID)->Check();
|
||||
|
||||
rulerMenu.AppendSeparator();
|
||||
|
||||
mProject->GetScrubber().PopulateMenu(rulerMenu);
|
||||
PopupMenu(&rulerMenu, pos);
|
||||
}
|
||||
@ -2695,7 +2817,7 @@ void AdornedRulerPanel::OnToggleQuickPlay(wxCommandEvent&)
|
||||
mQuickPlayEnabled = (mQuickPlayEnabled)? false : true;
|
||||
gPrefs->Write(wxT("/QuickPlay/QuickPlayEnabled"), mQuickPlayEnabled);
|
||||
gPrefs->Flush();
|
||||
RegenerateTooltips();
|
||||
RegenerateTooltips(mPrevZone);
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::OnSyncSelToQuickPlay(wxCommandEvent&)
|
||||
@ -2736,7 +2858,7 @@ void AdornedRulerPanel::OnTimelineToolTips(wxCommandEvent&)
|
||||
gPrefs->Write(wxT("/QuickPlay/ToolTips"), mTimelineToolTip);
|
||||
gPrefs->Flush();
|
||||
#if wxUSE_TOOLTIPS
|
||||
RegenerateTooltips();
|
||||
RegenerateTooltips(mPrevZone);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -2860,19 +2982,40 @@ wxRect AdornedRulerPanel::GetButtonRect( StatusChoice button ) const
|
||||
return rect;
|
||||
}
|
||||
|
||||
bool AdornedRulerPanel::InButtonRect( StatusChoice button ) const
|
||||
auto AdornedRulerPanel::InButtonRect( StatusChoice button, wxMouseEvent *pEvent ) const
|
||||
-> PointerState
|
||||
{
|
||||
return GetButtonRect(button).Contains(ScreenToClient(::wxGetMousePosition()));
|
||||
auto rect = GetButtonRect(button);
|
||||
auto state = pEvent ? *pEvent : ::wxGetMouseState();
|
||||
auto point = pEvent ? pEvent->GetPosition() : ScreenToClient(state.GetPosition());
|
||||
if(!rect.Contains(point))
|
||||
return PointerState::Out;
|
||||
else {
|
||||
auto rightDown = state.RightIsDown()
|
||||
#ifdef __WXMAC__
|
||||
// make drag with Mac Control down act like right drag
|
||||
|| (state.RawControlDown() && state.ButtonIsDown(wxMOUSE_BTN_ANY))
|
||||
#endif
|
||||
;
|
||||
if(rightDown ||
|
||||
(pEvent && pEvent->RightUp()) ||
|
||||
GetArrowRect(rect).Contains(point))
|
||||
return PointerState::InArrow;
|
||||
else
|
||||
return PointerState::In;
|
||||
}
|
||||
}
|
||||
|
||||
auto AdornedRulerPanel::FindButton( wxPoint position ) const -> StatusChoice
|
||||
auto AdornedRulerPanel::FindButton( wxMouseEvent &mouseEvent ) const
|
||||
-> CaptureState
|
||||
{
|
||||
for (auto button = StatusChoice::FirstButton; IsButton(button); ++button) {
|
||||
if(GetButtonRect(button).Contains(position))
|
||||
return button;
|
||||
auto state = InButtonRect( button, &mouseEvent );
|
||||
if (state != PointerState::Out)
|
||||
return CaptureState{ button, state };
|
||||
}
|
||||
|
||||
return StatusChoice::NoButton;
|
||||
return { StatusChoice::NoButton, PointerState::Out };
|
||||
}
|
||||
|
||||
bool AdornedRulerPanel::GetButtonState( StatusChoice button ) const
|
||||
@ -2890,30 +3033,33 @@ bool AdornedRulerPanel::GetButtonState( StatusChoice button ) const
|
||||
|
||||
void AdornedRulerPanel::ToggleButtonState( StatusChoice button )
|
||||
{
|
||||
wxCommandEvent dummy;
|
||||
switch(button) {
|
||||
case StatusChoice::QuickPlayButton: {
|
||||
wxCommandEvent dummy;
|
||||
case StatusChoice::QuickPlayButton:
|
||||
OnToggleQuickPlay(dummy);
|
||||
}
|
||||
break;
|
||||
case StatusChoice::ScrubBarButton:
|
||||
OnToggleScrubbing();
|
||||
OnToggleScrubbing(dummy);
|
||||
break;
|
||||
default:
|
||||
wxASSERT(false);
|
||||
}
|
||||
UpdateStatusBarAndTooltips(mCaptureState.button);
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::ShowButtonMenu( StatusChoice button, wxPoint position)
|
||||
{
|
||||
switch (button) {
|
||||
case StatusChoice::QuickPlayButton:
|
||||
return ShowMenu(position);
|
||||
ShowMenu(position); break;
|
||||
case StatusChoice::ScrubBarButton:
|
||||
return ShowScrubMenu(position);
|
||||
ShowScrubMenu(position); break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// dismiss and clear Quick-Play indicator
|
||||
HideQuickPlayIndicator();
|
||||
}
|
||||
|
||||
const AdornedRulerPanel::ButtonStrings AdornedRulerPanel::PushbuttonLabels
|
||||
@ -2924,48 +3070,105 @@ const AdornedRulerPanel::ButtonStrings AdornedRulerPanel::PushbuttonLabels
|
||||
{ XO("Scrub Bar"), XO("Show Scrub Bar"), XO("Hide Scrub Bar") },
|
||||
};
|
||||
|
||||
void AdornedRulerPanel::DoDrawPushbutton(wxDC *dc, StatusChoice button, bool down) const
|
||||
namespace {
|
||||
void DrawButtonBackground(wxDC *dc, const wxRect &rect, bool down, bool highlight) {
|
||||
// Choose the pen
|
||||
if (highlight)
|
||||
AColor::Light(dc, false);
|
||||
else
|
||||
// This color choice corresponds to part of TrackInfo::DrawBordersWithin() :
|
||||
AColor::Dark(dc, false);
|
||||
auto pen = dc->GetPen();
|
||||
// pen.SetWidth(2);
|
||||
|
||||
// Choose the brush
|
||||
if (down)
|
||||
AColor::Solo(dc, true, false);
|
||||
else
|
||||
AColor::MediumTrackInfo(dc, false);
|
||||
|
||||
dc->SetPen(pen);
|
||||
dc->DrawRectangle(rect);
|
||||
|
||||
// Draw the bevel
|
||||
auto rect2 = rect.Deflate(1, 1);
|
||||
Deflator def(rect2);
|
||||
AColor::BevelTrackInfo(*dc, !down, rect2);
|
||||
}
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::DoDrawPushbutton
|
||||
(wxDC *dc, StatusChoice button, PointerState down, PointerState pointerState) const
|
||||
{
|
||||
// Adapted from TrackInfo::DrawMuteSolo()
|
||||
ADCChanger changer(dc);
|
||||
|
||||
auto bev = GetButtonRect( button );
|
||||
const auto rect = GetButtonRect( button );
|
||||
const auto arrowRect = GetArrowRect(rect);
|
||||
auto arrowBev = arrowRect.Deflate(1, 1);
|
||||
const auto textRect = GetTextRect(rect);
|
||||
auto textBev = textRect.Deflate(1, 1);
|
||||
|
||||
// This part corresponds to part of TrackInfo::DrawBordersWithin() :
|
||||
AColor::Dark(dc, false);
|
||||
dc->DrawRectangle(bev);
|
||||
// Draw borders, bevels, and backgrounds of the split sections
|
||||
|
||||
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);
|
||||
if (pointerState == PointerState::InArrow) {
|
||||
// Draw highlighted arrow after
|
||||
DrawButtonBackground(dc, textRect, (down == PointerState::In), false);
|
||||
DrawButtonBackground(dc, arrowRect, (down == PointerState::InArrow), true);
|
||||
}
|
||||
else {
|
||||
// Draw maybe highlighted text after
|
||||
DrawButtonBackground(dc, arrowRect, (down == PointerState::InArrow), false);
|
||||
DrawButtonBackground(
|
||||
dc, textRect, (down == PointerState::In), (pointerState == PointerState::In));
|
||||
}
|
||||
|
||||
dc->SetTextForeground(theTheme.Colour(clrTrackPanelText));
|
||||
// Draw the menu triangle
|
||||
{
|
||||
auto x = arrowBev.GetX() + ArrowSpacing;
|
||||
auto y = arrowBev.GetY() + (arrowBev.GetHeight() - ArrowHeight) / 2;
|
||||
|
||||
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);
|
||||
// Color it as in TrackInfo::DrawTitleBar
|
||||
#ifdef EXPERIMENTAL_THEMING
|
||||
wxColour c = theTheme.Colour( clrTrackPanelText );
|
||||
#else
|
||||
wxColour c = *wxBLACK;
|
||||
#endif
|
||||
if (pointerState == PointerState::InArrow)
|
||||
dc->SetBrush( wxBrush{ c } );
|
||||
else
|
||||
dc->SetBrush( wxBrush{ *wxTRANSPARENT_BRUSH } ); // Make outlined arrow only
|
||||
dc->SetPen( wxPen{ c } );
|
||||
|
||||
// This function draws an arrow half as tall as wide:
|
||||
AColor::Arrow(*dc, x, y, ArrowWidth);
|
||||
}
|
||||
|
||||
// Draw the text
|
||||
|
||||
{
|
||||
dc->SetTextForeground(theTheme.Colour(clrTrackPanelText));
|
||||
wxCoord textWidth, textHeight;
|
||||
wxString str = wxGetTranslation(GetPushButtonStrings(button)->label);
|
||||
dc->SetFont(GetButtonFont());
|
||||
dc->GetTextExtent(str, &textWidth, &textHeight);
|
||||
auto xx = textBev.x + (textBev.width - textWidth) / 2;
|
||||
auto yy = textBev.y + (textBev.height - textHeight) / 2;
|
||||
if (down == PointerState::In)
|
||||
// Shift the text a bit for "down" appearance
|
||||
++xx, ++yy;
|
||||
dc->DrawText(str, xx, yy);
|
||||
}
|
||||
}
|
||||
|
||||
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 } );
|
||||
}
|
||||
auto pair = FindButton(evt);
|
||||
auto button = pair.button;
|
||||
if (IsButton(button) && evt.ButtonDown()) {
|
||||
CaptureMouse();
|
||||
mCaptureState = pair;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2974,11 +3177,21 @@ void AdornedRulerPanel::HandlePushbuttonEvent(wxMouseEvent &evt)
|
||||
if(evt.ButtonUp()) {
|
||||
if(HasCapture())
|
||||
ReleaseMouse();
|
||||
if(InButtonRect(mCaptureState)) {
|
||||
ToggleButtonState(mCaptureState);
|
||||
UpdateStatusBarAndTooltips(mCaptureState);
|
||||
|
||||
auto button = mCaptureState.button;
|
||||
auto capturedIn = mCaptureState.state;
|
||||
auto in = InButtonRect(button, &evt);
|
||||
if (in != capturedIn)
|
||||
;
|
||||
else if (in == PointerState::In)
|
||||
ToggleButtonState(button);
|
||||
else {
|
||||
auto rect = GetArrowRect(GetButtonRect(button));
|
||||
wxPoint point { rect.GetLeft() + 1, rect.GetBottom() + 1 };
|
||||
ShowButtonMenu(button, point);
|
||||
}
|
||||
mCaptureState = StatusChoice::NoButton;
|
||||
|
||||
mCaptureState = CaptureState{};
|
||||
}
|
||||
|
||||
Refresh();
|
||||
@ -2988,18 +3201,37 @@ void AdornedRulerPanel::DoDrawPushbuttons(wxDC *dc) const
|
||||
{
|
||||
// Paint the area behind the buttons
|
||||
wxRect background = GetButtonAreaRect();
|
||||
|
||||
#ifndef SCRUB_ABOVE
|
||||
// Reduce the height
|
||||
background.y = mInner.y;
|
||||
background.height = mInner.height;
|
||||
#endif
|
||||
|
||||
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);
|
||||
bool state = GetButtonState(button);
|
||||
auto in = InButtonRect(button, nullptr);
|
||||
auto down = PointerState::Out;
|
||||
if (button == mCaptureState.button && in == mCaptureState.state) {
|
||||
if (in == PointerState::In) {
|
||||
// Toggle button's apparent state for mouseover
|
||||
down = state ? PointerState::Out : in;
|
||||
}
|
||||
else if (in == PointerState::InArrow) {
|
||||
// Menu arrow is not sticky
|
||||
down = in;
|
||||
}
|
||||
}
|
||||
else if (state)
|
||||
down = PointerState::In;
|
||||
DoDrawPushbutton(dc, button, down, in);
|
||||
}
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::DoDrawBorder(wxDC * dc)
|
||||
void AdornedRulerPanel::DoDrawBackground(wxDC * dc)
|
||||
{
|
||||
// Draw AdornedRulerPanel border
|
||||
AColor::MediumTrackInfo( dc, false );
|
||||
@ -3012,6 +3244,10 @@ void AdornedRulerPanel::DoDrawBorder(wxDC * dc)
|
||||
dc->DrawRectangle(mScrubZone);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::DoDrawEdge(wxDC *dc)
|
||||
{
|
||||
wxRect r = mOuter;
|
||||
r.width -= RightMargin;
|
||||
r.height -= BottomMargin;
|
||||
@ -3112,6 +3348,7 @@ void AdornedRulerPanel::DrawIndicator( double time, bool rec )
|
||||
void AdornedRulerPanel::DoDrawIndicator
|
||||
(wxDC * dc, double time, bool playing, int width, bool scrub)
|
||||
{
|
||||
ADCChanger changer(dc); // Undo pen and brush changes at function exit
|
||||
|
||||
const int x = Time2Pos(time);
|
||||
AColor::IndicatorColor( dc, playing );
|
||||
@ -3122,12 +3359,15 @@ void AdornedRulerPanel::DoDrawIndicator
|
||||
const int IndicatorHalfWidth = width / 2;
|
||||
|
||||
// Double headed, left-right
|
||||
auto yy = mShowScrubbing
|
||||
? mScrubZone.y
|
||||
: (mInner.GetBottom() + 1) - 1 /* bevel */ - height;
|
||||
tri[ 0 ].x = x - IndicatorOffset;
|
||||
tri[ 0 ].y = mScrubZone.y;
|
||||
tri[ 0 ].y = yy;
|
||||
tri[ 1 ].x = x - IndicatorOffset;
|
||||
tri[ 1 ].y = mScrubZone.y + height;
|
||||
tri[ 1 ].y = yy + height;
|
||||
tri[ 2 ].x = x - IndicatorHalfWidth;
|
||||
tri[ 2 ].y = mScrubZone.y + height / 2;
|
||||
tri[ 2 ].y = yy + height / 2;
|
||||
dc->DrawPolygon( 3, tri );
|
||||
tri[ 0 ].x = tri[ 1 ].x = x + IndicatorOffset;
|
||||
tri[ 2 ].x = x + IndicatorHalfWidth;
|
||||
@ -3138,11 +3378,11 @@ void AdornedRulerPanel::DoDrawIndicator
|
||||
auto height = IndicatorHeightForWidth(width);
|
||||
const int IndicatorHalfWidth = width / 2;
|
||||
tri[ 0 ].x = x - IndicatorHalfWidth;
|
||||
tri[ 0 ].y = mScrubZone.y;
|
||||
tri[ 0 ].y = mInner.y;
|
||||
tri[ 1 ].x = x + IndicatorHalfWidth;
|
||||
tri[ 1 ].y = mScrubZone.y;
|
||||
tri[ 1 ].y = mInner.y;
|
||||
tri[ 2 ].x = x;
|
||||
tri[ 2 ].y = mScrubZone.y + height;
|
||||
tri[ 2 ].y = mInner.y + height;
|
||||
dc->DrawPolygon( 3, tri );
|
||||
}
|
||||
}
|
||||
@ -3159,13 +3399,13 @@ void AdornedRulerPanel::DoEraseIndicator(wxDC *dc, int x)
|
||||
|
||||
// Restore the background, but make it a little oversized to make
|
||||
// it happy OSX.
|
||||
dc->Blit(x - indsize - 1,
|
||||
mScrubZone.y - 1,
|
||||
auto xx = x - indsize - 1;
|
||||
auto yy = 0;
|
||||
dc->Blit(xx, yy,
|
||||
indsize * 2 + 1 + 2,
|
||||
mScrubZone.y + height + 2,
|
||||
GetSize().GetHeight(),
|
||||
&mBackDC,
|
||||
x - indsize - 1,
|
||||
0);
|
||||
xx, yy);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3180,7 +3420,7 @@ QuickPlayIndicatorOverlay *AdornedRulerPanel::GetOverlay()
|
||||
}
|
||||
|
||||
// Draws the vertical line and green triangle indicating the Quick Play cursor position.
|
||||
void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc)
|
||||
void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc, bool repainting)
|
||||
{
|
||||
double latestEnd = std::max(mTracks->GetEndTime(), mProject->GetSel1());
|
||||
if (dc == NULL || (mQuickPlayPos >= latestEnd)) {
|
||||
@ -3195,7 +3435,9 @@ void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc)
|
||||
!mProject->GetScrubber().IsScrubbing();
|
||||
GetOverlay()->Update(x, mIsSnapped, previewScrub);
|
||||
|
||||
DoEraseIndicator(dc, mLastQuickPlayX);
|
||||
if (!repainting)
|
||||
DoEraseIndicator(dc, mLastQuickPlayX);
|
||||
|
||||
mLastQuickPlayX = x;
|
||||
|
||||
auto scrub = mPrevZone == StatusChoice::EnteringScrubZone ||
|
||||
|
@ -312,20 +312,6 @@ public:
|
||||
void InvalidateRuler();
|
||||
|
||||
void UpdatePrefs();
|
||||
void RegenerateTooltips();
|
||||
void HideQuickPlayIndicator();
|
||||
|
||||
void UpdateQuickPlayPos(wxCoord &mousPosX);
|
||||
|
||||
private:
|
||||
void OnCapture(wxCommandEvent & evt);
|
||||
void OnPaint(wxPaintEvent &evt);
|
||||
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 {
|
||||
FirstButton = 0,
|
||||
@ -340,10 +326,38 @@ private:
|
||||
Leaving,
|
||||
NoChange
|
||||
};
|
||||
enum class PointerState {
|
||||
Out = 0, In, InArrow
|
||||
};
|
||||
struct CaptureState {
|
||||
CaptureState() {}
|
||||
CaptureState(StatusChoice s, PointerState p) : button(s), state(p) {}
|
||||
StatusChoice button { StatusChoice::NoButton };
|
||||
PointerState state { PointerState::Out };
|
||||
};
|
||||
|
||||
friend inline StatusChoice &operator++ (StatusChoice &choice) {
|
||||
choice = static_cast<StatusChoice>(1 + static_cast<int>(choice));
|
||||
return choice;
|
||||
}
|
||||
|
||||
void RegenerateTooltips(StatusChoice choice);
|
||||
void HideQuickPlayIndicator();
|
||||
|
||||
void UpdateQuickPlayPos(wxCoord &mousPosX);
|
||||
|
||||
private:
|
||||
void OnCapture(wxCommandEvent & evt);
|
||||
void OnPaint(wxPaintEvent &evt);
|
||||
void OnSize(wxSizeEvent &evt);
|
||||
void UpdateRects();
|
||||
void OnMouseEvents(wxMouseEvent &evt);
|
||||
void HandleQPDoubleClick(wxMouseEvent &event, wxCoord mousePosX);
|
||||
void HandleQPClick(wxMouseEvent &event, wxCoord mousePosX);
|
||||
void HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX);
|
||||
void HandleQPRelease(wxMouseEvent &event);
|
||||
void StartQPPlay(bool looped, bool cutPreview);
|
||||
|
||||
static inline bool IsButton(StatusChoice choice)
|
||||
{
|
||||
auto integer = static_cast<int>(choice);
|
||||
@ -357,14 +371,15 @@ private:
|
||||
|
||||
void OnCaptureLost(wxMouseCaptureLostEvent &evt);
|
||||
|
||||
void DoDrawBorder(wxDC * dc);
|
||||
void DoDrawBackground(wxDC * dc);
|
||||
void DoDrawEdge(wxDC *dc);
|
||||
void DoDrawMarks(wxDC * dc, bool /*text */ );
|
||||
void DoDrawCursor(wxDC * dc);
|
||||
void DoDrawSelection(wxDC * dc);
|
||||
void DoDrawIndicator(wxDC * dc, double time, bool playing, int width, bool scrub);
|
||||
void DoEraseIndicator(wxDC *dc, int x);
|
||||
QuickPlayIndicatorOverlay *GetOverlay();
|
||||
void DrawQuickPlayIndicator(wxDC * dc /*NULL to DELETE old only*/);
|
||||
void DrawQuickPlayIndicator(wxDC * dc /*NULL to DELETE old only*/, bool repainting = false);
|
||||
void DoDrawPlayRegion(wxDC * dc);
|
||||
|
||||
wxRect GetButtonAreaRect(bool includeBorder = false) const;
|
||||
@ -381,12 +396,13 @@ private:
|
||||
}
|
||||
|
||||
wxRect GetButtonRect( StatusChoice button ) const;
|
||||
bool InButtonRect( StatusChoice button ) const;
|
||||
StatusChoice FindButton( wxPoint position ) const;
|
||||
PointerState InButtonRect( StatusChoice button, wxMouseEvent *pEvent ) const;
|
||||
CaptureState FindButton( wxMouseEvent &mouseEvent ) 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 DoDrawPushbutton(wxDC *dc, StatusChoice button, PointerState down,
|
||||
PointerState pointerState) const;
|
||||
void DoDrawPushbuttons(wxDC *dc) const;
|
||||
void HandlePushbuttonClick(wxMouseEvent &evt);
|
||||
void HandlePushbuttonEvent(wxMouseEvent &evt);
|
||||
@ -453,14 +469,13 @@ private:
|
||||
void OnAutoScroll(wxCommandEvent &evt);
|
||||
void OnLockPlayRegion(wxCommandEvent &evt);
|
||||
|
||||
void OnToggleScrubbing();
|
||||
void OnToggleScrubbing(wxCommandEvent&);
|
||||
|
||||
bool mPlayRegionDragsSelection;
|
||||
bool mTimelineToolTip;
|
||||
bool mQuickPlayEnabled;
|
||||
|
||||
|
||||
StatusChoice mCaptureState { StatusChoice::NoButton };
|
||||
CaptureState mCaptureState {};
|
||||
|
||||
enum MouseEventState {
|
||||
mesNone,
|
||||
@ -483,6 +498,8 @@ private:
|
||||
mutable int mButtonFontSize { -1 };
|
||||
mutable wxFont mButtonFont;
|
||||
|
||||
bool mDoubleClick {};
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user