Audacity 3.2.0
ShareAudioDialog.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 ShareAudioDialog.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11#include "ShareAudioDialog.h"
12
13#include <wx/bmpbuttn.h>
14#include <wx/button.h>
15#include <wx/clipbrd.h>
16#include <wx/gauge.h>
17#include <wx/stattext.h>
18#include <wx/statline.h>
19#include <wx/textctrl.h>
20#include <wx/radiobut.h>
21
22#include "AllThemeResources.h"
23#include "BasicUI.h"
24#include "MemoryX.h"
25#include "Project.h"
26#include "ShuttleGui.h"
27#include "Theme.h"
28#include "Track.h"
29
30#include "ServiceConfig.h"
31#include "OAuthService.h"
32#include "UploadService.h"
33#include "UserService.h"
34
36#include "CloudExporterPlugin.h"
37
39#include "LinkAccountDialog.h"
40#include "UserImage.h"
41
42#include "CodeConversions.h"
43
44#include "export/Export.h"
46
47#include "WindowAccessible.h"
48#include "HelpSystem.h"
49
50#ifdef HAS_CUSTOM_URL_HANDLING
51#include "URLSchemesRegistry.h"
52#endif
53
54namespace cloud::audiocom
55{
56namespace
57{
58BoolSetting wasOpened { L"/cloud/audiocom/wasOpened", false };
59BoolSetting SharePublicly { L"/cloud/audiocom/sharePublicly", true };
60
61void UpdatePublicity(bool value)
62{
63 SharePublicly.Write(value);
64 gPrefs->Flush();
65}
66
67const wxSize avatarSize = { 32, 32 };
68
70{
71 const auto tempPath = GetUploadTempPath();
72
73 wxFileName fileName(
74 tempPath,
75 wxString::Format(
76 "%lld", std::chrono::system_clock::now().time_since_epoch().count()),
77 extension);
78
79 fileName.Mkdir(0700, wxPATH_MKDIR_FULL);
80
81 if (fileName.Exists())
82 {
83 if (!wxRemoveFile(fileName.GetFullPath()))
84 return {};
85 }
86
87 return fileName.GetFullPath();
88}
89
90const auto publicLabelText = XO("Public");
92 XO("Anyone will be able to listen to this audio.");
93
94const auto unlistedLabelText = XO("Unlisted");
96 "Only you and people you share a link with will be able to listen to this audio.");
97
98}
99
100// A helper structures holds UploadService and UploadPromise
102{
104
106
109 {
110 }
111};
112
113// Implementation of the ProgressDialog, which is not a dialog.
114// Instead, progress is forwarded to the parent
117{
119 : mParent(parent)
120 {
121 }
122
123 void Cancel()
124 {
125 mCancelled = true;
126 }
127
129 {
130 return mCancelled;
131 }
132
134 unsigned long long numerator, unsigned long long denominator,
135 const TranslatableString&) override
136 {
137 mParent.UpdateProgress(numerator, denominator);
138
139 const auto now = Clock::now();
140
141 // Exporter polls in the main thread. To make the dialog responsive
142 // periodic yielding is required
143 if ((now - mLastYield > std::chrono::milliseconds(50)) || (numerator == denominator))
144 {
146 mLastYield = now;
147 }
148
151 }
152
153 void SetMessage(const TranslatableString&) override
154 {
155 }
156
157 void SetDialogTitle(const TranslatableString&) override
158 {
159 }
160
161 void Reinit() override
162 {
163 }
164
166
167 using Clock = std::chrono::steady_clock;
168 Clock::time_point mLastYield;
169
170 bool mCancelled { false };
171};
172
175 parent, wxID_ANY, XO("Share Audio"), wxDefaultPosition, { 480, 250 },
176 wxDEFAULT_DIALOG_STYLE)
177 , mProject(project)
178 , mServices(std::make_unique<Services>())
179{
181
182 ShuttleGui s(this, eIsCreating);
183
184 s.StartVerticalLay();
185 {
186 Populate(s);
187 }
188 s.EndVerticalLay();
189
190 Layout();
191 Fit();
192 Centre();
193
194 const auto size = GetSize();
195
196 SetMinSize({ size.x, std::min(250, size.y) });
197 SetMaxSize({ size.x, -1 });
198
199 mContinueAction = [this]()
200 {
201 if (mInitialStatePanel.root->IsShown())
202 StartUploadProcess();
203 };
204
205 Bind(
206 wxEVT_CHAR_HOOK,
207 [this](auto& evt)
208 {
209 if (!IsEscapeKey(evt))
210 {
211 evt.Skip();
212 return;
213 }
214
215 if (mCancelButton->IsShown())
216 OnCancel();
217 else
218 OnClose();
219 });
220}
221
223{
225 // Clean up the temp file when the dialog is closed
226 if (!mFilePath.empty() && wxFileExists(mFilePath))
227 wxRemoveFile(mFilePath);
228}
229
231{
234
235 s.StartHorizontalLay(wxEXPAND, 0);
236 {
238 {
239 s.SetBorder(2);
240 s.StartHorizontalLay(wxEXPAND, 0);
241 {
242 s.AddSpace(0, 0, 1);
243
244 mCancelButton = s.AddButton(XXO("&Cancel"));
245 mCancelButton->Bind(wxEVT_BUTTON, [this](auto) { OnCancel(); });
246
247 mCloseButton = s.AddButton(XXO("&Close"));
248 mCloseButton->Bind(wxEVT_BUTTON, [this](auto) { OnClose(); });
249
250 s.AddSpace(4, 0, 0);
251
252 mContinueButton = s.AddButton(XXO("C&ontinue"));
253 mContinueButton->Bind(wxEVT_BUTTON, [this](auto) { OnContinue(); });
254
255 mGotoButton = s.AddButton(XXO("&Go to my file"));
256 }
258 }
260 }
262
263 // This two buttons are only used in the end of
264 // authorised upload flow
265 mGotoButton->Hide();
266 mCloseButton->Hide();
267}
268
270{
271 const auto hasExportStarted = mExportProgressHelper != nullptr;
272 const auto hasUploadStarted = !!mServices->uploadPromise;
273
274 if (mInProgress)
275 {
276 AudacityMessageDialog dlgMessage(
277 this, XO("Are you sure you want to cancel?"), XO("Cancel upload to Audio.com"),
278 wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT | wxSTAY_ON_TOP);
279
280 const auto result = dlgMessage.ShowModal();
281
282 if (result != wxID_YES)
283 return;
284
285 // If export has started, notify it that it should be canceled
286 if (mExportProgressHelper != nullptr)
287 static_cast<ExportProgressHelper&>(*mExportProgressHelper).Cancel();
288 }
289
290
291 // If upload was started - ask it to discard the result.
292 // The result should be discarded even after the upload has finished
293 if (mServices->uploadPromise)
294 mServices->uploadPromise->DiscardResult();
295
296 EndModal(wxID_CANCEL);
297}
298
300{
302}
303
305{
306 EndModal(wxID_CLOSE);
307}
308
309
311{
312 mExportProgressHelper = std::make_unique<ExportProgressHelper>(*this);
313
314 auto exporter = CreatePreferredExporter(GetServiceConfig().GetPreferredAudioFormats(), mProject);
315
316 if (!exporter)
317 return {};
318
319 const auto path = GenerateTempPath(exporter->GetFileExtension());
320
321 if (path.empty())
322 return {};
323
324
326 exporter->OnBeforeExport();
327
328 auto cleanupExporter = finally([&]() { exporter->OnAfterExport(); });
329
330 Exporter e { const_cast<AudacityProject&>(mProject) };
331
332 auto& tracks = TrackList::Get(mProject);
333
334 const double t0 = 0.0;
335 const double t1 = tracks.GetEndTime();
336
337 const int nChannels = (tracks.Any() - &Track::IsLeader).empty() ? 1 : 2;
338
339 const bool success = e.Process(
340 nChannels, // numChannels,
341 exporter->GetExporterID(), // type,
342 path, // full path,
343 false, // selectedOnly,
344 t0, // t0
345 t1, // t1
346 mExportProgressHelper // progress dialog
347 );
348
349 if (!success && wxFileExists(path))
350 // Try to remove the file if exporting has failed (or was canceled)
351 wxRemoveFile(path);
352
353 return success ? path : wxString {};
354}
355
357{
358 mInProgress = true;
359
360 mInitialStatePanel.root->Hide();
361 mProgressPanel.root->Show();
362
364 mProgressPanel.info->Hide();
365
366 mContinueButton->Hide();
367
368 Layout();
369 Fit();
370
372
374
375 if (mFilePath.empty())
376 {
378 .WasCancelled())
379 {
381 }
382
383 return;
384 }
385
386 mProgressPanel.title->SetLabel(XO("Uploading audio...").Translation());
388
389 mServices->uploadPromise = mServices->uploadService.Upload(
390 mFilePath,
392 mInitialStatePanel.isPublic->GetValue(),
393 [this](const auto& result)
394 {
395 CallAfter(
396 [this, result]()
397 {
398 mInProgress = false;
399
400 if (result.result == UploadOperationCompleted::Result::Success)
401 HandleUploadSucceeded(result.finishUploadURL, result.audioSlug);
402 else if (result.result != UploadOperationCompleted::Result::Aborted)
403 HandleUploadFailed(result.errorMessage);
404 });
405 },
406 [this](auto current, auto total)
407 {
408 CallAfter(
409 [this, current, total]()
410 {
411 UpdateProgress(current, total);
412 });
413 });
414}
415
416void ShareAudioDialog::HandleUploadSucceeded(
417 std::string_view finishUploadURL, std::string_view audioSlug)
418{
419 mProgressPanel.timePanel->Hide();
420 mProgressPanel.title->SetLabel(XO("Upload complete!").Translation());
421 mProgressPanel.info->Show();
422 mProgressPanel.info->SetLabel(
423 mInitialStatePanel.isPublic->GetValue() ? publicDescriptionText.Translation() :
424 unlistedDescriptionText.Translation());
425
426 if (!GetOAuthService().HasAccessToken())
427 {
428 mProgressPanel.info->SetLabel(
429 "By pressing continue, you will be taken to audio.com and given a shareable link.");
430 mProgressPanel.info->Wrap(mProgressPanel.root->GetSize().GetWidth());
431
432 mContinueAction = [this, url = std::string(finishUploadURL)]()
433 {
434 EndModal(wxID_CLOSE);
435 OpenInDefaultBrowser({ url });
436 };
437
438 mContinueButton->Show();
439 }
440 else
441 {
442 auto shareableLink = wxString::Format(
443 "https://audio.com/%s/%s", GetUserService().GetUserSlug(),
444 audacity::ToWXString(audioSlug));
445
446 mGotoButton->Show();
447 mCloseButton->Show();
448 mCancelButton->Hide();
449
450 mGotoButton->Bind(
451 wxEVT_BUTTON,
452 [this, url = shareableLink](auto)
453 {
454 EndModal(wxID_CLOSE);
455 OpenInDefaultBrowser({ url });
456 });
457
458 mProgressPanel.link->SetValue(shareableLink);
459 mProgressPanel.linkPanel->Show();
460 }
461
462 Layout();
463 Fit();
464}
465
466void ShareAudioDialog::HandleUploadFailed(std::string_view errorMessage)
467{
468 EndModal(wxID_ABORT);
469
471 {}, XO("Upload error"),
472 XO("We are unable to upload this file. Please try again and make sure to link to your audio.com account before uploading."),
473 {},
475 audacity::ToWString(errorMessage)));
476}
477
478void ShareAudioDialog::HandleExportFailure()
479{
480 EndModal(wxID_ABORT);
481
483 {}, XO("Export error"),
484 XO("We are unable to prepare this file for uploading."), {},
486}
487
488void ShareAudioDialog::ResetProgress()
489{
490 mStageStartTime = Clock::now();
491 mLastUIUpdateTime = mStageStartTime;
492
493 mProgressPanel.elapsedTime->SetLabel(" 00:00:00");
494 mProgressPanel.remainingTime->SetLabel(" 00:00:00");
495 mProgressPanel.progress->SetValue(0);
496
497 mLastProgressValue = 0;
498
500}
501
502namespace
503{
504void SetTimeLabel(wxStaticText* label, std::chrono::milliseconds time)
505{
506 wxTimeSpan tsElapsed(0, 0, 0, time.count());
507
508 label->SetLabel(tsElapsed.Format(wxT(" %H:%M:%S")));
509 label->SetName(label->GetLabel());
510 label->Update();
511}
512}
513
514void ShareAudioDialog::UpdateProgress(uint64_t current, uint64_t total)
515{
516 using namespace std::chrono;
517
518 const auto now = Clock::now();
519
520 if (current == 0)
521 return;
522
523 if (current > total)
524 current = total;
525
526 if (mLastProgressValue != current)
527 {
528 constexpr int scale = 10000;
529
530 mLastProgressValue = static_cast<int>(current);
531
532 mProgressPanel.progress->SetRange(scale);
533 mProgressPanel.progress->SetValue((current * scale) / total);
534
535 if (current == total && mServices->uploadPromise)
536 {
537 mProgressPanel.timePanel->Hide();
538 mProgressPanel.title->SetLabel(XO("Finalizing upload...").Translation());
539 }
540 }
541
542 const auto elapsedSinceUIUpdate = now - mLastUIUpdateTime;
543
544 constexpr auto uiUpdateTimeout = 500ms;
545
546 if (elapsedSinceUIUpdate < uiUpdateTimeout && current < total)
547 return;
548
549 mLastUIUpdateTime = now;
550
551 const auto elapsed = duration_cast<milliseconds>(now - mStageStartTime);
552
553 SetTimeLabel(mProgressPanel.elapsedTime, elapsed);
554
555 const auto estimate = elapsed * total / current;
556 const auto remains = estimate - elapsed;
557
559 mProgressPanel.remainingTime,
560 std::chrono::duration_cast<std::chrono::milliseconds>(remains));
561}
562
563ShareAudioDialog::InitialStatePanel::InitialStatePanel()
564 : mUserDataChangedSubscription(
565 GetUserService().Subscribe([this](const auto&) { UpdateUserData(); }))
566{
567}
568
570 ShuttleGui& s)
571{
572 root = s.StartInvisiblePanel();
573 s.StartVerticalLay(wxEXPAND, 1);
574 {
575 s.SetBorder(16);
576
577 s.StartHorizontalLay(wxEXPAND, 0);
578 {
579 avatar = safenew UserImage(s.GetParent(), avatarSize);
580
581 s.AddWindow(avatar);
582
583 s.StartVerticalLay(wxEXPAND, 1);
584 {
585 s.SetBorder(0);
586 s.AddSpace(0, 0, 1);
587 name = s.AddVariableText(XO("Anonymous"));
588 s.AddSpace(0, 0, 1);
589 }
590 s.EndVerticalLay();
591
592 s.AddSpace(0, 0, 1);
593
594 s.StartVerticalLay(wxEXPAND, 1);
595 {
596 s.AddSpace(0, 0, 1);
597
598 s.SetBorder(16);
599 oauthButton = s.AddButton(XXO("&Link Account"));
600 oauthButton->Bind(
601 wxEVT_BUTTON, [this](auto) { OnLinkButtonPressed(); });
602 s.AddSpace(0, 0, 1);
603 }
604 s.EndVerticalLay();
605 }
607
608 s.SetBorder(0);
609
610 s.AddWindow(safenew wxStaticLine { s.GetParent() }, wxEXPAND);
611
612 s.AddSpace(16);
613
614 s.StartHorizontalLay(wxEXPAND, 0);
615 {
616 constexpr auto maxWidth = 380;
617 s.AddSpace(30, 0, 0);
618 s.StartMultiColumn(2, wxEXPAND);
619 {
620 s.SetBorder(2);
621 s.SetStretchyCol(1);
622
623 const auto selectedIndex = SharePublicly.Read() ? 0 : 1;
624
625 isPublic = s.Name(Verbatim(
626 publicLabelText.Translation() + ". " +
627 publicDescriptionText.Translation()))
628 .AddRadioButton({}, 0, selectedIndex);
629#if wxUSE_ACCESSIBILITY
630 // so that name can be set on a standard control
631 isPublic->SetAccessible(safenew WindowAccessible(isPublic));
632#endif
633 isPublic->Bind(
634 wxEVT_RADIOBUTTON, [this](auto) { UpdatePublicity(true); });
635
637 ->Bind(
638 wxEVT_LEFT_UP,
639 [this](auto)
640 {
641 isPublic->SetValue(true);
642 UpdatePublicity(true);
643 });
644
645 s.AddFixedText({});
646 s.AddVariableText(publicDescriptionText, false, 0, maxWidth);
647
648 // Margin between options
649 s.AddFixedText({});
650 s.AddFixedText({});
651
652 auto rbUnlisted = s.Name(Verbatim(
653 unlistedLabelText.Translation() + ". " +
654 unlistedDescriptionText.Translation()))
655 .AddRadioButtonToGroup({}, 1, selectedIndex);
656
657#if wxUSE_ACCESSIBILITY
658 // so that name can be set on a standard control
659 rbUnlisted->SetAccessible(safenew WindowAccessible(rbUnlisted));
660#endif
661
662 rbUnlisted->Bind(
663 wxEVT_RADIOBUTTON, [this](auto) { UpdatePublicity(false); });
664
666 ->Bind(
667 wxEVT_LEFT_UP,
668 [this, rbUnlisted](auto)
669 {
670 rbUnlisted->SetValue(true);
671 UpdatePublicity(false);
672 });
673
674 s.AddFixedText({});
675 s.AddVariableText(unlistedDescriptionText, false, 0, maxWidth);
676 }
677 s.EndMultiColumn();
678 }
680
681 if (!wasOpened.Read())
682 PopulateFirstTimeNotice(s);
683 else
684 {
685 s.AddSpace(16);
686 s.StartHorizontalLay(wxEXPAND, 0);
687 {
688 s.AddSpace(30, 0, 0);
689 s.AddFixedText(XO("Press \"Continue\" to upload to audio.com"));
690 }
692 }
693
694 }
695 s.EndVerticalLay();
697
698 UpdateUserData();
699}
700
702{
703 s.AddSpace(16);
705 s.SetBorder(30);
706 {
707 AccessibleLinksFormatter privacyPolicy(XO(
708 "Your audio will be uploaded to our sharing service: %s,%%which requires a free account to use.\n\nIf you have problems uploading, try the Link Account button."));
709
710 privacyPolicy.FormatLink(
711 L"%s", XO("audio.com"),
712 "https://audio.com");
713
714 privacyPolicy.FormatLink(
715 L"%%", TranslatableString {},
717
718 privacyPolicy.Populate(s);
719 }
721
722 wasOpened.Write(true);
723 gPrefs->Flush();
724}
725
727{
728 auto parent = root->GetParent();
729 parent->Freeze();
730
731 auto layoutUpdater = finally(
732 [parent = root->GetParent(), this]()
733 {
734 oauthButton->Fit();
735 parent->Layout();
736
737 parent->Thaw();
738 });
739
740 auto& oauthService = GetOAuthService();
741
742 if (!oauthService.HasRefreshToken())
743 {
744 SetAnonymousState();
745 return;
746 }
747
748 if (!oauthService.HasAccessToken())
749 oauthService.ValidateAuth({});
750
751 auto& userService = GetUserService();
752
753 if (userService.GetUserSlug().empty())
754 {
755 SetAnonymousState();
756 return;
757 }
758
759 const auto displayName = userService.GetDisplayName();
760
761 if (!displayName.empty())
762 name->SetLabel(displayName);
763
764 const auto avatarPath = userService.GetAvatarPath();
765
766 if (!avatarPath.empty())
767 avatar->SetBitmap(avatarPath);
768 else
769 avatar->SetBitmap(theTheme.Bitmap(bmpAnonymousUser));
770
771 oauthButton->SetLabel(XXO("&Unlink Account").Translation());
772}
773
775{
776 auto& oauthService = GetOAuthService();
777
778 if (oauthService.HasAccessToken())
779 oauthService.UnlinkAccount();
780 else
781 {
783 { audacity::ToWXString(GetServiceConfig().GetOAuthLoginPage()) });
784
785#ifdef HAS_CUSTOM_URL_HANDLING
787#endif
788 {
789 LinkAccountDialog dlg(root);
790 dlg.ShowModal();
791 }
792 }
793}
794
796{
797 name->SetLabel(XO("Anonymous").Translation());
798 avatar->SetBitmap(theTheme.Bitmap(bmpAnonymousUser));
799 oauthButton->SetLabel(XXO("&Link Account").Translation());
800}
801
803{
804 root = s.StartInvisiblePanel(16);
805 root->Hide();
806 s.StartVerticalLay(wxEXPAND, 1);
807 {
808 s.SetBorder(0);
809
810 title = s.AddVariableText(XO("Preparing audio..."));
811 s.AddSpace(0, 16, 0);
812
813 progress = safenew wxGauge { s.GetParent(), wxID_ANY, 100 };
814 s.AddWindow(progress, wxEXPAND);
815
816 timePanel = s.StartInvisiblePanel();
817 {
818 s.AddSpace(0, 16, 0);
819
820 s.StartWrapLay();
821 {
822 s.AddFixedText(XO("Elapsed Time:"));
823 elapsedTime = s.AddVariableText(Verbatim(" 00:00:00"));
824 }
825 s.EndWrapLay();
826
827 s.StartWrapLay();
828 {
829 s.AddFixedText(XO("Remaining Time:"));
830 remainingTime = s.AddVariableText(Verbatim(" 00:00:00"));
831 }
832 s.EndWrapLay();
833 }
835
836 linkPanel = s.StartInvisiblePanel();
837 {
838 s.AddSpace(0, 16, 0);
839
840 s.AddFixedText(XO("Shareable link"));
841
842 s.StartHorizontalLay(wxEXPAND, 0);
843 {
844 link = s.AddTextBox(TranslatableString {}, "https://audio.com", 0);
845 link->SetName(XO("Shareable link").Translation());
846 link->SetEditable(false);
847 link->SetMinSize({ 360, -1 });
848
849 s.AddSpace(1, 0, 1);
850
851 copyButton = s.AddButton(XO("Copy"));
852 copyButton->Bind(
853 wxEVT_BUTTON,
854 [this](auto)
855 {
856 if (wxTheClipboard->Open())
857 {
858 wxTheClipboard->SetData(
859 safenew wxTextDataObject(link->GetValue()));
860 wxTheClipboard->Close();
861 }
862 });
863 }
865 }
867
868 s.AddSpace(0, 16, 0);
869
871 }
872
873 s.EndVerticalLay();
875
876 wxFont font = elapsedTime->GetFont();
877 font.MakeBold();
878
879 elapsedTime->SetFont(font);
880 remainingTime->SetFont(font);
881}
882} // namespace cloud::audiocom
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
Declare functions to perform UTF-8 to std::wstring conversions.
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
static RealtimeEffectState::EffectFactory::Scope scope
Inject a factory for realtime effects.
Definition: EffectUI.cpp:1560
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
wxString FileExtension
File extension, not including any leading dot.
Definition: Identifier.h:224
#define safenew
Definition: MemoryX.h:10
static const auto title
FileConfig * gPrefs
Definition: Prefs.cpp:70
@ eIsCreating
Definition: ShuttleGui.h:37
TranslatableString label
Definition: TagsEditor.cpp:164
THEME_API Theme theTheme
Definition: Theme.cpp:82
declares abstract base class Track, TrackList, and iterators over TrackList
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Wrap wxMessageDialog so that caption IS translatable.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
const wxString & GetProjectName() const
Definition: Project.cpp:100
Abstraction of a progress dialog with well defined time-to-completion estimate.
Definition: BasicUI.h:156
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:339
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:252
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
Makes temporary changes to preferences, then rolls them back at destruction.
Definition: Prefs.h:115
void SetBorder(int Border)
Definition: ShuttleGui.h:484
void EndVerticalLay()
void EndMultiColumn()
void EndInvisiblePanel()
wxPanel * StartInvisiblePanel(int border=0)
wxWindow * GetParent()
Definition: ShuttleGui.h:491
wxTextCtrl * AddTextBox(const TranslatableString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:637
void StartVerticalLay(int iProp=1)
wxButton * AddButton(const TranslatableString &Text, int PositionFlags=wxALIGN_CENTRE, bool setDefault=false)
Definition: ShuttleGui.cpp:359
void EndHorizontalLay()
void StartWrapLay(int PositionFlags=wxEXPAND, int iProp=0)
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
wxWindow * AddWindow(wxWindow *pWindow, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:298
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:201
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:439
wxStaticText * AddVariableText(const TranslatableString &Str, bool bCenter=false, int PositionFlags=0, int wrapWidth=0)
Definition: ShuttleGui.cpp:462
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
wxSizerItem * AddSpace(int width, int height, int prop=0)
ShuttleGui & Name(const TranslatableString &name)
Definition: ShuttleGui.h:657
wxBitmap & Bitmap(int iIndex)
bool IsLeader() const
Definition: Track.cpp:406
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:487
Holds a msgid for the translation catalog; may also bind format arguments.
bool IsURLHandlingSupported() const noexcept
Returns true, if Audacity can handle custom URLs.
static URLSchemesRegistry & Get()
Retrieves the registry instance.
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void UpdateProgress(uint64_t current, uint64_t total)
struct cloud::audiocom::ShareAudioDialog::InitialStatePanel mInitialStatePanel
ShareAudioDialog(AudacityProject &project, wxWindow *parent=nullptr)
struct cloud::audiocom::ShareAudioDialog::ProgressPanel mProgressPanel
std::function< void()> mContinueAction
std::unique_ptr< BasicUI::ProgressDialog > mExportProgressHelper
std::unique_ptr< Services > mServices
A unique_ptr like class that holds a pointer to UploadOperation.
Definition: UploadService.h:75
Service, responsible for uploading audio files to audio.com.
Definition: UploadService.h:97
ProgressResult
Definition: BasicUI.h:147
bool OpenInDefaultBrowser(const wxString &url)
Open an URL in default browser.
Definition: BasicUI.cpp:240
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
Definition: BasicUI.h:259
void Yield()
Dispatch waiting events, including actions enqueued by CallAfter.
Definition: BasicUI.cpp:219
void OnClose(const CommandContext &context)
Definition: FileMenus.cpp:214
std::wstring ToWString(const std::string &str)
wxString ToWXString(const std::string &str)
void SetTimeLabel(wxStaticText *label, std::chrono::milliseconds time)
AuthorizationHandler & GetAuthorizationHandler()
wxString GetUploadTempPath()
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
OAuthService & GetOAuthService()
Returns the instance of the OAuthService.
UserService & GetUserService()
std::unique_ptr< cloud::CloudExporterPlugin > CreatePreferredExporter(const MimeTypesList &mimeTypes, const AudacityProject &project)
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:51
void SetDialogTitle(const TranslatableString &) override
Change the dialog's title.
void SetMessage(const TranslatableString &) override
Change an existing dialog's message.
BasicUI::ProgressResult Poll(unsigned long long numerator, unsigned long long denominator, const TranslatableString &) override
Update the bar and poll for clicks. Call only on the main thread.