1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-07 23:32:53 +02:00

Merge release-3.0.3 into master

Conflicts:
	src/AboutDialog.cpp
	src/ShuttleGui.cpp
	src/prefs/ApplicationPrefs.cpp
This commit is contained in:
Paul Licameli 2021-07-14 21:59:57 -04:00
commit d8c4f54164
12 changed files with 574 additions and 57 deletions

View File

@ -28,21 +28,23 @@ hold information about one contributor to Audacity.
#include "AboutDialog.h" #include "AboutDialog.h"
#include <wx/dialog.h> #include <wx/dialog.h>
#include <wx/html/htmlwin.h> #include <wx/html/htmlwin.h>
#include <wx/button.h> #include <wx/button.h>
#include <wx/hyperlink.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/statbmp.h> #include <wx/statbmp.h>
#include <wx/intl.h> #include <wx/intl.h>
#include <wx/sstream.h> #include <wx/sstream.h>
#include <wx/txtstrm.h> #include <wx/txtstrm.h>
#include <wx/statbox.h>
#include <wx/stattext.h>
#include "FileNames.h" #include "FileNames.h"
#include "HelpText.h" #include "HelpText.h"
#include "ShuttleGui.h" #include "ShuttleGui.h"
#include "widgets/HelpSystem.h" #include "widgets/HelpSystem.h"
#include "ui/AccessibleLinksFormatter.h"
#include "AllThemeResources.h" #include "AllThemeResources.h"
#include "Theme.h" #include "Theme.h"
@ -324,6 +326,7 @@ AboutDialog::AboutDialog(wxWindow * parent)
.Prop(0) .Prop(0)
.AddButton(XXO("OK"), wxALIGN_CENTER, true); .AddButton(XXO("OK"), wxALIGN_CENTER, true);
Layout();
Fit(); Fit();
this->Centre(); this->Centre();
} }
@ -526,9 +529,9 @@ visit our %s.")
// It also makes it easier to revert to full size if we decide to. // It also makes it easier to revert to full size if we decide to.
const float fScale = 0.5f;// smaller size. const float fScale = 0.5f;// smaller size.
wxImage RescaledImage(logo.ConvertToImage()); wxImage RescaledImage(logo.ConvertToImage());
wxColour MainColour( wxColour MainColour(
RescaledImage.GetRed(1,1), RescaledImage.GetRed(1,1),
RescaledImage.GetGreen(1,1), RescaledImage.GetGreen(1,1),
RescaledImage.GetBlue(1,1)); RescaledImage.GetBlue(1,1));
pPage->SetBackgroundColour(MainColour); pPage->SetBackgroundColour(MainColour);
// wxIMAGE_QUALITY_HIGH not supported by wxWidgets 2.6.1, or we would use it here. // 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(); S.EndNotebookPage();
} }
/** \brief: Fills out the "Information" tab of the preferences dialogue /** \brief: Fills out the "Information" tab of the preferences dialogue
* *
* Provides as much information as possible about build-time options and * 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 ) void AboutDialog::PopulateLicensePage( ShuttleGui & S )
{ {
#if defined(HAS_PRIVACY_POLICY) #if defined(HAS_PRIVACY_POLICY)
@ -838,11 +844,47 @@ void AboutDialog::PopulateLicensePage( ShuttleGui & S )
#else #else
S.StartNotebookPage(XO("GPL License")); S.StartNotebookPage(XO("GPL License"));
#endif #endif
S.StartVerticalLay(1);
HtmlWindow *html = safenew LinkingHtmlWindow(S.GetParent(), -1, #if defined(HAS_PRIVACY_POLICY)
wxDefaultPosition, S.Prop(0).StartPanel();
wxSize(ABOUT_DIALOG_WIDTH, 264), {
wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER); 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 <pre> here to get a monospaced font, // I tried using <pre> here to get a monospaced font,
// as is normally used for the GPL. // as is normally used for the GPL.
@ -852,7 +894,7 @@ void AboutDialog::PopulateLicensePage( ShuttleGui & S )
// The GPL is not to be translated.... // The GPL is not to be translated....
constexpr auto GPL_TEXT = const wxString GPL_TEXT() { return
wxT(" <center>GNU GENERAL PUBLIC LICENSE\n</center>") wxT(" <center>GNU GENERAL PUBLIC LICENSE\n</center>")
wxT(" <center>Version 2, June 1991\n</center>") wxT(" <center>Version 2, June 1991\n</center>")
wxT("<p><p>") wxT("<p><p>")
@ -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("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("PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n")
wxT("POSSIBILITY OF SUCH DAMAGES.\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("<center>") +
privacyPolicyTitle.Translation() +
wxT("</center><p>") +
privacyPolicyText.Translation() +
wxT("</p><br/><br/>")
);
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 ) void AboutDialog::AddCredit( const wxString &name, Role role )

View File

@ -894,6 +894,10 @@ list( APPEND SOURCES
tracks/ui/ZoomHandle.cpp tracks/ui/ZoomHandle.cpp
tracks/ui/ZoomHandle.h tracks/ui/ZoomHandle.h
# ui helpers
ui/AccessibleLinksFormatter.h
ui/AccessibleLinksFormatter.cpp
# Widgets # Widgets
widgets/AButton.cpp widgets/AButton.cpp
@ -993,6 +997,8 @@ list( APPEND SOURCES
update/UpdateDataParser.cpp update/UpdateDataParser.cpp
update/UpdateManager.h update/UpdateManager.h
update/UpdateManager.cpp update/UpdateManager.cpp
update/UpdateNoticeDialog.h
update/UpdateNoticeDialog.cpp
update/UpdatePopupDialog.h update/UpdatePopupDialog.h
update/UpdatePopupDialog.cpp update/UpdatePopupDialog.cpp
prefs/ApplicationPrefs.h prefs/ApplicationPrefs.h

View File

@ -113,6 +113,8 @@ for registering for changes.
#include <wx/spinctrl.h> #include <wx/spinctrl.h>
#include <wx/stattext.h> #include <wx/stattext.h>
#include <wx/bmpbuttn.h> #include <wx/bmpbuttn.h>
#include <wx/wrapsizer.h>
#include "ComponentInterface.h" #include "ComponentInterface.h"
#include "widgets/ReadOnlyText.h" #include "widgets/ReadOnlyText.h"
#include "widgets/wxPanelWrapper.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. /// Used to modify an already placed FlexGridSizer to make a column stretchy.
void ShuttleGuiBase::SetStretchyCol( int i ) 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. /// Very generic 'Add' function. We can add anything we like.
/// Useful for unique controls /// Useful for unique controls
wxWindow * ShuttleGuiBase::AddWindow(wxWindow * pWindow) wxWindow* ShuttleGuiBase::AddWindow(wxWindow* pWindow, int PositionFlags)
{ {
if( mShuttleMode != eIsCreating ) if( mShuttleMode != eIsCreating )
return pWindow; return pWindow;
mpWind = pWindow; mpWind = pWindow;
SetProportions( 0 ); SetProportions( 0 );
UpdateSizersCore(false, wxALIGN_CENTRE | wxALL); UpdateSizersCore(false, PositionFlags | wxALL);
return pWindow; return pWindow;
} }
@ -1200,6 +1207,25 @@ void ShuttleGuiBase::EndVerticalLay()
PopSizer(); PopSizer();
} }
void ShuttleGuiBase::StartWrapLay(int PositionFlags, int iProp)
{
if (mShuttleMode != eIsCreating)
return;
miSizerProp = iProp;
mpSubSizer = std::make_unique<wxWrapSizer>(wxHORIZONTAL, 0);
UpdateSizersCore(false, PositionFlags | wxALL);
}
void ShuttleGuiBase::EndWrapLay()
{
if (mShuttleMode != eIsCreating)
return;
PopSizer();
}
void ShuttleGuiBase::StartMultiColumn(int nCols, int PositionFlags) void ShuttleGuiBase::StartMultiColumn(int nCols, int PositionFlags)
{ {
if( mShuttleMode != eIsCreating ) if( mShuttleMode != eIsCreating )

View File

@ -256,7 +256,7 @@ public:
void AddPrompt(const TranslatableString &Prompt, int wrapWidth = 0); void AddPrompt(const TranslatableString &Prompt, int wrapWidth = 0);
void AddUnits(const TranslatableString &Prompt, int wrapWidth = 0); void AddUnits(const TranslatableString &Prompt, int wrapWidth = 0);
void AddTitle(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( wxSlider * AddSlider(
const TranslatableString &Prompt, int pos, int Max, int Min = 0); const TranslatableString &Prompt, int pos, int Max, int Min = 0);
wxSlider * AddVSlider(const TranslatableString &Prompt, int pos, int Max); wxSlider * AddVSlider(const TranslatableString &Prompt, int pos, int Max);
@ -348,10 +348,14 @@ public:
// and create the appropriate widget. // and create the appropriate widget.
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1); void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1);
void EndHorizontalLay(); void EndHorizontalLay();
void StartVerticalLay(int iProp=1); void StartVerticalLay(int iProp=1);
void StartVerticalLay(int PositionFlags, int iProp); void StartVerticalLay(int PositionFlags, int iProp);
void EndVerticalLay(); void EndVerticalLay();
void StartWrapLay(int PositionFlags=wxEXPAND, int iProp = 0);
void EndWrapLay();
wxScrolledWindow * StartScroller(int iStyle=0); wxScrolledWindow * StartScroller(int iStyle=0);
void EndScroller(); void EndScroller();
wxPanel * StartPanel(int iStyle=0); wxPanel * StartPanel(int iStyle=0);
@ -483,6 +487,7 @@ public:
const int min); const int min);
//-- End of variants. //-- End of variants.
void SetBorder( int Border ) {miBorder = Border;}; void SetBorder( int Border ) {miBorder = Border;};
int GetBorder() const noexcept;
void SetSizerProportion( int iProp ) {miSizerProp = iProp;}; void SetSizerProportion( int iProp ) {miSizerProp = iProp;};
void SetStretchyCol( int i ); void SetStretchyCol( int i );
void SetStretchyRow( int i ); void SetStretchyRow( int i );

View File

@ -19,10 +19,13 @@
#include "update/UpdateManager.h" #include "update/UpdateManager.h"
#include <wx/defs.h> #include <wx/defs.h>
#include <wx/hyperlink.h>
#include "../Prefs.h" #include "../Prefs.h"
#include "../ShuttleGui.h" #include "../ShuttleGui.h"
#include "ui/AccessibleLinksFormatter.h"
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static ComponentInterfaceSymbol s_ComponentInterfaceSymbol{ XO("Application") }; static ComponentInterfaceSymbol s_ComponentInterfaceSymbol{ XO("Application") };
@ -68,10 +71,34 @@ void ApplicationPrefs::PopulateOrExchange(ShuttleGui & S)
S.SetBorder(2); S.SetBorder(2);
S.StartScroller(); 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"), DefaultUpdatesCheckingFlag); S.TieCheckBox(
/* 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.EndStatic();
S.EndScroller(); S.EndScroller();
} }

View File

@ -475,7 +475,9 @@ void KeyConfigPrefs::OnShow(wxShowEvent & event)
{ {
event.Skip(); 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(); mView->Refresh();
} }

View File

@ -782,6 +782,11 @@ void PrefsDialog::SelectPageByName(const wxString &pageName)
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
if (mCategories->GetPageText(i) == pageName) { if (mCategories->GetPageText(i) == pageName) {
mCategories->SetSelection(i); 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; return;
} }
} }

View File

@ -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 <unordered_map>
#include <algorithm>
#include <wx/hyperlink.h>
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<ProcessedArgument> 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::ProcessedArgument>
AccessibleLinksFormatter::ProcessArguments(wxString translatedMessage) const
{
std::vector<ProcessedArgument> 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<wxString, size_t> 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;
}

View File

@ -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 <functional>
#include <vector>
#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<void()>;
/*! @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<ProcessedArgument>
ProcessArguments(wxString translatedMessage) const;
TranslatableString mMessage;
std::vector<FormatArgument> mFormatArguments;
};

View File

@ -8,7 +8,9 @@
**********************************************************************/ **********************************************************************/
#include "UpdateManager.h" #include "UpdateManager.h"
#include "UpdatePopupDialog.h" #include "UpdatePopupDialog.h"
#include "UpdateNoticeDialog.h"
#include "AudioIO.h" #include "AudioIO.h"
#include "NetworkManager.h" #include "NetworkManager.h"
@ -26,6 +28,9 @@
static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime"; static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime";
static BoolSetting
prefUpdatesNoticeShown(wxT("/Update/UpdateNoticeShown"), false);
using Clock = std::chrono::system_clock; using Clock = std::chrono::system_clock;
using TimePoint = Clock::time_point; using TimePoint = Clock::time_point;
@ -51,10 +56,27 @@ void UpdateManager::Start()
auto& instance = GetInstance(); auto& instance = GetInstance();
static std::once_flag flag; static std::once_flag flag;
std::call_once(flag, [&instance] { std::call_once(flag, [&instance] {
instance.mTimer.SetOwner(&instance, ID_TIMER); instance.mTimer.SetOwner(&instance, ID_TIMER);
instance.mTimer.StartOnce(1); 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 VersionPatch UpdateManager::GetVersionPatch() const

View File

@ -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 <wx/button.h>
#include <wx/stattext.h>
#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);
}

View File

@ -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 ()
};