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