Audacity  3.0.3
AutoRecoveryDialog.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 AutoRecoveryDialog.cpp
6 
7 Paul Licameli split from AutoRecovery.cpp
8 
9 **********************************************************************/
10 
11 #include "AutoRecoveryDialog.h"
12 
13 #include "ActiveProjects.h"
14 #include "ProjectManager.h"
15 #include "ProjectFileIO.h"
16 #include "ProjectFileManager.h"
17 #include "ShuttleGui.h"
18 #include "TempDirectory.h"
20 #include "widgets/wxPanelWrapper.h"
21 
22 #include <wx/dir.h>
23 #include <wx/evtloop.h>
24 #include <wx/filefn.h>
25 #include <wx/filename.h>
26 #include <wx/listctrl.h>
27 
28 enum {
34 };
35 
36 class AutoRecoveryDialog final : public wxDialogWrapper
37 {
38 public:
39  explicit AutoRecoveryDialog(AudacityProject *proj);
40 
41  bool HasRecoverables() const;
43 
44 private:
46  void PopulateList();
47  bool HaveChecked();
48 
49  void OnQuitAudacity(wxCommandEvent &evt);
50  void OnDiscardSelected(wxCommandEvent &evt);
51  void OnRecoverSelected(wxCommandEvent &evt);
52  void OnSkip(wxCommandEvent &evt);
53  void OnColumnClicked(wxListEvent &evt);
54  void OnItemActivated(wxListEvent &evt);
55  void OnListKeyDown(wxKeyEvent &evt);
56 
58  wxListCtrl *mFileList;
60 
61 public:
62  DECLARE_EVENT_TABLE()
63 };
64 
65 BEGIN_EVENT_TABLE(AutoRecoveryDialog, wxDialogWrapper)
73 
75 : wxDialogWrapper(nullptr, wxID_ANY, XO("Automatic Crash Recovery"),
76  wxDefaultPosition, wxDefaultSize,
77  (wxDEFAULT_DIALOG_STYLE & (~wxCLOSE_BOX)) | wxRESIZE_BORDER), // no close box
78  mProject(project)
79 {
80  SetName();
81  ShuttleGui S(this, eIsCreating);
82  PopulateOrExchange(S);
83 }
84 
86 {
87  return mFiles.size() > 0;
88 }
89 
91 {
92  return mFiles;
93 }
94 
96 {
97  S.SetBorder(5);
98  S.StartVerticalLay(wxEXPAND, 1);
99  {
100  S.AddFixedText(
101  XO("The following projects were not saved properly the last time Audacity was run and "
102  "can be automatically recovered.\n\n"
103  "After recovery, save the projects to ensure changes are written to disk."),
104  false,
105  500);
106 
107  S.StartStatic(XO("Recoverable &projects"), 1);
108  {
111  .AddListControlReportMode(
112  {
113  /*i18n-hint: (verb). It instruct the user to select items.*/
114  XO("Select"),
115  /*i18n-hint: (noun). It's the name of the project to recover.*/
116  XO("Name")
117  });
118  mFileList->EnableCheckBoxes();
119  PopulateList();
120  }
121  S.EndStatic();
122 
123  S.StartHorizontalLay(wxALIGN_CENTRE, 0);
124  {
125  S.Id(ID_QUIT_AUDACITY).AddButton(XXO("&Quit Audacity"));
126  S.Id(ID_DISCARD_SELECTED).AddButton(XXO("&Discard Selected"));
127  S.Id(ID_RECOVER_SELECTED).AddButton(XXO("&Recover Selected"), wxALIGN_CENTRE, true);
128  S.Id(ID_SKIP).AddButton(XXO("&Skip"));
129 
130  SetAffirmativeId(ID_RECOVER_SELECTED);
131  SetEscapeId(ID_SKIP);
132  }
133  S.EndHorizontalLay();
134  }
135  S.EndVerticalLay();
136 
137  Layout();
138  Fit();
139  SetMinSize(GetSize());
140 
141  // Sometimes it centers on wxGTK and sometimes it doesn't.
142  // Yielding before centering seems to be a good workaround,
143  // but will leave to implement on a rainy day.
144  Center();
145 }
146 
148 {
149  wxString tempdir = TempDirectory::TempDir();
150  wxString pattern = wxT("*.") + FileNames::UnsavedProjectExtension();
151  FilePaths files;
152 
153  wxDir::GetAllFiles(tempdir, &files, pattern, wxDIR_FILES);
154 
156 
157  for (auto file : active)
158  {
159  wxFileName fn = file;
160  if (fn.FileExists())
161  {
162  FilePath fullPath = fn.GetFullPath();
163  if (files.Index(fullPath) == wxNOT_FOUND)
164  {
165  files.push_back(fullPath);
166  }
167  }
168  else
169  {
171  }
172  }
173 
174  FilePath activeFile;
175  if (mProject)
176  {
177  auto &projectFileIO = ProjectFileIO::Get(*mProject);
178  activeFile = projectFileIO.GetFileName();
179  }
180 
181  mFiles.clear();
182  mFileList->DeleteAllItems();
183  long item = 0;
184 
185  for (auto file : files)
186  {
187  wxFileName fn = file;
188  if (fn != activeFile)
189  {
190  mFiles.push_back(fn.GetFullPath());
191  mFileList->InsertItem(item, wxT(""));
192  mFileList->SetItem(item, 1, fn.GetName());
193  mFileList->CheckItem(item, true);
194  item++;
195  }
196  }
197  mFileList->SetMinSize(mFileList->GetBestSize());
198  mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
199  mFileList->SetColumnWidth(1, 500);
200 
201  if (item)
202  {
203  mFileList->SetItemState(0,
204  wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED,
205  wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
206  mFileList->SetFocus();
207  }
208 }
209 
211 {
212  long item = -1;
213  while (true)
214  {
215  item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
216  if (item == wxNOT_FOUND)
217  {
218  break;
219  }
220  if (mFileList->IsItemChecked(item))
221  {
222  return true;
223  }
224  }
225 
226  AudacityMessageBox(XO("No projects selected"), XO("Automatic Crash Recovery"));
227 
228  return false;
229 }
230 
231 void AutoRecoveryDialog::OnQuitAudacity(wxCommandEvent &WXUNUSED(evt))
232 {
233  EndModal(ID_QUIT_AUDACITY);
234 }
235 
236 void AutoRecoveryDialog::OnDiscardSelected(wxCommandEvent &WXUNUSED(evt))
237 {
238  if (!HaveChecked())
239  {
240  return;
241  }
242 
243  long item = -1;
244  bool selectedTemporary = false;
245  while (!selectedTemporary)
246  {
247  item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
248  if (item == wxNOT_FOUND)
249  break;
250  if (!mFileList->IsItemChecked(item))
251  continue;
252  FilePath fileName = mFiles[item];
253  wxFileName file(fileName);
254  if (file.GetExt().IsSameAs(FileNames::UnsavedProjectExtension()))
255  selectedTemporary = true;
256  }
257 
258  // Don't give this warning message if all of the checked items are
259  // previously saved, non-temporary projects.
260  if (selectedTemporary) {
261  int ret = AudacityMessageBox(
262  XO("Are you sure you want to discard the selected projects?\n\n"
263  "Choosing \"Yes\" permanently deletes the selected projects immediately."),
264  XO("Automatic Crash Recovery"),
265  wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this);
266 
267  if (ret == wxNO)
268  return;
269  }
270 
271  item = -1;
272  FilePaths files;
273  while (true)
274  {
275  item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
276  if (item == wxNOT_FOUND)
277  {
278  break;
279  }
280  if (!mFileList->IsItemChecked(item))
281  {
282  // Keep in list
283  files.push_back(mFiles[item]);
284  continue;
285  }
286  FilePath fileName = mFiles[item];
287 
288  // Only remove it from disk if it appears to be a temporary file.
289  wxFileName file(fileName);
290  if (file.GetExt().IsSameAs(FileNames::UnsavedProjectExtension()))
291  {
292  file.SetFullName(wxT(""));
293 
294  wxFileName temp(TempDirectory::TempDir(), wxT(""));
295  if (file == temp)
297  }
298  else
299  // Don't remove from disk, but do (later) open the database
300  // of this saved file, and discard edits
301  files.push_back(fileName);
302 
303  // Forget all about it
304  ActiveProjects::Remove(fileName);
305  }
306 
307  PopulateList();
308 
309  mFiles = files;
310 
311  if (mFileList->GetItemCount() == 0)
312  {
313  EndModal(ID_DISCARD_SELECTED);
314  }
315 }
316 
317 void AutoRecoveryDialog::OnRecoverSelected(wxCommandEvent &WXUNUSED(evt))
318 {
319  if (!HaveChecked())
320  {
321  return;
322  }
323 
324  FilePaths files;
325 
326  bool selected = false;
327  long item = -1;
328  while (true)
329  {
330  item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
331  if (item == wxNOT_FOUND)
332  {
333  if (!selected)
334  {
335  AudacityMessageBox(XO("No projects selected"), XO("Automatic Crash Recovery"));
336  }
337  break;
338  }
339  selected = true;
340 
341  if (!mFileList->IsItemChecked(item))
342  {
343  continue;
344  }
345 
346  files.push_back(mFiles[item]);
347  }
348 
349  mFiles = files;
350 
351  EndModal(ID_RECOVER_SELECTED);
352 }
353 
354 void AutoRecoveryDialog::OnSkip(wxCommandEvent &WXUNUSED(evt))
355 {
356  EndModal(ID_SKIP);
357 }
358 
360 {
361  if (evt.GetColumn() != 0)
362  {
363  return;
364  }
365 
366  long item = -1;
367  while (true)
368  {
369  item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
370  if (item == wxNOT_FOUND)
371  {
372  break;
373  }
374  mFileList->CheckItem(item, !mFileList->IsItemChecked(item));
375  }
376 }
377 
379 {
380  long item = evt.GetIndex();
381  mFileList->CheckItem(item, !mFileList->IsItemChecked(item));
382 }
383 
385 {
386  switch (evt.GetKeyCode())
387  {
388  case WXK_SPACE:
389  {
390  bool selected = false;
391  long item = -1;
392  while (true)
393  {
394  item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
395  if (item == wxNOT_FOUND)
396  {
397  break;
398  }
399 
400  mFileList->CheckItem(item, !mFileList->IsItemChecked(item));
401  }
402  }
403  break;
404 
405  case WXK_RETURN:
406  // Don't know why wxListCtrls prevent default dialog action,
407  // but they do, so handle it.
408  EmulateButtonClickIfPresent(GetAffirmativeId());
409  break;
410 
411  default:
412  evt.Skip();
413  break;
414  }
415 }
416 
418 
419 static bool RecoverAllProjects(const FilePaths &files,
420  AudacityProject *&pproj)
421 {
422  // Open a project window for each auto save file
423  wxString filename;
424  bool result = true;
425 
426  for (auto &file: files)
427  {
428  AudacityProject *proj = nullptr;
429  // Reuse any existing project window, which will be the empty project
430  // created at application startup
431  std::swap(proj, pproj);
432 
433  // Open project.
434  if (ProjectManager::OpenProject(proj, file, false, true) == nullptr)
435  {
436  result = false;
437  }
438  }
439 
440  return result;
441 }
442 
443 static void DiscardAllProjects(const FilePaths &files)
444 {
445  // Open and close each file, invisibly, removing its Autosave blob
446  for (auto &file: files)
448 }
449 
450 bool ShowAutoRecoveryDialogIfNeeded(AudacityProject *&pproj, bool *didRecoverAnything)
451 {
452  if (didRecoverAnything)
453  {
454  *didRecoverAnything = false;
455  }
456 
457  bool success = true;
458 
459  // Under wxGTK3, the auto recovery dialog will not get
460  // the focus since the project window hasn't been allowed
461  // to completely initialize.
462  //
463  // Yielding seems to allow the initialization to complete.
464  //
465  // Additionally, it also corrects a sizing issue in the dialog
466  // related to wxWidgets bug:
467  //
468  // http://trac.wxwidgets.org/ticket/16440
469  //
470  // This must be done before "dlg" is declared.
471  wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
472 
473  AutoRecoveryDialog dialog(pproj);
474 
475  if (dialog.HasRecoverables())
476  {
477  int ret = dialog.ShowModal();
478 
479  switch (ret)
480  {
481  case ID_SKIP:
482  success = true;
483  break;
484 
485  case ID_DISCARD_SELECTED:
487  success = true;
488  break;
489 
490  case ID_RECOVER_SELECTED:
491  success = RecoverAllProjects(dialog.GetRecoverables(), pproj);
492  if (success)
493  {
494  if (didRecoverAnything)
495  {
496  *didRecoverAnything = true;
497  }
498  }
499  break;
500 
501  default:
502  // This includes ID_QUIT_AUDACITY
503  return false;
504  }
505  }
506 
507  return success;
508 }
EVT_BUTTON
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
AutoRecoveryDialog::GetRecoverables
FilePaths GetRecoverables()
Definition: AutoRecoveryDialog.cpp:90
FileNames::UnsavedProjectExtension
FILES_API wxString UnsavedProjectExtension()
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:38
AutoRecoveryDialog::mFileList
wxListCtrl * mFileList
Definition: AutoRecoveryDialog.cpp:58
ID_SKIP
@ ID_SKIP
Definition: AutoRecoveryDialog.cpp:32
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1184
ProjectFileIO.h
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
fn
static const auto fn
Definition: WaveformView.cpp:1108
RecoverAllProjects
static bool RecoverAllProjects(const FilePaths &files, AudacityProject *&pproj)
Definition: AutoRecoveryDialog.cpp:419
ID_DISCARD_SELECTED
@ ID_DISCARD_SELECTED
Definition: AutoRecoveryDialog.cpp:30
AutoRecoveryDialog::OnRecoverSelected
void OnRecoverSelected(wxCommandEvent &evt)
Definition: AutoRecoveryDialog.cpp:317
ActiveProjects.h
ProjectFileIO::RemoveProject
static bool RemoveProject(const FilePath &filename)
Remove any files associated with a project at given path; return true if successful.
Definition: ProjectFileIO.cpp:1256
XO
#define XO(s)
Definition: Internat.h:31
wxPanelWrapper.h
ProjectFileIO::Get
static ProjectFileIO & Get(AudacityProject &project)
Definition: ProjectFileIO.cpp:266
ID_QUIT_AUDACITY
@ ID_QUIT_AUDACITY
Definition: AutoRecoveryDialog.cpp:29
ProjectManager::OpenProject
static AudacityProject * OpenProject(AudacityProject *pGivenProject, const FilePath &fileNameArg, bool addtohistory, bool reuseNonemptyProject)
Open a file into an AudacityProject, returning the project, or nullptr for failure.
Definition: ProjectManager.cpp:949
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
AutoRecoveryDialog::OnDiscardSelected
void OnDiscardSelected(wxCommandEvent &evt)
Definition: AutoRecoveryDialog.cpp:236
ShuttleGui::Id
ShuttleGui & Id(int id)
Definition: ShuttleGui.cpp:2274
XXO
#define XXO(s)
Definition: Internat.h:44
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1177
FilePath
wxString FilePath
Definition: Project.h:20
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1167
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1203
ShowAutoRecoveryDialogIfNeeded
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject *&pproj, bool *didRecoverAnything)
Definition: AutoRecoveryDialog.cpp:450
AutoRecoveryDialog::OnColumnClicked
void OnColumnClicked(wxListEvent &evt)
Definition: AutoRecoveryDialog.cpp:359
ShuttleGuiBase::AddFixedText
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:440
AutoRecoveryDialog::AutoRecoveryDialog
AutoRecoveryDialog(AudacityProject *proj)
Definition: AutoRecoveryDialog.cpp:74
ProjectFileManager.h
ProjectFileManager::DiscardAutosave
static void DiscardAutosave(const FilePath &filename)
Definition: ProjectFileManager.cpp:76
AutoRecoveryDialog::OnListKeyDown
void OnListKeyDown(wxKeyEvent &evt)
Definition: AutoRecoveryDialog.cpp:384
anonymous_namespace{NoteTrack.cpp}::swap
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:735
TempDirectory.h
ShuttleGui.h
ProjectManager.h
AutoRecoveryDialog::OnSkip
void OnSkip(wxCommandEvent &evt)
Definition: AutoRecoveryDialog.cpp:354
ActiveProjects::GetAll
FilePaths GetAll()
DiscardAllProjects
static void DiscardAllProjects(const FilePaths &files)
Definition: AutoRecoveryDialog.cpp:443
AutoRecoveryDialog::mFiles
FilePaths mFiles
Definition: AutoRecoveryDialog.cpp:57
ShuttleGuiBase::AddButton
wxButton * AddButton(const TranslatableString &Text, int PositionFlags=wxALIGN_CENTRE, bool setDefault=false)
Definition: ShuttleGui.cpp:360
ShuttleGuiBase::StartStatic
wxStaticBox * StartStatic(const TranslatableString &Str, int iProp=0)
Definition: ShuttleGui.cpp:893
AutoRecoveryDialog
Definition: AutoRecoveryDialog.cpp:37
wxDialogWrapper
Definition: wxPanelWrapper.h:81
ID_FILE_LIST
@ ID_FILE_LIST
Definition: AutoRecoveryDialog.cpp:33
ShuttleGui::ConnectRoot
auto ConnectRoot(wxEventTypeTag< Tag > eventType, void(Handler::*func)(Argument &)) -> typename std::enable_if< std::is_base_of< Argument, Tag >::value, ShuttleGui & >::type
Definition: ShuttleGui.h:699
AutoRecoveryDialog::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S)
Definition: AutoRecoveryDialog.cpp:95
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
AutoRecoveryDialog::HaveChecked
bool HaveChecked()
Definition: AutoRecoveryDialog.cpp:210
AutoRecoveryDialog::PopulateList
void PopulateList()
Definition: AutoRecoveryDialog.cpp:147
AudacityMessageBox.h
AutoRecoveryDialog.h
AutoRecoveryDialog::OnItemActivated
void OnItemActivated(wxListEvent &evt)
Definition: AutoRecoveryDialog.cpp:378
EVT_LIST_ITEM_ACTIVATED
EVT_LIST_ITEM_ACTIVATED(wxID_ANY, SuccessDialog::OnItemActivated) ExportMultipleDialog
Definition: ExportMultiple.cpp:123
ID_RECOVER_SELECTED
@ ID_RECOVER_SELECTED
Definition: AutoRecoveryDialog.cpp:31
ShuttleGuiBase::SetBorder
void SetBorder(int Border)
Definition: ShuttleGui.h:489
ActiveProjects::Remove
void Remove(const FilePath &path)
AutoRecoveryDialog::OnQuitAudacity
void OnQuitAudacity(wxCommandEvent &evt)
Definition: AutoRecoveryDialog.cpp:231
ShuttleGuiBase::EndStatic
void EndStatic()
Definition: ShuttleGui.cpp:922
END_EVENT_TABLE
END_EVENT_TABLE()
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:631
AutoRecoveryDialog::HasRecoverables
bool HasRecoverables() const
Definition: AutoRecoveryDialog.cpp:85
TempDirectory::TempDir
FILES_API wxString TempDir()
Definition: TempDirectory.cpp:26
AutoRecoveryDialog::mProject
AudacityProject * mProject
Definition: AutoRecoveryDialog.cpp:59