diff --git a/src/AudacityLogger.cpp b/src/AudacityLogger.cpp index dba1cd0f5..0a2735422 100644 --- a/src/AudacityLogger.cpp +++ b/src/AudacityLogger.cpp @@ -19,63 +19,20 @@ Provides thread-safe logging based on the wxWidgets log facility. -#include "FileNames.h" #include "Internat.h" -#include "SelectFile.h" -#include "ShuttleGui.h" +#include #include -#include -#include -#include #include -#include -#include -#include -#include +#include #include -#include - -#include "../images/AudacityLogoAlpha.xpm" -#include "widgets/AudacityMessageBox.h" // // AudacityLogger class // -// Two reasons for this class instead of the wxLogWindow class (or any WX GUI logging class) -// -// 1) If wxLogWindow is used and initialized before the Mac's "root" window, then -// Audacity may crash when terminating. It's not fully understood why this occurs -// but it probably has to do with the order of deletion. However, deferring the -// creation of the log window until it is actually shown circumvents the problem. -// 2) By providing an Audacity specific logging class, it can be made thread-safe and, +// By providing an Audacity specific logging class, it can be made thread-safe and, // as such, can be used by the ever growing threading within Audacity. // -enum -{ - LoggerID_Save = wxID_HIGHEST + 1, - LoggerID_Clear, - LoggerID_Close -}; - -namespace { -Destroy_ptr sFrame; -wxWeakRef sText; - -struct LogWindowUpdater : public PrefsListener -{ - // PrefsListener implementation - void UpdatePrefs() override; -}; -// Unique PrefsListener can't be statically constructed before the application -// object initializes, so use Optional -std::optional pUpdater; - -void OnCloseWindow(wxCloseEvent & e); -void OnClose(wxCommandEvent & e); -void OnClear(wxCommandEvent & e); -void OnSave(wxCommandEvent & e); -} AudacityLogger *AudacityLogger::Get() { @@ -161,120 +118,6 @@ bool AudacityLogger::ClearLog() return true; } -void LogWindow::Show(bool show) -{ - // Hide the frame if created, otherwise do nothing - if (!show) { - if (sFrame) { - sFrame->Show(false); - } - return; - } - - // If the frame already exists, refresh its contents and show it - auto pLogger = AudacityLogger::Get(); - if (sFrame) { - if (!sFrame->IsShown() && sText) { - if (pLogger) - sText->ChangeValue(pLogger->GetBuffer()); - sText->SetInsertionPointEnd(); - sText->ShowPosition(sText->GetLastPosition()); - } - sFrame->Show(); - sFrame->Raise(); - return; - } - - // This is the first use, so create the frame - Destroy_ptr frame - { safenew wxFrame(NULL, wxID_ANY, _("Audacity Log")) }; - frame->SetName(frame->GetTitle()); - frame->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)); - - // loads either the XPM or the windows resource, depending on the platform - { -#if !defined(__WXMAC__) && !defined(__WXX11__) -#if defined(__WXMSW__) - wxIcon ic{wxICON(AudacityLogo)}; -#elif defined(__WXGTK__) - wxIcon ic{wxICON(AudacityLogoAlpha)}; -#else - wxIcon ic{}; - ic.CopyFromBitmap(theTheme.Bitmap(bmpAudacityLogo48x48)); -#endif - frame->SetIcon(ic); -#endif - } - - // Log text - ShuttleGui S(frame.get(), eIsCreating); - - S.Style(wxNO_BORDER | wxTAB_TRAVERSAL).Prop(true).StartPanel(); - { - S.StartVerticalLay(true); - { - sText = S.Style(wxTE_MULTILINE | wxHSCROLL | wxTE_READONLY | wxTE_RICH) - .AddTextWindow({}); // Populate this text window below - - S.AddSpace(0, 5); - S.StartHorizontalLay(wxALIGN_CENTER, 0); - { - S.AddSpace(10, 0); - S.Id(LoggerID_Save).AddButton(XXO("&Save...")); - S.Id(LoggerID_Clear).AddButton(XXO("Cl&ear")); - S.Id(LoggerID_Close).AddButton(XXO("&Close")); - S.AddSpace(10, 0); - } - S.EndHorizontalLay(); - S.AddSpace(0, 3); - } - S.EndVerticalLay(); - } - S.EndPanel(); - - // Give a place for the menu help text to go - // frame->CreateStatusBar(); - - frame->Layout(); - - // Hook into the frame events - frame->Bind(wxEVT_CLOSE_WINDOW, OnCloseWindow ); - - frame->Bind( wxEVT_COMMAND_MENU_SELECTED, OnSave, LoggerID_Save); - frame->Bind( wxEVT_COMMAND_MENU_SELECTED, OnClear, LoggerID_Clear); - frame->Bind( wxEVT_COMMAND_MENU_SELECTED, OnClose, LoggerID_Close); - frame->Bind( wxEVT_COMMAND_BUTTON_CLICKED, OnSave, LoggerID_Save); - frame->Bind( wxEVT_COMMAND_BUTTON_CLICKED, OnClear, LoggerID_Clear); - frame->Bind( wxEVT_COMMAND_BUTTON_CLICKED, OnClose, LoggerID_Close); - - sFrame = std::move( frame ); - - sFrame->Show(); - - if (pLogger) - pLogger->Flush(); - - // Also create the listeners - if (!pUpdater) - pUpdater.emplace(); - - if (pLogger) { - pLogger->SetListener([]{ - if (auto pLogger = AudacityLogger::Get()) { - if (sFrame && sFrame->IsShown()) { - if (sText) - sText->ChangeValue(pLogger->GetBuffer()); - return true; - } - } - return false; - }); - - // Initial flush populates sText - pLogger->Flush(); - } -} - wxString AudacityLogger::GetLog(int count) { if (count == 0) @@ -292,71 +135,3 @@ wxString AudacityLogger::GetLog(int count) return buffer; } - -namespace { -void OnCloseWindow(wxCloseEvent & WXUNUSED(e)) -{ -#if defined(__WXMAC__) - // On the Mac, destroy the window rather than hiding it since the - // log menu will override the root windows menu if there is no - // project window open. - sFrame.reset(); -#else - sFrame->Show(false); -#endif -} - -void OnClose(wxCommandEvent & WXUNUSED(e)) -{ - wxCloseEvent dummy; - OnCloseWindow(dummy); -} - -void OnClear(wxCommandEvent & WXUNUSED(e)) -{ - if (auto pLogger = AudacityLogger::Get()) - pLogger->ClearLog(); -} - -void OnSave(wxCommandEvent & WXUNUSED(e)) -{ - wxString fName = _("log.txt"); - - fName = SelectFile(FileNames::Operation::Export, - XO("Save log to:"), - wxEmptyString, - fName, - wxT("txt"), - { FileNames::TextFiles }, - wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER, - sFrame.get()); - - if (fName.empty()) { - return; - } - - if (!(sText && sText->SaveFile(fName))) { - AudacityMessageBox( - XO("Couldn't save log to file: %s").Format( fName ), - XO("Warning"), - wxICON_EXCLAMATION, - sFrame.get()); - return; - } -} - -void LogWindowUpdater::UpdatePrefs() -{ - //! Re-create the non-modal window in case of change of preferred language - if (sFrame) { - bool shown = sFrame->IsShown(); - if (shown) { - LogWindow::Show(false); - } - sFrame.reset(); - if (shown) { - LogWindow::Show(true); - } - } -} -} diff --git a/src/AudacityLogger.h b/src/AudacityLogger.h index 1c98e9606..a5e61fe04 100644 --- a/src/AudacityLogger.h +++ b/src/AudacityLogger.h @@ -11,9 +11,6 @@ #ifndef __AUDACITY_LOGGER__ #define __AUDACITY_LOGGER__ -#include -#include "MemoryX.h" -#include "Prefs.h" #include // to inherit #include // to inherit wxEvtHandler @@ -61,12 +58,4 @@ protected: bool mUpdated; }; -//! Maintains the unique logging window which displays debug information -class AUDACITY_DLL_API LogWindow -{ -public: - //! Show or hide the unique logging window; create it on demand the first time it is shown - static void Show(bool show = true); -}; - #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c78e25a0..8ceada1a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -164,6 +164,8 @@ list( APPEND SOURCES Legacy.cpp Legacy.h LightThemeAsCeeCode.h + LogWindow.cpp + LogWindow.h Lyrics.cpp Lyrics.h LyricsWindow.cpp diff --git a/src/LogWindow.cpp b/src/LogWindow.cpp index dba1cd0f5..aecba44a7 100644 --- a/src/LogWindow.cpp +++ b/src/LogWindow.cpp @@ -1,56 +1,37 @@ /********************************************************************** - Audacity: A Digital Audio Editor +Audacity: A Digital Audio Editor - AudacityLogger.cpp +LogWindow.cpp -******************************************************************//** +Paul Licameli split from AudacityLogger.cpp -\class AudacityLogger -\brief AudacityLogger is a thread-safe logger class +**********************************************************************/ +#include "LogWindow.h" -Provides thread-safe logging based on the wxWidgets log facility. - -*//*******************************************************************/ - - - -#include "AudacityLogger.h" - - - -#include "FileNames.h" -#include "Internat.h" -#include "SelectFile.h" -#include "ShuttleGui.h" - -#include #include #include -#include -#include #include #include #include #include -#include #include -#include "../images/AudacityLogoAlpha.xpm" +#include "AudacityLogger.h" #include "widgets/AudacityMessageBox.h" +#include "FileNames.h" +#include "Internat.h" +#include "MemoryX.h" +#include "Prefs.h" +#include "SelectFile.h" +#include "ShuttleGui.h" -// -// AudacityLogger class -// -// Two reasons for this class instead of the wxLogWindow class (or any WX GUI logging class) -// -// 1) If wxLogWindow is used and initialized before the Mac's "root" window, then +#include "../images/AudacityLogoAlpha.xpm" + +// If wxLogWindow is used and initialized before the Mac's "root" window, then // Audacity may crash when terminating. It's not fully understood why this occurs // but it probably has to do with the order of deletion. However, deferring the // creation of the log window until it is actually shown circumvents the problem. -// 2) By providing an Audacity specific logging class, it can be made thread-safe and, -// as such, can be used by the ever growing threading within Audacity. -// enum { LoggerID_Save = wxID_HIGHEST + 1, @@ -77,90 +58,6 @@ void OnClear(wxCommandEvent & e); void OnSave(wxCommandEvent & e); } -AudacityLogger *AudacityLogger::Get() -{ - static std::once_flag flag; - std::call_once( flag, []{ - // wxWidgets will clean up the logger for the main thread, so we can say - // safenew. See: - // http://docs.wxwidgets.org/3.0/classwx_log.html#a2525bf54fa3f31dc50e6e3cd8651e71d - std::unique_ptr < wxLog > // DELETE any previous logger - { wxLog::SetActiveTarget(safenew AudacityLogger) }; - } ); - - // Use dynamic_cast so that we get a NULL ptr in case our logger - // is no longer the target. - return dynamic_cast(wxLog::GetActiveTarget()); -} - -AudacityLogger::AudacityLogger() -: wxEvtHandler(), - wxLog() -{ - mUpdated = false; -} - -AudacityLogger::~AudacityLogger() = default; - -void AudacityLogger::Flush() -{ - if (mUpdated && mListener && mListener()) - mUpdated = false; -} - -auto AudacityLogger::SetListener(Listener listener) -> Listener -{ - auto result = std::move(mListener); - mListener = std::move(listener); - return result; -} - -void AudacityLogger::DoLogText(const wxString & str) -{ - if (!wxIsMainThread()) { - wxMutexGuiEnter(); - } - - if (mBuffer.empty()) { - wxString stamp; - - TimeStamp(&stamp); - - mBuffer << stamp << _TS("Audacity ") << AUDACITY_VERSION_STRING << wxT("\n"); - } - - mBuffer << str << wxT("\n"); - - mUpdated = true; - - Flush(); - - if (!wxIsMainThread()) { - wxMutexGuiLeave(); - } -} - -bool AudacityLogger::SaveLog(const wxString &fileName) const -{ - wxFFile file(fileName, wxT("w")); - - if (file.IsOpened()) { - file.Write(mBuffer); - file.Close(); - return true; - } - - return false; -} - -bool AudacityLogger::ClearLog() -{ - mBuffer = wxEmptyString; - DoLogText(wxT("Log Cleared.")); - - return true; -} - void LogWindow::Show(bool show) { // Hide the frame if created, otherwise do nothing @@ -275,24 +172,6 @@ void LogWindow::Show(bool show) } } -wxString AudacityLogger::GetLog(int count) -{ - if (count == 0) - { - return mBuffer; - } - - wxString buffer; - - auto lines = wxStringTokenize(mBuffer, wxT("\r\n"), wxTOKEN_RET_DELIMS); - for (int index = lines.size() - 1; index >= 0 && count > 0; --index, --count) - { - buffer.Prepend(lines[index]); - } - - return buffer; -} - namespace { void OnCloseWindow(wxCloseEvent & WXUNUSED(e)) { @@ -314,7 +193,8 @@ void OnClose(wxCommandEvent & WXUNUSED(e)) void OnClear(wxCommandEvent & WXUNUSED(e)) { - if (auto pLogger = AudacityLogger::Get()) + auto pLogger = AudacityLogger::Get(); + if (pLogger) pLogger->ClearLog(); } diff --git a/src/LogWindow.h b/src/LogWindow.h index 1c98e9606..65ae6101e 100644 --- a/src/LogWindow.h +++ b/src/LogWindow.h @@ -1,65 +1,15 @@ /********************************************************************** - Audacity: A Digital Audio Editor +Audacity: A Digital Audio Editor - AudacityLogger.h +LogWindow.h - Dominic Mazzoni +Paul Licameli split from AudacityLogger.h **********************************************************************/ -#ifndef __AUDACITY_LOGGER__ -#define __AUDACITY_LOGGER__ - -#include -#include "MemoryX.h" -#include "Prefs.h" -#include // to inherit -#include // to inherit wxEvtHandler - -class wxFrame; -class wxTextCtrl; - -class AUDACITY_DLL_API AudacityLogger final : public wxEvtHandler, - public wxLog -{ - public: - - ~AudacityLogger() override; - - // Get the singleton instance or null - static AudacityLogger *Get(); - - bool SaveLog(const wxString &fileName) const; - bool ClearLog(); - - //! Retrieve all or some of the lines since most recent ClearLog or start of program - /*! If `count == 0` or is more than the number of lines, return all; else return the last `count` lines */ - wxString GetLog(int count = 0); - - //! Get all the accumulated text since program start or last ClearLog() - const wxString &GetBuffer() const { return mBuffer; } - - void Flush() override; - - //! Type of function called by Flush - /*! @return true if flush completed - */ - using Listener = std::function< bool() >; - - //! Set the unique listener, returning any previous one - Listener SetListener(Listener listener); - -protected: - void DoLogText(const wxString & msg) override; - - private: - AudacityLogger(); - - Listener mListener; - wxString mBuffer; - bool mUpdated; -}; +#ifndef __AUDACITY_LOG_WINDOW__ +#define __AUDACITY_LOG_WINDOW__ //! Maintains the unique logging window which displays debug information class AUDACITY_DLL_API LogWindow diff --git a/src/menus/HelpMenus.cpp b/src/menus/HelpMenus.cpp index 4f928ed7d..cb40802a3 100644 --- a/src/menus/HelpMenus.cpp +++ b/src/menus/HelpMenus.cpp @@ -6,12 +6,12 @@ #include "../AboutDialog.h" #include "../AllThemeResources.h" -#include "../AudacityLogger.h" #include "../AudioIOBase.h" #include "../CommonCommandFlags.h" #include "../CrashReport.h" // for HAS_CRASH_REPORT #include "../FileNames.h" #include "../HelpText.h" +#include "../LogWindow.h" #include "../Menus.h" #include "Prefs.h" #include "../Project.h" diff --git a/src/widgets/MultiDialog.cpp b/src/widgets/MultiDialog.cpp index 9c483ea36..4251a4c86 100644 --- a/src/widgets/MultiDialog.cpp +++ b/src/widgets/MultiDialog.cpp @@ -36,8 +36,8 @@ for each problem encountered, since there can be many orphans. #include -#include "../AudacityLogger.h" #include "wxPanelWrapper.h" +#include "../LogWindow.h" #include "../Theme.h" #include "../AllThemeResources.h" #include "../widgets/HelpSystem.h"