1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-03 01:19:24 +02:00

Bug1119: Windows menu on Mac should list all project names...

... Reimplemented without making dependency cycles.

Project and ProjectFileManager publish events for change of active project or
change of a project title.

WindowMenus.cpp can listen for those events, so that it can update the menu
appropriately.  So it's all done nonintrusively in the rest of the code.
This commit is contained in:
Paul Licameli 2020-04-21 12:26:37 -04:00
parent 3348e2fecd
commit c3d0d0370b
5 changed files with 112 additions and 2 deletions

View File

@ -19,6 +19,7 @@
#include <wx/frame.h>
wxDEFINE_EVENT(EVT_TRACK_PANEL_TIMER, wxCommandEvent);
wxDEFINE_EVENT(EVT_PROJECT_ACTIVATION, wxCommandEvent);
size_t AllProjects::size() const
{
@ -111,6 +112,7 @@ void SetActiveProject(AudacityProject * project)
if ( gActiveProject != project ) {
gActiveProject = project;
KeyboardCapture::Capture( nullptr );
wxTheApp->QueueEvent( safenew wxCommandEvent{ EVT_PROJECT_ACTIVATION } );
}
wxTheApp->SetTopWindow( FindProjectFrame( project ) );
}

View File

@ -93,6 +93,11 @@ using AttachedWindows = ClientData::Site<
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_TRACK_PANEL_TIMER, wxCommandEvent);
// This event is emitted by the application object when there is a change
// in the activated project
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_PROJECT_ACTIVATION, wxCommandEvent);
///\brief The top-level handle to an Audacity project. It serves as a source
/// of events that other objects can bind to, and a container of associated
/// sub-objects that it treats opaquely. It stores a filename and a status

View File

@ -24,6 +24,8 @@ Paul Licameli split from AudacityProject.cpp
#include "widgets/AudacityMessageBox.h"
#include "widgets/NumericTextCtrl.h"
wxDEFINE_EVENT(EVT_PROJECT_TITLE_CHANGE, wxCommandEvent);
static void RefreshAllTitles(bool bShowProjectNumbers )
{
for ( auto pProject : AllProjects{} ) {
@ -130,8 +132,13 @@ void ProjectFileIO::SetProjectTitle( int number)
name += _("(Recovered)");
}
window.SetTitle( name );
window.SetName(name); // to make the nvda screen reader read the correct title
if ( name != window.GetTitle() ) {
window.SetTitle( name );
window.SetName(name); // to make the nvda screen reader read the correct title
project.QueueEvent(
safenew wxCommandEvent{ EVT_PROJECT_TITLE_CHANGE } );
}
}
// Most of this string was duplicated 3 places. Made the warning consistent in this global.

View File

@ -96,4 +96,9 @@ public:
size_t UnnamedCount;
};
// This event is emitted by the project when there is a change
// in its title
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_PROJECT_TITLE_CHANGE, wxCommandEvent);
#endif

View File

@ -10,9 +10,11 @@
#include "../CommonCommandFlags.h"
#include "../Menus.h"
#include "../Project.h"
#include "../ProjectFileIO.h"
#include "../commands/CommandContext.h"
#include <wx/frame.h>
#include <wx/menu.h>
#undef USE_COCOA
@ -51,8 +53,64 @@ void DoMacMinimize(AudacityProject *project)
}
}
std::vector< wxWindowID > sReservedIds;
std::vector< std::weak_ptr< AudacityProject > > sProjects;
void RebuildMenu(wxCommandEvent &evt)
{
// Let other listeners hear it too
evt.Skip();
// This is a big hammer.
// Really we just need to recreate just the Window menu.
// This causes the checkmark to be put in the right place for the
// currently active project
MenuCreator::RebuildAllMenuBars();
}
wxWindowID ReservedID(
size_t index, const std::shared_ptr< AudacityProject > &pProject )
{
if ( sReservedIds.empty() ) {
// Do this once only per session, and don't worry about unbinding
wxTheApp->Bind( EVT_PROJECT_ACTIVATION, RebuildMenu );
wxTheApp->Bind( EVT_PROJECT_TITLE_CHANGE, RebuildMenu );
}
while ( sReservedIds.size() <= index )
sReservedIds.emplace_back( wxIdManager::ReserveId() );
if ( sProjects.size() < sReservedIds.size() )
sProjects.resize( sReservedIds.size() );
sProjects[ index ] = pProject;
return sReservedIds[ index ];
}
void OnWindow( wxCommandEvent &evt )
{
const auto begin = sReservedIds.begin(), end = sReservedIds.end(),
iter = std::find( begin, end, evt.GetId() );
size_t index = iter - begin;
if ( index < sProjects.size() ) {
auto pProject = sProjects[ index ].lock();
if ( pProject ) {
// Make it the active project
SetActiveProject(pProject.get());
// And ensure it's visible
wxFrame *frame = pProject->GetFrame();
if (frame->IsIconized())
{
frame->Restore();
}
frame->Raise();
}
}
}
} // namespace
/// Namespace for functions for window management (mac only?)
namespace WindowActions {
@ -144,6 +202,39 @@ BaseItemSharedPtr WindowMenu()
* windows un-hidden */
Command( wxT("MacBringAllToFront"), XXO("&Bring All to Front"),
FN(OnMacBringAllToFront), AlwaysEnabledFlag )
),
Section( "",
Special( wxT("PopulateWindowsStep"),
[](AudacityProject &, wxMenu &theMenu)
{
// Undo previous bindings
for ( auto id : sReservedIds )
wxTheApp->Unbind( wxEVT_MENU, OnWindow, id );
// Add all projects to this project's Window menu
size_t ii = 0;
for (auto project : AllProjects{})
{
int itemId = ReservedID( ii++, project );
wxString itemName = project->GetFrame()->GetTitle();
bool isActive = (GetActiveProject() == project.get());
// This should never really happen, but a menu item must have a name
if (itemName.empty())
{
itemName = _("<untitled>");
}
// Add it to the menu and check it if it's the active project
wxMenuItem *item = theMenu.Append(itemId, itemName);
item->SetCheckable(true);
item->Check(isActive);
// Bind the callback
wxTheApp->Bind( wxEVT_MENU, OnWindow, itemId );
}
} )
)
) ) };
return menu;