mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-02 08:39:46 +02:00
Merge release-3.0.3 into master
Conflicts: src/AboutDialog.cpp src/prefs/ApplicationPrefs.cpp
This commit is contained in:
commit
d0d64bf025
4
.github/workflows/cmake_build.yml
vendored
4
.github/workflows/cmake_build.yml
vendored
@ -74,9 +74,9 @@ jobs:
|
||||
cache-name: cache-conan-modules
|
||||
with:
|
||||
path: ${{ env.CONAN_USER_HOME }}
|
||||
key: host-${{ matrix.config.name }}-${{ hashFiles('cmake-proxies/CMakeLists.txt') }}
|
||||
key: host-3-${{ matrix.config.name }}-${{ hashFiles('cmake-proxies/CMakeLists.txt') }}
|
||||
restore-keys: |
|
||||
host-${{ matrix.config.name }}-
|
||||
host-3-${{ matrix.config.name }}-
|
||||
|
||||
- name: Configure
|
||||
env:
|
||||
|
@ -19,7 +19,7 @@ endif ()
|
||||
# still link to the alpha manual online.
|
||||
|
||||
# Set this value to 0 for alpha, 1 for beta, 2 for release builds
|
||||
set( AUDACITY_BUILD_LEVEL 0 )
|
||||
set( AUDACITY_BUILD_LEVEL 0 CACHE STRING "0 for alpha, 1 for beta, 2 for release builds" )
|
||||
|
||||
# The Audacity version
|
||||
# Increment as appropriate after release of a new version, and set back
|
||||
|
@ -246,7 +246,7 @@ namespace
|
||||
{
|
||||
static constexpr int MaxUserCommentLength = 2000;
|
||||
|
||||
auto frame = new wxFrame(
|
||||
auto dialog = new wxDialog(
|
||||
nullptr,
|
||||
wxID_ANY,
|
||||
_("Problem Report for Audacity"),
|
||||
@ -254,83 +254,89 @@ namespace
|
||||
wxDefaultSize,
|
||||
wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX)//disable frame resize
|
||||
);
|
||||
frame->SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
|
||||
|
||||
//fixes focus issue with Windows build-in screen reader, but breaks VoiceOver
|
||||
#if defined(__WXMSW__)
|
||||
dialog->SetFocus();
|
||||
#endif
|
||||
|
||||
auto mainLayout = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto headerText = new wxStaticText(frame, wxID_ANY, header);
|
||||
headerText->SetFont(wxFont(wxFontInfo().Bold()));
|
||||
|
||||
auto headerLayout = new wxBoxSizer(wxHORIZONTAL);
|
||||
headerLayout->Add(new wxStaticBitmap(frame, wxID_ANY, wxIcon(warning)));
|
||||
headerLayout->Add(new wxStaticBitmap(dialog, wxID_ANY, wxIcon(warning)));
|
||||
headerLayout->AddSpacer(5);
|
||||
|
||||
auto headerText = new wxStaticText(dialog, wxID_ANY, header);
|
||||
headerText->SetFont(wxFont(wxFontInfo().Bold()));
|
||||
headerLayout->Add(headerText, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
|
||||
|
||||
mainLayout->Add(headerLayout, wxSizerFlags().Border(wxALL));
|
||||
if (onSend != nullptr)
|
||||
{
|
||||
mainLayout->AddSpacer(5);
|
||||
mainLayout->Add(new wxStaticText(dialog, wxID_ANY, _("Click \"Send\" to submit the report to Audacity. This information is collected anonymously.")), wxSizerFlags().Border(wxALL));
|
||||
}
|
||||
mainLayout->AddSpacer(10);
|
||||
mainLayout->Add(new wxStaticText(dialog, wxID_ANY, _("Problem details")), wxSizerFlags().Border(wxALL));
|
||||
|
||||
auto dumpTextCtrl = new wxTextCtrl(dialog, wxID_ANY, dump, wxDefaultPosition, wxSize(500, 300), wxTE_RICH | wxTE_READONLY | wxTE_MULTILINE | wxTE_DONTWRAP);
|
||||
dumpTextCtrl->SetFont(wxFont(wxFontInfo().Family(wxFONTFAMILY_TELETYPE)));
|
||||
dumpTextCtrl->ShowPosition(0);//scroll to top
|
||||
mainLayout->Add(dumpTextCtrl, wxSizerFlags().Border(wxALL).Expand());
|
||||
|
||||
auto buttonsLayout = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxTextCtrl* commentCtrl = nullptr;
|
||||
if (onSend != nullptr)
|
||||
{
|
||||
commentCtrl = new wxTextCtrl(frame, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(500, 100), wxTE_MULTILINE);
|
||||
mainLayout->AddSpacer(10);
|
||||
mainLayout->Add(new wxStaticText(dialog, wxID_ANY, _("Comments")), wxSizerFlags().Border(wxALL));
|
||||
|
||||
commentCtrl = new wxTextCtrl(dialog, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(500, 100), wxTE_MULTILINE);
|
||||
commentCtrl->SetMaxLength(MaxUserCommentLength);
|
||||
|
||||
mainLayout->Add(commentCtrl, wxSizerFlags().Border(wxALL).Expand());
|
||||
}
|
||||
|
||||
if (onSend != nullptr)
|
||||
if (onSend != nullptr && commentCtrl != nullptr)
|
||||
{
|
||||
auto okButton = new wxButton(frame, wxID_ANY, XC("&Don't send", "crash reporter button"));
|
||||
auto sendButton = new wxButton(frame, wxID_ANY, XC("&Send", "crash reporter button"));
|
||||
auto dontSendButton = new wxButton(dialog, wxID_ANY, XC("&Don't send", "crash reporter button"));
|
||||
auto sendButton = new wxButton(dialog, wxID_ANY, XC("&Send", "crash reporter button"));
|
||||
|
||||
okButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
|
||||
dontSendButton->Bind(wxEVT_BUTTON, [dialog](wxCommandEvent&)
|
||||
{
|
||||
frame->Close(true);
|
||||
dialog->Close(true);
|
||||
});
|
||||
sendButton->Bind(wxEVT_BUTTON, [frame, commentCtrl, onSend](wxCommandEvent&)
|
||||
sendButton->Bind(wxEVT_BUTTON, [dialog, commentCtrl, onSend](wxCommandEvent&)
|
||||
{
|
||||
if (onSend(commentCtrl->GetValue()))
|
||||
{
|
||||
frame->Close(true);
|
||||
dialog->Close(true);
|
||||
}
|
||||
});
|
||||
|
||||
buttonsLayout->Add(okButton);
|
||||
buttonsLayout->Add(dontSendButton);
|
||||
buttonsLayout->AddSpacer(5);
|
||||
buttonsLayout->Add(sendButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto okButton = new wxButton(frame, wxID_OK, wxT("OK"));
|
||||
okButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
|
||||
auto okButton = new wxButton(dialog, wxID_OK, wxT("OK"));
|
||||
okButton->Bind(wxEVT_BUTTON, [dialog](wxCommandEvent&)
|
||||
{
|
||||
frame->Close(true);
|
||||
dialog->Close(true);
|
||||
});
|
||||
buttonsLayout->Add(okButton);
|
||||
}
|
||||
|
||||
mainLayout->Add(headerLayout, wxSizerFlags().Border(wxALL));
|
||||
if (onSend != nullptr)
|
||||
{
|
||||
mainLayout->AddSpacer(5);
|
||||
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Click \"Send\" to submit the report to Audacity. This information is collected anonymously.")), wxSizerFlags().Border(wxALL));
|
||||
}
|
||||
mainLayout->AddSpacer(10);
|
||||
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Problem details")), wxSizerFlags().Border(wxALL));
|
||||
|
||||
auto dumpTextCtrl = new wxTextCtrl(frame, wxID_ANY, dump, wxDefaultPosition, wxSize(500, 300), wxTE_RICH | wxTE_READONLY | wxTE_MULTILINE | wxTE_DONTWRAP);
|
||||
dumpTextCtrl->SetFont(wxFont(wxFontInfo().Family(wxFONTFAMILY_TELETYPE)));
|
||||
dumpTextCtrl->ShowPosition(0);//scroll to top
|
||||
mainLayout->Add(dumpTextCtrl, wxSizerFlags().Border(wxALL).Expand());
|
||||
|
||||
if (onSend != nullptr)
|
||||
{
|
||||
mainLayout->AddSpacer(10);
|
||||
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Comments")), wxSizerFlags().Border(wxALL));
|
||||
mainLayout->Add(commentCtrl, wxSizerFlags().Border(wxALL).Expand());
|
||||
}
|
||||
|
||||
mainLayout->Add(buttonsLayout, wxSizerFlags().Border(wxALL).Align(wxALIGN_RIGHT));
|
||||
frame->SetSizerAndFit(mainLayout);
|
||||
dialog->SetSizerAndFit(mainLayout);
|
||||
|
||||
frame->Show(true);
|
||||
dialog->Bind(wxEVT_CLOSE_WINDOW, [dialog](wxCloseEvent&) {
|
||||
dialog->Destroy();
|
||||
});
|
||||
|
||||
dialog->Show(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,7 +392,6 @@ void CrashReportApp::OnInitCmdLine(wxCmdLineParser& parser)
|
||||
{
|
||||
static const wxCmdLineEntryDesc cmdLineEntryDesc[] =
|
||||
{
|
||||
{ wxCMD_LINE_SWITCH, "h", "help", "Display help on the command line parameters", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
|
||||
{ wxCMD_LINE_SWITCH, "s", "silent", "Send without displaying the confirmation dialog" },
|
||||
{ wxCMD_LINE_OPTION, "u", "url", "Crash report server URL", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
|
||||
{ wxCMD_LINE_OPTION, "a", "args", "A set of arguments to send", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
|
||||
|
@ -45,7 +45,11 @@ if( ${_OPT}package_manual )
|
||||
) \
|
||||
")
|
||||
|
||||
install( DIRECTORY "${out_dir}/${host}/" DESTINATION "help/manual" )
|
||||
if( "${CMAKE_GENERATOR}" MATCHES "Xcode" )
|
||||
install( DIRECTORY "${out_dir}/${host}/" DESTINATION "${_APPDIR}/help/manual" )
|
||||
elseif( "${CMAKE_GENERATOR}" MATCHES "Visual Studio*" )
|
||||
install( DIRECTORY "${out_dir}/${host}/" DESTINATION "help/manual" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if( NOT CMAKE_SYSTEM_NAME MATCHES "Darwin" )
|
||||
|
@ -4822,7 +4822,7 @@ msgstr "Herzlich Willkommen zu Audacity-Version %s"
|
||||
#: src/ProjectManager.cpp
|
||||
#, c-format
|
||||
msgid "%sSave changes to %s?"
|
||||
msgstr "%sÄnderungen speichern nach %s?"
|
||||
msgstr "%sÄnderungen an %s speichern?"
|
||||
|
||||
#: src/ProjectManager.cpp
|
||||
msgid "Save project before closing?"
|
||||
|
@ -22,17 +22,22 @@ fi
|
||||
cmake --build build -j "${cpus}" --config "${AUDACITY_BUILD_TYPE}"
|
||||
|
||||
BIN_OUTPUT_DIR=build/bin/${AUDACITY_BUILD_TYPE}
|
||||
SHARED_OUTPUT_DIR=build/shared/${AUDACITY_BUILD_TYPE}
|
||||
SYMBOLS_OUTPUT_DIR=debug
|
||||
|
||||
mkdir ${SYMBOLS_OUTPUT_DIR}
|
||||
|
||||
if [[ "${OSTYPE}" == msys* ]]; then # Windows
|
||||
# copy PDBs to debug folder...
|
||||
find ${BIN_OUTPUT_DIR} -name '*.pdb' | xargs -I % cp % ${SYMBOLS_OUTPUT_DIR}
|
||||
find ${BIN_OUTPUT_DIR} -name '*.pdb' | xargs -I % cp -v % ${SYMBOLS_OUTPUT_DIR}
|
||||
find ${SHARED_OUTPUT_DIR} -name '*.pdb' | xargs -I % cp -v % ${SYMBOLS_OUTPUT_DIR}
|
||||
find $(cygpath ${CONAN_USER_HOME}) -name '*.pdb' | xargs -I % cp -v % ${SYMBOLS_OUTPUT_DIR}
|
||||
# and remove debug symbol files from the file tree before archieving
|
||||
find ${BIN_OUTPUT_DIR} -name '*.iobj' -o -name '*.ipdb' -o -name '*.pdb' -o -name '*.ilk' | xargs rm -f
|
||||
elif [[ "${OSTYPE}" == darwin* ]]; then # macOS
|
||||
find ${BIN_OUTPUT_DIR} -name '*.dSYM' | xargs -J % mv % ${SYMBOLS_OUTPUT_DIR}
|
||||
find ${SHARED_OUTPUT_DIR} -name '*.dSYM' | xargs -J % mv % ${SYMBOLS_OUTPUT_DIR}
|
||||
find ${CONAN_USER_HOME}/dsyms -name '*.dSYM' | xargs -J % cp -R % ${SYMBOLS_OUTPUT_DIR}
|
||||
else # Linux & others
|
||||
chmod +x scripts/ci/linux/split_debug_symbols.sh
|
||||
find ${BIN_OUTPUT_DIR} -type f -executable -o -name '*.so' | xargs -n 1 scripts/ci/linux/split_debug_symbols.sh
|
||||
|
@ -58,11 +58,32 @@ fi
|
||||
if [[ ${GIT_BRANCH} == release* ]]; then
|
||||
cmake_args+=(
|
||||
-D audacity_package_manual=yes
|
||||
-D AUDACITY_BUILD_LEVEL=2
|
||||
)
|
||||
fi
|
||||
|
||||
# Configure Audacity
|
||||
cmake "${cmake_args[@]}"
|
||||
|
||||
if [[ "${OSTYPE}" == msys* ]]; then # Windows
|
||||
# On Windows, preserve PDB files before clearing the build cache
|
||||
|
||||
conanUnixPath=$(cygpath ${CONAN_USER_HOME})
|
||||
pdbOutputPath="${conanUnixPath}/pdbs"
|
||||
|
||||
ls -la ${conanUnixPath}
|
||||
|
||||
mkdir -p "${pdbOutputPath}"
|
||||
find "${conanUnixPath}/.conan" -name '*.pdb' '!' -name "vc14?.pdb" -type f | xargs -I % cp -v % ${pdbOutputPath}
|
||||
elif [[ "${OSTYPE}" == darwin* ]]; then # macOS
|
||||
# On macOS - find all the .dylib files and generate dSYMs from them
|
||||
# in the same folder.
|
||||
# dsymutil requires *.o files, so we need to generate files before clearing
|
||||
# the build directories.
|
||||
|
||||
chmod +x scripts/ci/macos/generate_dsym.sh
|
||||
scripts/ci/macos/generate_dsym.sh
|
||||
fi
|
||||
|
||||
# Remove build directories and sources to reduce the cache size.
|
||||
conan remove "*" --src --builds --force
|
||||
|
25
scripts/ci/macos/generate_dsym.sh
Normal file
25
scripts/ci/macos/generate_dsym.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -xe
|
||||
|
||||
function extractDSym()
|
||||
{
|
||||
local lib=$1
|
||||
local libfile=$(basename $lib)
|
||||
local libname="${libfile%.*}"
|
||||
local targetdir=$2
|
||||
local dsymfile=${targetdir}/${libname}.dSYM
|
||||
|
||||
if [[ -d "$dsymfile" ]]; then
|
||||
echo "Skipping dSYM generation for $libfile: dSYM exists"
|
||||
elif [[ ! -L "$lib" && "$lib" != "{}" && $lib != *"dSYM"* ]]; then
|
||||
echo "Extracting dSYMs from $libfile to $dsymfile"
|
||||
dsymutil "$lib" -o "$dsymfile"
|
||||
fi
|
||||
}
|
||||
|
||||
export -f extractDSym
|
||||
|
||||
mkdir -p "$CONAN_USER_HOME/dsyms"
|
||||
|
||||
find $CONAN_USER_HOME -name "*.dylib" | xargs -I {} bash -c "extractDSym \"{}\" \"$CONAN_USER_HOME/dsyms\""
|
@ -6,10 +6,10 @@ set -euxo pipefail
|
||||
|
||||
cd build
|
||||
|
||||
cpack -C "${AUDACITY_BUILD_TYPE}" --verbose
|
||||
|
||||
if [[ "${OSTYPE}" == msys* && ${GIT_BRANCH} == release* ]]; then # Windows
|
||||
cmake --build . --target innosetup --config "${AUDACITY_BUILD_TYPE}"
|
||||
else
|
||||
cpack -C "${AUDACITY_BUILD_TYPE}" --verbose
|
||||
fi
|
||||
|
||||
# Remove the temporary directory
|
||||
|
@ -11,6 +11,6 @@ curl -sL https://sentry.io/get-cli/ | bash
|
||||
|
||||
SYMBOLS=$(find debug | xargs)
|
||||
|
||||
${INSTALL_DIR}/sentry-cli --auth-token ${SENTRY_AUTH_TOKEN} --url ${SENTRY_HOST} upload-dif \
|
||||
${INSTALL_DIR}/sentry-cli --auth-token ${SENTRY_AUTH_TOKEN} --url https://${SENTRY_HOST} upload-dif \
|
||||
--org ${SENTRY_ORG_SLUG} \
|
||||
--project ${SENTRY_PROJECT_SLUG} ${SYMBOLS}
|
||||
|
@ -714,7 +714,7 @@ void AColor::PreComputeGradient() {
|
||||
gradient_pre[selected][1][i][2] = (unsigned char) (255 * b);
|
||||
}
|
||||
|
||||
// colorScheme 2: Grayscale
|
||||
// colorScheme 3: Inverse Grayscale
|
||||
for (int i = 0; i < gradientSteps; i++) {
|
||||
float r, g, b;
|
||||
float value = float(i) / gradientSteps;
|
||||
@ -743,12 +743,12 @@ void AColor::PreComputeGradient() {
|
||||
b = 1.0f;
|
||||
break;
|
||||
}
|
||||
gradient_pre[selected][2][i][0] = (unsigned char)(255 * r);
|
||||
gradient_pre[selected][2][i][1] = (unsigned char)(255 * g);
|
||||
gradient_pre[selected][2][i][2] = (unsigned char)(255 * b);
|
||||
gradient_pre[selected][3][i][0] = (unsigned char)(255 * r);
|
||||
gradient_pre[selected][3][i][1] = (unsigned char)(255 * g);
|
||||
gradient_pre[selected][3][i][2] = (unsigned char)(255 * b);
|
||||
}
|
||||
|
||||
// colorScheme 3: Inv. Grayscale (=Old grayscale)
|
||||
// colorScheme 2: Grayscale (=Old grayscale)
|
||||
for (int i = 0; i<gradientSteps; i++) {
|
||||
float r, g, b;
|
||||
float value = float(i)/gradientSteps;
|
||||
@ -781,9 +781,9 @@ void AColor::PreComputeGradient() {
|
||||
b = 1.0f;
|
||||
break;
|
||||
}
|
||||
gradient_pre[selected][3][i][0] = (unsigned char) (255 * r);
|
||||
gradient_pre[selected][3][i][1] = (unsigned char) (255 * g);
|
||||
gradient_pre[selected][3][i][2] = (unsigned char) (255 * b);
|
||||
gradient_pre[selected][2][i][0] = (unsigned char) (255 * r);
|
||||
gradient_pre[selected][2][i][1] = (unsigned char) (255 * g);
|
||||
gradient_pre[selected][2][i][2] = (unsigned char) (255 * b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,10 @@ hold information about one contributor to Audacity.
|
||||
#define REV_IDENT (XO("No revision identifier was provided").Translation())
|
||||
#endif
|
||||
|
||||
#if defined(HAS_SENTRY_REPORTING) || defined(HAVE_UPDATES_CHECK) || defined(USE_BREAKPAD)
|
||||
#define HAS_PRIVACY_POLICY
|
||||
#endif
|
||||
|
||||
// To substitute into many other translatable strings
|
||||
static const auto ProgramName =
|
||||
//XO("Audacity");
|
||||
@ -129,7 +133,11 @@ void AboutDialog::CreateCreditsList()
|
||||
|
||||
// The Audacity Team: developers and support
|
||||
AddCredit(wxT("Anton Gerasimov"), developerFormat, roleTeamMember);
|
||||
AddCredit(wxT("Jouni Helminen"), roleTeamMember);
|
||||
AddCredit(wxT("Peter Jonas"), developerFormat, roleTeamMember);
|
||||
AddCredit(wxT("Martin Keary"), roleTeamMember);
|
||||
AddCredit(wxT("Paul Licameli"), developerFormat, roleTeamMember);
|
||||
AddCredit(wxT("Anita Sudan"), roleTeamMember);
|
||||
AddCredit(wxT("Vitaly Sverchinsky"), developerFormat, roleTeamMember);
|
||||
AddCredit(wxT("Dmitry Vedenko"), developerFormat, roleTeamMember);
|
||||
|
||||
@ -825,7 +833,11 @@ void AboutDialog::PopulateInformationPage( ShuttleGui & S )
|
||||
|
||||
void AboutDialog::PopulateLicensePage( ShuttleGui & S )
|
||||
{
|
||||
S.StartNotebookPage( XO("GPL License") );
|
||||
#if defined(HAS_PRIVACY_POLICY)
|
||||
S.StartNotebookPage(XC("Legal", "about dialog"));
|
||||
#else
|
||||
S.StartNotebookPage(XO("GPL License"));
|
||||
#endif
|
||||
S.StartVerticalLay(1);
|
||||
HtmlWindow *html = safenew LinkingHtmlWindow(S.GetParent(), -1,
|
||||
wxDefaultPosition,
|
||||
@ -838,7 +850,9 @@ void AboutDialog::PopulateLicensePage( ShuttleGui & S )
|
||||
// better proportionally spaced.
|
||||
//
|
||||
// The GPL is not to be translated....
|
||||
wxString PageText= FormatHtmlText(
|
||||
|
||||
|
||||
constexpr auto GPL_TEXT =
|
||||
wxT(" <center>GNU GENERAL PUBLIC LICENSE\n</center>")
|
||||
wxT(" <center>Version 2, June 1991\n</center>")
|
||||
wxT("<p><p>")
|
||||
@ -1120,7 +1134,37 @@ wxT("OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n
|
||||
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"));
|
||||
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 );
|
||||
|
||||
|
@ -241,7 +241,7 @@ void PopulatePreferences()
|
||||
wxYES_NO, NULL);
|
||||
if (action == wxYES) // reset
|
||||
{
|
||||
gPrefs->DeleteAll();
|
||||
ResetPreferences();
|
||||
writeLang = true;
|
||||
}
|
||||
}
|
||||
|
@ -1823,6 +1823,44 @@ int AudioIO::StartStream(const TransportTracks &tracks,
|
||||
return mStreamToken;
|
||||
}
|
||||
|
||||
void AudioIO::DelayActions(bool recording)
|
||||
{
|
||||
mDelayingActions = recording;
|
||||
}
|
||||
|
||||
bool AudioIO::DelayingActions() const
|
||||
{
|
||||
return mDelayingActions || (mPortStreamV19 && mNumCaptureChannels > 0);
|
||||
}
|
||||
|
||||
void AudioIO::CallAfterRecording(PostRecordingAction action)
|
||||
{
|
||||
if (!action)
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
|
||||
if (mPostRecordingAction) {
|
||||
// Enqueue it, even if perhaps not still recording,
|
||||
// but it wasn't cleared yet
|
||||
mPostRecordingAction = [
|
||||
prevAction = std::move(mPostRecordingAction),
|
||||
nextAction = std::move(action)
|
||||
]{ prevAction(); nextAction(); };
|
||||
return;
|
||||
}
|
||||
else if (DelayingActions()) {
|
||||
mPostRecordingAction = std::move(action);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't delay it except until idle time.
|
||||
// (Recording might start between now and then, but won't go far before
|
||||
// the action is done. So the system isn't bulletproof yet.)
|
||||
wxTheApp->CallAfter(std::move(action));
|
||||
}
|
||||
|
||||
bool AudioIO::AllocateBuffers(
|
||||
const AudioIOStartStreamOptions &options,
|
||||
const TransportTracks &tracks, double t0, double t1, double sampleRate,
|
||||
@ -2416,6 +2454,21 @@ void AudioIO::StopStream()
|
||||
if (pListener && mNumCaptureChannels > 0)
|
||||
pListener->OnAudioIOStopRecording();
|
||||
|
||||
wxTheApp->CallAfter([this]{
|
||||
if (mPortStreamV19 && mNumCaptureChannels > 0)
|
||||
// Recording was restarted between StopStream and idle time
|
||||
// So the actions can keep waiting
|
||||
return;
|
||||
// In case some other thread was waiting on the mutex too:
|
||||
std::this_thread::yield();
|
||||
std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
|
||||
if (mPostRecordingAction) {
|
||||
mPostRecordingAction();
|
||||
mPostRecordingAction = {};
|
||||
}
|
||||
DelayActions(false);
|
||||
});
|
||||
|
||||
//
|
||||
// Only set token to 0 after we're totally finished with everything
|
||||
//
|
||||
|
@ -18,9 +18,9 @@
|
||||
#include "AudioIOBase.h" // to inherit
|
||||
#include "PlaybackSchedule.h" // member variable
|
||||
|
||||
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <wx/atomic.h> // member variable
|
||||
|
||||
@ -621,6 +621,12 @@ public:
|
||||
* by the specified amount from where it is now */
|
||||
void SeekStream(double seconds) { mSeek = seconds; }
|
||||
|
||||
using PostRecordingAction = std::function<void()>;
|
||||
|
||||
//! Enqueue action for main thread idle time, not before the end of any recording in progress
|
||||
/*! This may be called from non-main threads */
|
||||
void CallAfterRecording(PostRecordingAction action);
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||
bool IsScrubbing() const { return IsBusy() && mScrubState != 0; }
|
||||
|
||||
@ -733,15 +739,18 @@ public:
|
||||
static void Init();
|
||||
static void Deinit();
|
||||
|
||||
|
||||
/*! For purposes of CallAfterRecording, treat time from now as if
|
||||
recording (when argument is true) or not necessarily so (false) */
|
||||
void DelayActions(bool recording);
|
||||
|
||||
private:
|
||||
|
||||
bool DelayingActions() const;
|
||||
|
||||
/** \brief Set the current VU meters - this should be done once after
|
||||
* each call to StartStream currently */
|
||||
void SetMeters();
|
||||
|
||||
|
||||
/** \brief Opens the portaudio stream(s) used to do playback or recording
|
||||
* (or both) through.
|
||||
*
|
||||
@ -787,6 +796,10 @@ private:
|
||||
*
|
||||
* If bOnlyBuffers is specified, it only cleans up the buffers. */
|
||||
void StartStreamCleanup(bool bOnlyBuffers = false);
|
||||
|
||||
std::mutex mPostRecordingActionMutex;
|
||||
PostRecordingAction mPostRecordingAction;
|
||||
bool mDelayingActions{ false };
|
||||
};
|
||||
|
||||
static constexpr unsigned ScrubPollInterval_ms = 50;
|
||||
|
@ -1055,7 +1055,7 @@ list( APPEND DEFINES
|
||||
__STDC_CONSTANT_MACROS
|
||||
STRICT
|
||||
>
|
||||
$<$<BOOL:${USE_UPDATE_CHECK}>:
|
||||
$<$<BOOL:${${_OPT}has_updates_check}>:
|
||||
HAVE_UPDATES_CHECK
|
||||
>
|
||||
)
|
||||
@ -1375,6 +1375,7 @@ if( "${CMAKE_GENERATOR}" MATCHES "Xcode|Visual Studio*" )
|
||||
USE_SOURCE_PERMISSIONS
|
||||
PATTERN "*.pdb" EXCLUDE
|
||||
PATTERN "*.ilk" EXCLUDE
|
||||
PATTERN "*.dSYM" EXCLUDE
|
||||
)
|
||||
else()
|
||||
if( CMAKE_SYSTEM_NAME MATCHES "Darwin" )
|
||||
|
@ -886,6 +886,9 @@ void PluginManager::Load()
|
||||
if (!registry.HasGroup(REGROOT))
|
||||
{
|
||||
// Must start over
|
||||
// This DeleteAll affects pluginregistry.cfg only, not audacity.cfg
|
||||
// That is, the memory of on/off states of effect (and generator,
|
||||
// analyzer, and tool) plug-ins
|
||||
registry.DeleteAll();
|
||||
registry.Flush();
|
||||
return;
|
||||
@ -1223,7 +1226,7 @@ void PluginManager::Save()
|
||||
{}, {}, FileNames::PluginRegistry());
|
||||
auto ®istry = *pRegistry;
|
||||
|
||||
// Clear it out
|
||||
// Clear pluginregistry.cfg (not audacity.cfg)
|
||||
registry.DeleteAll();
|
||||
|
||||
// Write the version string
|
||||
|
@ -63,6 +63,9 @@
|
||||
#include "MemoryX.h"
|
||||
#include <memory>
|
||||
|
||||
BoolSetting DefaultUpdatesCheckingFlag{
|
||||
L"/Update/DefaultUpdatesChecking", true };
|
||||
|
||||
std::unique_ptr<FileConfig> ugPrefs {};
|
||||
|
||||
FileConfig *gPrefs = nullptr;
|
||||
@ -179,6 +182,25 @@ void InitPreferences( std::unique_ptr<FileConfig> uPrefs )
|
||||
wxConfigBase::Set(gPrefs);
|
||||
}
|
||||
|
||||
void ResetPreferences()
|
||||
{
|
||||
// Future: make this a static registry table, so the settings objects
|
||||
// don't need to be defined in this source code file to avoid dependency
|
||||
// cycles
|
||||
std::pair<BoolSetting &, bool> stickyBoolSettings[] {
|
||||
{DefaultUpdatesCheckingFlag, 0},
|
||||
// ... others?
|
||||
};
|
||||
for (auto &pair : stickyBoolSettings)
|
||||
pair.second = pair.first.Read();
|
||||
|
||||
bool savedValue = DefaultUpdatesCheckingFlag.Read();
|
||||
gPrefs->DeleteAll();
|
||||
|
||||
for (auto &pair : stickyBoolSettings)
|
||||
pair.first.Write(pair.second);
|
||||
}
|
||||
|
||||
void FinishPreferences()
|
||||
{
|
||||
if (gPrefs) {
|
||||
|
@ -48,6 +48,12 @@
|
||||
class wxFileName;
|
||||
|
||||
void InitPreferences( std::unique_ptr<FileConfig> uPrefs );
|
||||
//! Call this to reset preferences to an (almost)-"new" default state
|
||||
/*!
|
||||
There is at least one exception to that: user preferences we want to make
|
||||
more "sticky." Notably, whether automatic update checking is preferred.
|
||||
*/
|
||||
void ResetPreferences();
|
||||
void FinishPreferences();
|
||||
|
||||
extern AUDACITY_DLL_API FileConfig *gPrefs;
|
||||
@ -423,4 +429,7 @@ struct AUDACITY_DLL_API PreferenceInitializer {
|
||||
static void ReinitializeAll();
|
||||
};
|
||||
|
||||
// Special extra-sticky settings
|
||||
extern AUDACITY_DLL_API BoolSetting DefaultUpdatesCheckingFlag;
|
||||
|
||||
#endif
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <wx/timer.h>
|
||||
#include <wx/dynlib.h> //<! For windows.h
|
||||
|
||||
#include "AudioIO.h"
|
||||
#include "ShuttleGui.h"
|
||||
#include "ProjectAudioManager.h"
|
||||
#include "ProjectFileIO.h"
|
||||
@ -481,58 +482,64 @@ int TimerRecordDialog::RunWaitDialog()
|
||||
{
|
||||
auto updateResult = ProgressResult::Success;
|
||||
|
||||
if (m_DateTime_Start > wxDateTime::UNow())
|
||||
updateResult = this->WaitForStart();
|
||||
const auto gAudioIO = AudioIO::Get();
|
||||
gAudioIO->DelayActions(true);
|
||||
{
|
||||
auto cleanup = finally([gAudioIO]{ gAudioIO->DelayActions(false); });
|
||||
|
||||
if (updateResult != ProgressResult::Success) {
|
||||
// Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
|
||||
return POST_TIMER_RECORD_CANCEL_WAIT;
|
||||
} else {
|
||||
// Record for specified time.
|
||||
ProjectAudioManager::Get( mProject ).OnRecord(false);
|
||||
bool bIsRecording = true;
|
||||
if (m_DateTime_Start > wxDateTime::UNow())
|
||||
updateResult = this->WaitForStart();
|
||||
|
||||
auto sPostAction = Verbatim(
|
||||
m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
|
||||
if (updateResult != ProgressResult::Success) {
|
||||
// Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
|
||||
return POST_TIMER_RECORD_CANCEL_WAIT;
|
||||
} else {
|
||||
// Record for specified time.
|
||||
ProjectAudioManager::Get( mProject ).OnRecord(false);
|
||||
bool bIsRecording = true;
|
||||
|
||||
// Two column layout.
|
||||
TimerProgressDialog::MessageTable columns{
|
||||
{
|
||||
XO("Recording start:") ,
|
||||
XO("Duration:") ,
|
||||
XO("Recording end:") ,
|
||||
{} ,
|
||||
XO("Automatic Save enabled:") ,
|
||||
XO("Automatic Export enabled:") ,
|
||||
XO("Action after Timer Recording:") ,
|
||||
},
|
||||
{
|
||||
GetDisplayDate(m_DateTime_Start) ,
|
||||
Verbatim( m_TimeSpan_Duration.Format() ),
|
||||
GetDisplayDate(m_DateTime_End) ,
|
||||
{} ,
|
||||
(m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
|
||||
(m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
|
||||
sPostAction ,
|
||||
auto sPostAction = Verbatim(
|
||||
m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
|
||||
|
||||
// Two column layout.
|
||||
TimerProgressDialog::MessageTable columns{
|
||||
{
|
||||
XO("Recording start:") ,
|
||||
XO("Duration:") ,
|
||||
XO("Recording end:") ,
|
||||
{} ,
|
||||
XO("Automatic Save enabled:") ,
|
||||
XO("Automatic Export enabled:") ,
|
||||
XO("Action after Timer Recording:") ,
|
||||
},
|
||||
{
|
||||
GetDisplayDate(m_DateTime_Start) ,
|
||||
Verbatim( m_TimeSpan_Duration.Format() ),
|
||||
GetDisplayDate(m_DateTime_End) ,
|
||||
{} ,
|
||||
(m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
|
||||
(m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
|
||||
sPostAction ,
|
||||
}
|
||||
};
|
||||
|
||||
TimerProgressDialog
|
||||
progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
|
||||
XO("Audacity Timer Record Progress"),
|
||||
columns,
|
||||
pdlgHideCancelButton | pdlgConfirmStopCancel);
|
||||
|
||||
// Make sure that start and end time are updated, so we always get the full
|
||||
// duration, even if there's some delay getting here.
|
||||
wxTimerEvent dummyTimerEvent;
|
||||
this->OnTimer(dummyTimerEvent);
|
||||
|
||||
// Loop for progress display during recording.
|
||||
while (bIsRecording && (updateResult == ProgressResult::Success)) {
|
||||
updateResult = progress.UpdateProgress();
|
||||
wxMilliSleep(kTimerInterval);
|
||||
bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy...
|
||||
}
|
||||
};
|
||||
|
||||
TimerProgressDialog
|
||||
progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
|
||||
XO("Audacity Timer Record Progress"),
|
||||
columns,
|
||||
pdlgHideCancelButton | pdlgConfirmStopCancel);
|
||||
|
||||
// Make sure that start and end time are updated, so we always get the full
|
||||
// duration, even if there's some delay getting here.
|
||||
wxTimerEvent dummyTimerEvent;
|
||||
this->OnTimer(dummyTimerEvent);
|
||||
|
||||
// Loop for progress display during recording.
|
||||
while (bIsRecording && (updateResult == ProgressResult::Success)) {
|
||||
updateResult = progress.UpdateProgress();
|
||||
wxMilliSleep(kTimerInterval);
|
||||
bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy...
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -648,6 +648,7 @@ wxString EffectManager::GetPreset(const PluginID & ID, const wxString & params,
|
||||
return preset;
|
||||
}
|
||||
|
||||
// This cleans a config "file" backed by a string in memory.
|
||||
eap.DeleteAll();
|
||||
|
||||
eap.Write(wxT("Use Preset"), preset);
|
||||
|
@ -106,17 +106,40 @@
|
||||
|
||||
return intgr ;
|
||||
}
|
||||
#elif (defined (WIN32) || defined (_WIN32)) && defined(_M_3X64)
|
||||
#elif (defined (WIN32) || defined (_WIN32)) && defined(_M_X64)
|
||||
|
||||
#include <math.h>
|
||||
#include <immintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
__inline long int
|
||||
lrintf (float flt)
|
||||
#ifdef _MSC_VER
|
||||
#pragma function(lrint, lrintf)
|
||||
#endif
|
||||
|
||||
__inline
|
||||
long int lrint(double flt)
|
||||
{
|
||||
return _mm_cvt_ss2si(_mm_set_ss(flt));
|
||||
return _mm_cvtsd_si32(_mm_set_sd(flt));
|
||||
}
|
||||
|
||||
__inline
|
||||
long int lrintf (float flt)
|
||||
{
|
||||
return _mm_cvtss_si32(_mm_set_ss(flt));
|
||||
}
|
||||
|
||||
__inline
|
||||
long long int llrint(double flt)
|
||||
{
|
||||
return _mm_cvtsd_si64(_mm_set_sd(flt));
|
||||
}
|
||||
|
||||
__inline
|
||||
long long int llrintf(float flt)
|
||||
{
|
||||
return _mm_cvtss_si64(_mm_set_ss(flt));
|
||||
}
|
||||
|
||||
#elif (HAVE_LRINT && HAVE_LRINTF)
|
||||
|
||||
/* These defines enable functionality introduced with the 1999 ISO C
|
||||
@ -146,6 +169,6 @@
|
||||
#include <math.h>
|
||||
|
||||
#define lrint(dbl) ((int)rint(dbl))
|
||||
#define lrintf(flt) ((int)rint(flt))
|
||||
#define lrintf(flt) ((int)rint(flt))
|
||||
|
||||
#endif
|
||||
|
@ -393,7 +393,7 @@ void OnResetConfig(const CommandContext &context)
|
||||
menuManager.mLastAnalyzer = "";
|
||||
menuManager.mLastTool = "";
|
||||
|
||||
gPrefs->DeleteAll();
|
||||
ResetPreferences();
|
||||
|
||||
// Directory will be reset on next restart.
|
||||
FileNames::UpdateDefaultPath(FileNames::Operation::Temp, TempDirectory::DefaultTempDir());
|
||||
|
@ -70,8 +70,7 @@ void ApplicationPrefs::PopulateOrExchange(ShuttleGui & S)
|
||||
|
||||
S.StartStatic(XO("Update Audacity"));
|
||||
{
|
||||
S.TieCheckBox(XO("&Check for Updates"),
|
||||
UpdatesCheckingSettings::DefaultUpdatesCheckingFlag);
|
||||
S.TieCheckBox(XO("&Check for Updates"), DefaultUpdatesCheckingFlag);
|
||||
}
|
||||
S.EndStatic();
|
||||
S.EndScroller();
|
||||
|
@ -182,7 +182,7 @@ void SpectrogramSettings::ColorSchemeEnumSetting::Migrate(wxString &value)
|
||||
// Migrate old grayscale option to Color scheme choice
|
||||
bool isGrayscale = (gPrefs->Read(wxT("/Spectrum/Grayscale"), 0L) != 0);
|
||||
if (isGrayscale && !gPrefs->Read(wxT("/Spectrum/ColorScheme"), &value)) {
|
||||
value = GetColorSchemeNames().at(csInvGrayscale).Internal();
|
||||
value = GetColorSchemeNames().at(csGrayscale).Internal();
|
||||
Write(value);
|
||||
gPrefs->Flush();
|
||||
}
|
||||
|
@ -56,23 +56,41 @@ bool UpdateDataParser::HandleXMLTag(const wxChar* tag, const wxChar** attrs)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kWindowsTag]) == 0)
|
||||
const wxPlatformInfo& info = wxPlatformInfo::Get();
|
||||
|
||||
constexpr bool is32Bit = sizeof(void*) == 4;
|
||||
constexpr bool is64Bit = sizeof(void*) == 8;
|
||||
|
||||
if (is32Bit)
|
||||
{
|
||||
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
return true;
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kWin32Tag]) == 0)
|
||||
{
|
||||
if (info.GetOperatingSystemId() & wxOS_WINDOWS)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is64Bit)
|
||||
{
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kWin64Tag]) == 0)
|
||||
{
|
||||
if (info.GetOperatingSystemId() & wxOS_WINDOWS)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kMacosTag]) == 0)
|
||||
{
|
||||
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_MAC)
|
||||
if (info.GetOperatingSystemId() & wxOS_MAC)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kLinuxTag]) == 0)
|
||||
{
|
||||
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_UNIX_LINUX)
|
||||
if (info.GetOperatingSystemId() & wxOS_UNIX_LINUX)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
return true;
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ private:
|
||||
kUpdateTag,
|
||||
kDescriptionTag,
|
||||
kOsTag,
|
||||
kWindowsTag,
|
||||
kWin32Tag,
|
||||
kWin64Tag,
|
||||
kMacosTag,
|
||||
kLinuxTag,
|
||||
kVersionTag,
|
||||
@ -48,7 +49,8 @@ private:
|
||||
{ XmlParsedTags::kUpdateTag, wxT("Updates") },
|
||||
{ XmlParsedTags::kDescriptionTag, wxT("Description") },
|
||||
{ XmlParsedTags::kOsTag, wxT("OS") },
|
||||
{ XmlParsedTags::kWindowsTag, wxT("Windows") },
|
||||
{ XmlParsedTags::kWin32Tag, wxT("Win32") },
|
||||
{ XmlParsedTags::kWin64Tag, wxT("Win64") },
|
||||
{ XmlParsedTags::kMacosTag, wxT("Macos") },
|
||||
{ XmlParsedTags::kLinuxTag, wxT("Linux") },
|
||||
{ XmlParsedTags::kVersionTag, wxT("Version") },
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "UpdateManager.h"
|
||||
#include "UpdatePopupDialog.h"
|
||||
|
||||
#include "AudioIO.h"
|
||||
#include "NetworkManager.h"
|
||||
#include "IResponse.h"
|
||||
#include "Request.h"
|
||||
@ -21,26 +22,23 @@
|
||||
#include <wx/frame.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
BoolSetting UpdatesCheckingSettings::DefaultUpdatesCheckingFlag{
|
||||
L"/Update/DefaultUpdatesChecking", true };
|
||||
#include <cstdint>
|
||||
|
||||
static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime";
|
||||
|
||||
|
||||
using Clock = std::chrono::system_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
using Duration = TimePoint::duration;
|
||||
|
||||
constexpr Duration updatesCheckInterval = std::chrono::hours(12);
|
||||
|
||||
enum { ID_TIMER = wxID_HIGHEST + 1 };
|
||||
|
||||
BEGIN_EVENT_TABLE(UpdateManager, wxEvtHandler)
|
||||
EVT_TIMER(ID_TIMER, UpdateManager::OnTimer)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
UpdateManager::UpdateManager()
|
||||
: mUpdateCheckingInterval(
|
||||
std::chrono::milliseconds(std::chrono::hours(12)).count())
|
||||
{}
|
||||
|
||||
UpdateManager::~UpdateManager()
|
||||
{}
|
||||
|
||||
UpdateManager& UpdateManager::GetInstance()
|
||||
{
|
||||
static UpdateManager updateManager;
|
||||
@ -64,38 +62,48 @@ VersionPatch UpdateManager::GetVersionPatch() const
|
||||
return mVersionPatch;
|
||||
}
|
||||
|
||||
void UpdateManager::GetUpdates()
|
||||
void UpdateManager::GetUpdates(bool ignoreNetworkErrors)
|
||||
{
|
||||
const audacity::network_manager::Request request("https://updates.audacityteam.org/feed/latest.xml");
|
||||
auto response = audacity::network_manager::NetworkManager::GetInstance().doGet(request);
|
||||
|
||||
response->setRequestFinishedCallback([response, this](audacity::network_manager::IResponse*) {
|
||||
response->setRequestFinishedCallback([response, ignoreNetworkErrors, this](audacity::network_manager::IResponse*) {
|
||||
|
||||
auto gAudioIO = AudioIO::Get();
|
||||
if (response->getError() != audacity::network_manager::NetworkError::NoError)
|
||||
{
|
||||
wxTheApp->CallAfter([] {ShowExceptionDialog(nullptr,
|
||||
XC("Error checking for update", "update dialog"),
|
||||
XC("Unable to connect to Audacity update server.", "update dialog"),
|
||||
wxString());
|
||||
});
|
||||
|
||||
return;
|
||||
if (!ignoreNetworkErrors)
|
||||
{
|
||||
gAudioIO->CallAfterRecording([] {
|
||||
ShowExceptionDialog(
|
||||
nullptr, XC("Error checking for update", "update dialog"),
|
||||
XC("Unable to connect to Audacity update server.",
|
||||
"update dialog"),
|
||||
wxString());
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mUpdateDataParser.Parse(response->readAll<VersionPatch::UpdateDataFormat>(), &mVersionPatch))
|
||||
{
|
||||
wxTheApp->CallAfter([] {ShowExceptionDialog(nullptr,
|
||||
XC("Error checking for update", "update dialog"),
|
||||
XC("Update data was corrupted.", "update dialog"),
|
||||
wxString());
|
||||
});
|
||||
|
||||
return;
|
||||
if (!ignoreNetworkErrors)
|
||||
{
|
||||
gAudioIO->CallAfterRecording([] {
|
||||
ShowExceptionDialog(
|
||||
nullptr, XC("Error checking for update", "update dialog"),
|
||||
XC("Update data was corrupted.", "update dialog"),
|
||||
wxString());
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mVersionPatch.version > CurrentBuildVersion())
|
||||
{
|
||||
wxTheApp->CallAfter([this] {
|
||||
gAudioIO->CallAfterRecording([this] {
|
||||
UpdatePopupDialog dlg(nullptr, mVersionPatch);
|
||||
const int code = dlg.ShowModal();
|
||||
|
||||
@ -109,41 +117,56 @@ void UpdateManager::GetUpdates()
|
||||
wxString());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateManager::OnTimer(wxTimerEvent& WXUNUSED(event))
|
||||
{
|
||||
bool updatesCheckingEnabled = UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Read();
|
||||
bool updatesCheckingEnabled = DefaultUpdatesCheckingFlag.Read();
|
||||
|
||||
if (updatesCheckingEnabled && IsTimeForUpdatesChecking())
|
||||
GetUpdates();
|
||||
GetUpdates(true);
|
||||
|
||||
mTimer.StartOnce(mUpdateCheckingInterval);
|
||||
mTimer.StartOnce(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
updatesCheckInterval)
|
||||
.count());
|
||||
}
|
||||
|
||||
bool UpdateManager::IsTimeForUpdatesChecking()
|
||||
{
|
||||
long long nextUpdatesCheckingTime = std::stoll(
|
||||
gPrefs->Read(prefsUpdateScheduledTime, "0").ToStdString());
|
||||
// We use atoll here, so there is no need to handle the exception,
|
||||
// if prefsUpdateScheduledTime is corrupted.
|
||||
// atoll will return 0 on failure, which suits us well.
|
||||
const TimePoint nextUpdatesCheckingTime(std::chrono::milliseconds(
|
||||
atoll(gPrefs->Read(prefsUpdateScheduledTime, "0").c_str())));
|
||||
|
||||
// Get current time in milliseconds
|
||||
auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now());
|
||||
|
||||
auto currentTimeInMillisec = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now_ms.time_since_epoch()).count();
|
||||
// Get current time
|
||||
const TimePoint currentTime = Clock::now();
|
||||
|
||||
// If next update time 0 or less then current time -> show update dialog,
|
||||
// else this condition allow us to avoid from duplicating update notifications.
|
||||
if (nextUpdatesCheckingTime < currentTimeInMillisec)
|
||||
if (nextUpdatesCheckingTime < currentTime)
|
||||
{
|
||||
nextUpdatesCheckingTime = currentTimeInMillisec + mUpdateCheckingInterval;
|
||||
|
||||
gPrefs->Write(prefsUpdateScheduledTime,
|
||||
wxString(std::to_string(nextUpdatesCheckingTime)));
|
||||
// Round down the nextUpdatesChecking time to a day.
|
||||
// This is required to ensure, that update is
|
||||
// checked daily
|
||||
using DayDuration =
|
||||
std::chrono::duration<int32_t, std::ratio<60 * 60 * 24>>;
|
||||
|
||||
const auto postponeUpdateUntil =
|
||||
std::chrono::time_point_cast<DayDuration>(
|
||||
currentTime) + DayDuration(1);
|
||||
|
||||
const std::chrono::milliseconds postponeUpdateUntilMS(
|
||||
postponeUpdateUntil.time_since_epoch());
|
||||
|
||||
gPrefs->Write(
|
||||
prefsUpdateScheduledTime,
|
||||
wxString(std::to_string(postponeUpdateUntilMS.count())));
|
||||
|
||||
gPrefs->Flush();
|
||||
|
||||
return true;
|
||||
|
@ -18,10 +18,6 @@
|
||||
#include <wx/event.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
namespace UpdatesCheckingSettings {
|
||||
extern AUDACITY_DLL_API BoolSetting DefaultUpdatesCheckingFlag;
|
||||
}
|
||||
|
||||
/// A class that managing of updates.
|
||||
/**
|
||||
Opt-in request and show update dialog by the scheduled time.
|
||||
@ -31,13 +27,12 @@ namespace UpdatesCheckingSettings {
|
||||
class UpdateManager final : public wxEvtHandler
|
||||
{
|
||||
public:
|
||||
UpdateManager();
|
||||
~UpdateManager();
|
||||
UpdateManager() = default;
|
||||
|
||||
static UpdateManager& GetInstance();
|
||||
static void Start();
|
||||
|
||||
void GetUpdates();
|
||||
void GetUpdates(bool ignoreNetworkErrors);
|
||||
|
||||
VersionPatch GetVersionPatch() const;
|
||||
|
||||
@ -46,7 +41,6 @@ private:
|
||||
VersionPatch mVersionPatch;
|
||||
|
||||
wxTimer mTimer;
|
||||
const int mUpdateCheckingInterval;
|
||||
|
||||
void OnTimer(wxTimerEvent& event);
|
||||
|
||||
|
@ -45,7 +45,7 @@ UpdatePopupDialog::UpdatePopupDialog (wxWindow* parent, const VersionPatch& vers
|
||||
|
||||
S.Id (DontShowID).AddCheckBox (
|
||||
XO ("Don't show this again at start up"),
|
||||
!UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Read());
|
||||
!DefaultUpdatesCheckingFlag.Read());
|
||||
|
||||
S.Prop(1).AddSpace(1, 0, 1);
|
||||
|
||||
@ -80,7 +80,7 @@ void UpdatePopupDialog::OnSkip (wxCommandEvent&)
|
||||
|
||||
void UpdatePopupDialog::OnDontShow (wxCommandEvent& event)
|
||||
{
|
||||
UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Write(!event.IsChecked());
|
||||
DefaultUpdatesCheckingFlag.Write(!event.IsChecked());
|
||||
}
|
||||
|
||||
HtmlWindow* UpdatePopupDialog::AddHtmlContent (wxWindow* parent)
|
||||
|
@ -12,10 +12,10 @@
|
||||
|
||||
if( BUILDING_64_BIT )
|
||||
set( INSTALLER_SUFFIX "x64" )
|
||||
set( INSTALLER_X64_MODE "ArchitecturesInstallIn64BitMode=x64")
|
||||
set( INSTALLER_X64_MODE "ArchitecturesInstallIn64BitMode=x64" )
|
||||
else()
|
||||
set( INSTALLER_SUFFIX "x86" )
|
||||
set( INSTALLER_X64_MODE "ArchitecturesInstallIn64BitMode=x64")
|
||||
set( INSTALLER_X64_MODE "" )
|
||||
endif()
|
||||
|
||||
if( SIGN )
|
||||
|
@ -96,7 +96,7 @@ Source: ".\FirstTimeModel.ini"; DestDir: "{app}"; DestName: "FirstTime.ini"; Per
|
||||
Source: "Additional\README.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
Source: "Additional\LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#AppExe}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "Package\*.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Manual, which should be got from the manual wiki using ..\scripts\mw2html_audacity\wiki2htm.bat
|
||||
@MANUAL@
|
||||
|
Loading…
x
Reference in New Issue
Block a user