1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-22 07:10:06 +02:00

Move LogWindow to new files

This commit is contained in:
Paul Licameli 2021-04-16 23:39:10 -04:00
parent c14e8114ca
commit 4fd6e8a151
7 changed files with 29 additions and 433 deletions

View File

@ -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 <memory>
#include <mutex>
#include <optional>
#include <wx/filedlg.h>
#include <wx/log.h>
#include <wx/ffile.h>
#include <wx/frame.h>
#include <wx/icon.h>
#include <wx/settings.h>
#include <wx/textctrl.h>
#include <wx/log.h>
#include <wx/tokenzr.h>
#include <wx/weakref.h>
#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<wxFrame> sFrame;
wxWeakRef<wxTextCtrl> 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<LogWindowUpdater> 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<wxFrame> 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);
}
}
}
}

View File

@ -11,9 +11,6 @@
#ifndef __AUDACITY_LOGGER__
#define __AUDACITY_LOGGER__
#include <functional>
#include "MemoryX.h"
#include "Prefs.h"
#include <wx/log.h> // to inherit
#include <wx/event.h> // 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

View File

@ -164,6 +164,8 @@ list( APPEND SOURCES
Legacy.cpp
Legacy.h
LightThemeAsCeeCode.h
LogWindow.cpp
LogWindow.h
Lyrics.cpp
Lyrics.h
LyricsWindow.cpp

View File

@ -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 <mutex>
#include <optional>
#include <wx/filedlg.h>
#include <wx/log.h>
#include <wx/ffile.h>
#include <wx/frame.h>
#include <wx/icon.h>
#include <wx/settings.h>
#include <wx/textctrl.h>
#include <wx/tokenzr.h>
#include <wx/weakref.h>
#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<AudacityLogger *>(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();
}

View File

@ -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 <functional>
#include "MemoryX.h"
#include "Prefs.h"
#include <wx/log.h> // to inherit
#include <wx/event.h> // 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

View File

@ -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"

View File

@ -36,8 +36,8 @@ for each problem encountered, since there can be many orphans.
#include <wx/bmpbuttn.h>
#include "../AudacityLogger.h"
#include "wxPanelWrapper.h"
#include "../LogWindow.h"
#include "../Theme.h"
#include "../AllThemeResources.h"
#include "../widgets/HelpSystem.h"