/********************************************************************** Audacity: A Digital Audio Editor AudacityLogger.cpp ******************************************************************//** \class AudacityLogger \brief AudacityLogger is a thread-safe logger class Provides thread-safe logging based on the wxWidgets log facility. *//*******************************************************************/ #include "Audacity.h" // This should always be included first #include "AudacityLogger.h" #include "FileDialog.h" #include "ShuttleGui.h" #include #include #include #include #include #include "../images/AudacityLogoAlpha.xpm" // // 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, // as such, can be used by the ever growing threading within Audacity. // enum { LoggerID_Save = wxID_HIGHEST + 1, LoggerID_Clear, LoggerID_Close }; AudacityLogger::AudacityLogger() : wxEvtHandler(), wxLog() { mFrame = NULL; mText = NULL; mUpdated = false; } AudacityLogger::~AudacityLogger() { Destroy(); } void AudacityLogger::Flush() { if (mUpdated && mFrame && mFrame->IsShown()) { mUpdated = false; mText->ChangeValue(mBuffer); } } void AudacityLogger::DoLogText(const wxString & str) { if (!wxIsMainThread()) { wxMutexGuiEnter(); } wxString stamp; TimeStamp(&stamp); if (mBuffer.IsEmpty()) { mBuffer << stamp << wxT("Audacity ") << AUDACITY_VERSION_STRING << wxT("\n"); } mBuffer << stamp << str << wxT("\n"); mUpdated = true; Flush(); if (!wxIsMainThread()) { wxMutexGuiLeave(); } } void AudacityLogger::Destroy() { if (mFrame) { wxFrame *frame = mFrame; mFrame = NULL; mText = NULL; frame->Disconnect(LoggerID_Save, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudacityLogger::OnSave), NULL, this); frame->Disconnect(LoggerID_Clear, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudacityLogger::OnClear), NULL, this); frame->Disconnect(LoggerID_Close, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudacityLogger::OnClose), NULL, this); frame->Disconnect(LoggerID_Save, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(AudacityLogger::OnSave), NULL, this); frame->Disconnect(LoggerID_Clear, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(AudacityLogger::OnClear), NULL, this); frame->Disconnect(LoggerID_Close, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(AudacityLogger::OnClose), NULL, this); frame->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(AudacityLogger::OnCloseWindow), NULL, this); frame->Destroy(); } } void AudacityLogger::Show(bool show) { // Hide the frame if created, otherwise do nothing if (!show) { if (mFrame) { mFrame->Show(false); } return; } // If the frame already exists, refresh its contents and show it if (mFrame) { if (!mFrame->IsShown()) { mText->ChangeValue(mBuffer); mText->SetInsertionPointEnd(); mText->ShowPosition(mText->GetLastPosition()); } mFrame->Show(); mFrame->Raise(); return; } // This is the first use, so create the frame wxFrame *frame = new 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__) wxIcon *ic; #if defined(__WXMSW__) ic = new wxIcon(wxICON(AudacityLogo)); #elif defined(__WXGTK__) ic = new wxIcon(wxICON(AudacityLogoAlpha)); #else ic = new wxIcon(); ic.CopyFromBitmap(theTheme.Bitmap(bmpAudacityLogo48x48)); #endif frame->SetIcon(*ic); delete ic; #endif // Log text ShuttleGui S(frame, eIsCreating); S.SetStyle(wxNO_BORDER | wxTAB_TRAVERSAL); S.Prop(true).StartPanel(); { S.StartVerticalLay(true); { S.SetStyle(wxTE_MULTILINE | wxHSCROLL | wxTE_READONLY); mText = S.AddTextWindow(mBuffer); S.AddSpace(0, 5); S.StartHorizontalLay(wxALIGN_CENTER, 0); { S.AddSpace(10, 0); S.Id(LoggerID_Save).AddButton(_("&Save...")); S.Id(LoggerID_Clear).AddButton(_("Cl&ear")); S.Id(LoggerID_Close).AddButton(_("&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->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(AudacityLogger::OnCloseWindow), NULL, this); frame->Connect(LoggerID_Save, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(AudacityLogger::OnSave), NULL, this); frame->Connect(LoggerID_Clear, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(AudacityLogger::OnClear), NULL, this); frame->Connect(LoggerID_Close, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(AudacityLogger::OnClose), NULL, this); frame->Connect(LoggerID_Save, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudacityLogger::OnSave), NULL, this); frame->Connect(LoggerID_Clear, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudacityLogger::OnClear), NULL, this); frame->Connect(LoggerID_Close, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudacityLogger::OnClose), NULL, this); mFrame = frame; mFrame->Show(); Flush(); } #if defined(EXPERIMENTAL_CRASH_REPORT) wxString AudacityLogger::GetLog() { return mBuffer; } #endif void AudacityLogger::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. Destroy(); #else Show(false); #endif } void AudacityLogger::OnClose(wxCommandEvent & WXUNUSED(e)) { wxCloseEvent dummy; OnCloseWindow(dummy); } void AudacityLogger::OnClear(wxCommandEvent & WXUNUSED(e)) { mBuffer = wxEmptyString; DoLogText(wxT("Log Cleared.")); } void AudacityLogger::OnSave(wxCommandEvent & WXUNUSED(e)) { wxString fName = _("log.txt"); fName = FileSelector(_("Save log to:"), wxEmptyString, fName, wxT("txt"), wxT("*.txt"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER, mFrame); if (fName == wxEmptyString) { return; } if (!mText->SaveFile(fName)) { wxMessageBox(_("Couldn't save log to file: ") + fName, _("Warning"), wxICON_EXCLAMATION, mFrame); return; } }