Audacity  3.0.3
AudacityLogger.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  AudacityLogger.cpp
6 
7 ******************************************************************//*******************************************************************/
15 
16 
17 
18 #include "AudacityLogger.h"
19 
20 
21 
22 #include "FileNames.h"
23 #include "Internat.h"
24 #include "ShuttleGui.h"
25 
26 #include <mutex>
27 #include <wx/filedlg.h>
28 #include <wx/log.h>
29 #include <wx/ffile.h>
30 #include <wx/frame.h>
31 #include <wx/icon.h>
32 #include <wx/settings.h>
33 #include <wx/textctrl.h>
34 #include <wx/tokenzr.h>
35 
36 #include "../images/AudacityLogoAlpha.xpm"
38 
39 //
40 // AudacityLogger class
41 //
42 // Two reasons for this class instead of the wxLogWindow class (or any WX GUI logging class)
43 //
44 // 1) If wxLogWindow is used and initialized before the Mac's "root" window, then
45 // Audacity may crash when terminating. It's not fully understood why this occurs
46 // but it probably has to do with the order of deletion. However, deferring the
47 // creation of the log window until it is actually shown circumvents the problem.
48 // 2) By providing an Audacity specific logging class, it can be made thread-safe and,
49 // as such, can be used by the ever growing threading within Audacity.
50 //
51 enum
52 {
53  LoggerID_Save = wxID_HIGHEST + 1,
56 };
57 
59 {
60  static std::once_flag flag;
61  std::call_once( flag, []{
62  // wxWidgets will clean up the logger for the main thread, so we can say
63  // safenew. See:
64  // http://docs.wxwidgets.org/3.0/classwx_log.html#a2525bf54fa3f31dc50e6e3cd8651e71d
65  std::unique_ptr < wxLog > // DELETE any previous logger
66  { wxLog::SetActiveTarget(safenew AudacityLogger) };
67  } );
68 
69  // Use dynamic_cast so that we get a NULL ptr in case our logger
70  // is no longer the target.
71  return dynamic_cast<AudacityLogger *>(wxLog::GetActiveTarget());
72 }
73 
75 : wxEvtHandler(),
76  wxLog()
77 {
78  mText = NULL;
79  mUpdated = false;
80 }
81 
83 
85 {
86  if (mUpdated && mFrame && mFrame->IsShown()) {
87  mUpdated = false;
88  mText->ChangeValue(mBuffer);
89  }
90 }
91 
92 void AudacityLogger::DoLogText(const wxString & str)
93 {
94  if (!wxIsMainThread()) {
95  wxMutexGuiEnter();
96  }
97 
98  if (mBuffer.empty()) {
99  wxString stamp;
100 
101  TimeStamp(&stamp);
102 
103  mBuffer << stamp << _TS("Audacity ") << AUDACITY_VERSION_STRING << wxT("\n");
104  }
105 
106  mBuffer << str << wxT("\n");
107 
108  mUpdated = true;
109 
110  Flush();
111 
112  if (!wxIsMainThread()) {
113  wxMutexGuiLeave();
114  }
115 }
116 
117 bool AudacityLogger::SaveLog(const wxString &fileName) const
118 {
119  wxFFile file(fileName, wxT("w"));
120 
121  if (file.IsOpened()) {
122  file.Write(mBuffer);
123  file.Close();
124  return true;
125  }
126 
127  return false;
128 }
129 
131 {
132  mBuffer = wxEmptyString;
133  DoLogText(wxT("Log Cleared."));
134 
135  return true;
136 }
137 
138 void AudacityLogger::Show(bool show)
139 {
140  // Hide the frame if created, otherwise do nothing
141  if (!show) {
142  if (mFrame) {
143  mFrame->Show(false);
144  }
145  return;
146  }
147 
148  // If the frame already exists, refresh its contents and show it
149  if (mFrame) {
150  if (!mFrame->IsShown()) {
151  mText->ChangeValue(mBuffer);
152  mText->SetInsertionPointEnd();
153  mText->ShowPosition(mText->GetLastPosition());
154  }
155  mFrame->Show();
156  mFrame->Raise();
157  return;
158  }
159 
160  // This is the first use, so create the frame
162  { safenew wxFrame(NULL, wxID_ANY, _("Audacity Log")) };
163  frame->SetName(frame->GetTitle());
164  frame->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
165 
166  // loads either the XPM or the windows resource, depending on the platform
167  {
168 #if !defined(__WXMAC__) && !defined(__WXX11__)
169 #if defined(__WXMSW__)
170  wxIcon ic{wxICON(AudacityLogo)};
171 #elif defined(__WXGTK__)
172  wxIcon ic{wxICON(AudacityLogoAlpha)};
173 #else
174  wxIcon ic{};
175  ic.CopyFromBitmap(theTheme.Bitmap(bmpAudacityLogo48x48));
176 #endif
177  frame->SetIcon(ic);
178 #endif
179  }
180 
181  // Log text
182  ShuttleGui S(frame.get(), eIsCreating);
183 
184  S.Style(wxNO_BORDER | wxTAB_TRAVERSAL).Prop(true).StartPanel();
185  {
186  S.StartVerticalLay(true);
187  {
188  mText = S.Style(wxTE_MULTILINE | wxHSCROLL | wxTE_READONLY | wxTE_RICH)
190 
191  S.AddSpace(0, 5);
192  S.StartHorizontalLay(wxALIGN_CENTER, 0);
193  {
194  S.AddSpace(10, 0);
195  S.Id(LoggerID_Save).AddButton(XXO("&Save..."));
196  S.Id(LoggerID_Clear).AddButton(XXO("Cl&ear"));
197  S.Id(LoggerID_Close).AddButton(XXO("&Close"));
198  S.AddSpace(10, 0);
199  }
200  S.EndHorizontalLay();
201  S.AddSpace(0, 3);
202  }
203  S.EndVerticalLay();
204  }
205  S.EndPanel();
206 
207  // Give a place for the menu help text to go
208  // frame->CreateStatusBar();
209 
210  frame->Layout();
211 
212  // Hook into the frame events
213  frame->Bind(wxEVT_CLOSE_WINDOW,
214  wxCloseEventHandler(AudacityLogger::OnCloseWindow),
215  this);
216 
217  frame->Bind( wxEVT_COMMAND_MENU_SELECTED,
219  this, LoggerID_Save);
220  frame->Bind( wxEVT_COMMAND_MENU_SELECTED,
222  this, LoggerID_Clear);
223  frame->Bind( wxEVT_COMMAND_MENU_SELECTED,
225  this, LoggerID_Close);
226  frame->Bind( wxEVT_COMMAND_BUTTON_CLICKED,
228  this, LoggerID_Save);
229  frame->Bind( wxEVT_COMMAND_BUTTON_CLICKED,
231  this, LoggerID_Clear);
232  frame->Bind( wxEVT_COMMAND_BUTTON_CLICKED,
234  this, LoggerID_Close);
235 
236  mFrame = std::move( frame );
237 
238  mFrame->Show();
239 
240  Flush();
241 }
242 
243 wxString AudacityLogger::GetLog(int count)
244 {
245  if (count == 0)
246  {
247  return mBuffer;
248  }
249 
250  wxString buffer;
251 
252  auto lines = wxStringTokenize(mBuffer, wxT("\r\n"), wxTOKEN_RET_DELIMS);
253  for (int index = lines.size() - 1; index >= 0 && count > 0; --index, --count)
254  {
255  buffer.Prepend(lines[index]);
256  }
257 
258  return buffer;
259 }
260 
261 void AudacityLogger::OnCloseWindow(wxCloseEvent & WXUNUSED(e))
262 {
263 #if defined(__WXMAC__)
264  // On the Mac, destroy the window rather than hiding it since the
265  // log menu will override the root windows menu if there is no
266  // project window open.
267  mFrame.reset();
268 #else
269  Show(false);
270 #endif
271 }
272 
273 void AudacityLogger::OnClose(wxCommandEvent & WXUNUSED(e))
274 {
275  wxCloseEvent dummy;
276  OnCloseWindow(dummy);
277 }
278 
279 void AudacityLogger::OnClear(wxCommandEvent & WXUNUSED(e))
280 {
281  ClearLog();
282 }
283 
284 void AudacityLogger::OnSave(wxCommandEvent & WXUNUSED(e))
285 {
286  wxString fName = _("log.txt");
287 
288  fName = FileNames::SelectFile(FileNames::Operation::Export,
289  XO("Save log to:"),
290  wxEmptyString,
291  fName,
292  wxT("txt"),
294  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
295  mFrame.get());
296 
297  if (fName.empty()) {
298  return;
299  }
300 
301  if (!mText->SaveFile(fName)) {
303  XO("Couldn't save log to file: %s").Format( fName ),
304  XO("Warning"),
305  wxICON_EXCLAMATION,
306  mFrame.get());
307  return;
308  }
309 }
310 
312 {
313  if (mFrame) {
314  bool shown = mFrame->IsShown();
315  if (shown) {
316  Show(false);
317  }
318  mFrame.reset();
319  if (shown) {
320  Show(true);
321  }
322  }
323 }
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:36
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1177
AudacityLogger::mText
wxTextCtrl * mText
Definition: AudacityLogger.h:63
flag
static std::once_flag flag
Definition: WaveformView.cpp:1113
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: AudacityMessageBox.h:20
AudacityLogger::UpdatePrefs
void UpdatePrefs() override
Definition: AudacityLogger.cpp:311
Format
Abstract base class used in importing a file.
AudacityLogger
AudacityLogger is a thread-safe logger class.
Definition: AudacityLogger.h:32
ShuttleGui::AddSpace
wxSizerItem * AddSpace(int width, int height, int prop=0)
Definition: ShuttleGui.cpp:2421
AudacityLogger::OnSave
void OnSave(wxCommandEvent &e)
Definition: AudacityLogger.cpp:284
XO
#define XO(s)
Definition: Internat.h:32
AudacityLogger::mUpdated
bool mUpdated
Definition: AudacityLogger.h:65
LoggerID_Save
@ LoggerID_Save
Definition: AudacityLogger.cpp:53
AudacityLogger::OnClear
void OnClear(wxCommandEvent &e)
Definition: AudacityLogger.cpp:279
ShuttleGuiBase::StartPanel
wxPanel * StartPanel(int iStyle=0)
Definition: ShuttleGui.cpp:983
ShuttleGuiBase::EndPanel
void EndPanel()
Definition: ShuttleGui.cpp:1011
LoggerID_Clear
@ LoggerID_Clear
Definition: AudacityLogger.cpp:54
ShuttleGui::Id
ShuttleGui & Id(int id)
Definition: ShuttleGui.cpp:2248
AudacityLogger::DoLogText
void DoLogText(const wxString &msg) override
Definition: AudacityLogger.cpp:92
ThemeBase::Bitmap
wxBitmap & Bitmap(int iIndex)
Definition: Theme.cpp:1211
_TS
#define _TS(s)
Definition: Internat.h:28
AudacityLogger::OnClose
void OnClose(wxCommandEvent &e)
Definition: AudacityLogger.cpp:273
ShuttleGui::Style
ShuttleGui & Style(long iStyle)
Definition: ShuttleGui.h:734
XXO
#define XXO(s)
Definition: Internat.h:45
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1170
AudacityLogger::ClearLog
bool ClearLog()
Definition: AudacityLogger.cpp:130
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1160
FileNames::TextFiles
AUDACITY_DLL_API const FileType TextFiles
Definition: FileNames.h:77
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1196
AudacityLogger::Get
static AudacityLogger * Get()
Definition: AudacityLogger.cpp:58
AudacityLogger::SaveLog
bool SaveLog(const wxString &fileName) const
Definition: AudacityLogger.cpp:117
AudacityLogger::OnCloseWindow
void OnCloseWindow(wxCloseEvent &e)
Definition: AudacityLogger.cpp:261
ShuttleGui.h
AudacityLogger::AudacityLogger
AudacityLogger()
Definition: AudacityLogger.cpp:74
ShuttleGui::Prop
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:732
ShuttleGuiBase::AddButton
wxButton * AddButton(const TranslatableString &Text, int PositionFlags=wxALIGN_CENTRE, bool setDefault=false)
Definition: ShuttleGui.cpp:353
Internat.h
AudacityLogger.h
AudacityLogger::Flush
void Flush() override
Definition: AudacityLogger.cpp:84
AudacityLogger::Show
void Show(bool show=true)
Definition: AudacityLogger.cpp:138
FileNames::SelectFile
AUDACITY_DLL_API FilePath SelectFile(Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
_
#define _(s)
Definition: Internat.h:76
FileNames.h
AudacityMessageBox.h
AudacityLogger::~AudacityLogger
~AudacityLogger() override
LoggerID_Close
@ LoggerID_Close
Definition: AudacityLogger.cpp:55
ShuttleGuiBase::AddTextWindow
wxTextCtrl * AddTextWindow(const wxString &Value)
Multiline text box that grows.
Definition: ShuttleGui.cpp:705
AudacityLogger::mBuffer
wxString mBuffer
Definition: AudacityLogger.h:64
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:201
Destroy_ptr
std::unique_ptr< T, Destroyer< T > > Destroy_ptr
a convenience for using Destroyer
Definition: MemoryX.h:365
safenew
#define safenew
Definition: MemoryX.h:8
AudacityLogger::mFrame
Destroy_ptr< wxFrame > mFrame
Definition: AudacityLogger.h:62
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:638
AudacityLogger::GetLog
wxString GetLog(int count=0)
Definition: AudacityLogger.cpp:243