1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-18 00:50:05 +02:00
audacity/src/Menus.cpp

832 lines
24 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Menus.cpp
Dominic Mazzoni
Brian Gunlogson
et al.
*******************************************************************//**
\file Menus.cpp
\brief Functions that provide most of the menu actions.
This file implements the method that creates the menu bar, plus
most of the methods that get called when you select an item
from a menu.
*//****************************************************************//**
\class MenuCommandHandler
\brief MenuCommandHandler contains many command handlers for individual
menu items.
*//****************************************************************//**
\class MenuCreator
\brief MenuCreator is responsible for creating the main menu bar.
*//****************************************************************//**
\class MenuManager
\brief MenuManager handles updates to menu state.
*//*******************************************************************/
#include "Audacity.h"
#include "Menus.h"
#include "commands/CommandManager.h"
#include "commands/CommandContext.h"
#include <cfloat>
#include <iterator>
#include <algorithm>
#include <limits>
#include <math.h>
#include <wx/defs.h>
#include <wx/docview.h>
#include <wx/filedlg.h>
#include <wx/textfile.h>
#include <wx/textdlg.h>
#include <wx/progdlg.h>
#include <wx/scrolbar.h>
#include <wx/ffile.h>
#include <wx/statusbr.h>
#include <wx/utils.h>
#include "TrackPanel.h"
#include "widgets/Ruler.h"
#include "effects/EffectManager.h"
#include "AudacityApp.h"
#include "AudioIO.h"
#include "float_cast.h"
#include "LabelTrack.h"
#include "import/ImportRaw.h"
#include "prefs/PrefsDialog.h"
#include "prefs/PlaybackPrefs.h"
#include "LyricsWindow.h"
#include "MixerBoard.h"
#include "Project.h"
#include "Internat.h"
#include "FileFormats.h"
#include "ModuleManager.h"
#include "Prefs.h"
#ifdef USE_MIDI
#include "NoteTrack.h"
#endif // USE_MIDI
#include "ondemand/ODManager.h"
#include "prefs/BatchPrefs.h"
#include "toolbars/ToolManager.h"
#include "toolbars/ControlToolBar.h"
#include "toolbars/EditToolBar.h"
#include "tracks/ui/SelectHandle.h"
#include "widgets/LinkingHtmlWindow.h"
#include "Experimental.h"
#include "PlatformCompatibility.h"
#include "UndoManager.h"
#include "WaveTrack.h"
#include "prefs/TracksPrefs.h"
#include "widgets/ErrorDialog.h"
#include "./commands/AudacityCommand.h"
static const AudacityProject::RegisteredAttachedObjectFactory factory{ []{
return std::make_unique< MenuCommandHandler >();
} };
PrefsListener::~PrefsListener()
{
}
void PrefsListener::UpdatePrefs()
{
}
MenuCommandHandler &GetMenuCommandHandler(AudacityProject &project)
{
return static_cast<MenuCommandHandler&>(
project.GetAttachedObject( factory ) );
}
MenuManager &GetMenuManager(AudacityProject &project)
{ return *project.mMenuManager; }
MenuCommandHandler::MenuCommandHandler()
{
}
MenuCommandHandler::~MenuCommandHandler()
{
}
MenuCreator::MenuCreator(){
}
MenuCreator::~MenuCreator()
{
}
void MenuCommandHandler::UpdatePrefs()
{
}
void MenuManager::UpdatePrefs()
{
bool bSelectAllIfNone;
gPrefs->Read(wxT("/GUI/SelectAllOnNone"), &bSelectAllIfNone, false);
// 0 is grey out, 1 is Autoselect, 2 is Give warnings.
#ifdef EXPERIMENTAL_DA
// DA warns or greys out.
mWhatIfNoSelection = bSelectAllIfNone ? 2 : 0;
#else
// Audacity autoselects or warns.
mWhatIfNoSelection = bSelectAllIfNone ? 1 : 2;
#endif
mStopIfWasPaused = true; // not configurable for now, but could be later.
}
namespace MenuTable {
BaseItem::~BaseItem() {}
ComputedItem::~ComputedItem() {}
GroupItem::GroupItem( BaseItemPtrs &&items_ )
: items{ std::move( items_ ) }
{
}
void GroupItem::AppendOne( BaseItemPtr&& ptr )
{
items.push_back( std::move( ptr ) );
}
GroupItem::~GroupItem() {}
MenuItem::MenuItem( const wxString &title_, BaseItemPtrs &&items_ )
: GroupItem{ std::move( items_ ) }, title{ title_ }
{
wxASSERT( !title.empty() );
}
MenuItem::~MenuItem() {}
ConditionalGroupItem::ConditionalGroupItem(
Condition condition_, BaseItemPtrs &&items_ )
: GroupItem{ std::move( items_ ) }, condition{ condition_ }
{
}
ConditionalGroupItem::~ConditionalGroupItem() {}
SeparatorItem::~SeparatorItem() {}
CommandItem::CommandItem(const wxString &name_,
const wxString &label_in_,
bool hasDialog_,
CommandHandlerFinder finder_,
CommandFunctorPointer callback_,
CommandFlag flags_,
const CommandManager::Options &options_)
: name{ name_ }, label_in{ label_in_ }, hasDialog{ hasDialog_ }
, finder{ finder_ }, callback{ callback_ }
, flags{ flags_ }, options{ options_ }
{}
CommandItem::~CommandItem() {}
CommandGroupItem::CommandGroupItem(const wxString &name_,
const IdentInterfaceSymbol items_[],
size_t nItems_,
CommandHandlerFinder finder_,
CommandFunctorPointer callback_,
CommandFlag flags_,
bool isEffect_)
: name{ name_ }, items{ items_, items_ + nItems_ }
, finder{ finder_ }, callback{ callback_ }
, flags{ flags_ }, isEffect{ isEffect_ }
{}
CommandGroupItem::~CommandGroupItem() {}
SpecialItem::~SpecialItem() {}
}
namespace {
void VisitItem( AudacityProject &project, MenuTable::BaseItem *pItem );
void VisitItems(
AudacityProject &project, const MenuTable::BaseItemPtrs &items )
{
for ( auto &pSubItem : items )
VisitItem( project, pSubItem.get() );
}
void VisitItem( AudacityProject &project, MenuTable::BaseItem *pItem )
{
if (!pItem)
return;
auto &manager = *project.GetCommandManager();
using namespace MenuTable;
if (const auto pComputed =
dynamic_cast<ComputedItem*>( pItem )) {
// TODO maybe? memo-ize the results of the function, but that requires
// invalidating the memo at the right times
auto result = pComputed->factory( project );
if (result)
// recursion
VisitItem( project, result.get() );
}
else
if (const auto pCommand =
dynamic_cast<CommandItem*>( pItem )) {
manager.AddItem(
pCommand->name, pCommand->label_in, pCommand->hasDialog,
pCommand->finder, pCommand->callback,
pCommand->flags, pCommand->options
);
}
else
if (const auto pCommandList =
dynamic_cast<CommandGroupItem*>( pItem ) ) {
manager.AddItemList(pCommandList->name,
pCommandList->items.data(), pCommandList->items.size(),
pCommandList->finder, pCommandList->callback,
pCommandList->flags, pCommandList->isEffect);
}
else
if (const auto pMenu =
dynamic_cast<MenuItem*>( pItem )) {
manager.BeginMenu( pMenu->title );
// recursion
VisitItems( project, pMenu->items );
manager.EndMenu();
}
else
if (const auto pConditionalGroup =
dynamic_cast<ConditionalGroupItem*>( pItem )) {
const auto flag = pConditionalGroup->condition();
if (!flag)
manager.BeginOccultCommands();
// recursion
VisitItems( project, pConditionalGroup->items );
if (!flag)
manager.EndOccultCommands();
}
else
if (const auto pGroup =
dynamic_cast<GroupItem*>( pItem )) {
// recursion
VisitItems( project, pGroup->items );
}
else
if (dynamic_cast<SeparatorItem*>( pItem )) {
manager.AddSeparator();
}
else
if (const auto pSpecial =
dynamic_cast<SpecialItem*>( pItem )) {
const auto pMenu = manager.CurrentMenu();
wxASSERT( pMenu );
pSpecial->fn( project, *pMenu );
}
else
wxASSERT( false );
}
}
/// CreateMenusAndCommands builds the menus, and also rebuilds them after
/// changes in configured preferences - for example changes in key-bindings
/// affect the short-cut key legend that appears beside each command,
// To supply the "finder" argument in AddItem calls
static CommandHandlerObject &findMenuCommandHandler(AudacityProject &project)
{ return GetMenuCommandHandler( project ); }
#define FN(X) findMenuCommandHandler, \
static_cast<CommandFunctorPointer>(& MenuCommandHandler :: X)
#define XXO(X) _(X), wxString{X}.Contains("...")
MenuTable::BaseItemPtr FileMenu( AudacityProject& );
MenuTable::BaseItemPtr EditMenu( AudacityProject& );
MenuTable::BaseItemPtr SelectMenu( AudacityProject& );
MenuTable::BaseItemPtr ViewMenu( AudacityProject& );
MenuTable::BaseItemPtr TransportMenu( AudacityProject& );
MenuTable::BaseItemPtr TracksMenu( AudacityProject& );
MenuTable::BaseItemPtr GenerateMenu( AudacityProject& );
MenuTable::BaseItemPtr EffectMenu( AudacityProject& );
MenuTable::BaseItemPtr AnalyzeMenu( AudacityProject& );
MenuTable::BaseItemPtr ToolsMenu( AudacityProject& );
MenuTable::BaseItemPtr WindowMenu( AudacityProject& );
MenuTable::BaseItemPtr ExtraMenu( AudacityProject& );
MenuTable::BaseItemPtr HelpMenu( AudacityProject& );
// Table of menu factories.
// TODO: devise a registration system instead.
static const auto menuTree = MenuTable::Items(
FileMenu
, EditMenu
, SelectMenu
, ViewMenu
, TransportMenu
, TracksMenu
, GenerateMenu
, EffectMenu
, AnalyzeMenu
, ToolsMenu
, WindowMenu
, ExtraMenu
, HelpMenu
);
void MenuCreator::CreateMenusAndCommands(AudacityProject &project)
{
CommandManager *c = project.GetCommandManager();
// The list of defaults to exclude depends on
// preference wxT("/GUI/Shortcuts/FullDefaults"), which may have changed.
c->SetMaxList();
auto menubar = c->AddMenuBar(wxT("appmenu"));
wxASSERT(menubar);
VisitItem( project, menuTree.get() );
project.SetMenuBar(menubar.release());
mLastFlags = AlwaysEnabledFlag;
#if defined(__WXDEBUG__)
// c->CheckDups();
#endif
}
#undef XXO
#undef FN
// TODO: This surely belongs in CommandManager?
void MenuManager::ModifyUndoMenuItems(AudacityProject &project)
{
wxString desc;
auto &undoManager = *project.GetUndoManager();
auto &commandManager = *project.GetCommandManager();
int cur = undoManager.GetCurrentState();
if (undoManager.UndoAvailable()) {
undoManager.GetShortDescription(cur, &desc);
commandManager.Modify(wxT("Undo"),
wxString::Format(_("&Undo %s"),
desc));
commandManager.Enable(wxT("Undo"), project.UndoAvailable());
}
else {
commandManager.Modify(wxT("Undo"),
_("&Undo"));
}
if (undoManager.RedoAvailable()) {
undoManager.GetShortDescription(cur+1, &desc);
commandManager.Modify(wxT("Redo"),
wxString::Format(_("&Redo %s"),
desc));
commandManager.Enable(wxT("Redo"), project.RedoAvailable());
}
else {
commandManager.Modify(wxT("Redo"),
_("&Redo"));
commandManager.Enable(wxT("Redo"), false);
}
}
void MenuCreator::RebuildMenuBar(AudacityProject &project)
{
// On OSX, we can't rebuild the menus while a modal dialog is being shown
// since the enabled state for menus like Quit and Preference gets out of
// sync with wxWidgets idea of what it should be.
#if defined(__WXMAC__) && defined(__WXDEBUG__)
{
wxDialog *dlg =
wxDynamicCast(wxGetTopLevelParent(wxWindow::FindFocus()), wxDialog);
wxASSERT((!dlg || !dlg->IsModal()));
}
#endif
// Delete the menus, since we will soon recreate them.
// Rather oddly, the menus don't vanish as a result of doing this.
{
std::unique_ptr<wxMenuBar> menuBar{ project.GetMenuBar() };
project.DetachMenuBar();
// menuBar gets deleted here
}
project.GetCommandManager()->PurgeData();
CreateMenusAndCommands(project);
ModuleManager::Get().Dispatch(MenusRebuilt);
}
CommandFlag MenuManager::GetFocusedFrame(AudacityProject &project)
{
wxWindow *w = wxWindow::FindFocus();
while (w && project.GetToolManager() && project.GetTrackPanel()) {
if (w == project.GetToolManager()->GetTopDock()) {
return TopDockHasFocus;
}
if (w == project.GetRulerPanel())
return RulerHasFocus;
if (w == project.GetTrackPanel()) {
return TrackPanelHasFocus;
}
// LIE if Lyrics window has focus.
// we want to act as if TrackPanel has focus.
if (w == project.GetLyricsWindow()) {
return TrackPanelHasFocus;
}
if (w == project.GetToolManager()->GetBotDock()) {
return BotDockHasFocus;
}
w = w->GetParent();
}
return AlwaysEnabledFlag;
}
CommandFlag MenuManager::GetUpdateFlags
(AudacityProject &project, bool checkActive)
{
// 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.
auto flags = AlwaysEnabledFlag;
// static variable, used to remember flags for next time.
static auto lastFlags = flags;
if (auto focus = wxWindow::FindFocus()) {
while (focus && focus->GetParent())
focus = focus->GetParent();
if (focus && !static_cast<wxTopLevelWindow*>(focus)->IsIconized())
flags |= NotMinimizedFlag;
}
// quick 'short-circuit' return.
if ( checkActive && !project.IsActive() ){
// short cirucit return should preserve flags that have not been calculated.
flags = (lastFlags & ~NotMinimizedFlag) | flags;
lastFlags = flags;
return flags;
}
if (!gAudioIO->IsAudioTokenActive(project.GetAudioIOToken()))
flags |= AudioIONotBusyFlag;
else
flags |= AudioIOBusyFlag;
if( gAudioIO->IsPaused() )
flags |= PausedFlag;
else
flags |= NotPausedFlag;
auto &viewInfo = project.GetViewInfo();
const auto &selectedRegion = viewInfo.selectedRegion;
if (!selectedRegion.isPoint())
flags |= TimeSelectedFlag;
auto tracks = project.GetTracks();
auto trackRange = tracks->Any();
if ( trackRange )
flags |= TracksExistFlag;
trackRange.Visit(
[&](LabelTrack *lt) {
flags |= LabelTracksExistFlag;
if (lt->GetSelected()) {
flags |= TracksSelectedFlag;
for (int i = 0; i < lt->GetNumLabels(); i++) {
const LabelStruct *ls = lt->GetLabel(i);
if (ls->getT0() >= selectedRegion.t0() &&
ls->getT1() <= selectedRegion.t1()) {
flags |= LabelsSelectedFlag;
break;
}
}
}
if (lt->IsTextSelected()) {
flags |= CutCopyAvailableFlag;
}
},
[&](WaveTrack *t) {
flags |= WaveTracksExistFlag;
flags |= PlayableTracksExistFlag;
if (t->GetSelected()) {
flags |= TracksSelectedFlag;
// TODO: more-than-two-channels
if (TrackList::Channels(t).size() > 1) {
flags |= StereoRequiredFlag;
}
flags |= WaveTracksSelectedFlag;
flags |= AudioTracksSelectedFlag;
}
if( t->GetEndTime() > t->GetStartTime() )
flags |= HasWaveDataFlag;
}
#if defined(USE_MIDI)
,
[&](NoteTrack *nt) {
flags |= NoteTracksExistFlag;
#ifdef EXPERIMENTAL_MIDI_OUT
flags |= PlayableTracksExistFlag;
#endif
if (nt->GetSelected()) {
flags |= TracksSelectedFlag;
flags |= NoteTracksSelectedFlag;
flags |= AudioTracksSelectedFlag; // even if not EXPERIMENTAL_MIDI_OUT
}
}
#endif
);
if((AudacityProject::msClipT1 - AudacityProject::msClipT0) > 0.0)
flags |= ClipboardFlag;
auto &undoManager = *project.GetUndoManager();
if (undoManager.UnsavedChanges() || !project.IsProjectSaved())
flags |= UnsavedChangesFlag;
if (!mLastEffect.IsEmpty())
flags |= HasLastEffectFlag;
if (project.UndoAvailable())
flags |= UndoAvailableFlag;
if (project.RedoAvailable())
flags |= RedoAvailableFlag;
if (project.ZoomInAvailable() && (flags & TracksExistFlag))
flags |= ZoomInAvailableFlag;
if (project.ZoomOutAvailable() && (flags & TracksExistFlag))
flags |= ZoomOutAvailableFlag;
// TextClipFlag is currently unused (Jan 2017, 2.1.3 alpha)
// and LabelTrack::IsTextClipSupported() is quite slow on Linux,
// so disable for now (See bug 1575).
// if ((flags & LabelTracksExistFlag) && LabelTrack::IsTextClipSupported())
// flags |= TextClipFlag;
flags |= GetFocusedFrame(project);
double start, end;
project.GetPlayRegion(&start, &end);
if (project.IsPlayRegionLocked())
flags |= PlayRegionLockedFlag;
else if (start != end)
flags |= PlayRegionNotLockedFlag;
if (flags & AudioIONotBusyFlag) {
if (flags & TimeSelectedFlag) {
if (flags & TracksSelectedFlag) {
flags |= CutCopyAvailableFlag;
}
}
}
if (wxGetApp().GetRecentFiles()->GetCount() > 0)
flags |= HaveRecentFiles;
if (project.IsSyncLocked())
flags |= IsSyncLockedFlag;
else
flags |= IsNotSyncLockedFlag;
if (!EffectManager::Get().RealtimeIsActive())
flags |= IsRealtimeNotActiveFlag;
if (!project.IsCapturing())
flags |= CaptureNotBusyFlag;
ControlToolBar *bar = project.GetControlToolBar();
if (bar->ControlToolBar::CanStopAudioStream())
flags |= CanStopAudioStreamFlag;
lastFlags = flags;
return flags;
}
void MenuManager::ModifyAllProjectToolbarMenus()
{
AProjectArray::iterator i;
for (i = gAudacityProjects.begin(); i != gAudacityProjects.end(); ++i) {
auto &project = **i;
GetMenuManager(project).ModifyToolbarMenus(project);
}
}
void MenuManager::ModifyToolbarMenus(AudacityProject &project)
{
// Refreshes can occur during shutdown and the toolmanager may already
// be deleted, so protect against it.
auto toolManager = project.GetToolManager();
if (!toolManager) {
return;
}
auto &commandManager = *project.GetCommandManager();
commandManager.Check(wxT("ShowScrubbingTB"),
toolManager->IsVisible(ScrubbingBarID));
commandManager.Check(wxT("ShowDeviceTB"),
toolManager->IsVisible(DeviceBarID));
commandManager.Check(wxT("ShowEditTB"),
toolManager->IsVisible(EditBarID));
commandManager.Check(wxT("ShowMeterTB"),
toolManager->IsVisible(MeterBarID));
commandManager.Check(wxT("ShowRecordMeterTB"),
toolManager->IsVisible(RecordMeterBarID));
commandManager.Check(wxT("ShowPlayMeterTB"),
toolManager->IsVisible(PlayMeterBarID));
commandManager.Check(wxT("ShowMixerTB"),
toolManager->IsVisible(MixerBarID));
commandManager.Check(wxT("ShowSelectionTB"),
toolManager->IsVisible(SelectionBarID));
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
commandManager.Check(wxT("ShowSpectralSelectionTB"),
toolManager->IsVisible(SpectralSelectionBarID));
#endif
commandManager.Check(wxT("ShowToolsTB"),
toolManager->IsVisible(ToolsBarID));
commandManager.Check(wxT("ShowTranscriptionTB"),
toolManager->IsVisible(TranscriptionBarID));
commandManager.Check(wxT("ShowTransportTB"),
toolManager->IsVisible(TransportBarID));
// Now, go through each toolbar, and call EnableDisableButtons()
for (int i = 0; i < ToolBarCount; i++) {
toolManager->GetToolBar(i)->EnableDisableButtons();
}
// These don't really belong here, but it's easier and especially so for
// the Edit toolbar and the sync-lock menu item.
bool active;
gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"),&active, false);
commandManager.Check(wxT("SoundActivation"), active);
#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"),&active, false);
commandManager.Check(wxT("AutomatedInputLevelAdjustmentOnOff"), active);
#endif
active = TracksPrefs::GetPinnedHeadPreference();
commandManager.Check(wxT("PinnedHead"), active);
#ifdef EXPERIMENTAL_DA
gPrefs->Read(wxT("/AudioIO/Duplex"),&active, false);
#else
gPrefs->Read(wxT("/AudioIO/Duplex"),&active, true);
#endif
commandManager.Check(wxT("Overdub"), active);
gPrefs->Read(wxT("/AudioIO/SWPlaythrough"),&active, false);
commandManager.Check(wxT("SWPlaythrough"), active);
gPrefs->Read(wxT("/GUI/SyncLockTracks"), &active, false);
project.SetSyncLock(active);
commandManager.Check(wxT("SyncLock"), active);
gPrefs->Read(wxT("/GUI/TypeToCreateLabel"),&active, true);
commandManager.Check(wxT("TypeToCreateLabel"), active);
}
// checkActive is a temporary hack that should be removed as soon as we
// get multiple effect preview working
void MenuManager::UpdateMenus(AudacityProject &project, bool checkActive)
{
//ANSWER-ME: Why UpdateMenus only does active project?
//JKC: Is this test fixing a bug when multiple projects are open?
//so that menu states work even when different in different projects?
if (&project != GetActiveProject())
return;
auto flags = GetMenuManager(project).GetUpdateFlags(project, checkActive);
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()?
//ANSWER: Because flags2 is used in the menu enable/disable.
//The effect still needs flags to determine whether it will need
//to actually do the 'select all' to make the command valid.
if (mWhatIfNoSelection != 0)
{
if ((flags & TracksExistFlag))
{
flags2 |= TracksSelectedFlag;
if ((flags & WaveTracksExistFlag))
{
flags2 |= TimeSelectedFlag
| WaveTracksSelectedFlag
| CutCopyAvailableFlag;
}
}
}
if( mStopIfWasPaused )
{
if( flags & PausedFlag ){
flags2 |= AudioIONotBusyFlag;
}
}
// Return from this function if nothing's changed since
// the last time we were here.
if (flags == mLastFlags)
return;
mLastFlags = flags;
auto &commandManager = *project.GetCommandManager();
commandManager.EnableUsingFlags(flags2 , NoFlagsSpecified);
// 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.
// 0 is grey out, 1 is Autoselect, 2 is Give warnings.
if (mWhatIfNoSelection != 0)
{
if (!(flags & TimeSelectedFlag) | !(flags & TracksSelectedFlag))
{
commandManager.Enable(wxT("SplitCut"), false);
commandManager.Enable(wxT("SplitDelete"), false);
}
if (!(flags & WaveTracksSelectedFlag))
{
commandManager.Enable(wxT("Split"), false);
}
if (!(flags & TimeSelectedFlag) | !(flags & WaveTracksSelectedFlag))
{
commandManager.Enable(wxT("ExportSel"), false);
commandManager.Enable(wxT("SplitNew"), false);
}
if (!(flags & TimeSelectedFlag) | !(flags & AudioTracksSelectedFlag))
{
commandManager.Enable(wxT("Trim"), false);
}
}
#if 0
if (flags & CutCopyAvailableFlag) {
GetCommandManager()->Enable(wxT("Copy"), true);
GetCommandManager()->Enable(wxT("Cut"), true);
}
#endif
MenuManager::ModifyToolbarMenus(project);
}
/// The following method moves to the previous track
/// selecting and unselecting depending if you are on the start of a
/// block or not.
void MenuCreator::RebuildAllMenuBars()
{
for( size_t i = 0; i < gAudacityProjects.size(); i++ ) {
AudacityProject *p = gAudacityProjects[i].get();
GetMenuManager(*p).RebuildMenuBar(*p);
#if defined(__WXGTK__)
// Workaround for:
//
// http://bugzilla.audacityteam.org/show_bug.cgi?id=458
//
// This workaround should be removed when Audacity updates to wxWidgets 3.x which has a fix.
wxRect r = p->GetRect();
p->SetSize(wxSize(1,1));
p->SetSize(r.GetSize());
#endif
}
}