Audacity 3.2.0
AudioComDialogBase.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 AudioComDialogBase.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11#include "AudioComDialogBase.h"
12
13#include <cassert>
14#include <list>
15
16#include <wx/button.h>
17#include <wx/checkbox.h>
18#include <wx/sizer.h>
19#include <wx/statline.h>
20#include <wx/stattext.h>
21
22#include "AccessibilityUtils.h"
23#include "Prefs.h"
24#include "Project.h"
25#include "ProjectWindow.h"
26
28#include "ShuttleGui.h"
29
30#include "AppEvents.h"
31#include "Observer.h"
32
34{
35namespace
36{
38{
39 if (project == nullptr)
40 return nullptr;
41
42 return &ProjectWindow::Get(*const_cast<AudacityProject*>(project));
43}
44
46{
47 if (identifier.empty())
48 return {};
49
50 return wxString::Format("/cloud/audiocom/%s/skip", identifier.GET());
51}
52} // namespace
53
56 std::function<DialogButtonIdentifier()> poller)
57{
58 mDialogSizer->AddStretchSpacer(1);
59
60 if (HasSeparator())
61 {
62 mDialogSizer->Add(
63 safenew wxStaticLine { this },
64 wxSizerFlags {}.Border(wxTOP, 16).Expand());
65 mDialogSizer->AddSpacer(8);
66 }
67 else
68 mDialogSizer->AddSpacer(16);
69
70 mDialogSizer->Add(
71 mButtonSizer, wxSizerFlags {}.Border(wxLEFT | wxRIGHT, 16).Expand());
72 mDialogSizer->AddSpacer(8);
73
74 SetSizerAndFit(mDialogSizer);
76 Center();
77
78 // We cannot do it earlier to avoid licking the sizers and children
79 if (!mOptionalPrefsIdentifier.empty())
80 {
81 if (gPrefs->ReadBool(mOptionalPrefsIdentifier.GET(), false))
82 return mEscButtonIdentifier;
83 }
84
85 Show();
86 Raise();
87
88 {
89 wxWindowDisabler disabler { this };
90
91 if (!poller)
92 poller = [] { return DialogButtonIdentifier {}; };
93
94 while (IsShown())
95 {
96 wxYield();
97
98 if (auto result = poller(); !result.empty())
99 return result;
100 }
101 }
102
103 // On Windows, parent window will lose focus after the wxWindowDisabler is destroyed
104 if (auto parent = GetParent())
105 parent->Raise();
106
107 // mResultButtonIdentifier is empty if the dialog was closed using the
108 // cross button
109 return mResultButtonIdentifier.empty() ? mEscButtonIdentifier :
110 mResultButtonIdentifier;
111}
112
114{
115 return { L"Cancel" };
116}
117
120 const DialogIdentifier& optionalPrefsIdentifier, DialogMode dialogMode)
122 dialogMode == DialogMode::Saving ?
123 XO("Save to audio.com") :
124 XO("Open from audio.com") }
125 , mProject { project }
126 , mOptionalPrefsIdentifier { GetOptionalPrefsIdentifier(
127 optionalPrefsIdentifier) }
128 , mDialogSizer { new wxBoxSizer(wxVERTICAL) }
129 , mButtonSizer { new wxBoxSizer(wxHORIZONTAL) }
130{
131 mDialogSizer->SetMinSize({ 420, 140 });
133 {
134 const auto skipDialog =
136
137 auto checkbox =
138 safenew wxCheckBox { this, wxID_ANY,
139 XO("Don't show this again").Translation() };
140
141 checkbox->SetValue(skipDialog);
142
143 mButtonSizer->Add(checkbox, wxSizerFlags {}.CenterVertical());
144
145 checkbox->Bind(
146 wxEVT_CHECKBOX,
147 [this, checkbox](auto&)
148 {
149 gPrefs->Write(mOptionalPrefsIdentifier.GET(), checkbox->GetValue());
150 gPrefs->Flush();
151 });
152 }
153
154 mButtonSizer->AddStretchSpacer();
155
156 Bind(
157 wxEVT_CHAR_HOOK,
158 [this](auto& evt)
159 {
160 if (!IsEscapeKey(evt))
161 {
162 evt.Skip();
163 return;
164 }
165
167 });
168}
169
171{
172 auto font = GetFont().Bold();
173
174 font.SetFractionalPointSize(font.GetFractionalPointSize() * 1.5f);
175
176 auto statText = safenew wxStaticText { this, wxID_ANY, title.Translation() };
177
178 statText->SetFont(font);
179
180 mDialogSizer->AddSpacer(16);
181 mDialogSizer->Add(statText, wxSizerFlags {}.Border(wxLEFT | wxRIGHT, 16));
182}
183
185{
186 auto statText =
187 safenew wxStaticText { this, wxID_ANY, paragraph.Translation() };
188
189 mDialogSizer->AddSpacer(16);
190 mDialogSizer->Add(statText, wxSizerFlags {}.Border(wxLEFT | wxRIGHT, 16));
191
192 statText->Wrap(400);
193}
194
196 DialogButtonIdentifier identifier, const TranslatableString& text, int type)
197{
198 auto button = safenew wxButton { this, wxID_ANY, text.Translation() };
199
200 mButtonSizer->Add(button, wxSizerFlags {}.Border(wxLEFT, 8));
201
202 if (type & EscButton)
203 mEscButtonIdentifier = identifier;
204
205 button->Bind(
206 wxEVT_BUTTON, [this, identifier = std::move(identifier)](auto&)
207 { EndDialog(identifier); });
208
209 if (type & DefaultButton)
210 button->SetDefault();
211}
212
214{
215 SetTitle(dialog);
216}
217
219{
220 return true;
221}
222
224{
225 mResultButtonIdentifier = std::move(identifier);
226 Close();
227}
228
229namespace
230{
231struct IdleItem final
232{
233 std::function<bool()> Condition;
234 std::function<void()> DialogFactory;
235};
236
237struct Idler final
238{
239 std::list<IdleItem> Items;
240
242
243 bool IdlerLocked {};
244
246 : Subsctiption { AppEvents::OnAppIdle([this] { OnIdle(); }) }
247 {
248 }
249
250 void OnIdle()
251 {
252 if (IdlerLocked)
253 return;
254
255 for (auto it = Items.begin(); it != Items.end();)
256 {
257 if (it->Condition())
258 {
259 IdlerLocked = true;
260
261 auto swapFlag = finally([this] { IdlerLocked = false; });
262
263 it->DialogFactory();
264 it = Items.erase(it);
265 }
266 else
267 ++it;
268 }
269 }
270};
271} // namespace
272
274 std::function<bool()> condition, std::function<void()> dialogFactory)
275{
276 static Idler idler;
277
278 idler.Items.push_back({ std::move(condition), std::move(dialogFactory) });
279}
280
281} // namespace audacity::cloud::audiocom::sync
void SetupAccessibility(wxWindow *window)
XO("Cut/Copy/Paste")
#define safenew
Definition: MemoryX.h:10
static const auto title
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
const auto project
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
bool empty() const
Definition: Identifier.h:61
const wxString & GET() const
Explicit conversion to wxString, meant to be ugly-looking and demanding of a comment why it's correct...
Definition: Identifier.h:66
A move-only handle representing a connection to a Publisher.
Definition: Observer.h:70
static ProjectWindow & Get(AudacityProject &project)
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
bool ReadBool(const wxString &key, bool defaultValue) const
void AddTitle(const TranslatableString &title)
void AddButton(DialogButtonIdentifier identifier, const TranslatableString &text, int type=None)
DialogButtonIdentifier ShowDialog(std::function< DialogButtonIdentifier()> poller={})
AudioComDialogBase(const AudacityProject *project, const DialogIdentifier &optionalPrefsIdentifier={}, DialogMode dialogMode=DialogMode::Saving)
void AddParagraph(const TranslatableString &paragraph)
void SetDialogTitle(const TranslatableString &dialog)
void EndDialog(DialogButtonIdentifier identifier)
void SetTitle(const TranslatableString &title)
Observer::Subscription OnAppIdle(std::function< void()> callback)
Definition: AppEvents.cpp:72
IMPORT_EXPORT_API ExportResult Show(ExportTask exportTask)
constexpr auto Items
Definition: MenuRegistry.h:427
TaggedIdentifier< DialogButtonIdentifierTag > DialogButtonIdentifier
void ShowDialogOn(std::function< bool()> condition, std::function< void()> dialogFactory)