Audacity 3.2.0
DirectoriesPrefs.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 DirectoriesPrefs.cpp
6
7 Joshua Haberman
8 James Crook
9
10
11*******************************************************************//*******************************************************************/
17
18
19#include "DirectoriesPrefs.h"
20
21#include <math.h>
22
23#include <wx/defs.h>
24#include <wx/intl.h>
25#include <wx/log.h>
26#include <wx/stattext.h>
27#include <wx/textctrl.h>
28#include <wx/button.h>
29#include <wx/dirdlg.h>
30#include <wx/event.h>
31#include <wx/filefn.h>
32#include <wx/filename.h>
33#include <wx/utils.h>
34
35#include "Prefs.h"
36#include "../ShuttleGui.h"
37#include "TempDirectory.h"
38#include "../widgets/AudacityMessageBox.h"
39#include "../widgets/ReadOnlyText.h"
40#include "../widgets/wxTextCtrlWrapper.h"
41#include "FileNames.h"
42
43using namespace FileNames;
44using namespace TempDirectory;
45
46class FilesystemValidator : public wxValidator
47{
48public:
50 : wxValidator()
51 {
52 mMessage = message;
53 }
54
55 virtual wxObject* Clone() const wxOVERRIDE
56 {
58 }
59
60 virtual bool Validate(wxWindow* WXUNUSED(parent)) wxOVERRIDE
61 {
62 wxTextCtrl* tc = wxDynamicCast(GetWindow(), wxTextCtrl);
63 if (!tc) {
64 return true;
65 }
66
67 if (FATFilesystemDenied(tc->GetValue(), mMessage)) {
68 return false;
69 }
70
71 return true;
72 }
73
74 virtual bool TransferToWindow() wxOVERRIDE
75 {
76 return true;
77 }
78
79 virtual bool TransferFromWindow() wxOVERRIDE
80 {
81 return true;
82 }
83
84 void OnChar(wxKeyEvent &evt)
85 {
86 evt.Skip();
87
88 wxTextCtrl* tc = wxDynamicCast(GetWindow(), wxTextCtrl);
89 if (!tc) {
90 return;
91 }
92
93 auto keycode = evt.GetUnicodeKey();
94 if (keycode < WXK_SPACE || keycode == WXK_DELETE) {
95 return;
96 }
97
98 wxString path = tc->GetValue();
99 path.insert(tc->GetInsertionPoint(), keycode);
100
101 if (FATFilesystemDenied(path, mMessage)) {
102 evt.Skip(false);
103 return;
104 }
105 }
106
108
110};
111
114wxEND_EVENT_TABLE()
115
116enum
117{
118 TempTextID = 1000,
119 TempButtonID,
120
121 TextsStart = 1010,
122 OpenTextID,
123 SaveTextID,
124 ImportTextID,
125 ExportTextID,
126 MacrosTextID,
127 TextsEnd,
128
129 ButtonsStart = 1020,
130 OpenButtonID,
134 MacrosButtonID,
135 ButtonsEnd
136};
137
138BEGIN_EVENT_TABLE(DirectoriesPrefs, PrefsPanel)
139 EVT_TEXT(TempTextID, DirectoriesPrefs::OnTempText)
141 EVT_COMMAND_RANGE(ButtonsStart, ButtonsEnd, wxEVT_BUTTON, DirectoriesPrefs::OnBrowse)
143
144DirectoriesPrefs::DirectoriesPrefs(wxWindow * parent, wxWindowID winid)
145/* i18n-hint: Directories, also called directories, in computer file systems */
146: PrefsPanel(parent, winid, XO("Directories")),
147 mFreeSpace(NULL),
148 mTempText(NULL)
149{
150 Populate();
151}
152
154{
155}
156
158{
160}
161
163{
164 return XO("Preferences for Directories");
165}
166
168{
169 return "Directories_Preferences";
170}
171
174{
175 //------------------------- Main section --------------------
176 // Now construct the GUI itself.
177 // Use 'eIsCreatingFromPrefs' so that the GUI is
178 // initialised with values from gPrefs.
181 // ----------------------- End of main section --------------
182
183 wxCommandEvent e;
184 OnTempText(e);
185}
186
188{
189 S.SetBorder(2);
190 S.StartScroller();
191
192 S.StartStatic(XO("Default directories"));
193 {
194 S.AddSpace(1);
195 S.AddFixedText(XO("Leave a field empty to go to the last directory used for that operation.\n"
196 "Fill in a field to always go to that directory for that operation."), false, 450);
197 S.AddSpace(5);
198
199 S.StartMultiColumn(3, wxEXPAND);
200 {
201 S.SetStretchyCol(1);
202
203 S.Id(OpenTextID);
204 mOpenText = S.TieTextBox(XXO("O&pen:"),
205 {PreferenceKey(Operation::Open, PathType::User),
206 wxT("")},
207 30);
208 S.Id(OpenButtonID).AddButton(XXO("&Browse..."));
209
210 S.Id(SaveTextID);
211 mSaveText = S.TieTextBox(XXO("S&ave:"),
212 {PreferenceKey(Operation::Save, PathType::User),
213 wxT("")},
214 30);
215 if( mSaveText )
216 mSaveText->SetValidator(FilesystemValidator(XO("Projects cannot be saved to FAT drives.")));
217 S.Id(SaveButtonID).AddButton(XXO("B&rowse..."));
218
219 S.Id(ImportTextID);
220 mImportText = S.TieTextBox(XXO("&Import:"),
221 {PreferenceKey(Operation::Import, PathType::User),
222 wxT("")},
223 30);
224 S.Id(ImportButtonID).AddButton(XXO("Br&owse..."));
225
226 S.Id(ExportTextID);
227 mExportText = S.TieTextBox(XXO("&Export:"),
228 {PreferenceKey(Operation::Export, PathType::User),
229 wxT("")},
230 30);
231 S.Id(ExportButtonID).AddButton(XXO("Bro&wse..."));
232
233 S.Id(MacrosTextID);
234 mMacrosText = S.TieTextBox(XXO("&Macro output:"),
235 {PreferenceKey(Operation::MacrosOut, PathType::User),
236 wxT("")},
237 30);
238 S.Id(MacrosButtonID).AddButton(XXO("Bro&wse..."));
239
240
241 }
242 S.EndMultiColumn();
243 }
244 S.EndStatic();
245
246 S.StartStatic(XO("Temporary files directory"));
247 {
248 S.StartMultiColumn(3, wxEXPAND);
249 {
250 S.SetStretchyCol(1);
251
252 S.Id(TempTextID);
253 mTempText = S.TieTextBox(XXO("&Location:"),
254 {PreferenceKey(Operation::Temp, PathType::_None),
255 wxT("")},
256 30);
257 if( mTempText )
258 mTempText->SetValidator(FilesystemValidator(XO("Temporary files directory cannot be on a FAT drive.")));
259 S.Id(TempButtonID).AddButton(XXO("Brow&se..."));
260
261 mFreeSpace = S
262 .AddReadOnlyText(XXO("&Free Space:"), "");
263 }
264 S.EndMultiColumn();
265 }
266 S.EndStatic();
267
268 S.EndScroller();
269}
270
271void DirectoriesPrefs::OnTempBrowse(wxCommandEvent &evt)
272{
273 wxString oldTemp = gPrefs->Read(PreferenceKey(Operation::Open, PathType::_None),
275
276 // Because we went through InitTemp() during initialisation,
277 // the old temp directory name in prefs should already be OK. Just in case there is
278 // some way we hadn't thought of for it to be not OK,
279 // we avoid prompting with it in that case and use the suggested default instead.
280 if (!IsTempDirectoryNameOK(oldTemp))
281 {
282 oldTemp = DefaultTempDir();
283 }
284
285 wxDirDialogWrapper dlog(this,
286 XO("Choose a location to place the temporary directory"),
287 oldTemp);
288 int retval = dlog.ShowModal();
289 if (retval != wxID_CANCEL && !dlog.GetPath().empty())
290 {
291 wxFileName tmpDirPath;
292 tmpDirPath.AssignDir(dlog.GetPath());
293
294 if (FATFilesystemDenied(tmpDirPath.GetFullPath(),
295 XO("Temporary files directory cannot be on a FAT drive."))) {
296 return;
297 }
298
299 //Checks if the temporary directory has write permissions(via Browse Button)
300 if (!FileNames::WritableLocationCheck(dlog.GetPath(), XO("Cannot set the preference.")))
301 {
302 return;
303 }
304
305 // Append an "audacity_temp" directory to this path if necessary (the
306 // default, the existing pref (as stored in the control), and any path
307 // ending in a directory with the same name as what we'd add should be OK
308 // already)
309 wxString newDirName;
310#if defined(__WXMAC__)
311 newDirName = wxT("SessionData");
312#elif defined(__WXMSW__)
313 // Clearing Bug 1271 residual issue. Let's NOT have temp in the name.
314 newDirName = wxT("SessionData");
315#else
316 newDirName = wxT(".audacity_temp");
317#endif
318 auto dirsInPath = tmpDirPath.GetDirs();
319
320 // If the default temp dir or user's pref dir don't end in '/' they cause
321 // wxFileName's == operator to construct a wxFileName representing a file
322 // (that doesn't exist) -- hence the constructor calls
323 if (tmpDirPath != wxFileName(DefaultTempDir(), wxT("")) &&
324 tmpDirPath != wxFileName(mTempText->GetValue(), wxT("")) &&
325 (dirsInPath.size() == 0 ||
326 dirsInPath[dirsInPath.size()-1] != newDirName))
327 {
328 tmpDirPath.AppendDir(newDirName);
329 }
330
331 mTempText->SetValue(tmpDirPath.GetPath(wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR));
332 OnTempText(evt);
333 }
334}
335
336void DirectoriesPrefs::OnTempText(wxCommandEvent & WXUNUSED(evt))
337{
339
340 if (mTempText && mFreeSpace)
341 {
342 FilePath path = mTempText->GetValue();
343
344 wxLongLong space;
345 wxGetDiskSpace(path, NULL, &space);
346
347 label = wxDirExists(path)
348 ? Internat::FormatSize(space)
349 : XO("unavailable - above location doesn't exist");
350
352 }
353}
354
355void DirectoriesPrefs::OnBrowse(wxCommandEvent &evt)
356{
357 long id = evt.GetId() - ButtonsStart;
358 wxTextCtrl *tc = (wxTextCtrl *) FindWindow(id + TextsStart);
359
360 wxString location = tc->GetValue();
361
362 wxDirDialogWrapper dlog(this,
363 XO("Choose a location"),
364 location);
365 int retval = dlog.ShowModal();
366
367 if (retval == wxID_CANCEL)
368 {
369 return;
370 }
371
372 if (evt.GetId() == SaveButtonID)
373 {
374 if (FATFilesystemDenied(dlog.GetPath(),
375 XO("Projects cannot be saved to FAT drives.")))
376 {
377 return;
378 }
379 }
380
381 //Checks if the location for Open,Save.Import,Export and Macro Output has write permissions(Browse Buttons)
382 if (!FileNames::WritableLocationCheck(dlog.GetPath(), XO("Cannot set the preference.")))
383 {
384 return;
385 }
386
387 tc->SetValue(dlog.GetPath());
388}
389
390// Offers the user a dialog with an option to create a directory if it does not exist.
391// message is the explanation given to the user to show for which case is the directory creation is prompted.
392bool CreateDirectory(const wxString pathString, const TranslatableString & message) {
393 const wxFileName path { pathString };
394 int ans = AudacityMessageBox(
395 message +
396 XO("\nDirectory %s does not exist. Create it?")
397 .Format( pathString ),
398 XO("Warning"),
399 wxYES_NO | wxCENTRE | wxICON_EXCLAMATION);
400
401 if (ans != wxYES) {
402 return false;
403 }
404
405 if (!path.Mkdir(0755, wxPATH_MKDIR_FULL)) {
406 /* wxWidgets throws up a decent looking dialog */
407 using namespace BasicUI;
409 XO("Directory creation failed.") +
410 XO("\n%s").Format(message),
412 .Caption(XO("Error"))
413 .IconStyle(Icon::Error)
414 .ButtonStyle(Button::Ok)
415 );
416 return false;
417 }
418 return true;
419}
420
422{
423 wxFileName Temp;
424 Temp.SetPath(mTempText->GetValue());
425
426 wxString path{Temp.GetPath()};
427 if( !IsTempDirectoryNameOK( path ) ) {
429 XO("Directory %s is not suitable (at risk of being cleaned out)")
430 .Format( path ),
431 XO("Error"),
432 wxOK | wxICON_ERROR);
433 return false;
434 }
435
436 if (!Temp.DirExists()) {
437 if(CreateDirectory(path, XO("'Temporary Directory' cannot be set.")) == false)
438 return false;
439 }
440 else {
441 /* If the directory already exists, make sure it is writable */
443 XO("'Temporary files' directory cannot be set.")))
444 {
445 return false;
446 }
447 wxLogNull logNo;
448 Temp.AppendDir(wxT("canicreate"));
449 path = Temp.GetPath();
450 if (!Temp.Mkdir(0755)) {
452 XO("Directory %s is not writable")
453 .Format( path ),
454 XO("Error"),
455 wxOK | wxICON_ERROR);
456 return false;
457 }
458 Temp.Rmdir();
459 Temp.RemoveLastDir();
460 }
461
462 wxFileName oldDir;
463 oldDir.SetPath(TempDir());
464 if (Temp != oldDir) {
466 XO(
467"Changes to temporary directory will not take effect until Audacity is restarted"),
468 XO("Temp Directory Update"),
469 wxOK | wxCENTRE | wxICON_INFORMATION);
470 }
471
472 const wxString openPathString = mOpenText->GetValue();
473 const wxString savePathString = mSaveText->GetValue();
474 const wxString importPathString = mImportText->GetValue();
475 const wxString exportPathString = mExportText->GetValue();
476 const wxString macroPathString = mMacrosText->GetValue();
477 //error messages if the directories could not be set.
478 const std::initializer_list<TranslatableString> messagesPreference{
479 XO("'Open' directory cannot be set.") ,
480 XO("'Save' directory cannot be set.") ,
481 XO("'Import' directory cannot be set.") ,
482 XO("'Export' directory cannot be set.") ,
483 XO("'Macro Output' directory cannot be set.") ,
484 };
485
486 //flag for checking if at least one directory write protected
487 //will not be 0 if any of the paths are not writable
488 int flag = 0;
489 //id for indexing error messages to the initializer_list.
490 int id = 0;
491 //Checks if the location for Open,Save,Import,Export and Macro Output has write permissions(When OK is clicked)
492 for (auto &string : { openPathString, savePathString, importPathString, exportPathString, macroPathString} ) {
493 const wxFileName currentPath { string };
494 const auto & message = *(messagesPreference.begin() + id);
495 if(!string.empty()){
496 if (currentPath.DirExists()){
497 if(!FileNames::WritableLocationCheck(string, message))
498 flag++;
499 }
500 else {
501 return CreateDirectory(string, message);
502 }
503 }
504 id++;
505 }
506 if (flag != 0)
507 return false;
508
509 return true;
510}
511
513{
516
517 return true;
518}
519
522{
523 return [](wxWindow *parent, wxWindowID winid, AudacityProject *)
524 {
525 wxASSERT(parent); // to justify safenew
526 return safenew DirectoriesPrefs(parent, winid);
527 };
528}
529
530namespace
531{
533 {
534 "Directories",
536 };
537};
538
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
END_EVENT_TABLE()
@ SaveButtonID
@ ImportButtonID
@ ExportButtonID
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
PrefsPanel::Factory DirectoriesPrefsFactory()
wxBEGIN_EVENT_TABLE(FilesystemValidator, wxValidator) wxEND_EVENT_TABLE() enum
bool CreateDirectory(const wxString pathString, const TranslatableString &message)
#define DIRECTORIES_PREFS_PLUGIN_SYMBOL
EVT_COMMAND_RANGE(ID_Slider, ID_Slider+NUMBER_OF_BANDS - 1, wxEVT_COMMAND_SLIDER_UPDATED, EffectEqualization::OnSlider) EffectEqualization
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:71
wxString FilePath
Definition: Project.h:20
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:48
@ eIsSavingToPrefs
Definition: ShuttleGui.h:49
TranslatableString label
Definition: TagsEditor.cpp:163
#define S(N)
Definition: ToChars.cpp:64
int id
static std::once_flag flag
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
A PrefsPanel used to select directories.
void Populate()
Creates the dialog and its contents.
wxTextCtrl * mMacrosText
wxTextCtrl * mTempText
ReadOnlyText * mFreeSpace
bool Validate() override
wxTextCtrl * mOpenText
ManualPageID HelpPageName() override
If not empty string, the Help button is added below the panel.
void PopulateOrExchange(ShuttleGui &S) override
void OnTempBrowse(wxCommandEvent &evt)
ComponentInterfaceSymbol GetSymbol() const override
wxTextCtrl * mSaveText
wxTextCtrl * mExportText
void OnBrowse(wxCommandEvent &evt)
bool Commit() override
void OnTempText(wxCommandEvent &evt)
wxTextCtrl * mImportText
TranslatableString GetDescription() const override
virtual bool TransferToWindow() wxOVERRIDE
TranslatableString mMessage
void OnChar(wxKeyEvent &evt)
virtual wxObject * Clone() const wxOVERRIDE
FilesystemValidator(const TranslatableString &message)
virtual bool TransferFromWindow() wxOVERRIDE
virtual bool Validate(wxWindow *WXUNUSED(parent)) wxOVERRIDE
Abstract base class used in importing a file.
static TranslatableString FormatSize(wxLongLong size)
Convert a number to a string while formatting it in bytes, KB, MB, GB.
Definition: Internat.cpp:204
Base class for a panel in the PrefsDialog. Classes derived from this class include BatchPrefs,...
Definition: PrefsPanel.h:51
std::function< PrefsPanel *(wxWindow *parent, wxWindowID winid, AudacityProject *) > Factory
Definition: PrefsPanel.h:79
void SetValue(const wxString &value)
Definition: ReadOnlyText.h:125
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:269
FILES_API wxString PreferenceKey(FileNames::Operation op, FileNames::PathType type)
FILES_API bool WritableLocationCheck(const FilePath &path, const TranslatableString &message)
Check location on writable access and return true if checked successfully.
FILES_API bool FATFilesystemDenied(const FilePath &path, const TranslatableString &msg, const BasicUI::WindowPlacement &placement={})
FILES_API bool IsTempDirectoryNameOK(const FilePath &Name)
FILES_API wxString TempDir()
FILES_API const FilePath & DefaultTempDir()
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:98