diff --git a/reaper-adm-extension/src/reaper_adm/CMakeLists.txt b/reaper-adm-extension/src/reaper_adm/CMakeLists.txt index f30f167a7..23078fbb6 100644 --- a/reaper-adm-extension/src/reaper_adm/CMakeLists.txt +++ b/reaper-adm-extension/src/reaper_adm/CMakeLists.txt @@ -8,6 +8,7 @@ find_package(JUCE REQUIRED QUIET) set(EXTENSION_SOURCES ${EPS_SHARED_DIR}/helper/common_definition_helper.cpp + ${EPS_SHARED_DIR}/update_check_settings_file.cpp actionmanager.cpp admchannel.cpp @@ -84,9 +85,9 @@ set(EXTENSION_HEADERS ${EPS_SHARED_DIR}/helper/resource_paths.hpp ${EPS_SHARED_DIR}/helper/native_message_box.hpp ${EPS_SHARED_DIR}/daw_channel_count.h + ${EPS_SHARED_DIR}/update_check_settings_file.h AppConfig.h - juce_modules.h actionmanager.h admchannel.h @@ -197,12 +198,13 @@ find_package(WDL REQUIRED QUIET) target_include_directories(reaper_adm_object PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} $ $ $ $ $ - ${EPS_SHARED_DIR} + ${EPS_SHARED_DIR} ) target_link_libraries(reaper_adm_dependencies @@ -214,9 +216,9 @@ target_link_libraries(reaper_adm_dependencies Boost::filesystem nng::nng ear-version - Juce::core + Juce::core ) - + add_dependencies(reaper_adm_dependencies reaper_adm_object) set_target_properties(reaper_adm diff --git a/reaper-adm-extension/src/reaper_adm/juce_modules.h b/reaper-adm-extension/src/reaper_adm/juce_modules.h deleted file mode 100644 index 92238132d..000000000 --- a/reaper-adm-extension/src/reaper_adm/juce_modules.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "AppConfig.h" -#include -#include -// Will leave these here in case we want to use any other modules at a later date -// Don't forget to change the associated macro in AppConfig.h -//include -//include -//include -//include -//include -//include -//include -//include -//include -//include diff --git a/reaper-adm-extension/src/reaper_adm/pluginmain.cpp b/reaper-adm-extension/src/reaper_adm/pluginmain.cpp index aa00b5015..5a19edb5d 100644 --- a/reaper-adm-extension/src/reaper_adm/pluginmain.cpp +++ b/reaper-adm-extension/src/reaper_adm/pluginmain.cpp @@ -12,7 +12,7 @@ #include "pluginsuite.h" #include "pluginregistry.h" #include "pluginsuite_ear.h" -#include "update_check.h" +#include #include #include #include diff --git a/reaper-adm-extension/src/reaper_adm/update_check.cpp b/reaper-adm-extension/src/reaper_adm/update_check.cpp index 2954c2a6b..f6d4fe75b 100644 --- a/reaper-adm-extension/src/reaper_adm/update_check.cpp +++ b/reaper-adm-extension/src/reaper_adm/update_check.cpp @@ -1,221 +1,159 @@ -#include "update_check.h" -#include -#include -#include -#include -#include - -UpdateChecker::UpdateChecker() -{ - if (eps::versionInfoAvailable()) { - currentVersion = Version( - eps::versionMajor(), - eps::versionMinor(), - eps::versionRevision()); - } - - settingsFile = ResourcePaths::getSettingsDirectory(true).getChildFile("UpdateCheck.settings"); - if (!settingsFileExists()) { - // No settings file - perhaps first run? - if (saveSettings() && settingsFileExists()) { - // Settings file is writable so probably was just first run. - // Set autocheck on initially. - setAutoCheckEnabled(true); - } - } - loadSettings(); -} - -UpdateChecker::~UpdateChecker() -{ -} - -bool UpdateChecker::getAutoCheckEnabled() -{ - return settingAutoCheckEnabled; -} - -bool UpdateChecker::setAutoCheckEnabled(bool enabled, bool displayConfirmation) -{ - settingAutoCheckEnabled = enabled; - auto success = saveSettings(); - if (displayConfirmation) { - if (success) { - if (enabled) { - displayMessageBox(messageBoxTitles, "Update check will now be performed each time REAPER is started.", MB_ICONINFORMATION); - } - else { - displayMessageBox(messageBoxTitles, "Update check will no longer be performed when REAPER is started.", MB_ICONINFORMATION); - } - } - else { - displayMessageBox(messageBoxTitles, "Error: Failed to save preferences.", MB_ICONEXCLAMATION); - } - } - return success; -} - -void UpdateChecker::doUpdateCheck(bool manualCheck, int timeoutMs) -{ - Version remoteVersion; - std::string remoteVersionStr; - auto success = getRemoteVersion(manualCheck, timeoutMs, remoteVersion, remoteVersionStr); - - if (success) { - if (remoteVersion > currentVersion) { - if (manualCheck || remoteVersion != settingLastReportedVersion) { - // Haven't mentioned this version before or told to always show result - settingLastReportedVersion = remoteVersion; - saveSettings(); - displayUpdateAvailable(remoteVersionStr, manualCheck); - } - } - else if (manualCheck) { - displayUpdateUnavailable(); - } - } -} - -bool UpdateChecker::getRemoteVersion(bool reportErrors, int timeoutMs, Version& version, std::string& versionStr) -{ - std::string body; - auto getSuccess = getHTTPResponseBody(versionJsonUrl, body, timeoutMs); - if (!getSuccess) { - if (reportErrors) { - displayError("Failed to connect to server. Do you have a working internet connection?"); - } - return false; - } - - juce::var j; - auto parseResult = juce::JSON::parse(body, j); - if (parseResult.failed()) { - if (reportErrors) { - displayError("Failed to parse data."); - } - return false; - } - - juce::var varVersionMajor = j.getProperty("version_major", juce::var()); - juce::var varVersionMinor = j.getProperty("version_minor", juce::var()); - juce::var varVersionRevision = j.getProperty("version_revision", juce::var()); - - if (!varVersionMajor.isInt() || - !varVersionMinor.isInt() || - !varVersionRevision.isInt()) { - if (reportErrors) { - displayError("Unexpected data."); - } - return false; - } - - version.major = static_cast (varVersionMajor); - version.minor = static_cast (varVersionMinor); - version.revision = static_cast (varVersionRevision); - - versionStr.clear(); - juce::var varVersionText = j.getProperty("version", juce::var()); - if (varVersionText.isString()) { - versionStr = varVersionText.toString().toStdString(); - } - - return true; -} - -bool UpdateChecker::getHTTPResponseBody(const std::string& url, std::string& responseBody, int timeoutMs) -{ - juce::URL jUrl{ url }; - auto isOpt = juce::URL::InputStreamOptions(juce::URL::ParameterHandling::inAddress).withConnectionTimeoutMs(timeoutMs); - auto is = jUrl.createInputStream(isOpt); - if (is != nullptr) - { - juce::MemoryBlock memoryBlock; - is->readIntoMemoryBlock(memoryBlock); - responseBody = memoryBlock.toString().toStdString(); - return true; - } - return false; -} - -void UpdateChecker::displayError(const std::string& errorText) -{ - std::string text{ "An error occurred whilst checking for updates:\n\n" }; - text += errorText; - displayMessageBox(messageBoxTitles, text, MB_ICONEXCLAMATION); -} - -void UpdateChecker::displayUpdateAvailable(const std::string& versionText, bool instigatedManually) -{ - std::string text; - if (versionText.empty()) { - text = "A new version of the EAR Production Suite is now available."; - } - else { - text = "EAR Production Suite " + versionText + " is now available."; - } - text += "\n\nDownload from https://ear-production-suite.ebu.io/"; - if (!instigatedManually) { - text += "\n\nNo further notifications will appear for this version. You can disable all future notifications through the Extensions menu."; - } - displayMessageBox(messageBoxTitles, text, MB_ICONINFORMATION); -} - -void UpdateChecker::displayUpdateUnavailable() -{ - displayMessageBox(messageBoxTitles, "No updates are currently available.", MB_ICONINFORMATION); -} - -void UpdateChecker::displayMessageBox(const std::string& title, const std::string& text, long winIcon) -{ - // Windows version of Reaper locks up if you try show a message box during splash - NativeMessageBox::splashCompatibleMessage(title.c_str(), text.c_str(), nullptr, winIcon); -} - -bool UpdateChecker::loadSettings() -{ - if (!settingsFileExists()) { - return false; - } - - juce::XmlDocument xml(settingsFile); - auto updateCheckElement = xml.getDocumentElementIfTagMatches("UpdateCheck"); - if (!updateCheckElement) { - return false; - } - - auto lastReportedElement = updateCheckElement->getChildByName("LastReportedVersion"); - if (lastReportedElement) { - settingLastReportedVersion.major = lastReportedElement->getIntAttribute("VersionMajor", 0); - settingLastReportedVersion.minor = lastReportedElement->getIntAttribute("VersionMinor", 0); - settingLastReportedVersion.revision = lastReportedElement->getIntAttribute("VersionRevision", 0); - } - - auto autoCheckElement = updateCheckElement->getChildByName("AutoCheck"); - if (autoCheckElement) { - settingAutoCheckEnabled = autoCheckElement->getBoolAttribute("OnStartUp", false); - } - - return true; -} - -bool UpdateChecker::saveSettings() -{ - auto updateCheckElement = juce::XmlElement("UpdateCheck"); - - auto lastReportedElement = new juce::XmlElement("LastReportedVersion"); - lastReportedElement->setAttribute("VersionMajor", settingLastReportedVersion.major); - lastReportedElement->setAttribute("VersionMinor", settingLastReportedVersion.minor); - lastReportedElement->setAttribute("VersionRevision", settingLastReportedVersion.revision); - updateCheckElement.addChildElement(lastReportedElement); - - auto autoCheckElement = new juce::XmlElement("AutoCheck"); - autoCheckElement->setAttribute("OnStartUp", settingAutoCheckEnabled); - updateCheckElement.addChildElement(autoCheckElement); - - return updateCheckElement.writeTo(settingsFile); -} - -bool UpdateChecker::settingsFileExists() -{ - return settingsFile.existsAsFile(); -} +#include "update_check.h" +#include +#include +#include +#include +#include + +UpdateChecker::UpdateChecker() +{ + if (eps::versionInfoAvailable()) { + currentVersion = Version( + eps::versionMajor(), + eps::versionMinor(), + eps::versionRevision()); + } +} + +UpdateChecker::~UpdateChecker() +{ +} + +bool UpdateChecker::getAutoCheckEnabled() +{ + return settingsFile.getAutoCheckEnabled(); +} + +bool UpdateChecker::setAutoCheckEnabled(bool enabled, bool displayConfirmation) +{ + auto success = settingsFile.setAutoCheckEnabled(enabled); + if (displayConfirmation) { + if (success) { + if (enabled) { + displayMessageBox(messageBoxTitles, "Update check will now be performed each time REAPER is started.", MB_ICONINFORMATION); + } + else { + displayMessageBox(messageBoxTitles, "Update check will no longer be performed when REAPER is started.", MB_ICONINFORMATION); + } + } + else { + displayMessageBox(messageBoxTitles, "Error: Failed to save preferences.", MB_ICONEXCLAMATION); + } + } + return success; +} + +void UpdateChecker::doUpdateCheck(bool manualCheck, int timeoutMs) +{ + Version remoteVersion; + std::string remoteVersionStr; + auto success = getRemoteVersion(manualCheck, timeoutMs, remoteVersion, remoteVersionStr); + + if (success) { + if (remoteVersion > currentVersion) { + if (manualCheck || remoteVersion != settingsFile.getLastReportedVersion()) { + // Haven't mentioned this version before or told to always show result + settingsFile.setLastReportedVersion(remoteVersion); + displayUpdateAvailable(remoteVersionStr, manualCheck); + } + } + else if (manualCheck) { + displayUpdateUnavailable(); + } + } +} + +bool UpdateChecker::getRemoteVersion(bool reportErrors, int timeoutMs, Version& version, std::string& versionStr) +{ + std::string body; + auto getSuccess = getHTTPResponseBody(versionJsonUrl, body, timeoutMs); + if (!getSuccess) { + if (reportErrors) { + displayError("Failed to connect to server. Do you have a working internet connection?"); + } + return false; + } + + juce::var j; + auto parseResult = juce::JSON::parse(body, j); + if (parseResult.failed()) { + if (reportErrors) { + displayError("Failed to parse data."); + } + return false; + } + + juce::var varVersionMajor = j.getProperty("version_major", juce::var()); + juce::var varVersionMinor = j.getProperty("version_minor", juce::var()); + juce::var varVersionRevision = j.getProperty("version_revision", juce::var()); + + if (!varVersionMajor.isInt() || + !varVersionMinor.isInt() || + !varVersionRevision.isInt()) { + if (reportErrors) { + displayError("Unexpected data."); + } + return false; + } + + version.major = static_cast (varVersionMajor); + version.minor = static_cast (varVersionMinor); + version.revision = static_cast (varVersionRevision); + + versionStr.clear(); + juce::var varVersionText = j.getProperty("version", juce::var()); + if (varVersionText.isString()) { + versionStr = varVersionText.toString().toStdString(); + } + + return true; +} + +bool UpdateChecker::getHTTPResponseBody(const std::string& url, std::string& responseBody, int timeoutMs) +{ + juce::URL jUrl{ url }; + auto isOpt = juce::URL::InputStreamOptions(juce::URL::ParameterHandling::inAddress).withConnectionTimeoutMs(timeoutMs); + auto is = jUrl.createInputStream(isOpt); + if (is != nullptr) + { + juce::MemoryBlock memoryBlock; + is->readIntoMemoryBlock(memoryBlock); + responseBody = memoryBlock.toString().toStdString(); + return true; + } + return false; +} + +void UpdateChecker::displayError(const std::string& errorText) +{ + std::string text{ "An error occurred whilst checking for updates:\n\n" }; + text += errorText; + displayMessageBox(messageBoxTitles, text, MB_ICONEXCLAMATION); +} + +void UpdateChecker::displayUpdateAvailable(const std::string& versionText, bool instigatedManually) +{ + std::string text; + if (versionText.empty()) { + text = "A new version of the EAR Production Suite is now available."; + } + else { + text = "EAR Production Suite " + versionText + " is now available."; + } + text += "\n\nDownload from https://ear-production-suite.ebu.io/"; + if (!instigatedManually) { + text += "\n\nNo further notifications will appear for this version. You can disable all future notifications through the Extensions menu."; + } + displayMessageBox(messageBoxTitles, text, MB_ICONINFORMATION); +} + +void UpdateChecker::displayUpdateUnavailable() +{ + displayMessageBox(messageBoxTitles, "No updates are currently available.", MB_ICONINFORMATION); +} + +void UpdateChecker::displayMessageBox(const std::string& title, const std::string& text, long winIcon) +{ + // Windows version of Reaper locks up if you try show a message box during splash + NativeMessageBox::splashCompatibleMessage(title.c_str(), text.c_str(), nullptr, winIcon); +} \ No newline at end of file diff --git a/reaper-adm-extension/src/reaper_adm/update_check.h b/reaper-adm-extension/src/reaper_adm/update_check.h index 1dc5246bf..c332ca488 100644 --- a/reaper-adm-extension/src/reaper_adm/update_check.h +++ b/reaper-adm-extension/src/reaper_adm/update_check.h @@ -1,39 +1,33 @@ -#pragma once -#include -#include "reaperhost.h" -#include "juce_modules.h" -#include - -class UpdateChecker { -public: - UpdateChecker(); - ~UpdateChecker(); - - bool getAutoCheckEnabled(); - bool setAutoCheckEnabled(bool enabled, bool displayConfirmation=false); - - void doUpdateCheck(bool manualCheck=false, int timeoutMs=3000); - -private: - const std::string versionJsonUrl{ "https://ear-production-suite.ebu.io/version_info.json" }; - const std::string messageBoxTitles{ "EAR Production Suite Update" }; - - bool getRemoteVersion(bool reportErrors, int timeoutMs, Version& version, std::string& versionStr); - bool getHTTPResponseBody(const std::string& url, std::string& responseBody, int timeoutMs); - - void displayError(const std::string& errorText); - void displayUpdateAvailable(const std::string& versionText, bool instigatedManually); - void displayUpdateUnavailable(); - void displayMessageBox(const std::string& title, const std::string& text, long winIcon); - - juce::File settingsFile; - bool loadSettings(); - bool saveSettings(); - bool settingsFileExists(); - - bool settingAutoCheckEnabled{ false }; - Version settingLastReportedVersion; - - Version currentVersion; - -}; +#pragma once +#include +#include +#include +#include +#include + +class UpdateChecker { +public: + UpdateChecker(); + ~UpdateChecker(); + + bool getAutoCheckEnabled(); + bool setAutoCheckEnabled(bool enabled, bool displayConfirmation=false); + + void doUpdateCheck(bool manualCheck=false, int timeoutMs=3000); + +private: + const std::string versionJsonUrl{ "https://ear-production-suite.ebu.io/version_info.json" }; + const std::string messageBoxTitles{ "EAR Production Suite Update" }; + + bool getRemoteVersion(bool reportErrors, int timeoutMs, Version& version, std::string& versionStr); + bool getHTTPResponseBody(const std::string& url, std::string& responseBody, int timeoutMs); + + void displayError(const std::string& errorText); + void displayUpdateAvailable(const std::string& versionText, bool instigatedManually); + void displayUpdateUnavailable(); + void displayMessageBox(const std::string& title, const std::string& text, long winIcon); + + UpdateCheckerSettingsFile settingsFile; + Version currentVersion; + +}; diff --git a/shared/helper/native_message_box.hpp b/shared/helper/native_message_box.hpp index fd81f0c24..8d9902187 100644 --- a/shared/helper/native_message_box.hpp +++ b/shared/helper/native_message_box.hpp @@ -5,7 +5,7 @@ #define WIN32_LEAN_AND_MEAN #include #else -#include +#include #endif #include diff --git a/shared/update_check_settings_file.cpp b/shared/update_check_settings_file.cpp new file mode 100644 index 000000000..bdc7dad0b --- /dev/null +++ b/shared/update_check_settings_file.cpp @@ -0,0 +1,108 @@ +#include "update_check_settings_file.h" +#include +#include + +UpdateCheckerSettingsFile::UpdateCheckerSettingsFile() +{ + settingsFile = ResourcePaths::getSettingsDirectory(true).getChildFile("UpdateCheck.settings"); + if (!settingsFileExists()) { + // No settings file - perhaps first run? + if (ensureSettingsFileExists()) { + // Settings file is writable so probably was just first run. + // Set autocheck on initially. + setAutoCheckEnabled(true); + } + } + loadSettings(); +} + +const bool UpdateCheckerSettingsFile::getAutoCheckEnabled() +{ + return settingAutoCheckEnabled; +} + +const bool UpdateCheckerSettingsFile::setAutoCheckEnabled(bool enabled) +{ + settingAutoCheckEnabled = enabled; + return saveSettings(); +} + +const Version UpdateCheckerSettingsFile::getLastReportedVersion() +{ + return settingLastReportedVersion; +} + +const bool UpdateCheckerSettingsFile::setLastReportedVersion(const Version& version) +{ + settingLastReportedVersion = version; + return saveSettings(); +} + + +const bool UpdateCheckerSettingsFile::canRead() +{ + return loadSettings(); +} + +const bool UpdateCheckerSettingsFile::canWrite() +{ + return saveSettings(); +} + +bool UpdateCheckerSettingsFile::loadSettings() +{ + if (!settingsFileExists()) { + return false; + } + + juce::XmlDocument xml(settingsFile); + auto updateCheckElement = xml.getDocumentElementIfTagMatches("UpdateCheck"); + if (!updateCheckElement) { + return false; + } + + auto lastReportedElement = updateCheckElement->getChildByName("LastReportedVersion"); + if (lastReportedElement) { + settingLastReportedVersion.major = lastReportedElement->getIntAttribute("VersionMajor", 0); + settingLastReportedVersion.minor = lastReportedElement->getIntAttribute("VersionMinor", 0); + settingLastReportedVersion.revision = lastReportedElement->getIntAttribute("VersionRevision", 0); + } + + auto autoCheckElement = updateCheckElement->getChildByName("AutoCheck"); + if (autoCheckElement) { + settingAutoCheckEnabled = autoCheckElement->getBoolAttribute("OnStartUp", false); + } + + return true; +} + +bool UpdateCheckerSettingsFile::saveSettings() +{ + auto updateCheckElement = juce::XmlElement("UpdateCheck"); + + auto lastReportedElement = new juce::XmlElement("LastReportedVersion"); + lastReportedElement->setAttribute("VersionMajor", settingLastReportedVersion.major); + lastReportedElement->setAttribute("VersionMinor", settingLastReportedVersion.minor); + lastReportedElement->setAttribute("VersionRevision", settingLastReportedVersion.revision); + updateCheckElement.addChildElement(lastReportedElement); + + auto autoCheckElement = new juce::XmlElement("AutoCheck"); + autoCheckElement->setAttribute("OnStartUp", settingAutoCheckEnabled); + updateCheckElement.addChildElement(autoCheckElement); + + return updateCheckElement.writeTo(settingsFile); +} + +bool UpdateCheckerSettingsFile::settingsFileExists() +{ + return settingsFile.existsAsFile(); +} + +bool UpdateCheckerSettingsFile::ensureSettingsFileExists() +{ + bool success = settingsFileExists(); + if (!success) { + success = saveSettings() && settingsFileExists(); + } + return success; +} diff --git a/shared/update_check_settings_file.h b/shared/update_check_settings_file.h new file mode 100644 index 000000000..fd945483b --- /dev/null +++ b/shared/update_check_settings_file.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include + +class UpdateCheckerSettingsFile { +public: + UpdateCheckerSettingsFile(); + + const bool getAutoCheckEnabled(); + const bool setAutoCheckEnabled(bool enabled); + + const Version getLastReportedVersion(); + const bool setLastReportedVersion(const Version& version); + + const bool canRead(); + const bool canWrite(); + +private: + + juce::File settingsFile; + bool loadSettings(); + bool saveSettings(); + bool settingsFileExists(); + bool ensureSettingsFileExists(); + + bool settingAutoCheckEnabled{ false }; + Version settingLastReportedVersion; +}; diff --git a/tools/setup/CMakeLists.txt b/tools/setup/CMakeLists.txt index 723f2dbe0..715f142ad 100644 --- a/tools/setup/CMakeLists.txt +++ b/tools/setup/CMakeLists.txt @@ -24,6 +24,7 @@ set(HEADERS_SETUP install_phases/component_complete.h helpers/manifests.h ${EPS_SHARED_DIR}/helper/resource_paths_juce-file.hpp + ${EPS_SHARED_DIR}/update_check_settings_file.h ) set(SOURCES_SETUP @@ -41,6 +42,7 @@ set(SOURCES_SETUP install_phases/component_complete.cpp helpers/manifests.cpp ${EPS_SHARED_DIR}/binary_data.cpp + ${EPS_SHARED_DIR}/update_check_settings_file.cpp ) source_group("Header Files" FILES ${HEADERS_SETUP}) @@ -81,6 +83,6 @@ add_custom_command(TARGET setup PRE_BUILD COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/helpers/gen_license_header.cmake ) -set_target_properties(setup PROPERTIES FOLDER tools) +set_target_properties(setup PROPERTIES FOLDER tools) target_link_libraries(setup PRIVATE Juce::core ear-version) target_include_directories(setup PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${EPS_SHARED_DIR}) diff --git a/tools/setup/install_phases/component_complete.cpp b/tools/setup/install_phases/component_complete.cpp index 756268dd2..e30fd7c93 100644 --- a/tools/setup/install_phases/component_complete.cpp +++ b/tools/setup/install_phases/component_complete.cpp @@ -39,7 +39,15 @@ void ComponentComplete::resized() auto sectionHeight = area.getHeight() / 4; titleLabel.setBounds(area.removeFromTop(sectionHeight)); - descriptionLabel.setBounds(area.removeFromTop(sectionHeight)); + descriptionLabel.setBounds(area.removeFromTop(sectionHeight / 2)); + + if (autoUpdateCheck) { + const int autoUpdateCheckHeight{ 35 }; + auto autoUpdateCheckWidth = autoUpdateCheck->getMinReqWidth(autoUpdateCheckHeight); + auto autoUpdateCheckArea = area.removeFromTop(sectionHeight / 2).withSizeKeepingCentre(autoUpdateCheckWidth, autoUpdateCheckHeight); + if (autoUpdateCheckArea.getX() < 0) autoUpdateCheckArea.setX(0); + autoUpdateCheck->setBounds(autoUpdateCheckArea); + } auto buttonSectionWidth = area.getWidth(); auto buttonHeight = std::min(area.getHeight(), 40); @@ -51,6 +59,10 @@ void ComponentComplete::resized() void ComponentComplete::configureForUninstallPhase() { + if (autoUpdateCheck) { + removeChildComponent(autoUpdateCheck.get()); + autoUpdateCheck.reset(); + } titleLabel.setText("Uninstall Complete", juce::NotificationType::dontSendNotification); descriptionLabel.setText("Uninstallation is complete.", @@ -59,6 +71,10 @@ void ComponentComplete::configureForUninstallPhase() void ComponentComplete::configureForUninstallUnnecessaryPhase() { + if (autoUpdateCheck) { + removeChildComponent(autoUpdateCheck.get()); + autoUpdateCheck.reset(); + } titleLabel.setText("Uninstall", juce::NotificationType::dontSendNotification); descriptionLabel.setText("Setup did not find any files relating to a previous installation.", @@ -67,8 +83,75 @@ void ComponentComplete::configureForUninstallUnnecessaryPhase() void ComponentComplete::configureForInstallPhase() { + if (!autoUpdateCheck) { + // Only instantiate this on successful install because UpdateCheck will want to write a settings file. + autoUpdateCheck = std::make_unique(); + addAndMakeVisible(autoUpdateCheck.get()); + resized(); + } titleLabel.setText("Installation Complete", juce::NotificationType::dontSendNotification); descriptionLabel.setText("The EAR Production Suite has been installed.\nUse the software via the REAPER digital audio workstation.", juce::NotificationType::dontSendNotification); } + +AutoUpdateCheckButton::AutoUpdateCheckButton() +{ + bool success = updateCheckerSettingsFile.canWrite(); + + text1.setFont(EarFontsSingleton::instance().Label); + text1.setColour(Label::textColourId, EarColours::Label.withAlpha(Emphasis::high)); + text1.setJustificationType(Justification::centredLeft); + text1.setText(success ? "Automatically check for updates on start-up (requires internet connection.)" : "Automatic update checking disabled - cannot write preferences to settings file.", dontSendNotification); + addAndMakeVisible(text1); + + text2.setFont(EarFontsSingleton::instance().Label); + text2.setColour(Label::textColourId, EarColours::Label.withAlpha(Emphasis::medium)); + text2.setJustificationType(Justification::centredLeft); + text2.setText(success ? "You can change your preference at any time from the REAPER Extensions menu." : "You can still perform a manual update check from the REAPER Extensions menu.", dontSendNotification); + addAndMakeVisible(text2); + + this->setMouseCursor(MouseCursor::PointingHandCursor); + text1.setMouseCursor(MouseCursor::PointingHandCursor); + text2.setMouseCursor(MouseCursor::PointingHandCursor); + text1.setInterceptsMouseClicks(false, false); + text2.setInterceptsMouseClicks(false, false); +} + +AutoUpdateCheckButton::~AutoUpdateCheckButton() +{ +} + +void AutoUpdateCheckButton::paint(Graphics& g) +{ + auto area = getLocalBounds(); + auto checked = updateCheckerSettingsFile.getAutoCheckEnabled(); + getLookAndFeel().drawTickBox(g, text1, 1, 1, area.getHeight() - 2, area.getHeight() - 2, checked, true, true, false); +} + +void AutoUpdateCheckButton::resized() +{ + auto area = getLocalBounds(); + area.removeFromLeft(area.getHeight() + 10); + text1.setBounds(area.removeFromTop(area.getHeight() / 2)); + text2.setBounds(area); +} + +int AutoUpdateCheckButton::getMinReqWidth(int forComponentHeight) +{ + auto text1Width = EarFontsSingleton::instance().Label.getStringWidthFloat(text1.getText()); + auto text2Width = EarFontsSingleton::instance().Label.getStringWidthFloat(text2.getText()); + auto textWidth = std::max(text1Width, text2Width) * 1.05f; // 5% excess + return forComponentHeight + marginBoxText + textWidth; +} + +void AutoUpdateCheckButton::setState(bool checked) +{ + updateCheckerSettingsFile.setAutoCheckEnabled(checked); + repaint(); +} + +void AutoUpdateCheckButton::mouseDown(const MouseEvent& event) +{ + setState(!updateCheckerSettingsFile.getAutoCheckEnabled()); +} diff --git a/tools/setup/install_phases/component_complete.h b/tools/setup/install_phases/component_complete.h index 96a6eea4c..cf01ef63f 100644 --- a/tools/setup/install_phases/component_complete.h +++ b/tools/setup/install_phases/component_complete.h @@ -1,6 +1,34 @@ #pragma once #include "JuceHeader.h" +#include +#include + +class AutoUpdateCheckButton : public Component +{ +public: + AutoUpdateCheckButton(); + ~AutoUpdateCheckButton(); + + void paint(Graphics&) override; + void resized() override; + + int getMinReqWidth(int forComponentHeight); + + void setState(bool checked); + + void mouseDown(const MouseEvent& event) override; + +private: + Label text1; + Label text2; + + const int marginBoxText{ 10 }; + + UpdateCheckerSettingsFile updateCheckerSettingsFile; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AutoUpdateCheckButton) +}; class ComponentComplete : public Component { @@ -19,6 +47,7 @@ class ComponentComplete : public Component Label titleLabel; Label descriptionLabel; TextButton exitButton; + std::unique_ptr autoUpdateCheck; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentComplete) }; \ No newline at end of file