diff --git a/src/AudacityLogger.cpp b/src/AudacityLogger.cpp index 68d7401a7..c94200d46 100644 --- a/src/AudacityLogger.cpp +++ b/src/AudacityLogger.cpp @@ -31,6 +31,7 @@ Provides thread-safe logging based on the wxWidgets log facility. #include #include #include +#include #include "../images/AudacityLogoAlpha.xpm" #include "widgets/AudacityMessageBox.h" @@ -237,12 +238,23 @@ void AudacityLogger::Show(bool show) Flush(); } -#if defined(EXPERIMENTAL_CRASH_REPORT) -wxString AudacityLogger::GetLog() +wxString AudacityLogger::GetLog(int count) { - return mBuffer; + 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; } -#endif void AudacityLogger::OnCloseWindow(wxCloseEvent & WXUNUSED(e)) { diff --git a/src/AudacityLogger.h b/src/AudacityLogger.h index 1d5d35626..0b86c1e2b 100644 --- a/src/AudacityLogger.h +++ b/src/AudacityLogger.h @@ -40,9 +40,7 @@ class AudacityLogger final : public wxEvtHandler, bool SaveLog(const wxString &fileName) const; bool ClearLog(); -#if defined(EXPERIMENTAL_CRASH_REPORT) - wxString GetLog(); -#endif + wxString GetLog(int count = 0); protected: void Flush() override; diff --git a/src/DBConnection.cpp b/src/DBConnection.cpp index 61c5a1ef4..39cf646fc 100644 --- a/src/DBConnection.cpp +++ b/src/DBConnection.cpp @@ -16,6 +16,7 @@ Paul Licameli -- split from ProjectFileIO.cpp #include #include +#include "AudacityLogger.h" #include "FileNames.h" #include "Internat.h" #include "Project.h" @@ -66,13 +67,23 @@ void DBConnection::SetError( const TranslatableString &msg, const TranslatableString &libraryError, int errorCode) { wxLogMessage(wxT("Connection msg: %s"), msg.Debug()); + printf("Connection msg: %s", msg.Debug().mb_str().data()); if (!libraryError.empty()) + { wxLogMessage(wxT("Connection error: %s"), libraryError.Debug()); + printf("Connection error: %s", libraryError.Debug().mb_str().data()); + } mpErrors->mLastError = msg; mpErrors->mLibraryError = libraryError; mpErrors->mErrorCode = errorCode; + + auto logger = AudacityLogger::Get(); + if (logger) + { + mpErrors->mLog = logger->GetLog(10); + } } void DBConnection::SetDBError( @@ -96,6 +107,12 @@ void DBConnection::SetDBError( wxLogMessage(wxT(" Lib error: %s"), mpErrors->mLibraryError.Debug()); printf(" Lib error: %s", mpErrors->mLibraryError.Debug().mb_str().data()); + + auto logger = AudacityLogger::Get(); + if (logger) + { + mpErrors->mLog = logger->GetLog(10); + } } bool DBConnection::Open(const FilePath fileName) diff --git a/src/DBConnection.h b/src/DBConnection.h index 7a1c39ad7..76449ff22 100644 --- a/src/DBConnection.h +++ b/src/DBConnection.h @@ -32,6 +32,7 @@ struct DBConnectionErrors TranslatableString mLastError; TranslatableString mLibraryError; int mErrorCode { 0 }; + wxString mLog; }; class DBConnection diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp index 961eb4a53..71ad0e941 100644 --- a/src/ProjectFileIO.cpp +++ b/src/ProjectFileIO.cpp @@ -1132,7 +1132,7 @@ bool ProjectFileIO::RenameOrWarn(const FilePath &src, const FilePath &dst) if (!success) { - ShowErrorDialog( + ShowError( &window, XO("Error Writing to File"), XO("Audacity failed to write file %s.\n" @@ -1538,7 +1538,7 @@ bool ProjectFileIO::HandleXMLTag(const wxChar *tag, const wxChar **attrs) auto msg = XO("This file was saved using Audacity %s.\nYou are using Audacity %s. You may need to upgrade to a newer version to open this file.") .Format(audacityVersion, AUDACITY_VERSION_STRING); - ShowErrorDialog( + ShowError( &window, XO("Can't open project file"), msg, @@ -1926,7 +1926,7 @@ bool ProjectFileIO::SaveProject( if (!reopened) { wxTheApp->CallAfter([this]{ - ShowErrorDialog(nullptr, + ShowError(nullptr, XO("Warning"), XO( "The project's database failed to reopen, " @@ -1950,7 +1950,7 @@ bool ProjectFileIO::SaveProject( // after we switch to the new file. if (!CopyTo(fileName, XO("Saving project"), false)) { - ShowErrorDialog( + ShowError( nullptr, XO("Error Saving Project"), FileException::WriteFailureMessage(fileName), @@ -2001,7 +2001,7 @@ bool ProjectFileIO::SaveProject( if (!success) { // Additional help via a Help button links to the manual. - ShowErrorDialog(nullptr, + ShowError(nullptr, XO("Error Saving Project"), XO("The project failed to open, possibly due to limited space\n" "on the storage device.\n\n%s").Format(GetLastError()), @@ -2020,7 +2020,7 @@ bool ProjectFileIO::SaveProject( if (!AutoSaveDelete()) { // Additional help via a Help button links to the manual. - ShowErrorDialog(nullptr, + ShowError(nullptr, XO("Error Saving Project"), XO("Unable to remove autosave information, possibly due to limited space\n" "on the storage device.\n\n%s").Format(GetLastError()), @@ -2059,7 +2059,7 @@ bool ProjectFileIO::SaveProject( else { if ( !UpdateSaved( nullptr ) ) { - ShowErrorDialog( + ShowError( nullptr, XO("Error Saving Project"), FileException::WriteFailureMessage(fileName), @@ -2182,6 +2182,15 @@ wxLongLong ProjectFileIO::GetFreeDiskSpace() const return -1; } +/// Displays an error dialog with a button that offers help +void ProjectFileIO::ShowError(wxWindow *parent, + const TranslatableString &dlogTitle, + const TranslatableString &message, + const wxString &helpPage) +{ + ShowErrorDialog(parent, dlogTitle, message, helpPage, true, GetLastLog()); +} + const TranslatableString &ProjectFileIO::GetLastError() const { return mpErrors->mLastError; @@ -2197,6 +2206,11 @@ int ProjectFileIO::GetLastErrorCode() const return mpErrors->mErrorCode; } +const wxString &ProjectFileIO::GetLastLog() const +{ + return mpErrors->mLog; +} + void ProjectFileIO::SetError( const TranslatableString& msg, const TranslatableString& libraryError, int errorCode) { diff --git a/src/ProjectFileIO.h b/src/ProjectFileIO.h index 5425c8a3a..2917f3f66 100644 --- a/src/ProjectFileIO.h +++ b/src/ProjectFileIO.h @@ -114,9 +114,15 @@ public: // specific database. This is the workhorse for the above 3 methods. static int64_t GetDiskUsage(DBConnection &conn, SampleBlockID blockid); + // Displays an error dialog with a button that offers help + void ShowError(wxWindow *parent, + const TranslatableString &dlogTitle, + const TranslatableString &message, + const wxString &helpPage); const TranslatableString &GetLastError() const; const TranslatableString &GetLibraryError() const; int GetLastErrorCode() const; + const wxString &GetLastLog() const; // Provides a means to bypass "DELETE"s at shutdown if the database // is just going to be deleted anyway. This prevents a noticeable diff --git a/src/ProjectFileManager.cpp b/src/ProjectFileManager.cpp index a8e4839c4..ef8f5075e 100644 --- a/src/ProjectFileManager.cpp +++ b/src/ProjectFileManager.cpp @@ -1021,7 +1021,7 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory wxLogError(wxT("Could not parse file \"%s\". \nError: %s"), fileName, errorStr.Debug()); - ShowErrorDialog( + projectFileIO.ShowError( &window, XO("Error Opening Project"), errorStr, diff --git a/src/widgets/ErrorDialog.cpp b/src/widgets/ErrorDialog.cpp index 3e53029d0..0ef50d700 100644 --- a/src/widgets/ErrorDialog.cpp +++ b/src/widgets/ErrorDialog.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include "HelpSystem.h" BEGIN_EVENT_TABLE(ErrorDialog, wxDialogWrapper) + EVT_COLLAPSIBLEPANE_CHANGED( wxID_ANY, ErrorDialog::OnPane ) EVT_BUTTON( wxID_OK, ErrorDialog::OnOk) EVT_BUTTON( wxID_HELP, ErrorDialog::OnHelp) END_EVENT_TABLE() @@ -47,8 +49,11 @@ ErrorDialog::ErrorDialog( const TranslatableString & dlogTitle, const TranslatableString & message, const wxString & helpPage, - const bool Close, const bool modal): - wxDialogWrapper(parent, (wxWindowID)-1, dlogTitle) + const wxString & log, + const bool Close, const bool modal) +: wxDialogWrapper(parent, wxID_ANY, dlogTitle, + wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { SetName(); @@ -62,31 +67,58 @@ ErrorDialog::ErrorDialog( ShuttleGui S(this, eIsCreating); - S.StartHorizontalLay(); + S.SetBorder(2); + S.StartHorizontalLay(wxEXPAND, 0); { - // wxART_ERROR and wxART_INFORMATION are other possibilities. -// S.AddIcon( &wxArtProvider::GetBitmap( wxART_WARNING)); - S.SetBorder( 20 ); + S.SetBorder(20); wxBitmap bitmap = wxArtProvider::GetBitmap(wxART_WARNING); - auto icon = safenew wxStaticBitmap(S.GetParent(), -1, bitmap); - S.AddWindow( icon ); - S.StartVerticalLay(); - { - S.SetBorder(20); - S.AddFixedText(message, false, 500); - S.SetBorder(2); - S.AddStandardButtons(buttonMask); - } - S.EndVerticalLay(); + S.AddWindow(safenew wxStaticBitmap(S.GetParent(), -1, bitmap)); + + S.SetBorder(20); + S.AddFixedText(message, false, 500); } S.EndHorizontalLay(); + S.SetBorder(2); + if (!log.empty()) + { + S.StartHorizontalLay(wxEXPAND, 1); + { + S.SetBorder(5); + + auto pane = safenew wxCollapsiblePane(S.GetParent(), + wxID_ANY, + XO("Show &Log...").Translation()); + S.Style(wxEXPAND | wxALIGN_LEFT); + S.Prop(1); + S.AddWindow(pane); + + ShuttleGui SI(pane->GetPane(), eIsCreating); + auto text = SI.AddTextWindow(log); + text->SetInsertionPointEnd(); + text->ShowPosition(text->GetLastPosition()); + text->SetMinSize(wxSize(700, 250)); + } + S.EndHorizontalLay(); + } + + S.SetBorder(2); + S.AddStandardButtons(buttonMask); + Layout(); GetSizer()->Fit(this); SetMinSize(GetSize()); Center(); } +void ErrorDialog::OnPane(wxCollapsiblePaneEvent & event) +{ + if (!event.GetCollapsed()) + { + Center(); + } +} + void ErrorDialog::OnOk(wxCommandEvent & WXUNUSED(event)) { if (dModal) @@ -95,7 +127,6 @@ void ErrorDialog::OnOk(wxCommandEvent & WXUNUSED(event)) Destroy(); } - void ErrorDialog::OnHelp(wxCommandEvent & WXUNUSED(event)) { if( dhelpPage.StartsWith(wxT("innerlink:")) ) @@ -118,9 +149,10 @@ void ShowErrorDialog(wxWindow *parent, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage, - const bool Close) + const bool Close, + const wxString &log) { - ErrorDialog dlog(parent, dlogTitle, message, helpPage, Close); + ErrorDialog dlog(parent, dlogTitle, message, helpPage, log, Close); dlog.CentreOnParent(); dlog.ShowModal(); } @@ -131,13 +163,14 @@ void ShowModelessErrorDialog(wxWindow *parent, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage, - const bool Close) + const bool Close, + const wxString &log) { // ensure it has some parent. if( !parent ) parent = wxTheApp->GetTopWindow(); wxASSERT(parent); - ErrorDialog *dlog = safenew ErrorDialog(parent, dlogTitle, message, helpPage, Close, false); + ErrorDialog *dlog = safenew ErrorDialog(parent, dlogTitle, message, helpPage, log, Close, false); dlog->CentreOnParent(); dlog->Show(); // ANSWER-ME: Vigilant Sentry flagged this method as not deleting dlog, so diff --git a/src/widgets/ErrorDialog.h b/src/widgets/ErrorDialog.h index 0765afb2f..e91c763f1 100644 --- a/src/widgets/ErrorDialog.h +++ b/src/widgets/ErrorDialog.h @@ -19,6 +19,7 @@ #include "wxPanelWrapper.h" // to inherit class AudacityProject; +class wxCollapsiblePaneEvent; class ErrorDialog /* not final */ : public wxDialogWrapper { @@ -28,6 +29,7 @@ public: const TranslatableString & dlogTitle, const TranslatableString & message, const wxString & helpPage, + const wxString & log, const bool Close = true, const bool modal = true); virtual ~ErrorDialog(){} @@ -37,6 +39,7 @@ private: bool dClose; bool dModal; + void OnPane( wxCollapsiblePaneEvent &event ); void OnOk( wxCommandEvent &event ); void OnHelp( wxCommandEvent &event ); DECLARE_EVENT_TABLE() @@ -47,14 +50,16 @@ void ShowErrorDialog(wxWindow *parent, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage, - bool Close = true); + bool Close = true, + const wxString &log = {}); /// Displays a modeless error dialog with a button that offers help void ShowModelessErrorDialog(wxWindow *parent, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage, - bool Close = true); + bool Close = true, + const wxString &log = {}); #include // to inherit