Audacity 3.2.0
AudacityFileConfig.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5AudacityFileConfig.cpp
6
7Paul Licameli split from Prefs.cpp
8
9**********************************************************************/
10
11
12#include "AudacityFileConfig.h"
13
14#include "HelpSystem.h"
15#include "wxPanelWrapper.h"
16#include "ShuttleGui.h"
17#include "../images/Help.xpm"
18
19#include <wx/app.h>
20#include <wx/bmpbuttn.h>
21#include <wx/sizer.h>
22#include <wx/wfstream.h>
23
25 const wxString& appName,
26 const wxString& vendorName,
27 const wxString& localFilename,
28 const wxString& globalFilename,
29 long style,
30 const wxMBConv& conv
31)
32: wxFileConfig{ appName, vendorName, localFilename, globalFilename, style, conv }
33, mLocalFilename(localFilename)
34{
35 // https://github.com/audacity/audacity/issues/6448 :
36 // We do not write environment variable names in the config files, and
37 // wxWidgets' implementation of environment variable expansion thinks a dollar
38 // sign at the beginning of a directory name is an environment variable - see
39 // https://github.com/wxWidgets/wxWidgets/issues/19214
40 SetExpandEnvVars(false);
41}
42
44{
45 // Prevent wxFileConfig from attempting a Flush() during object deletion. This happens
46 // because we don't use the wxFileConfig::Flush() method and so the wxFileConfig dirty
47 // flag never gets reset. During deletion, the dirty flag is checked and a Flush()
48 // performed. This can (and probably will) create bogus temporary files.
49 DisableAutoSave();
50
51 while (true)
52 {
53 bool canRead = false;
54 bool canWrite = false;
55 int fd;
56
57 fd = wxOpen(mLocalFilename, O_RDONLY, S_IREAD);
58 if (fd != -1 || errno == ENOENT)
59 {
60 canRead = true;
61 if (fd != -1)
62 {
63 wxClose(fd);
64 }
65 }
66
67 fd = wxOpen(mLocalFilename, O_WRONLY | O_CREAT, S_IWRITE);
68 if (fd != -1)
69 {
70 canWrite = true;
71 wxClose(fd);
72 }
73
74 if (canRead && canWrite)
75 {
76 break;
77 }
78
79 Warn();
80 }
81}
82
84{
85 wxASSERT(mDirty == false);
86}
87
88std::unique_ptr<AudacityFileConfig> AudacityFileConfig::Create(
89 const wxString& appName,
90 const wxString& vendorName,
91 const wxString& localFilename,
92 const wxString& globalFilename,
93 long style,
94 const wxMBConv& conv
95)
96{
97 // Private ctor means make_unique can't compile, so this verbosity:
98 auto result = std::unique_ptr<AudacityFileConfig>{
100 appName, vendorName, localFilename, globalFilename, style, conv } };
101 result->Init();
102 return result;
103}
104
105bool AudacityFileConfig::Flush(bool bCurrentOnly)
106{
107 if (!mDirty)
108 {
109 return true;
110 }
111
112 while (true)
113 {
114 FilePath backup = mLocalFilename + ".bkp";
115
116 if (!wxFileExists(backup) || (wxRemove(backup) == 0))
117 {
118 if (!wxFileExists(mLocalFilename) || (wxRename(mLocalFilename, backup) == 0))
119 {
120 wxFileOutputStream stream(mLocalFilename);
121 if (stream.IsOk())
122 {
123 if (Save(stream))
124 {
125 stream.Sync();
126 if (stream.IsOk() && stream.Close())
127 {
128 if (!wxFileExists(backup) || (wxRemove(backup) == 0))
129 {
130 mDirty = false;
131 return true;
132 }
133 }
134 }
135 }
136
137 if (wxFileExists(backup))
138 {
139 wxRemove(mLocalFilename);
140 wxRename(backup, mLocalFilename);
141 }
142 }
143 }
144
145 Warn();
146 }
147}
148
150{
151 wxDialogWrapper dlg(nullptr, wxID_ANY, XO("Audacity Configuration Error"));
152
153 ShuttleGui S(&dlg, eIsCreating);
154
155 wxButton *retryButton;
156 wxButton *quitButton;
157
158 S.SetBorder(5);
159 S.StartVerticalLay(wxEXPAND, 1);
160 {
161 S.SetBorder(15);
162 S.StartHorizontalLay(wxALIGN_RIGHT, 0);
163 {
164 S.AddFixedText(
165 XO("The following configuration file could not be accessed:\n\n"
166 "\t%s\n\n"
167 "This could be caused by many reasons, but the most likely are that "
168 "the disk is full or you do not have write permissions to the file. "
169 "\n\n"
170 "You can attempt to correct the issue and then click \"Retry\" to continue.\n\n"
171 "If you choose to \"Quit Audacity\", your project may be left in an unsaved "
172 "state which will be recovered the next time you open it.")
174 false,
175 500);
176 }
177 S.EndHorizontalLay();
178
179 S.SetBorder(5);
180 S.StartHorizontalLay(wxALIGN_RIGHT, 0);
181 {
182 // Can't use themed bitmap since the theme manager might not be
183 // initialized yet and it requires a configuration file.
184 wxButton *b = S.Id(wxID_HELP).AddBitmapButton(wxBitmap(Help_xpm));
185 b->SetToolTip( XO("Help").Translation() );
186 b->SetLabel(XO("Help").Translation()); // for screen readers
187
188 b = S.Id(wxID_CANCEL).AddButton(XXO("&Quit Audacity"));
189 b = S.Id(wxID_OK).AddButton(XXO("&Retry"));
190 dlg.SetAffirmativeId(wxID_OK);
191
192 b->SetDefault();
193 b->SetFocus();
194 }
195 S.EndHorizontalLay();
196 }
197 S.EndVerticalLay();
198
199 dlg.Layout();
200 dlg.GetSizer()->Fit(&dlg);
201 dlg.SetMinSize(dlg.GetSize());
202 dlg.Center();
203
204 auto onButton = [&](wxCommandEvent &e)
205 {
206 dlg.EndModal(e.GetId());
207 };
208
209 dlg.Bind(wxEVT_BUTTON, onButton);
210
211 switch (dlg.ShowModal())
212 {
213 case wxID_HELP:
214 // Can't use the HelpSystem since the theme manager may not
215 // yet be initialized and it requires a configuration file.
216 OpenInDefaultBrowser("https://" +
219 "Error:_Audacity_settings_file_unwritable");
220 break;
221
222 case wxID_CANCEL:
223 _exit(-1);
224 break;
225 }
226
227 dlg.Unbind(wxEVT_BUTTON, onButton);
228}
229
230bool AudacityFileConfig::RenameEntry(const wxString& oldName, const wxString& newName)
231{
232 auto res = wxFileConfig::RenameEntry(oldName, newName);
233 if (res)
234 {
235 mDirty = true;
236 }
237 return res;
238}
239
240bool AudacityFileConfig::RenameGroup(const wxString& oldName, const wxString& newName)
241{
242 auto res = wxFileConfig::RenameGroup(oldName, newName);
243 if (res)
244 {
245 mDirty = true;
246 }
247 return res;
248}
249
250bool AudacityFileConfig::DeleteEntry(const wxString& key, bool bDeleteGroupIfEmpty)
251{
252 auto res = wxFileConfig::DeleteEntry(key, bDeleteGroupIfEmpty);
253 if (res)
254 {
255 mDirty = true;
256 }
257 return res;
258}
259
261{
262 auto res = wxFileConfig::DeleteGroup(key);
263 if (res)
264 {
265 mDirty = true;
266 }
267 return res;
268}
269
271{
272 auto res = wxFileConfig::DeleteAll();
273 if (res)
274 {
275 mDirty = true;
276 }
277 return res;
278}
279
280bool AudacityFileConfig::DoWriteString(const wxString& key, const wxString& szValue)
281{
282 bool res = wxFileConfig::DoWriteString(key, szValue);
283 if (res)
284 {
285 mDirty = true;
286 }
287 return res;
288}
289
290bool AudacityFileConfig::DoWriteLong(const wxString& key, long lValue)
291{
292 bool res = wxFileConfig::DoWriteLong(key, lValue);
293 if (res)
294 {
295 mDirty = true;
296 }
297 return res;
298}
299
300#if wxUSE_BASE64
301bool AudacityFileConfig::DoWriteBinary(const wxString& key, const wxMemoryBuffer& buf)
302{
303 bool res = wxFileConfig::DoWriteBinary(key, buf);
304 if (res)
305 {
306 mDirty = true;
307 }
308 return res;
309}
310#endif // wxUSE_BASE64
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define safenew
Definition: MemoryX.h:10
static const AudacityProject::AttachedObjects::RegisteredFactory key
wxString FilePath
Definition: Project.h:21
@ eIsCreating
Definition: ShuttleGui.h:37
#define S(N)
Definition: ToChars.cpp:64
Our own specialisation of FileConfig.
const wxString mLocalFilename
bool DeleteAll() override
static std::unique_ptr< AudacityFileConfig > Create(const wxString &appName={}, const wxString &vendorName={}, const wxString &localFilename={}, const wxString &globalFilename={}, long style=wxCONFIG_USE_LOCAL_FILE|wxCONFIG_USE_GLOBAL_FILE, const wxMBConv &conv=wxConvAuto())
Require a call to this factory, to guarantee proper two-phase initialization.
AudacityFileConfig(const wxString &appName, const wxString &vendorName, const wxString &localFilename, const wxString &globalFilename, long style, const wxMBConv &conv)
Disallow direct constructor call, because a two-phase initialization is required.
bool RenameEntry(const wxString &oldName, const wxString &newName) override
bool DeleteGroup(const wxString &key) override
bool DoWriteLong(const wxString &key, long lValue) override
bool DoWriteString(const wxString &key, const wxString &szValue) override
bool RenameGroup(const wxString &oldName, const wxString &newName) override
bool Flush(bool bCurrentOnly) override
bool DeleteEntry(const wxString &key, bool bDeleteGroupIfEmpty) override
Abstract base class used in importing a file.
static const wxString HelpHostname
Definition: HelpSystem.h:96
static const wxString HelpServerHomeDir
Definition: HelpSystem.h:101
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
bool OpenInDefaultBrowser(const wxString &url)
Open an URL in default browser.
Definition: BasicUI.cpp:246