From ad0342979c4bbd4053f6f05eeb4014d22c512e1a Mon Sep 17 00:00:00 2001 From: James Crook Date: Tue, 13 Jul 2021 13:01:04 +0100 Subject: [PATCH 1/5] James -> Emeritus in AboutDialog.cpp --- src/AboutDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AboutDialog.cpp b/src/AboutDialog.cpp index 83a515ad4..fc77b7a40 100644 --- a/src/AboutDialog.cpp +++ b/src/AboutDialog.cpp @@ -132,7 +132,6 @@ void AboutDialog::CreateCreditsList() XO("%s, graphics"); // The Audacity Team: developers and support - AddCredit(wxT("James Crook"), developerFormat, roleTeamMember); AddCredit(wxT("Roger Dannenberg"), coFounderFormat, roleTeamMember); AddCredit(wxT("Steve Daulton"), roleTeamMember); AddCredit(wxT("Anton Gerasimov"), developerFormat, roleTeamMember); @@ -157,6 +156,7 @@ void AboutDialog::CreateCreditsList() AddCredit(wxT("Matt Brubeck"), developerFormat, roleEmeritusTeam); AddCredit(wxT("Arturo \"Buanzo\" Busleiman"), sysAdminFormat, roleEmeritusTeam); AddCredit(wxT("Michael Chinen"), developerFormat, roleEmeritusTeam); + AddCredit(wxT("James Crook"), developerFormat, roleEmeritusTeam); AddCredit(wxT("Al Dimond"), developerFormat, roleEmeritusTeam); AddCredit(wxT("Benjamin Drung"), developerFormat, roleEmeritusTeam); AddCredit(wxT("Joshua Haberman"), developerFormat, roleEmeritusTeam); From e9b33785a69b10b765d0d8da2b687668ef4af134 Mon Sep 17 00:00:00 2001 From: Dmitry Vedenko Date: Wed, 14 Jul 2021 22:14:35 +0300 Subject: [PATCH 2/5] Adds a class that allows keyboard accessible links in the text. ShuttleGUI::AddWindow has a new argument now: positionFlags. This argument does not change the default behavior. StartWrapLay/EndWrapLay are added, so wxWrapSizer can be used from the ShuttleGUI. --- src/CMakeLists.txt | 4 + src/ShuttleGui.cpp | 30 ++++- src/ShuttleGui.h | 9 +- src/ui/AccessibleLinksFormatter.cpp | 185 ++++++++++++++++++++++++++++ src/ui/AccessibleLinksFormatter.h | 79 ++++++++++++ 5 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 src/ui/AccessibleLinksFormatter.cpp create mode 100644 src/ui/AccessibleLinksFormatter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da0e9d1f4..23c8d5929 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -892,6 +892,10 @@ list( APPEND SOURCES tracks/ui/ZoomHandle.cpp tracks/ui/ZoomHandle.h + # ui helpers + ui/AccessibleLinksFormatter.h + ui/AccessibleLinksFormatter.cpp + # Widgets widgets/AButton.cpp diff --git a/src/ShuttleGui.cpp b/src/ShuttleGui.cpp index 68732513c..c5caff7e8 100644 --- a/src/ShuttleGui.cpp +++ b/src/ShuttleGui.cpp @@ -113,6 +113,8 @@ for registering for changes. #include #include #include +#include + #include "../include/audacity/ComponentInterface.h" #include "widgets/ReadOnlyText.h" #include "widgets/wxPanelWrapper.h" @@ -191,6 +193,11 @@ void ShuttleGuiBase::ResetId() } +int ShuttleGuiBase::GetBorder() const noexcept +{ + return miBorder; +} + /// Used to modify an already placed FlexGridSizer to make a column stretchy. void ShuttleGuiBase::SetStretchyCol( int i ) { @@ -289,13 +296,13 @@ void ShuttleGuiBase::AddTitle(const TranslatableString &Prompt, int wrapWidth) /// Very generic 'Add' function. We can add anything we like. /// Useful for unique controls -wxWindow * ShuttleGuiBase::AddWindow(wxWindow * pWindow) +wxWindow* ShuttleGuiBase::AddWindow(wxWindow* pWindow, int PositionFlags) { if( mShuttleMode != eIsCreating ) return pWindow; mpWind = pWindow; SetProportions( 0 ); - UpdateSizersCore(false, wxALIGN_CENTRE | wxALL); + UpdateSizersCore(false, PositionFlags | wxALL); return pWindow; } @@ -1200,6 +1207,25 @@ void ShuttleGuiBase::EndVerticalLay() PopSizer(); } +void ShuttleGuiBase::StartWrapLay(int PositionFlags, int iProp) +{ + if (mShuttleMode != eIsCreating) + return; + + miSizerProp = iProp; + mpSubSizer = std::make_unique(wxHORIZONTAL, 0); + + UpdateSizersCore(false, PositionFlags | wxALL); +} + +void ShuttleGuiBase::EndWrapLay() +{ + if (mShuttleMode != eIsCreating) + return; + + PopSizer(); +} + void ShuttleGuiBase::StartMultiColumn(int nCols, int PositionFlags) { if( mShuttleMode != eIsCreating ) diff --git a/src/ShuttleGui.h b/src/ShuttleGui.h index c66a5f21e..9a99ad4d8 100644 --- a/src/ShuttleGui.h +++ b/src/ShuttleGui.h @@ -255,7 +255,7 @@ public: void AddPrompt(const TranslatableString &Prompt, int wrapWidth = 0); void AddUnits(const TranslatableString &Prompt, int wrapWidth = 0); void AddTitle(const TranslatableString &Prompt, int wrapWidth = 0); - wxWindow * AddWindow(wxWindow * pWindow); + wxWindow * AddWindow(wxWindow* pWindow, int PositionFlags = wxALIGN_CENTRE); wxSlider * AddSlider( const TranslatableString &Prompt, int pos, int Max, int Min = 0); wxSlider * AddVSlider(const TranslatableString &Prompt, int pos, int Max); @@ -347,10 +347,14 @@ public: // and create the appropriate widget. void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1); void EndHorizontalLay(); + void StartVerticalLay(int iProp=1); void StartVerticalLay(int PositionFlags, int iProp); - void EndVerticalLay(); + + void StartWrapLay(int PositionFlags=wxEXPAND, int iProp = 0); + void EndWrapLay(); + wxScrolledWindow * StartScroller(int iStyle=0); void EndScroller(); wxPanel * StartPanel(int iStyle=0); @@ -482,6 +486,7 @@ public: const int min); //-- End of variants. void SetBorder( int Border ) {miBorder = Border;}; + int GetBorder() const noexcept; void SetSizerProportion( int iProp ) {miSizerProp = iProp;}; void SetStretchyCol( int i ); void SetStretchyRow( int i ); diff --git a/src/ui/AccessibleLinksFormatter.cpp b/src/ui/AccessibleLinksFormatter.cpp new file mode 100644 index 000000000..d4e52135f --- /dev/null +++ b/src/ui/AccessibleLinksFormatter.cpp @@ -0,0 +1,185 @@ +/*!******************************************************************** + + Audacity: A Digital Audio Editor + + @file AccessibleLinksFormatter.h + @brief Define a helper class to format text with link in a way, accessible to VI users. + + Dmitry Vedenko + **********************************************************************/ + +#include "AccessibleLinksFormatter.h" + +#include "ShuttleGui.h" + +#include +#include + +#include + + +namespace +{ +size_t OffsetPosition(size_t position, size_t length) +{ + if (position == wxString::npos) + return wxString::npos; + + return position + length; +} +} + +AccessibleLinksFormatter::AccessibleLinksFormatter(TranslatableString message) + : mMessage(std::move(message)) +{ +} + +AccessibleLinksFormatter& AccessibleLinksFormatter::FormatLink( + wxString placeholder, TranslatableString value, std::string targetURL) +{ + mFormatArguments.push_back({ + std::move(placeholder), + std::move(value), + {}, + std::move(targetURL) + }); + + return *this; +} + +AccessibleLinksFormatter& AccessibleLinksFormatter::FormatLink( + wxString placeholder, TranslatableString value, + LinkClickedHandler handler) +{ + mFormatArguments.push_back({ + std::move(placeholder), + std::move(value), + std::move(handler), + {} + }); + + return *this; +} + +void AccessibleLinksFormatter::Populate(ShuttleGui& S) const +{ + // Just add the text, if there are no links to format + if (mFormatArguments.empty()) + { + S.AddFixedText(mMessage); + return; + } + + wxString translated = mMessage.Translation(); + + std::vector processedArguments = + ProcessArguments(translated); + + if (processedArguments.empty()) + { + S.AddFixedText(mMessage); + return; + } + + const int borderSize = S.GetBorder(); + + S.Prop(0).StartInvisiblePanel(); + S.StartWrapLay(); + { + size_t currentPosition = 0; + + S.SetBorder(0); + + if (borderSize > 0) + S.AddSpace(borderSize); + + for (const ProcessedArgument& processedArgument : processedArguments) + { + const FormatArgument* argument = processedArgument.Argument; + + // Add everything between currentPosition and PlaceholderPosition + + if (currentPosition != processedArgument.PlaceholderPosition) + { + const size_t substrLength = + processedArgument.PlaceholderPosition - currentPosition; + + S.Prop(0).AddFixedText( + Verbatim(translated.substr(currentPosition, substrLength))); + } + + // Add hyperlink + + wxHyperlinkCtrl* hyperlink = safenew wxHyperlinkCtrl( + S.GetParent(), wxID_ANY, argument->Value.Translation(), + argument->TargetURL); + + if (argument->Handler) + { + hyperlink->Bind( + wxEVT_HYPERLINK, [handler = argument->Handler](wxHyperlinkEvent& evt) { + handler(); + }); + } + + S.AddWindow(hyperlink, wxALIGN_TOP | wxALIGN_LEFT); + + // Update the currentPostion to the first symbol after the Placeholder + + currentPosition = OffsetPosition( + processedArgument.PlaceholderPosition, + argument->Placeholder.Length()); + + if (currentPosition >= translated.Length()) + break; + } + + if (currentPosition < translated.Length()) + S.AddFixedText(Verbatim(translated.substr(currentPosition))); + } + S.EndWrapLay(); + S.EndInvisiblePanel(); + + S.SetBorder(borderSize); +} + +std::vector +AccessibleLinksFormatter::ProcessArguments(wxString translatedMessage) const +{ + std::vector result; + result.reserve(mFormatArguments.size()); + // Arguments with the same placeholder are processed left-to-right. + // Lets track the last known position of the placeholder + std::unordered_map knownPlaceholderPosition; + + for (const FormatArgument& argument : mFormatArguments) + { + auto it = knownPlaceholderPosition.find(argument.Placeholder); + + const size_t startingPosition = + it != knownPlaceholderPosition.end() ? + OffsetPosition(it->second, argument.Placeholder.length()) : + 0; + + const size_t placeholderPosition = + startingPosition == wxString::npos ? + wxString::npos : + translatedMessage.find(argument.Placeholder, startingPosition); + + knownPlaceholderPosition[argument.Placeholder] = placeholderPosition; + + if (placeholderPosition != wxString::npos) + { + result.emplace_back( + ProcessedArgument { &argument, placeholderPosition }); + } + } + + std::sort( + result.begin(), result.end(), + [](const ProcessedArgument& lhs, const ProcessedArgument& rhs) { + return lhs.PlaceholderPosition < rhs.PlaceholderPosition; + }); + + return result; +} diff --git a/src/ui/AccessibleLinksFormatter.h b/src/ui/AccessibleLinksFormatter.h new file mode 100644 index 000000000..d96ee08ee --- /dev/null +++ b/src/ui/AccessibleLinksFormatter.h @@ -0,0 +1,79 @@ +/*!******************************************************************** + + Audacity: A Digital Audio Editor + + @file AccessibleLinksFormatter.h + @brief Define a helper class to format text with link in a way, accessible to VI users. + + Dmitry Vedenko + **********************************************************************/ + +#pragma once + +#include +#include + +#include "TranslatableString.h" + +class ShuttleGui; + +/*! @brief A class that allows translatable text to have accessible links in it in a way +* that is friendly to translators. +* +* This class allows to replace arbitrary placeholders (like %s, %url, {} or anyting of the choice) +* with links, that are accessible from the keyboard. +* +* In case there are multiple placeholders with the same name - they will be replaced in order +* they appear in the message. +*/ +class AccessibleLinksFormatter final +{ +public: + //! Handler to be called, when the Link is activated + using LinkClickedHandler = std::function; + + /*! @brief Create AccessibleLinksFormatter using a TranslatableString. + * + * TranslatableString may have the formatting options attached. + * TranslatableString copy will be stored, so formatting options that are appended + * after AccessibleLinksFormatter is created won't have any effect on the + * AccessibleLinksFormatter instance. + */ + explicit AccessibleLinksFormatter(TranslatableString message); + + //! Replace placeholder with a link, that will open URL in default browser. + AccessibleLinksFormatter& FormatLink( + wxString placeholder, TranslatableString value, std::string targetURL); + + //! Replace placeholder with a link, that will call a callback provided. + AccessibleLinksFormatter& FormatLink( + wxString placeholder, TranslatableString value, + LinkClickedHandler handler); + + //! Generate the UI. + void Populate(ShuttleGui& S) const; +private: + struct FormatArgument final + { + wxString Placeholder; + TranslatableString Value; + + LinkClickedHandler Handler; + std::string TargetURL; + }; + + struct ProcessedArgument final + { + const FormatArgument* Argument { nullptr }; + size_t PlaceholderPosition { wxString::npos }; + }; + + /* Find the positions of the placeholders and sort + * arguments according to the positions. + */ + std::vector + ProcessArguments(wxString translatedMessage) const; + + TranslatableString mMessage; + std::vector mFormatArguments; +}; From 2a2477eda0cb32b5d4dc2fb47aeebc02336eca39 Mon Sep 17 00:00:00 2001 From: Dmitry Vedenko Date: Wed, 14 Jul 2021 22:15:11 +0300 Subject: [PATCH 3/5] Adds a dialog, that notifies the user about update checks. This dialog is only shown once. It has links to privacy policy and application preferences. --- src/CMakeLists.txt | 2 + src/prefs/KeyConfigPrefs.cpp | 4 +- src/prefs/PrefsDialog.cpp | 5 ++ src/update/UpdateManager.cpp | 22 ++++++ src/update/UpdateNoticeDialog.cpp | 127 ++++++++++++++++++++++++++++++ src/update/UpdateNoticeDialog.h | 30 +++++++ 6 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 src/update/UpdateNoticeDialog.cpp create mode 100644 src/update/UpdateNoticeDialog.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23c8d5929..4fa7af0e4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -995,6 +995,8 @@ list( APPEND SOURCES update/UpdateDataParser.cpp update/UpdateManager.h update/UpdateManager.cpp + update/UpdateNoticeDialog.h + update/UpdateNoticeDialog.cpp update/UpdatePopupDialog.h update/UpdatePopupDialog.cpp prefs/ApplicationPrefs.h diff --git a/src/prefs/KeyConfigPrefs.cpp b/src/prefs/KeyConfigPrefs.cpp index 1d1def9a3..b7c25c1ef 100644 --- a/src/prefs/KeyConfigPrefs.cpp +++ b/src/prefs/KeyConfigPrefs.cpp @@ -475,7 +475,9 @@ void KeyConfigPrefs::OnShow(wxShowEvent & event) { event.Skip(); - if (event.IsShown()) + // This is required to prevent a crash if Preferences + // were opened without a project. + if (event.IsShown() && mView != nullptr) { mView->Refresh(); } diff --git a/src/prefs/PrefsDialog.cpp b/src/prefs/PrefsDialog.cpp index 5db3d0fdb..eb63a90cf 100644 --- a/src/prefs/PrefsDialog.cpp +++ b/src/prefs/PrefsDialog.cpp @@ -782,6 +782,11 @@ void PrefsDialog::SelectPageByName(const wxString &pageName) for (size_t i = 0; i < n; i++) { if (mCategories->GetPageText(i) == pageName) { mCategories->SetSelection(i); + // This covers the case, when ShowModal is called + // after selecting the page. + // ShowModal will select the page previously used by + // user + SavePreferredPage(); return; } } diff --git a/src/update/UpdateManager.cpp b/src/update/UpdateManager.cpp index f7637f4db..5270536b3 100644 --- a/src/update/UpdateManager.cpp +++ b/src/update/UpdateManager.cpp @@ -8,7 +8,9 @@ **********************************************************************/ #include "UpdateManager.h" + #include "UpdatePopupDialog.h" +#include "UpdateNoticeDialog.h" #include "AudioIO.h" #include "NetworkManager.h" @@ -26,6 +28,9 @@ static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime"; +static BoolSetting + prefUpdatesNoticeShown(wxT("/Update/UpdateNoticeShown"), false); + using Clock = std::chrono::system_clock; using TimePoint = Clock::time_point; @@ -51,10 +56,27 @@ void UpdateManager::Start() auto& instance = GetInstance(); static std::once_flag flag; + std::call_once(flag, [&instance] { instance.mTimer.SetOwner(&instance, ID_TIMER); instance.mTimer.StartOnce(1); }); + + // Show the dialog only once. + if (!prefUpdatesNoticeShown.Read()) + { + // DefaultUpdatesCheckingFlag survives the "Reset Preferences" + // action, so check, if the updates were previously disabled as well. + if (DefaultUpdatesCheckingFlag.Read()) + { + UpdateNoticeDialog notice(nullptr); + + notice.ShowModal(); + } + + prefUpdatesNoticeShown.Write(true); + gPrefs->Flush(); + } } VersionPatch UpdateManager::GetVersionPatch() const diff --git a/src/update/UpdateNoticeDialog.cpp b/src/update/UpdateNoticeDialog.cpp new file mode 100644 index 000000000..66735a368 --- /dev/null +++ b/src/update/UpdateNoticeDialog.cpp @@ -0,0 +1,127 @@ + +/*!******************************************************************** + + Audacity: A Digital Audio Editor + + @file UpdateNoticeDialog.cpp + @brief Declare a dialog to notify the user about automatic update checking. + + Dmitry Vedenko + **********************************************************************/ + +#include "UpdateNoticeDialog.h" + +#include +#include + +#include "ShuttleGui.h" +#include "CodeConversions.h" +#include "prefs/PrefsDialog.h" +#include "ui/AccessibleLinksFormatter.h" + + +static const auto title = + /* i18n-hint: Title of the app update notice dialog. */ + XO("App update checking"); + +static const auto firstParagraph = + /* i18-hint: The first paragraph of app update notice dialog. */ + XO("To stay up to date, you will receive an in-app notification whenever there is a new version of Audacity available to download."); + +static const auto secondParagraph = + /* i18-hint: The second paragraph of app update notice dialog */ + XO("In order to protect your privacy, Audacity does not collect any personal information. However, app update checking does require network access."); + +static const auto thirdParagraph = + /* i18-hint: Hint to the user about how to turn the app update off. %s is replaced with "Preferences > Application" link*/ + XO("You can turn off app update checking at any time in %s."); + + +BEGIN_EVENT_TABLE(UpdateNoticeDialog, wxDialogWrapper) + EVT_BUTTON(wxID_OK, UpdateNoticeDialog::OnOk) +END_EVENT_TABLE() + +IMPLEMENT_CLASS(UpdateNoticeDialog, wxDialogWrapper) + +UpdateNoticeDialog::UpdateNoticeDialog(wxWindow* parent) + : wxDialogWrapper( + /* i18n-hint: Title of the app update notice dialog. */ + parent, -1, XO("App updates"), wxDefaultPosition, wxDefaultSize, + wxCAPTION | wxCLOSE_BOX) +{ + ShuttleGui S(this, eIsCreating); + + S.StartVerticalLay(); + { + S.AddSpace(0, 16); + + S.StartHorizontalLay(); + { + S.AddSpace(24, 0); + + S.StartPanel(); + { + S.SetBorder(8); + + wxStaticText* titleCtrl = S.AddVariableText(title, false, 0, 500); + + wxFont font = titleCtrl->GetFont().MakeLarger().MakeBold(); + + titleCtrl->SetFont(font); + + S.AddFixedText(firstParagraph, false, 500); + + S.AddFixedText(secondParagraph, false, 500); + + /* i18n-hint: %s will be replaced with "our Privacy Policy" */ + AccessibleLinksFormatter privacyPolicy(XO("See %s for more info.")); + + privacyPolicy.FormatLink( + /* i18n-hint: Title of hyperlink to the privacy policy. This is an object of "See". */ + wxT("%s"), XO("our Privacy Policy"), + "https://www.audacityteam.org/about/desktop-privacy-notice/"); + + privacyPolicy.Populate(S); + + S.AddSpace(0, 8); + + AccessibleLinksFormatter preferencesMessage(thirdParagraph); + + preferencesMessage.FormatLink( + wxT("%s"), XO("Preferences > Application"), [this]() { + GlobalPrefsDialog dialog(this /* parent */, nullptr); + + dialog.SelectPageByName(XO("Application").Translation()); + dialog.ShowModal(); + }); + + preferencesMessage.Populate(S); + } + S.EndPanel(); + + S.AddSpace(24, 0); + } + S.EndHorizontalLay(); + + S.StartHorizontalLay(wxEXPAND, 0); + { + S.AddSpace(1, 0, 1); + + S.Id(wxID_OK).AddButton(XO("&OK"))->SetFocus(); + + S.AddSpace(8, 0); + } + S.EndHorizontalLay(); + } + + S.EndVerticalLay(); + + Layout(); + Fit(); + Center(); +} + +void UpdateNoticeDialog::OnOk(wxCommandEvent&) +{ + EndModal(wxOK); +} diff --git a/src/update/UpdateNoticeDialog.h b/src/update/UpdateNoticeDialog.h new file mode 100644 index 000000000..c9bf6b154 --- /dev/null +++ b/src/update/UpdateNoticeDialog.h @@ -0,0 +1,30 @@ +/*!******************************************************************** + + Audacity: A Digital Audio Editor + + @file UpdateNoticeDialog.h + @brief Define a dialog to notify the user about automatic update checking. + + Dmitry Vedenko + **********************************************************************/ + +#pragma once + +#include "widgets/wxPanelWrapper.h" +#include "wx/string.h" + +class HtmlWindow; +class wxWindow; + +//! Dialog, that notifies the users about automatic updates checking +class UpdateNoticeDialog final : public wxDialogWrapper +{ + DECLARE_DYNAMIC_CLASS (AboutDialog) +public: + explicit UpdateNoticeDialog (wxWindow* parent); + + private: + void OnOk (wxCommandEvent& event); + + DECLARE_EVENT_TABLE () +}; From f55b0c73f5ba2fd1fa0d9707a862c12e10c13bcd Mon Sep 17 00:00:00 2001 From: Dmitry Vedenko Date: Wed, 14 Jul 2021 22:15:20 +0300 Subject: [PATCH 4/5] Adds keyboard accessible Privacy Policy link to Preferences --- src/prefs/ApplicationPrefs.cpp | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/prefs/ApplicationPrefs.cpp b/src/prefs/ApplicationPrefs.cpp index 6143fa920..d048f3f4b 100644 --- a/src/prefs/ApplicationPrefs.cpp +++ b/src/prefs/ApplicationPrefs.cpp @@ -19,10 +19,13 @@ #include "update/UpdateManager.h" #include +#include #include "../Prefs.h" #include "../ShuttleGui.h" +#include "ui/AccessibleLinksFormatter.h" + //////////////////////////////////////////////////////////////////////////////// static ComponentInterfaceSymbol s_ComponentInterfaceSymbol{ XO("Application") }; @@ -68,12 +71,34 @@ void ApplicationPrefs::PopulateOrExchange(ShuttleGui & S) S.SetBorder(2); S.StartScroller(); - S.StartStatic(XO("Update Audacity")); + /* i18n-hint: Title for the update notifications panel in the preferences dialog. */ + S.StartStatic(XO("Update notifications")); { S.TieCheckBox( - XO("&Check for Updates...").Stripped(TranslatableString::Ellipses | TranslatableString::MenuCodes), - DefaultUpdatesCheckingFlag); + /* i18n-hint: Check-box title that configures periodic updates checking. */ + XXC("&Check for updates", "application preferences"), + DefaultUpdatesCheckingFlag); + + S.StartVerticalLay(); + { + S.AddFixedText(XO( + "App update checking requires network access. In order to protect your privacy, Audacity does not store any personal information."), + false, 470); + + /* i18n-hint: %s will be replaced with "our Privacy Policy" */ + AccessibleLinksFormatter privacyPolicy(XO("See %s for more info.")); + + privacyPolicy.FormatLink( + /* i18n-hint: Title of hyperlink to the privacy policy. This is an object of "See". */ + wxT("%s"), XO("our Privacy Policy"), + "https://www.audacityteam.org/about/desktop-privacy-notice/"); + + privacyPolicy.Populate(S); + } + + S.EndVerticalLay(); } + S.EndStatic(); S.EndScroller(); } From 0dea39a2ba01c7b9f507a4baa91b694ae6f0df7f Mon Sep 17 00:00:00 2001 From: Dmitry Vedenko Date: Wed, 14 Jul 2021 14:23:03 +0300 Subject: [PATCH 5/5] Makes Privacy Policy accessible in AboutDialog --- src/AboutDialog.cpp | 103 +++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/src/AboutDialog.cpp b/src/AboutDialog.cpp index fc77b7a40..f32cf6511 100644 --- a/src/AboutDialog.cpp +++ b/src/AboutDialog.cpp @@ -28,21 +28,23 @@ hold information about one contributor to Audacity. #include "AboutDialog.h" - - #include #include #include +#include #include #include #include #include #include +#include +#include #include "FileNames.h" #include "HelpText.h" #include "ShuttleGui.h" #include "widgets/HelpSystem.h" +#include "ui/AccessibleLinksFormatter.h" #include "AllThemeResources.h" #include "Theme.h" @@ -324,6 +326,7 @@ AboutDialog::AboutDialog(wxWindow * parent) .Prop(0) .AddButton(XXO("OK"), wxALIGN_CENTER, true); + Layout(); Fit(); this->Centre(); } @@ -526,9 +529,9 @@ visit our %s.") // It also makes it easier to revert to full size if we decide to. const float fScale = 0.5f;// smaller size. wxImage RescaledImage(logo.ConvertToImage()); - wxColour MainColour( - RescaledImage.GetRed(1,1), - RescaledImage.GetGreen(1,1), + wxColour MainColour( + RescaledImage.GetRed(1,1), + RescaledImage.GetGreen(1,1), RescaledImage.GetBlue(1,1)); pPage->SetBackgroundColour(MainColour); // wxIMAGE_QUALITY_HIGH not supported by wxWidgets 2.6.1, or we would use it here. @@ -560,6 +563,7 @@ visit our %s.") S.EndNotebookPage(); } + /** \brief: Fills out the "Information" tab of the preferences dialogue * * Provides as much information as possible about build-time options and @@ -831,6 +835,8 @@ void AboutDialog::PopulateInformationPage( ShuttleGui & S ) } +static const wxString GPL_TEXT(); + void AboutDialog::PopulateLicensePage( ShuttleGui & S ) { #if defined(HAS_PRIVACY_POLICY) @@ -838,11 +844,47 @@ void AboutDialog::PopulateLicensePage( ShuttleGui & S ) #else S.StartNotebookPage(XO("GPL License")); #endif - S.StartVerticalLay(1); - HtmlWindow *html = safenew LinkingHtmlWindow(S.GetParent(), -1, - wxDefaultPosition, - wxSize(ABOUT_DIALOG_WIDTH, 264), - wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER); + +#if defined(HAS_PRIVACY_POLICY) + S.Prop(0).StartPanel(); + { + S.AddSpace(0, 8); + /* i18n-hint: For "About Audacity...": Title for Privacy Policy section */ + S.AddVariableText(XC("PRIVACY POLICY", "about dialog"), true); + + S.AddFixedText( + XO("App update checking and error reporting require network access. " + "These features are optional.")); + + /* i18n-hint: %s will be replaced with "our Privacy Policy" */ + AccessibleLinksFormatter privacyPolicy(XO("See %s for more info.")); + + privacyPolicy.FormatLink( + /* i18n-hint: Title of hyperlink to the privacy policy. This is an object of "See". */ + wxT("%s"), XO("our Privacy Policy"), + "https://www.audacityteam.org/about/desktop-privacy-notice/"); + + privacyPolicy.Populate(S); + } + S.EndPanel(); + + S.AddSpace(0, 8); +#endif + + S.Prop(1).StartPanel(); + { + HtmlWindow* html = safenew LinkingHtmlWindow( + S.GetParent(), -1, wxDefaultPosition, wxSize(ABOUT_DIALOG_WIDTH, 264), + wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER); + + html->SetPage(FormatHtmlText(GPL_TEXT())); + + S.Prop(1).Position(wxEXPAND).AddWindow( html ); + } + S.EndPanel(); + + S.EndNotebookPage(); +} // I tried using
 here to get a monospaced font,
 // as is normally used for the GPL.
@@ -852,7 +894,7 @@ void AboutDialog::PopulateLicensePage( ShuttleGui & S )
 // The GPL is not to be translated....
    
 
-constexpr auto GPL_TEXT = 
+const wxString GPL_TEXT() { return
 wxT("		    
GNU GENERAL PUBLIC LICENSE\n
") wxT("
Version 2, June 1991\n
") wxT("

") @@ -1135,45 +1177,6 @@ wxT("TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n" wxT("YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n") wxT("PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n") wxT("POSSIBILITY OF SUCH DAMAGES.\n"); - -#if defined(HAS_PRIVACY_POLICY) - /* i18n-hint: For "About Audacity...": Title for Privacy Policy section */ - const auto privacyPolicyTitle = XC("PRIVACY POLICY", "about dialog"); - - /* i18n-hint: For "About Audacity...": Title of hyperlink to the privacy policy. This is an object of "See". */ - const auto privacyPolicyURLText = XO("our Privacy Policy"); - - /* i18n-hint: %s will be replaced with "our Privacy Policy" */ - const auto privacyPolicyText = XO( - "App update checking and error reporting require network access. " - "These features are optional. See %s " - "for more information.") - .Format( - Verbatim( - "[[https://www.audacityteam.org/about/desktop-privacy-notice/|%s]]") - .Format(privacyPolicyURLText)); - - const wxString privacyPolicyHTML = wxString( - wxT("

") + - privacyPolicyTitle.Translation() + - wxT("

") + - privacyPolicyText.Translation() + - wxT("



") - ); - - wxString PageText = FormatHtmlText(privacyPolicyHTML + GPL_TEXT); -#else - wxString PageText = FormatHtmlText(GPL_TEXT); -#endif - - html->SetPage( PageText ); - - S.Prop(1) - .Position( wxEXPAND ) - .AddWindow( html ); - - S.EndVerticalLay(); - S.EndNotebookPage(); } void AboutDialog::AddCredit( const wxString &name, Role role )