2018-10-23 20:34:33 +00:00
|
|
|
#include "../Audacity.h"
|
2018-11-11 02:40:37 +00:00
|
|
|
|
Command flag functions out of Menus.cpp, reducing its dependencies...
... Mostly into CommonCommandFlags.cpp, but some elsewhere, to avoid giving
that new file problematic dependencies on LabelTrack, ControlToolBar, and
EffectManager.
Note that CutCopyAvailableFlag is critically ordered, for message purposes,
only with AudioIONotAvailableFlag, the only flag with a message that it combines
with in menu item definitions.
The dependency on LabelTrack.cpp might not be a bad one later, if the track and
its view can be separated, and that would allow CutCopyAvailableFlag to be
put with the others. But much other work on LabelTrack must happen first.
2019-06-14 15:39:12 +00:00
|
|
|
#include "../CommonCommandFlags.h"
|
2018-10-23 20:34:33 +00:00
|
|
|
#include "../Prefs.h"
|
|
|
|
#include "../Project.h"
|
2019-06-06 13:55:34 +00:00
|
|
|
#include "../ProjectHistory.h"
|
2019-05-29 16:05:22 +00:00
|
|
|
#include "../ProjectWindow.h"
|
2019-05-06 23:00:10 +00:00
|
|
|
#include "../Track.h"
|
2019-01-22 17:06:46 +00:00
|
|
|
#include "../SelectionState.h"
|
2018-10-23 20:34:33 +00:00
|
|
|
#include "../TrackPanel.h"
|
|
|
|
#include "../TrackPanelAx.h"
|
|
|
|
#include "../commands/CommandContext.h"
|
|
|
|
#include "../commands/CommandManager.h"
|
|
|
|
#include "../toolbars/ToolManager.h"
|
|
|
|
#include "../widgets/AButton.h"
|
|
|
|
#include "../widgets/ASlider.h"
|
|
|
|
#include "../widgets/Meter.h"
|
|
|
|
|
|
|
|
// private helper classes and functions
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
void NextOrPrevFrame(AudacityProject &project, bool forward)
|
|
|
|
{
|
|
|
|
// Focus won't take in a dock unless at least one descendant window
|
|
|
|
// accepts focus. Tell controls to take focus for the duration of this
|
|
|
|
// function, only. Outside of this, they won't steal the focus when
|
|
|
|
// clicked.
|
|
|
|
auto temp1 = AButton::TemporarilyAllowFocus();
|
|
|
|
auto temp2 = ASlider::TemporarilyAllowFocus();
|
|
|
|
auto temp3 = MeterPanel::TemporarilyAllowFocus();
|
|
|
|
|
2019-05-01 17:18:39 +00:00
|
|
|
auto &toolManager = ToolManager::Get( project );
|
|
|
|
auto botDock = toolManager.GetBotDock();
|
2018-10-23 20:34:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Define the set of windows we rotate among.
|
|
|
|
static const unsigned rotationSize = 3u;
|
|
|
|
|
|
|
|
wxWindow *const begin [rotationSize] = {
|
2019-05-28 17:12:56 +00:00
|
|
|
ProjectWindow::Get( project ).GetTopPanel(),
|
2019-04-21 21:01:31 +00:00
|
|
|
&TrackPanel::Get( project ),
|
2018-10-23 20:34:33 +00:00
|
|
|
botDock,
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto end = begin + rotationSize;
|
|
|
|
|
|
|
|
// helper functions
|
|
|
|
auto IndexOf = [&](wxWindow *pWindow) {
|
|
|
|
return std::find(begin, end, pWindow) - begin;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto FindAncestor = [&]() {
|
|
|
|
wxWindow *pWindow = wxWindow::FindFocus();
|
|
|
|
unsigned index = rotationSize;
|
|
|
|
while ( pWindow &&
|
|
|
|
(rotationSize == (index = IndexOf(pWindow) ) ) )
|
|
|
|
pWindow = pWindow->GetParent();
|
|
|
|
return index;
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto idx = FindAncestor();
|
|
|
|
if (idx == rotationSize)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto idx2 = idx;
|
|
|
|
auto increment = (forward ? 1 : rotationSize - 1);
|
|
|
|
|
|
|
|
while( idx != (idx2 = (idx2 + increment) % rotationSize) ) {
|
|
|
|
wxWindow *toFocus = begin[idx2];
|
|
|
|
bool bIsAnEmptyDock=false;
|
|
|
|
if( idx2 != 1 )
|
2019-05-01 17:18:39 +00:00
|
|
|
bIsAnEmptyDock = ((idx2==0) ? toolManager.GetTopDock() : botDock)->
|
2018-10-23 20:34:33 +00:00
|
|
|
GetChildren().GetCount() < 1;
|
|
|
|
|
|
|
|
// Skip docks that are empty (Bug 1564).
|
|
|
|
if( !bIsAnEmptyDock ){
|
|
|
|
toFocus->SetFocus();
|
|
|
|
if ( FindAncestor() == idx2 )
|
|
|
|
// The focus took!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \todo Merge related methods, OnPrevTrack and OnNextTrack.
|
|
|
|
void DoPrevTrack(
|
|
|
|
AudacityProject &project, bool shift, bool circularTrackNavigation )
|
|
|
|
{
|
2019-06-06 13:55:34 +00:00
|
|
|
auto &projectHistory = ProjectHistory::Get( project );
|
2019-07-01 22:32:18 +00:00
|
|
|
auto &trackFocus = TrackFocus::Get( project );
|
2019-05-06 23:00:10 +00:00
|
|
|
auto &tracks = TrackList::Get( project );
|
2019-01-22 17:06:46 +00:00
|
|
|
auto &selectionState = SelectionState::Get( project );
|
2018-10-23 20:34:33 +00:00
|
|
|
|
2019-07-01 22:32:18 +00:00
|
|
|
auto t = trackFocus.Get();
|
2018-10-23 20:34:33 +00:00
|
|
|
if( t == NULL ) // if there isn't one, focus on last
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
t = *tracks.Any().rbegin();
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( t );
|
2019-06-10 15:20:07 +00:00
|
|
|
if (t)
|
|
|
|
t->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Track* p = NULL;
|
|
|
|
bool tSelected = false;
|
|
|
|
bool pSelected = false;
|
|
|
|
if( shift )
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
p = * -- tracks.FindLeader( t ); // Get previous track
|
2018-10-23 20:34:33 +00:00
|
|
|
if( p == NULL ) // On first track
|
|
|
|
{
|
|
|
|
// JKC: wxBell() is probably for accessibility, so a blind
|
|
|
|
// user knows they were at the top track.
|
|
|
|
wxBell();
|
|
|
|
if( circularTrackNavigation )
|
2019-05-06 23:00:10 +00:00
|
|
|
p = *tracks.Any().rbegin();
|
2018-10-23 20:34:33 +00:00
|
|
|
else
|
|
|
|
{
|
2019-06-10 15:20:07 +00:00
|
|
|
t->EnsureVisible();
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tSelected = t->GetSelected();
|
|
|
|
if (p)
|
|
|
|
pSelected = p->GetSelected();
|
|
|
|
if( tSelected && pSelected )
|
|
|
|
{
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *t, false, false );
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( p ); // move focus to next track up
|
2019-06-10 15:20:07 +00:00
|
|
|
if (p)
|
|
|
|
p->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if( tSelected && !pSelected )
|
|
|
|
{
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *p, true, false );
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( p ); // move focus to next track up
|
2019-06-10 15:20:07 +00:00
|
|
|
if (p)
|
|
|
|
p->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if( !tSelected && pSelected )
|
|
|
|
{
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *p, false, false );
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( p ); // move focus to next track up
|
2019-06-10 15:20:07 +00:00
|
|
|
if (p)
|
|
|
|
p->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if( !tSelected && !pSelected )
|
|
|
|
{
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *t, true, false );
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( p ); // move focus to next track up
|
2019-06-10 15:20:07 +00:00
|
|
|
if (p)
|
|
|
|
p->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
p = * -- tracks.FindLeader( t ); // Get previous track
|
2018-10-23 20:34:33 +00:00
|
|
|
if( p == NULL ) // On first track so stay there?
|
|
|
|
{
|
|
|
|
wxBell();
|
|
|
|
if( circularTrackNavigation )
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
auto range = tracks.Leaders();
|
2018-10-23 20:34:33 +00:00
|
|
|
p = * range.rbegin(); // null if range is empty
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( p ); // Wrap to the last track
|
2019-06-10 15:20:07 +00:00
|
|
|
if (p)
|
|
|
|
p->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-06-10 15:20:07 +00:00
|
|
|
t->EnsureVisible();
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( p ); // move focus to next track up
|
2019-06-10 15:20:07 +00:00
|
|
|
p->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The following method moves to the next track,
|
|
|
|
/// selecting and unselecting depending if you are on the start of a
|
|
|
|
/// block or not.
|
|
|
|
void DoNextTrack(
|
|
|
|
AudacityProject &project, bool shift, bool circularTrackNavigation )
|
|
|
|
{
|
2019-06-06 13:55:34 +00:00
|
|
|
auto &projectHistory = ProjectHistory::Get( project );
|
2019-07-01 22:32:18 +00:00
|
|
|
auto &trackFocus = TrackFocus::Get( project );
|
2019-05-06 23:00:10 +00:00
|
|
|
auto &tracks = TrackList::Get( project );
|
2019-01-22 17:06:46 +00:00
|
|
|
auto &selectionState = SelectionState::Get( project );
|
2018-10-23 20:34:33 +00:00
|
|
|
|
2019-07-01 22:32:18 +00:00
|
|
|
auto t = trackFocus.Get(); // Get currently focused track
|
2018-10-23 20:34:33 +00:00
|
|
|
if( t == NULL ) // if there isn't one, focus on first
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
t = *tracks.Any().begin();
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( t );
|
2019-06-10 15:20:07 +00:00
|
|
|
if (t)
|
|
|
|
t->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( shift )
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
auto n = * ++ tracks.FindLeader( t ); // Get next track
|
2018-10-23 20:34:33 +00:00
|
|
|
if( n == NULL ) // On last track so stay there
|
|
|
|
{
|
|
|
|
wxBell();
|
|
|
|
if( circularTrackNavigation )
|
2019-05-06 23:00:10 +00:00
|
|
|
n = *tracks.Any().begin();
|
2018-10-23 20:34:33 +00:00
|
|
|
else
|
|
|
|
{
|
2019-06-10 15:20:07 +00:00
|
|
|
t->EnsureVisible();
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto tSelected = t->GetSelected();
|
|
|
|
auto nSelected = n->GetSelected();
|
|
|
|
if( tSelected && nSelected )
|
|
|
|
{
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *t, false, false );
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( n ); // move focus to next track down
|
2019-06-10 15:20:07 +00:00
|
|
|
if (n)
|
|
|
|
n->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if( tSelected && !nSelected )
|
|
|
|
{
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *n, true, false );
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( n ); // move focus to next track down
|
2019-06-10 15:20:07 +00:00
|
|
|
if (n)
|
|
|
|
n->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if( !tSelected && nSelected )
|
|
|
|
{
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *n, false, false );
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( n ); // move focus to next track down
|
2019-06-10 15:20:07 +00:00
|
|
|
if (n)
|
|
|
|
n->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if( !tSelected && !nSelected )
|
|
|
|
{
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *t, true, false );
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( n ); // move focus to next track down
|
2019-06-10 15:20:07 +00:00
|
|
|
if (n)
|
|
|
|
n->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
auto n = * ++ tracks.FindLeader( t ); // Get next track
|
2018-10-23 20:34:33 +00:00
|
|
|
if( n == NULL ) // On last track so stay there
|
|
|
|
{
|
|
|
|
wxBell();
|
|
|
|
if( circularTrackNavigation )
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
n = *tracks.Any().begin();
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( n ); // Wrap to the first track
|
2019-06-10 15:20:07 +00:00
|
|
|
if (n)
|
|
|
|
n->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-06-10 15:20:07 +00:00
|
|
|
t->EnsureVisible();
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set( n ); // move focus to next track down
|
2019-06-10 15:20:07 +00:00
|
|
|
n->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-11-03 13:17:54 +00:00
|
|
|
/// Namespace for functions for project navigation menu (part of Extra menu)
|
2018-10-23 20:34:33 +00:00
|
|
|
namespace NavigationActions {
|
|
|
|
|
|
|
|
// exported helper functions
|
|
|
|
// none
|
|
|
|
|
|
|
|
// Menu handler functions
|
|
|
|
|
|
|
|
struct Handler
|
|
|
|
: CommandHandlerObject // MUST be the first base class!
|
2019-04-27 17:35:49 +00:00
|
|
|
, ClientData::Base
|
2018-10-23 20:34:33 +00:00
|
|
|
, PrefsListener
|
|
|
|
{
|
|
|
|
|
|
|
|
void OnPrevWindow(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
2019-05-28 17:12:56 +00:00
|
|
|
auto &window = GetProjectFrame( project );
|
|
|
|
auto isEnabled = window.IsEnabled();
|
2018-10-23 20:34:33 +00:00
|
|
|
|
|
|
|
wxWindow *w = wxGetTopLevelParent(wxWindow::FindFocus());
|
2019-05-28 17:12:56 +00:00
|
|
|
const auto & list = window.GetChildren();
|
2018-10-23 20:34:33 +00:00
|
|
|
auto iter = list.rbegin(), end = list.rend();
|
|
|
|
|
|
|
|
// If the project window has the current focus, start the search with the
|
|
|
|
// last child
|
2019-05-28 17:12:56 +00:00
|
|
|
if (w == &window)
|
2018-10-23 20:34:33 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
// Otherwise start the search with the current window's previous sibling
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (iter != end && *iter != w)
|
|
|
|
++iter;
|
|
|
|
if (iter != end)
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search for the previous toplevel window
|
|
|
|
for (; iter != end; ++iter)
|
|
|
|
{
|
|
|
|
// If it's a toplevel and is visible (we have come hidden windows), then
|
|
|
|
// we're done
|
|
|
|
w = *iter;
|
|
|
|
if (w->IsTopLevel() && w->IsShown() && isEnabled)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ran out of siblings, so make the current project active
|
|
|
|
if ((iter == end) && isEnabled)
|
|
|
|
{
|
2019-05-28 17:12:56 +00:00
|
|
|
w = &window;
|
2018-10-23 20:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// And make sure it's on top (only for floating windows...project window will
|
|
|
|
// not raise)
|
|
|
|
// (Really only works on Windows)
|
|
|
|
w->Raise();
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(__WXMAC__) || defined(__WXGTK__)
|
|
|
|
// bug 868
|
|
|
|
// Simulate a TAB key press before continuing, else the cycle of
|
|
|
|
// navigation among top level windows stops because the keystrokes don't
|
|
|
|
// go to the CommandManager.
|
|
|
|
if (dynamic_cast<wxDialog*>(w)) {
|
|
|
|
w->SetFocus();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnNextWindow(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
2019-05-28 17:12:56 +00:00
|
|
|
auto &window = GetProjectFrame( project );
|
|
|
|
auto isEnabled = window.IsEnabled();
|
2018-10-23 20:34:33 +00:00
|
|
|
|
|
|
|
wxWindow *w = wxGetTopLevelParent(wxWindow::FindFocus());
|
2019-05-28 17:12:56 +00:00
|
|
|
const auto & list = window.GetChildren();
|
2018-10-23 20:34:33 +00:00
|
|
|
auto iter = list.begin(), end = list.end();
|
|
|
|
|
|
|
|
// If the project window has the current focus, start the search with the
|
|
|
|
// first child
|
2019-05-28 17:12:56 +00:00
|
|
|
if (w == &window)
|
2018-10-23 20:34:33 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
// Otherwise start the search with the current window's next sibling
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Find the window in this projects children. If the window with the
|
|
|
|
// focus isn't a child of this project (like when a dialog is created
|
|
|
|
// without specifying a parent), then we'll get back NULL here.
|
|
|
|
while (iter != end && *iter != w)
|
|
|
|
++iter;
|
|
|
|
if (iter != end)
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search for the next toplevel window
|
|
|
|
for (; iter != end; ++iter)
|
|
|
|
{
|
|
|
|
// If it's a toplevel, visible (we have hidden windows) and is enabled,
|
|
|
|
// then we're done. The IsEnabled() prevents us from moving away from
|
|
|
|
// a modal dialog because all other toplevel windows will be disabled.
|
|
|
|
w = *iter;
|
|
|
|
if (w->IsTopLevel() && w->IsShown() && w->IsEnabled())
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ran out of siblings, so make the current project active
|
|
|
|
if ((iter == end) && isEnabled)
|
|
|
|
{
|
2019-05-28 17:12:56 +00:00
|
|
|
w = &window;
|
2018-10-23 20:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// And make sure it's on top (only for floating windows...project window will
|
|
|
|
// not raise)
|
|
|
|
// (Really only works on Windows)
|
|
|
|
w->Raise();
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(__WXMAC__) || defined(__WXGTK__)
|
|
|
|
// bug 868
|
|
|
|
// Simulate a TAB key press before continuing, else the cycle of
|
|
|
|
// navigation among top level windows stops because the keystrokes don't
|
|
|
|
// go to the CommandManager.
|
|
|
|
if (dynamic_cast<wxDialog*>(w)) {
|
|
|
|
w->SetFocus();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnPrevFrame(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
|
|
|
NextOrPrevFrame(project, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnNextFrame(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
|
|
|
NextOrPrevFrame(project, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handler state:
|
|
|
|
bool mCircularTrackNavigation{};
|
|
|
|
|
|
|
|
void OnCursorUp(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
|
|
|
DoPrevTrack( project, false, mCircularTrackNavigation );
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnCursorDown(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
|
|
|
DoNextTrack( project, false, mCircularTrackNavigation );
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnFirstTrack(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
2019-07-01 22:32:18 +00:00
|
|
|
auto &trackFocus = TrackFocus::Get( project );
|
2019-05-06 23:00:10 +00:00
|
|
|
auto &tracks = TrackList::Get( project );
|
2018-10-23 20:34:33 +00:00
|
|
|
|
2019-07-01 22:32:18 +00:00
|
|
|
auto t = trackFocus.Get();
|
2018-10-23 20:34:33 +00:00
|
|
|
if (!t)
|
|
|
|
return;
|
|
|
|
|
2019-05-06 23:00:10 +00:00
|
|
|
auto f = *tracks.Any().begin();
|
2018-10-23 20:34:33 +00:00
|
|
|
if (t != f)
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set(f);
|
2019-06-10 15:20:07 +00:00
|
|
|
if (f)
|
|
|
|
f->EnsureVisible( t != f );
|
2018-10-23 20:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void OnLastTrack(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
2019-07-01 22:32:18 +00:00
|
|
|
auto &trackFocus = TrackFocus::Get( project );
|
2019-05-06 23:00:10 +00:00
|
|
|
auto &tracks = TrackList::Get( project );
|
2018-10-23 20:34:33 +00:00
|
|
|
|
2019-07-01 22:32:18 +00:00
|
|
|
Track *t = trackFocus.Get();
|
2018-10-23 20:34:33 +00:00
|
|
|
if (!t)
|
|
|
|
return;
|
|
|
|
|
2019-05-06 23:00:10 +00:00
|
|
|
auto l = *tracks.Any().rbegin();
|
2018-10-23 20:34:33 +00:00
|
|
|
if (t != l)
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.Set(l);
|
2019-06-10 15:20:07 +00:00
|
|
|
if (l)
|
|
|
|
l->EnsureVisible( t != l );
|
2018-10-23 20:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void OnShiftUp(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
|
|
|
DoPrevTrack( project, true, mCircularTrackNavigation );
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnShiftDown(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
|
|
|
DoNextTrack( project, true, mCircularTrackNavigation );
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnToggle(const CommandContext &context)
|
|
|
|
{
|
|
|
|
auto &project = context.project;
|
2019-07-01 22:32:18 +00:00
|
|
|
auto &trackFocus = TrackFocus::Get( project );
|
2019-01-22 17:06:46 +00:00
|
|
|
auto &selectionState = SelectionState::Get( project );
|
2018-10-23 20:34:33 +00:00
|
|
|
|
|
|
|
Track *t;
|
|
|
|
|
2019-07-01 22:32:18 +00:00
|
|
|
t = trackFocus.Get(); // Get currently focused track
|
2018-10-23 20:34:33 +00:00
|
|
|
if (!t)
|
|
|
|
return;
|
|
|
|
|
|
|
|
selectionState.SelectTrack
|
2018-02-05 23:30:18 +00:00
|
|
|
( *t, !t->GetSelected(), true );
|
2019-06-10 15:20:07 +00:00
|
|
|
t->EnsureVisible( true );
|
2018-10-23 20:34:33 +00:00
|
|
|
|
2019-07-01 22:32:18 +00:00
|
|
|
trackFocus.UpdateAccessibility();
|
2018-10-23 20:34:33 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UpdatePrefs() override
|
|
|
|
{
|
|
|
|
mCircularTrackNavigation =
|
|
|
|
gPrefs->ReadBool(wxT("/GUI/CircularTrackNavigation"), false);
|
|
|
|
}
|
2018-10-28 16:58:39 +00:00
|
|
|
Handler()
|
|
|
|
{
|
|
|
|
UpdatePrefs();
|
|
|
|
}
|
2019-07-02 22:57:53 +00:00
|
|
|
Handler( const Handler & ) PROHIBITED;
|
|
|
|
Handler &operator=( const Handler & ) PROHIBITED;
|
2018-10-23 20:34:33 +00:00
|
|
|
|
|
|
|
}; // struct Handler
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// Handler is stateful. Needs a factory registered with
|
|
|
|
// AudacityProject.
|
2019-04-27 17:35:49 +00:00
|
|
|
static const AudacityProject::AttachedObjects::RegisteredFactory key{
|
|
|
|
[](AudacityProject&) {
|
|
|
|
return std::make_unique< NavigationActions::Handler >(); } };
|
|
|
|
|
2018-10-23 20:34:33 +00:00
|
|
|
static CommandHandlerObject &findCommandHandler(AudacityProject &project) {
|
2019-04-27 17:35:49 +00:00
|
|
|
return project.AttachedObjects::Get< NavigationActions::Handler >( key );
|
2018-10-23 20:34:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Menu definitions
|
|
|
|
|
2019-01-06 16:31:52 +00:00
|
|
|
#define FN(X) (& NavigationActions::Handler :: X)
|
2018-10-23 20:34:33 +00:00
|
|
|
|
2020-02-01 04:44:00 +00:00
|
|
|
namespace {
|
|
|
|
using namespace MenuTable;
|
|
|
|
BaseItemSharedPtr ExtraGlobalCommands()
|
2018-10-23 20:34:33 +00:00
|
|
|
{
|
|
|
|
// Ceci n'est pas un menu
|
|
|
|
using Options = CommandManager::Options;
|
2019-01-06 16:31:52 +00:00
|
|
|
|
2019-01-09 16:27:32 +00:00
|
|
|
static BaseItemSharedPtr items{
|
2020-01-29 18:37:02 +00:00
|
|
|
( FinderScope{ findCommandHandler },
|
2019-01-09 21:02:51 +00:00
|
|
|
Items( wxT("Navigation"),
|
2018-10-23 20:34:33 +00:00
|
|
|
Command( wxT("PrevWindow"), XXO("Move Backward Through Active Windows"),
|
|
|
|
FN(OnPrevWindow), AlwaysEnabledFlag,
|
|
|
|
Options{ wxT("Alt+Shift+F6") }.IsGlobal() ),
|
|
|
|
Command( wxT("NextWindow"), XXO("Move Forward Through Active Windows"),
|
|
|
|
FN(OnNextWindow), AlwaysEnabledFlag,
|
|
|
|
Options{ wxT("Alt+F6") }.IsGlobal() )
|
2019-01-09 16:27:32 +00:00
|
|
|
) ) };
|
|
|
|
return items;
|
2018-10-23 20:34:33 +00:00
|
|
|
}
|
|
|
|
|
2020-02-01 04:44:00 +00:00
|
|
|
AttachedItem sAttachment2{
|
|
|
|
wxT("Optional/Extra/Part2"),
|
|
|
|
Shared( ExtraGlobalCommands() )
|
|
|
|
};
|
|
|
|
|
|
|
|
BaseItemSharedPtr ExtraFocusMenu()
|
2018-10-23 20:34:33 +00:00
|
|
|
{
|
2020-01-28 21:37:11 +00:00
|
|
|
static const auto FocusedTracksFlags = TracksExistFlag() | TrackPanelHasFocus();
|
2018-10-23 20:34:33 +00:00
|
|
|
|
2019-01-09 16:27:32 +00:00
|
|
|
static BaseItemSharedPtr menu{
|
2020-01-29 18:37:02 +00:00
|
|
|
( FinderScope{ findCommandHandler },
|
2019-01-09 21:02:51 +00:00
|
|
|
Menu( wxT("Focus"), XO("F&ocus"),
|
2018-10-23 20:34:33 +00:00
|
|
|
Command( wxT("PrevFrame"),
|
|
|
|
XXO("Move &Backward from Toolbars to Tracks"), FN(OnPrevFrame),
|
|
|
|
AlwaysEnabledFlag, wxT("Ctrl+Shift+F6") ),
|
|
|
|
Command( wxT("NextFrame"),
|
|
|
|
XXO("Move F&orward from Toolbars to Tracks"), FN(OnNextFrame),
|
|
|
|
AlwaysEnabledFlag, wxT("Ctrl+F6") ),
|
|
|
|
Command( wxT("PrevTrack"), XXO("Move Focus to &Previous Track"),
|
|
|
|
FN(OnCursorUp), FocusedTracksFlags, wxT("Up") ),
|
|
|
|
Command( wxT("NextTrack"), XXO("Move Focus to &Next Track"),
|
|
|
|
FN(OnCursorDown), FocusedTracksFlags, wxT("Down") ),
|
|
|
|
Command( wxT("FirstTrack"), XXO("Move Focus to &First Track"),
|
|
|
|
FN(OnFirstTrack), FocusedTracksFlags, wxT("Ctrl+Home") ),
|
|
|
|
Command( wxT("LastTrack"), XXO("Move Focus to &Last Track"),
|
|
|
|
FN(OnLastTrack), FocusedTracksFlags, wxT("Ctrl+End") ),
|
|
|
|
Command( wxT("ShiftUp"), XXO("Move Focus to P&revious and Select"),
|
|
|
|
FN(OnShiftUp), FocusedTracksFlags, wxT("Shift+Up") ),
|
|
|
|
Command( wxT("ShiftDown"), XXO("Move Focus to N&ext and Select"),
|
|
|
|
FN(OnShiftDown), FocusedTracksFlags, wxT("Shift+Down") ),
|
|
|
|
Command( wxT("Toggle"), XXO("&Toggle Focused Track"), FN(OnToggle),
|
|
|
|
FocusedTracksFlags, wxT("Return") ),
|
|
|
|
Command( wxT("ToggleAlt"), XXO("Toggle Focuse&d Track"), FN(OnToggle),
|
|
|
|
FocusedTracksFlags, wxT("NUMPAD_ENTER") )
|
2019-01-09 16:27:32 +00:00
|
|
|
) ) };
|
|
|
|
return menu;
|
2018-10-23 20:34:33 +00:00
|
|
|
}
|
|
|
|
|
2020-02-01 04:44:00 +00:00
|
|
|
AttachedItem sAttachment3{
|
|
|
|
wxT("Optional/Extra/Part2"),
|
|
|
|
Shared( ExtraFocusMenu() )
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-10-23 20:34:33 +00:00
|
|
|
#undef FN
|