From 91d8f132a6e24578b78f565efda9e69fff39929e Mon Sep 17 00:00:00 2001 From: mchinen Date: Tue, 29 Mar 2011 23:39:00 +0000 Subject: [PATCH] Bug 330 (P2) - Fix an issue where multiple projects would cause the warning dialogs to pop up above the wrong project. I refactored the code into AudacityApp with a new timer. This is provisional pending discussion - if it is decided that it should go somewhere else I will move it. --- src/AudacityApp.cpp | 105 ++++++++++++++++++++++++++ src/AudacityApp.h | 38 ++++++++++ src/AudioIO.cpp | 42 +---------- src/AudioIO.h | 29 ------- src/BlockFile.h | 1 + src/DirManager.cpp | 5 ++ src/DirManager.h | 3 + src/Menus.cpp | 18 ++--- src/Project.cpp | 24 +----- src/blockfile/ODPCMAliasBlockFile.cpp | 4 +- src/blockfile/PCMAliasBlockFile.cpp | 4 +- src/widgets/ErrorDialog.cpp | 7 +- 12 files changed, 167 insertions(+), 113 deletions(-) diff --git a/src/AudacityApp.cpp b/src/AudacityApp.cpp index fbc3b3cbf..6dad64295 100644 --- a/src/AudacityApp.cpp +++ b/src/AudacityApp.cpp @@ -88,6 +88,8 @@ It handles initialization and termination by subclassing wxApp. #include "BlockFile.h" #include "ondemand/ODManager.h" #include "commands/Keyboard.h" +#include "widgets/ErrorDialog.h" + //temporarilly commented out till it is added to all projects //#include "Profiler.h" @@ -712,12 +714,16 @@ typedef int (AudacityApp::*SPECIALKEYEVENT)(wxKeyEvent&); #define ID_RECENT_FIRST 6101 #define ID_RECENT_LAST 6112 +// we don't really care about the timer id, but set this value just in case we do in the future +#define kAudacityAppTimerID 0 + BEGIN_EVENT_TABLE(AudacityApp, wxApp) EVT_QUERY_END_SESSION(AudacityApp::OnEndSession) EVT_KEY_DOWN(AudacityApp::OnKeyDown) EVT_CHAR(AudacityApp::OnChar) EVT_KEY_UP(AudacityApp::OnKeyUp) + EVT_TIMER(kAudacityAppTimerID, AudacityApp::OnTimer) #ifdef __WXMAC__ EVT_MENU(wxID_NEW, AudacityApp::OnMenuNew) EVT_MENU(wxID_OPEN, AudacityApp::OnMenuOpen) @@ -805,6 +811,99 @@ void AudacityApp::OnMRUFile(wxCommandEvent& event) { mRecentFiles->RemoveFileFromHistory(n); } +void AudacityApp::OnTimer(wxTimerEvent& event) +{ + // Check if a warning for missing aliased files should be displayed + if (ShouldShowMissingAliasedFileWarning()) { + if (GetMissingAliasFileDialog()) { + // if it is already shown, just bring it to the front instead of + // creating a new one. + GetMissingAliasFileDialog()->Raise(); + } else { + wxString errorMessage = _( +"One or more external audio files could not be found.\n\ +It is possible they were moved, deleted, or the drive they \ +were on was unmounted.\n\ +Silence is being substituted for the affected audio.\n\ +Choose File > Check Dependencies to view the \ +original location of the missing files."); + + // find which project owns the blockfile + // note: there may be more than 1, but just go with the first one. + size_t numProjects = gAudacityProjects.Count(); + AudacityProject *offendingProject = NULL; + if (numProjects == 1) { + // if there is only one project open, no need to search + offendingProject = gAudacityProjects[0]; + } else if (numProjects > 1) { + + m_LastMissingBlockFileLock.Lock(); + for (size_t i = 0; i < numProjects; i++) + { + // search each project for the blockfile + if (gAudacityProjects[i]->GetDirManager()->ContainsBlockFile(m_LastMissingBlockFile)) { + offendingProject = gAudacityProjects[i]; + break; + } + } + m_LastMissingBlockFileLock.Unlock(); + } + + // if there are no projects open, don't show the warning (user has closed it) + if (offendingProject) + ShowAliasMissingDialog(offendingProject, _("Files Missing"), + errorMessage, wxT(""), true); + // Only show this warning once per playback. + } + SetMissingAliasedFileWarningShouldShow(false); + } +} + +void AudacityApp::MarkAliasedFilesMissingWarning(BlockFile *b) +{ + // the reference counting provides thread safety. + if (b) + b->Ref(); + + m_LastMissingBlockFileLock.Lock(); + if (m_LastMissingBlockFile) + m_LastMissingBlockFile->Deref(); + + m_LastMissingBlockFile = b; + + m_LastMissingBlockFileLock.Unlock(); +} + +void AudacityApp::SetMissingAliasedFileWarningShouldShow(bool b) +{ + // Note that this is can be called by both the main thread and other threads. + // I don't believe we need a mutex because we are checking zero vs non-zero, + // and the setting from other threads will always be non-zero (true), and the + // setting from the main thread is always false. + m_aliasMissingWarningShouldShow = b; + // reset the warnings as they were probably marked by a previous run + if (m_aliasMissingWarningShouldShow) { + MarkAliasedFilesMissingWarning(NULL); + } +} + +bool AudacityApp::ShouldShowMissingAliasedFileWarning() +{ + bool ret = m_LastMissingBlockFile && m_aliasMissingWarningShouldShow; + + return ret; +} + +void AudacityApp::SetMissingAliasFileDialog(wxDialog *dialog) +{ + m_aliasMissingWarningDialog = dialog; +} + +wxDialog *AudacityApp::GetMissingAliasFileDialog() +{ + return m_aliasMissingWarningDialog; +} + void AudacityApp::InitLang( const wxString & lang ) { if( mLocale ) @@ -862,6 +961,10 @@ void AudacityApp::OnFatalException() // main frame bool AudacityApp::OnInit() { + m_aliasMissingWarningShouldShow = true; + m_LastMissingBlockFile = NULL; + m_aliasMissingWarningDialog = NULL; + #if defined(__WXGTK__) // Workaround for bug 154 -- initialize to false inKbdHandler = false; @@ -1296,6 +1399,8 @@ bool AudacityApp::OnInit() wxLog::FlushActive(); // Make sure all log messages are written. + mTimer = new wxTimer(this, kAudacityAppTimerID); + mTimer->Start(200); return TRUE; } diff --git a/src/AudacityApp.h b/src/AudacityApp.h index 74834535c..4e634375c 100644 --- a/src/AudacityApp.h +++ b/src/AudacityApp.h @@ -25,6 +25,7 @@ #include #include "widgets/FileHistory.h" +#include "ondemand/ODTaskThread.h" class IPCServ; class Importer; @@ -87,6 +88,8 @@ enum NoFlagsSpecifed = 0xffffffff }; +class BlockFile; + class AudacityApp:public wxApp { public: virtual bool OnInit(void); @@ -127,6 +130,33 @@ class AudacityApp:public wxApp { void OnReceiveCommand(AppCommandEvent &event); + void OnTimer(wxTimerEvent & event); + + /** \brief Mark playback as having missing aliased blockfiles + * + * Playback will continue, but the missing files will be silenced + * ShouldShowMissingAliasedFileWarning can be called to determine + * if the user should be notified + */ + void MarkAliasedFilesMissingWarning(BlockFile *b); + + /** \brief Changes the behavior of missing aliased blockfiles warnings + */ + void SetMissingAliasedFileWarningShouldShow(bool b); + + /** \brief Returns true if the user should be notified of missing alias warnings + */ + bool ShouldShowMissingAliasedFileWarning(); + + /** \brief Sets the wxDialog that is being displayed + * Used by the custom dialog warning constructor and destructor + */ + void SetMissingAliasFileDialog(wxDialog *dialog); + + /** \brief returns a pointer to the wxDialog if it is displayed, NULL otherwise. + */ + wxDialog *GetMissingAliasFileDialog(); + #ifdef __WXMAC__ // In response to Apple Events virtual void MacOpenFile(const wxString &fileName) ; @@ -186,6 +216,14 @@ class AudacityApp:public wxApp { wxSingleInstanceChecker *mChecker; + wxTimer *mTimer; + + bool m_aliasMissingWarningShouldShow; + wxDialog *m_aliasMissingWarningDialog; + BlockFile *m_LastMissingBlockFile; + + ODLock m_LastMissingBlockFileLock; + void InitCommandHandler(); void DeInitCommandHandler(); diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index f512a4d75..44defc033 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -477,42 +477,6 @@ void DeinitAudioIO() delete gAudioIO; } -void AudioIO::MarkAliasedFilesMissingWarning() -{ - m_aliasMissingWarning = true; -} - -void AudioIO::SetMissingAliasedFileWarningShouldShow(bool b) -{ - // Note that this is can be called by both the main thread and other threads. - // I don't believe we need a mutex because we are checking zero vs non-zero, - // and the setting from other threads will always be non-zero (true), and the - // setting from the main thread is always false. - m_aliasMissingWarningShouldShow = b; - // reset the warnings as they were probably marked by a previous run - if (m_aliasMissingWarningShouldShow) { - m_aliasMissingWarning = false; - } -} - -bool AudioIO::ShouldShowMissingAliasedFileWarning() -{ - bool ret = m_aliasMissingWarning && m_aliasMissingWarningShouldShow; - - return ret; -} - -void AudioIO::SetMissingAliasFileDialog(wxDialog *dialog) -{ - m_aliasMissingWarningDialog = dialog; -} - -wxDialog *AudioIO::GetMissingAliasFileDialog() -{ - return m_aliasMissingWarningDialog; -} - - wxString DeviceName(const PaDeviceInfo* info) { wxString infoName(info->name, wxConvLocal); @@ -546,10 +510,6 @@ AudioIO::AudioIO() mAudioThreadFillBuffersLoopActive = false; mPortStreamV19 = NULL; - m_aliasMissingWarningShouldShow = true; - m_aliasMissingWarning = false; - m_aliasMissingWarningDialog = NULL; - #ifdef EXPERIMENTAL_MIDI_OUT mMidiStream = NULL; mMidiThreadFillBuffersLoopRunning = false; @@ -1417,7 +1377,7 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks, #endif // Enable warning popups for unfound aliased blockfiles. - SetMissingAliasedFileWarningShouldShow(true); + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); // // Generate an unique value each time, to be returned to diff --git a/src/AudioIO.h b/src/AudioIO.h index e538debae..f436b4b6c 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -306,31 +306,6 @@ class AUDACITY_DLL_API AudioIO { */ static bool ValidateDeviceNames(wxString play, wxString rec); - /** \brief Mark playback as having missing aliased blockfiles - * - * Playback will continue, but the missing files will be silenced - * ShouldShowMissingAliasedFileWarning can be called to determine - * if the user should be notified - */ - void MarkAliasedFilesMissingWarning(); - - /** \brief Changes the behavior of missing aliased blockfiles warnings - */ - void SetMissingAliasedFileWarningShouldShow(bool b); - - /** \brief Returns true if the user should be notified of missing alias warnings - */ - bool ShouldShowMissingAliasedFileWarning(); - - /** \brief Sets the wxDialog that is being displayed - * Used by the custom dialog warning constructor and destructor - */ - void SetMissingAliasFileDialog(wxDialog *dialog); - - /** \brief returns a pointer to the wxDialog if it is displayed, NULL otherwise. - */ - wxDialog *GetMissingAliasFileDialog(); - /** \brief Function to automatically set an acceptable volume * */ @@ -535,10 +510,6 @@ private: volatile bool mAudioThreadFillBuffersLoopRunning; volatile bool mAudioThreadFillBuffersLoopActive; - bool m_aliasMissingWarningShouldShow; - bool m_aliasMissingWarning; - wxDialog *m_aliasMissingWarningDialog; - #ifdef EXPERIMENTAL_MIDI_OUT volatile bool mMidiThreadFillBuffersLoopRunning; volatile bool mMidiThreadFillBuffersLoopActive; diff --git a/src/BlockFile.h b/src/BlockFile.h index 6ad9bf4ce..3982ed268 100644 --- a/src/BlockFile.h +++ b/src/BlockFile.h @@ -133,6 +133,7 @@ class BlockFile { private: friend class DirManager; + friend class AudacityApp; //needed for Ref/Deref access. friend class ODComputeSummaryTask; friend class ODDecodeTask; diff --git a/src/DirManager.cpp b/src/DirManager.cpp index a4fb80ace..27adcf128 100644 --- a/src/DirManager.cpp +++ b/src/DirManager.cpp @@ -910,6 +910,11 @@ BlockFile *DirManager::NewODDecodeBlockFile( return newBlockFile; } +bool DirManager::ContainsBlockFile(BlockFile *b) { + // check what the hash returns in case the blockfile is from a different project + return b ? mBlockFileHash[b->GetFileName().GetName()] == b : NULL; +} + // Adds one to the reference count of the block file, // UNLESS it is "locked", then it makes a new copy of // the BlockFile. diff --git a/src/DirManager.h b/src/DirManager.h index 61199c3f5..9d6d51586 100644 --- a/src/DirManager.h +++ b/src/DirManager.h @@ -70,6 +70,9 @@ class DirManager: public XMLTagHandler { BlockFile *NewODDecodeBlockFile( wxString aliasedFile, sampleCount aliasStart, sampleCount aliasLen, int aliasChannel, int decodeType); + /// Returns true if the blockfile is contained by the DirManager + bool ContainsBlockFile(BlockFile *b); + // Adds one to the reference count of the block file, // UNLESS it is "locked", then it makes a new copy of // the BlockFile. diff --git a/src/Menus.cpp b/src/Menus.cpp index 7d69984c9..8db2de565 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -2665,8 +2665,7 @@ bool AudacityProject::OnEffect(int type, wxString params, bool saveState) { - if (gAudioIO) - gAudioIO->SetMissingAliasedFileWarningShouldShow(true); + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); TrackListIterator iter(mTracks); Track *t = iter.First(); @@ -3027,8 +3026,7 @@ void AudacityProject::OnExport() { Exporter e; - if (gAudioIO) - gAudioIO->SetMissingAliasedFileWarningShouldShow(true); + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); e.Process(this, false, 0.0, mTracks->GetEndTime()); } @@ -3036,8 +3034,7 @@ void AudacityProject::OnExportSelection() { Exporter e; - if (gAudioIO) - gAudioIO->SetMissingAliasedFileWarningShouldShow(true); + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); e.Process(this, true, mViewInfo.sel0, mViewInfo.sel1); } @@ -3045,8 +3042,7 @@ void AudacityProject::OnExportMultiple() { ExportMultiple em(this); - if (gAudioIO) - gAudioIO->SetMissingAliasedFileWarningShouldShow(true); + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); em.ShowModal(); } @@ -4614,8 +4610,7 @@ void AudacityProject::OnImport() { // An import trigger for the alias missing dialog might not be intuitive, but // this serves to track the file if the users zooms in and such. - if (gAudioIO) - gAudioIO->SetMissingAliasedFileWarningShouldShow(true); + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); wxArrayString selectedFiles = ShowOpenDialog(wxT("")); if (selectedFiles.GetCount() == 0) { @@ -4762,8 +4757,7 @@ void AudacityProject::HandleMixAndRender(bool toNewTrack) WaveTrack *newLeft = NULL; WaveTrack *newRight = NULL; - if (gAudioIO) - gAudioIO->SetMissingAliasedFileWarningShouldShow(true); + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); if (::MixAndRender(mTracks, mTrackFactory, mRate, mDefaultFormat, 0.0, 0.0, &newLeft, &newRight)) { diff --git a/src/Project.cpp b/src/Project.cpp index 91dc207b1..3b39980c3 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -760,8 +760,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mStatusBar = CreateStatusBar(2); mStatusBar->SetStatusWidths(2, widths); - if (gAudioIO) - gAudioIO->SetMissingAliasedFileWarningShouldShow(true); + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); // MM: DirManager is created dynamically, freed on demand via ref-counting // MM: We don't need to Ref() here because it start with refcount=1 @@ -3931,27 +3930,6 @@ void AudacityProject::OnTimer(wxTimerEvent& event) if( mixerToolBar ) mixerToolBar->UpdateControls(); - // Check if a warning for missing aliased files should be displayed - if (gAudioIO->ShouldShowMissingAliasedFileWarning()) { - if (gAudioIO->GetMissingAliasFileDialog()) { - // if it is already shown, just bring it to the front instead of - // creating a new one. - gAudioIO->GetMissingAliasFileDialog()->Raise(); - } else { - wxString errorMessage = _( -"One or more external audio files could not be found.\n\ -It is possible they were moved, deleted, or the drive they \ -were on was unmounted.\n\ -Silence is being substituted for the affected audio.\n\ -Choose File > Check Dependencies to view the \ -original location of the missing files."); - ShowAliasMissingDialog(this, _("Files Missing"), - errorMessage, wxT(""), true); - // Only show this warning once per playback. - } - gAudioIO->SetMissingAliasedFileWarningShouldShow(false); - } - if (::wxGetUTCTime() - mLastStatusUpdateTime < 3) return; diff --git a/src/blockfile/ODPCMAliasBlockFile.cpp b/src/blockfile/ODPCMAliasBlockFile.cpp index f0cee5d3f..d3201bcf9 100644 --- a/src/blockfile/ODPCMAliasBlockFile.cpp +++ b/src/blockfile/ODPCMAliasBlockFile.cpp @@ -634,8 +634,8 @@ int ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, mSilentAliasLog=TRUE; // Set a marker to display an error message - if (gAudioIO) - gAudioIO->MarkAliasedFilesMissingWarning(); + if (!wxGetApp().ShouldShowMissingAliasedFileWarning()) + wxGetApp().MarkAliasedFilesMissingWarning(this); UnlockRead(); return len; diff --git a/src/blockfile/PCMAliasBlockFile.cpp b/src/blockfile/PCMAliasBlockFile.cpp index bf79b7e78..050bcebbc 100644 --- a/src/blockfile/PCMAliasBlockFile.cpp +++ b/src/blockfile/PCMAliasBlockFile.cpp @@ -109,8 +109,8 @@ int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format, mSilentAliasLog=TRUE; // Set a marker to display an error message for the silence - if (gAudioIO) - gAudioIO->MarkAliasedFilesMissingWarning(); + if (!wxGetApp().ShouldShowMissingAliasedFileWarning()) + wxGetApp().MarkAliasedFilesMissingWarning(this); return len; } diff --git a/src/widgets/ErrorDialog.cpp b/src/widgets/ErrorDialog.cpp index 5186dd4a6..6b2dab983 100644 --- a/src/widgets/ErrorDialog.cpp +++ b/src/widgets/ErrorDialog.cpp @@ -35,8 +35,7 @@ Gives an Error message with an option for help. #include "../Internat.h" #include "../Project.h" #include "../Prefs.h" -#include "../AudioIO.h" - +#include "../AudacityApp.h" class ErrorDialog : public wxDialog @@ -87,12 +86,12 @@ AliasedFileMissingDialog::AliasedFileMissingDialog(wxWindow *parent, const bool Close, const bool modal): ErrorDialog(parent, dlogTitle, message, helpURL, Close, modal) { - gAudioIO->SetMissingAliasFileDialog(this); + wxGetApp().SetMissingAliasFileDialog(this); } AliasedFileMissingDialog::~AliasedFileMissingDialog() { - gAudioIO->SetMissingAliasFileDialog(NULL); + wxGetApp().SetMissingAliasFileDialog(NULL); } ErrorDialog::ErrorDialog(