From 80b6378fec9b48309ef8cd086c5ff727c79247aa Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Mon, 22 Nov 2021 17:04:48 -0800 Subject: [PATCH 01/30] change file name from DisabledControllers to Controllers avoids confusing users; changed in wiki too --- src/application/Devices.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/application/Devices.cpp b/src/application/Devices.cpp index 640a37439..4421aa694 100644 --- a/src/application/Devices.cpp +++ b/src/application/Devices.cpp @@ -38,8 +38,15 @@ Devices::Devices() { try { /* open file with xml list of devices */ - const auto source {GetSource(MIDI2LR_UC_LITERAL("DisabledControllers.xml"))}; - if (source.exists()) { device_xml_ = juce::parseXML(source); } + auto source {GetSource(MIDI2LR_UC_LITERAL("Controllers.xml"))}; + if (source.existsAsFile()) { device_xml_ = juce::parseXML(source); } + else { + source = GetSource(MIDI2LR_UC_LITERAL("DisabledControllers.xml")); + if (source.existsAsFile()) { + device_xml_ = juce::parseXML(source); + (void) source.deleteFile(); //don't want to use name again + } + } } catch (const std::exception& e) { /* log and carry on */ MIDI2LR_E_RESPONSE; @@ -80,7 +87,7 @@ Devices::~Devices() { try { /* open file with xml list of devices */ - const auto source {GetSource(MIDI2LR_UC_LITERAL("DisabledControllers.xml"))}; + const auto source {GetSource(MIDI2LR_UC_LITERAL("Controllers.xml"))}; // ReSharper disable once CppExpressionWithoutSideEffects device_xml_->writeTo(source); } From 441c6957ee95bc95e89fbde88101c9dbba59981b Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sun, 28 Nov 2021 11:35:14 -0800 Subject: [PATCH 02/30] reduce noise in log; use std::ignore to silence nodiscard for ignore --- src/application/CommandSet.cpp | 5 ++++- src/application/Devices.cpp | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/application/CommandSet.cpp b/src/application/CommandSet.cpp index 43354a161..39487ed86 100644 --- a/src/application/CommandSet.cpp +++ b/src/application/CommandSet.cpp @@ -96,9 +96,12 @@ const CommandSet::Impl& CommandSet::MakeImpl() const size_t CommandSet::CommandTextIndex(const std::string& command) const { try { + using namespace std::string_literals; const auto found {cmd_idx_.find(command)}; if (found == cmd_idx_.end()) { - rsj::Log(fmt::format("Command not found in CommandTextIndex: {}.", command)); + if (command != "Unmapped"s) { /*Old version of Unassigned*/ + rsj::Log(fmt::format("Command not found in CommandTextIndex: {}.", command)); + } return 0; } return found->second; diff --git a/src/application/Devices.cpp b/src/application/Devices.cpp index 4421aa694..5288c6eb5 100644 --- a/src/application/Devices.cpp +++ b/src/application/Devices.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -44,7 +45,7 @@ Devices::Devices() source = GetSource(MIDI2LR_UC_LITERAL("DisabledControllers.xml")); if (source.existsAsFile()) { device_xml_ = juce::parseXML(source); - (void) source.deleteFile(); //don't want to use name again + std::ignore = source.deleteFile(); // don't want to use name again } } } From 37b1e38cfded601e643a522e10a6d48b0e56cd95 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Wed, 1 Dec 2021 11:32:41 -0800 Subject: [PATCH 03/30] Move constrain to warp to Transforms panel closes #569 --- src/plugin/Database.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/Database.lua b/src/plugin/Database.lua index 8b0559755..2c23ab445 100644 --- a/src/plugin/Database.lua +++ b/src/plugin/Database.lua @@ -600,7 +600,6 @@ local DataBase = { {Command='LensProfileDistortionScale',Type='parameter',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/LensProfileDistortionScale=Lens Profile Distortion Scale'),Group=lensCorrections,Explanation='The default value 100 applies 100% of the distortion correction in the profile. Values over 100 apply greater correction to the distortion; values under 100 apply less correction to the distortion.',Panel='lensCorrectionsPanel'}, {Command='LensProfileChromaticAberrationScale',Type='parameter',Translation=LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/ChromaticAberration=chromatic Aberration')..' — '..LOC('$$$/AgCameraRawUI/ControlTitle/LensProfileDistortionScale=Scale'),Group=lensCorrections,Explanation='The default value 100 applies 100% of the chromatic aberration correction in the profile. Values over 100 apply greater correction to color fringing; values under 100 apply less correction to color fringing.',Panel='lensCorrectionsPanel'}, {Command='LensProfileVignettingScale',Type='parameter',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/LensProfileVignettingScale=Lens Profile Vignetting Scale'),Group=lensCorrections,Explanation='The default value 100 applies 100% of the vignetting correction in the profile. Values over 100 apply greater correction to vignetting; values under 100 apply less correction to vignetting.',Panel='lensCorrectionsPanel'}, - {Command='CropConstrainToWarp',Type='button',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/CropConstrainToWarp=Constrain to Warp'),Group=lensCorrections,Explanation='Enable or disable Constrain Crop.',Panel='lensCorrectionsPanel'}, {Command='ResetLensProfileDistortionScale',Type='button',Translation=LOC('$$$/AgCameraRawController/TargetAdjustment/Reset=Reset ^1',LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/LensProfileDistortionScale=Lens Profile Distortion Scale')),Group=lensCorrections,Explanation='Reset to default.',Panel='lensCorrectionsPanel'}, {Command='ResetLensProfileChromaticAberrationScale',Type='button',Translation=LOC('$$$/AgCameraRawController/TargetAdjustment/Reset=Reset ^1',LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/ChromaticAberration=chromatic Aberration'))..' — '..LOC('$$$/AgCameraRawUI/ControlTitle/LensProfileDistortionScale=Scale'),Group=lensCorrections,Explanation='Reset to default.',Panel='lensCorrectionsPanel'}, {Command='ResetLensProfileVignettingScale',Type='button',Translation=LOC('$$$/AgCameraRawController/TargetAdjustment/Reset=Reset ^1',LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/LensProfileVignettingScale=Lens Profile Vignetting Scale')),Group=lensCorrections,Explanation='Reset to default.',Panel='lensCorrectionsPanel'}, @@ -633,6 +632,7 @@ local DataBase = { {Command='UprightVertical',Type='button',Translation=percor..' '..LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/ValueLevelVertical=Vertical'),Group=transform,Explanation='Perspective corrections are weighted toward vertical details.',Panel='transformPanel'}, {Command='UprightGuided',Type='button',Translation=percor..' '..LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/ValueLevelGuided=Guided'),Group=transform,Explanation='Perspective corrections guide by drawing lines.',Panel='transformPanel'}, {Command='UprightFull',Type='button',Translation=percor..' '..LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/ValueFull=Full'),Group=transform,Explanation='Combination of full Level, Vertical, and Auto perspective corrections.',Panel='transformPanel'}, + {Command='CropConstrainToWarp',Type='button',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/CropConstrainToWarp=Constrain to Warp'),Group=transform,Explanation='Enable or disable Constrain Crop. Takes effect in both Transforms panel and Crop panel.',Panel='lensCorrectionsPanel'}, {Command='ResetPerspectiveUpright',Type='button',Translation=LOC('$$$/AgCameraRawNamedSettings/Ops/ResetUprightSettings=Reset Upright Settings'),Group=transform,Explanation='Reset to default.',Panel='transformPanel'}, {Command='PerspectiveVertical',Type='parameter',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/PerspectiveVertical=Perspective Vertical'),Group=transform,Explanation='Corrects perspective caused by tilting the camera up or down. Makes vertical lines appear parallel.',Panel='transformPanel'}, {Command='PerspectiveHorizontal',Type='parameter',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/PerspectiveHorizontal=Perspective Horizontal'),Group=transform,Explanation='Corrects perspective caused by angling the camera left or right. Makes horizontal lines parallel.',Panel='transformPanel'}, From 48dfc90b60ccd74bb390faa178752b1518673dce Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Wed, 1 Dec 2021 16:26:05 -0800 Subject: [PATCH 04/30] fix panel assignment for constrain to warp --- src/plugin/Database.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/Database.lua b/src/plugin/Database.lua index 2c23ab445..caa72d060 100644 --- a/src/plugin/Database.lua +++ b/src/plugin/Database.lua @@ -632,7 +632,7 @@ local DataBase = { {Command='UprightVertical',Type='button',Translation=percor..' '..LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/ValueLevelVertical=Vertical'),Group=transform,Explanation='Perspective corrections are weighted toward vertical details.',Panel='transformPanel'}, {Command='UprightGuided',Type='button',Translation=percor..' '..LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/ValueLevelGuided=Guided'),Group=transform,Explanation='Perspective corrections guide by drawing lines.',Panel='transformPanel'}, {Command='UprightFull',Type='button',Translation=percor..' '..LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/ValueFull=Full'),Group=transform,Explanation='Combination of full Level, Vertical, and Auto perspective corrections.',Panel='transformPanel'}, - {Command='CropConstrainToWarp',Type='button',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/CropConstrainToWarp=Constrain to Warp'),Group=transform,Explanation='Enable or disable Constrain Crop. Takes effect in both Transforms panel and Crop panel.',Panel='lensCorrectionsPanel'}, + {Command='CropConstrainToWarp',Type='button',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/CropConstrainToWarp=Constrain to Warp'),Group=transform,Explanation='Enable or disable Constrain Crop. Takes effect in both Transforms panel and Crop panel.',Panel='transformPanel'}, {Command='ResetPerspectiveUpright',Type='button',Translation=LOC('$$$/AgCameraRawNamedSettings/Ops/ResetUprightSettings=Reset Upright Settings'),Group=transform,Explanation='Reset to default.',Panel='transformPanel'}, {Command='PerspectiveVertical',Type='parameter',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/PerspectiveVertical=Perspective Vertical'),Group=transform,Explanation='Corrects perspective caused by tilting the camera up or down. Makes vertical lines appear parallel.',Panel='transformPanel'}, {Command='PerspectiveHorizontal',Type='parameter',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/PerspectiveHorizontal=Perspective Horizontal'),Group=transform,Explanation='Corrects perspective caused by angling the camera left or right. Makes horizontal lines parallel.',Panel='transformPanel'}, From 5bbf936b38db4ed549b8f6e33a110bbf9bbd781a Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Fri, 3 Dec 2021 17:09:55 -0800 Subject: [PATCH 05/30] check for failure writing wil logs --- src/application/Main.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/application/Main.cpp b/src/application/Main.cpp index 376e08b7a..43cbc6ee0 100644 --- a/src/application/Main.cpp +++ b/src/application/Main.cpp @@ -87,9 +87,13 @@ namespace { #ifdef _WIN32 try { wil::SetResultLoggingCallback([](wil::FailureInfo const& failure) noexcept { - std::array debug_string {}; - wil::GetFailureLogString(debug_string.data(), debug_string.size(), failure); - rsj::Log(debug_string.data()); + std::array dbg {}; + if (SUCCEEDED(wil::GetFailureLogString(dbg.data(), dbg.size(), failure))) { + rsj::Log(dbg.data()); + } + else { + rsj::Log(L"Call to wil::GetFailureLogString failed."); + } }); } catch (const std::exception& e) { From 136701574b86d79281973819951acc038e5997df Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Thu, 9 Dec 2021 15:47:49 -0800 Subject: [PATCH 06/30] code cleanup static analyzer; xcode doesn't support erase, contains --- src/application/Concurrency.h | 19 +++++++++---------- src/application/ControlsModel.h | 3 --- src/application/LR_IPC_In.cpp | 4 +++- src/application/Main.cpp | 13 +++++++++---- src/application/Profile.cpp | 3 ++- src/application/VersionChecker.cpp | 2 +- 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/application/Concurrency.h b/src/application/Concurrency.h index 79c6214fc..5a091fb4d 100644 --- a/src/application/Concurrency.h +++ b/src/application/Concurrency.h @@ -206,22 +206,21 @@ namespace rsj { } condition_.notify_all(); } - void clear() noexcept( - std::is_nothrow_default_constructible_v && - std::is_nothrow_destructible_v&& - std::is_nothrow_swappable_v&& - noexcept(std::scoped_lock(std::declval()))) + void clear() noexcept(std::is_nothrow_default_constructible_v&& + std::is_nothrow_destructible_v&& + std::is_nothrow_swappable_v&& noexcept( + std::scoped_lock(std::declval()))) { /*https://devblogs.microsoft.com/oldnewthing/20201112-00/?p=104444 */ Container trash {}; auto lock {std::scoped_lock(mutex_)}; std::swap(trash, queue_); } [[nodiscard]] size_type clear_count() noexcept( - std::is_nothrow_default_constructible_v && - std::is_nothrow_destructible_v && - std::is_nothrow_swappable_v && - noexcept(std::declval().size()) && - noexcept(std::scoped_lock(std::declval()))) + std::is_nothrow_default_constructible_v&& + std::is_nothrow_destructible_v&& + std::is_nothrow_swappable_v&& noexcept( + std::declval() + .size()) && noexcept(std::scoped_lock(std::declval()))) { Container trash {}; { diff --git a/src/application/ControlsModel.h b/src/application/ControlsModel.h index 8615779af..0ce94d48e 100644 --- a/src/application/ControlsModel.h +++ b/src/application/ControlsModel.h @@ -88,9 +88,6 @@ namespace rsj { case CCmethod::kTwosComplement: methodstr = "TwosComplement"; break; - default: - /* leave "undefined" */ - break; } archive(cereal::make_nvp("CC", control_number), CEREAL_NVP(high), CEREAL_NVP(low), cereal::make_nvp("method", methodstr)); diff --git a/src/application/LR_IPC_In.cpp b/src/application/LR_IPC_In.cpp index a596f5da6..eb10f6b66 100644 --- a/src/application/LR_IPC_In.cpp +++ b/src/application/LR_IPC_In.cpp @@ -22,6 +22,7 @@ #include #include +#include #include //ReSharper false alarm #include @@ -211,7 +212,8 @@ void LrIpcIn::Read() } else { std::string command {buffers_begin(streambuf_.data()), - buffers_begin(streambuf_.data()) + bytes_transferred}; + buffers_begin(streambuf_.data()) + + gsl::narrow(bytes_transferred)}; if (command == "TerminateApplication 1\n") { thread_should_exit_.store(true, std::memory_order_release); } diff --git a/src/application/Main.cpp b/src/application/Main.cpp index 43cbc6ee0..880524d61 100644 --- a/src/application/Main.cpp +++ b/src/application/Main.cpp @@ -75,7 +75,7 @@ namespace { LookAndFeelMIDI2LR& operator=(LookAndFeelMIDI2LR&& s) = delete; juce::Font getTextButtonFont(juce::TextButton&, const int button_height) override { - return juce::Font(std::min(16.0F, static_cast(button_height) * 0.7F)); + return {std::min(16.0F, static_cast(button_height) * 0.7F)}; } }; @@ -108,7 +108,12 @@ namespace { ~SetLogger() { #ifdef _WIN32 - wil::SetResultLoggingCallback(nullptr); + try { + wil::SetResultLoggingCallback(nullptr); + } + catch (...) { + rsj::Log("Unable to reset wil logging callback."); + } #endif juce::Logger::setCurrentLogger(nullptr); } @@ -124,8 +129,8 @@ namespace { [[noreturn]] void OnTerminate() noexcept { - static std::mutex terminate_mutex; try { + static std::mutex terminate_mutex; auto lock {std::scoped_lock(terminate_mutex)}; if (const auto exc {std::current_exception()}) { /* we have an exception */ @@ -263,7 +268,7 @@ class MIDI2LRApplication final : public juce::JUCEApplication { bool ready {false}; #endif - juce::MessageManager::callAsync([&]() { + juce::MessageManager::callAsync([&] { try { const auto result {juce::NativeMessageBox::showYesNoBox( juce::AlertWindow::WarningIcon, juce::translate("MIDI2LR profiles"), diff --git a/src/application/Profile.cpp b/src/application/Profile.cpp index 209120f62..c24ded43d 100644 --- a/src/application/Profile.cpp +++ b/src/application/Profile.cpp @@ -170,7 +170,8 @@ void Profile::RemoveRow(const size_t row) auto guard {std::unique_lock {mutex_}}; const auto msg {GetMessageForNumberI(row)}; command_string_map_.erase(message_map_.at(msg)); - command_table_.erase(command_table_.cbegin() + row); + command_table_.erase( + command_table_.cbegin() + gsl::narrow(row)); message_map_.erase(msg); profile_unsaved_ = true; } diff --git a/src/application/VersionChecker.cpp b/src/application/VersionChecker.cpp index e0206a323..13d239f97 100644 --- a/src/application/VersionChecker.cpp +++ b/src/application/VersionChecker.cpp @@ -30,7 +30,7 @@ namespace { [[nodiscard]] std::string IntToVersion(unsigned int vers) { static_assert(std::is_unsigned_v, "Avoid sign extension"); - static_assert(sizeof vers >= 4, "At least 4 bytes"); + static_assert(sizeof vers >= 4, "At least 4 bytes"); //-V112 const auto major {vers >> 24 & 0xFFU}; const auto minor {vers >> 16 & 0xFFU}; const auto rev {vers >> 8 & 0xFFU}; From 7b5cc6b9cdfac3ba4c6695f5675411c0851fb155 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Fri, 10 Dec 2021 17:53:53 -0800 Subject: [PATCH 07/30] see commit 807ee5 in fmt repository. for xcode to work C++20 mode revert when getting next version of fmt --- external/fmt/core.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/external/fmt/core.h b/external/fmt/core.h index d058398ac..828e3ad34 100644 --- a/external/fmt/core.h +++ b/external/fmt/core.h @@ -285,17 +285,8 @@ # define FMT_UNICODE !FMT_MSC_VER #endif -#ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - __cplusplus > 201703L) || \ - (defined(__cpp_consteval) && \ - !FMT_MSC_VER) // consteval is broken in MSVC. -# define FMT_CONSTEVAL consteval -# define FMT_HAS_CONSTEVAL -# else # define FMT_CONSTEVAL -# endif -#endif + #ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS # if defined(__cpp_nontype_template_args) && \ From 4e2841370dfff5c473f86e8db3a9041b8d0ecb66 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Fri, 10 Dec 2021 17:55:49 -0800 Subject: [PATCH 08/30] VS 2022, Xcode C++20 support --- MIDI2LR.jucer | 24 +++++++++---------- appveyor.yml | 2 +- build/MacOS/MIDI2LR.xcodeproj/project.pbxproj | 15 +++++++----- build/Windows/MIDI2LR.sln | 3 ++- build/Windows/MIDI2LR_App.vcxproj | 10 ++++---- build/Windows/MIDI2LR_App.vcxproj.filters | 2 +- docs/BUILD.txt | 2 +- src/application/Main.cpp | 5 ++++ src/application/Profile.cpp | 4 +--- src/application/Profile.h | 4 ++-- 10 files changed, 39 insertions(+), 32 deletions(-) diff --git a/MIDI2LR.jucer b/MIDI2LR.jucer index 556ed9a0c..f42d7302e 100644 --- a/MIDI2LR.jucer +++ b/MIDI2LR.jucer @@ -122,22 +122,22 @@ - + - - + + - - - - - - - + + + + + + + - + diff --git a/appveyor.yml b/appveyor.yml index 822122596..37f80e209 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ version: 1.0.{build} -image: Visual Studio 2019 +image: Visual Studio 2022 configuration: - Release - Debug diff --git a/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj b/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj index 8bbcd5676..707644948 100644 --- a/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj +++ b/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj @@ -358,7 +358,7 @@ DFD30DF9237597CD5AF365AE = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1310; ORGANIZATIONNAME = "Rory Jaffe"; TargetAttributes = { FD946FDE29418E71B61DACDC = { @@ -470,7 +470,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_64_BIT)"; - CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LIBRARY = "libc++"; CLANG_LINK_OBJC_RUNTIME = NO; CODE_SIGN_ENTITLEMENTS = "App.entitlements"; @@ -481,6 +481,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = VN44PD5JV9; ENABLE_HARDENED_RUNTIME = YES; + EXCLUDED_ARCHS = "i386 arm64e"; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = 3; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -488,7 +489,7 @@ "NDEBUG=1", "JUCE_DISPLAY_SPLASH_SCREEN=0", "JUCE_USE_DARK_SPLASH_SCREEN=1", - "JUCE_PROJUCER_VERSION=0x60102", + "JUCE_PROJUCER_VERSION=0x60103", "JUCE_MODULE_AVAILABLE_juce_audio_basics=1", "JUCE_MODULE_AVAILABLE_juce_audio_devices=1", "JUCE_MODULE_AVAILABLE_juce_core=1", @@ -600,7 +601,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_64_BIT)"; - CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LIBRARY = "libc++"; CLANG_LINK_OBJC_RUNTIME = NO; CODE_SIGN_ENTITLEMENTS = "App.entitlements"; @@ -611,6 +612,7 @@ COPY_PHASE_STRIP = NO; DEVELOPMENT_TEAM = VN44PD5JV9; ENABLE_HARDENED_RUNTIME = YES; + EXCLUDED_ARCHS = "i386 arm64e"; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -618,7 +620,7 @@ "DEBUG=1", "JUCE_DISPLAY_SPLASH_SCREEN=0", "JUCE_USE_DARK_SPLASH_SCREEN=1", - "JUCE_PROJUCER_VERSION=0x60102", + "JUCE_PROJUCER_VERSION=0x60103", "JUCE_MODULE_AVAILABLE_juce_audio_basics=1", "JUCE_MODULE_AVAILABLE_juce_audio_devices=1", "JUCE_MODULE_AVAILABLE_juce_core=1", @@ -669,7 +671,8 @@ INSTALL_PATH = "$(HOME)/Applications"; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../external/JuceLibraryCode $(SRCROOT)/../../external/JuceLibraryCode/modules $(SRCROOT)/../../external/asio/ $(SRCROOT)/../../external/"; - OTHER_CPLUSPLUSFLAGS = "-Wall -Wshadow-all -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -Wunused-private-field -Wbool-conversion -Wextra-semi -Wunreachable-code -Wzero-as-null-pointer-constant -Wcast-align -Winconsistent-missing-destructor-override -Wshift-sign-overflow -Wnullable-to-nonnull-conversion -Wno-missing-field-initializers -Wno-ignored-qualifiers -Wswitch-enum"; + OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion"; + OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion"; PRODUCT_BUNDLE_IDENTIFIER = com.rsjaffe.MIDI2LR; PRODUCT_NAME = "MIDI2LR"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/build/Windows/MIDI2LR.sln b/build/Windows/MIDI2LR.sln index 1e3c6a101..5abbaf8ef 100644 --- a/build/Windows/MIDI2LR.sln +++ b/build/Windows/MIDI2LR.sln @@ -1,5 +1,6 @@ + Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2019 +# Visual Studio Version 17 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MIDI2LR - App", "MIDI2LR_App.vcxproj", "{686AE150-57A0-ABD4-6033-28AEE68BADB0}" EndProject diff --git a/build/Windows/MIDI2LR_App.vcxproj b/build/Windows/MIDI2LR_App.vcxproj index a2a3f1704..d520144ad 100644 --- a/build/Windows/MIDI2LR_App.vcxproj +++ b/build/Windows/MIDI2LR_App.vcxproj @@ -1,7 +1,7 @@ @@ -22,7 +22,7 @@ Application false false - v142 + v143 10.0 Application false true - v142 + v143 10.0 @@ -64,7 +64,7 @@ Disabled ProgramDatabase ..\..\external\JuceLibraryCode;..\..\external\JuceLibraryCode\modules;../../external/;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60102;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2019_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60103;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) MultiThreadedDebugDLL true NotUsing @@ -105,7 +105,7 @@ Full ..\..\external\JuceLibraryCode;..\..\external\JuceLibraryCode\modules;../../external/;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60102;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2019_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60103;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) MultiThreaded true NotUsing diff --git a/build/Windows/MIDI2LR_App.vcxproj.filters b/build/Windows/MIDI2LR_App.vcxproj.filters index c6e9a1a65..d5dd24ff6 100644 --- a/build/Windows/MIDI2LR_App.vcxproj.filters +++ b/build/Windows/MIDI2LR_App.vcxproj.filters @@ -1,6 +1,6 @@ - + {2AFD23E6-68D5-9245-9CAD-FBED51B35B63} diff --git a/docs/BUILD.txt b/docs/BUILD.txt index d3fcf16db..2c47ddf37 100644 --- a/docs/BUILD.txt +++ b/docs/BUILD.txt @@ -3,7 +3,7 @@ Prerequisites Lightroom is required for building the application. -On Windows, you need Visual Studio 2019 (the Community edition is free). +On Windows, you need Visual Studio 2022 (the Community edition is free). On Mac, you need XCode, also free. The .clang-format file should be loaded into Visual Studio to maintain the diff --git a/src/application/Main.cpp b/src/application/Main.cpp index 880524d61..9469b410f 100644 --- a/src/application/Main.cpp +++ b/src/application/Main.cpp @@ -51,6 +51,11 @@ #else #include #endif +/*weird xcode error for semaphore in this file only with cpp20: release has been explicitly marked unavailable here*/ +#ifndef _WIN32 +#include +#undef __cpp_lib_semaphore +#endif namespace fs = std::filesystem; diff --git a/src/application/Profile.cpp b/src/application/Profile.cpp index c24ded43d..6cf2af73a 100644 --- a/src/application/Profile.cpp +++ b/src/application/Profile.cpp @@ -190,9 +190,7 @@ void Profile::RemoveUnassignedMessages() profile_unsaved_ = true; do { message_map_.erase(it->second); - command_table_.erase( - std::remove(command_table_.begin(), command_table_.end(), it->second), - command_table_.end()); + std::erase(command_table_, it->second); command_string_map_.erase(it); it = command_string_map_.find("Unassigned"); } while (it != command_string_map_.end()); diff --git a/src/application/Profile.h b/src/application/Profile.h index 980ddc078..75dfdc0bd 100644 --- a/src/application/Profile.h +++ b/src/application/Profile.h @@ -82,7 +82,7 @@ inline void Profile::AddCommandForMessage(size_t command, rsj::MidiMessageId mes inline bool Profile::CommandHasAssociatedMessage(const std::string& command) const { auto guard {std::shared_lock {mutex_}}; - return command_string_map_.find(command) != command_string_map_.end(); + return command_string_map_.contains(command); } inline const std::string& Profile::GetCommandForMessage(rsj::MidiMessageId message) const @@ -122,7 +122,7 @@ inline bool Profile::MessageExistsInMap(rsj::MidiMessageId message) const inline bool Profile::MessageExistsInMapI(rsj::MidiMessageId message) const { - return message_map_.find(message) != message_map_.end(); + return message_map_.contains(message); } inline bool Profile::ProfileUnsaved() const From 46f6791872958bc1b7992a275c72a59a14c4a610 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Fri, 10 Dec 2021 17:56:21 -0800 Subject: [PATCH 09/30] Juce version 6.1.3 --- .../audio_play_head/juce_AudioPlayHead.h | 96 +- .../buffers/juce_AudioChannelSet.cpp | 58 +- .../buffers/juce_AudioChannelSet.h | 33 +- .../buffers/juce_AudioDataConverters.cpp | 79 +- .../buffers/juce_AudioDataConverters.h | 147 ++- .../buffers/juce_AudioProcessLoadMeasurer.cpp | 36 +- .../buffers/juce_AudioProcessLoadMeasurer.h | 12 +- .../buffers/juce_FloatVectorOperations.cpp | 4 +- .../juce_audio_basics/juce_audio_basics.h | 2 +- .../midi/juce_MidiBuffer.cpp | 8 +- .../juce_audio_basics/midi/juce_MidiBuffer.h | 19 +- .../juce_audio_basics/midi/juce_MidiFile.cpp | 1 - .../juce_audio_basics/midi/juce_MidiFile.h | 3 - .../midi/juce_MidiKeyboardState.cpp | 4 - .../midi/juce_MidiKeyboardState.h | 1 - .../juce_audio_basics/midi/juce_MidiMessage.h | 12 +- .../midi/juce_MidiMessageSequence.cpp | 526 +++++++++- .../midi/juce_MidiMessageSequence.h | 21 +- .../juce_audio_basics/midi/ump/juce_UMP.h | 4 + .../midi/ump/juce_UMPConversion.h | 4 + .../midi/ump/juce_UMPConverters.h | 4 + .../midi/ump/juce_UMPDispatcher.h | 4 + .../midi/ump/juce_UMPFactory.h | 4 + .../midi/ump/juce_UMPIterator.h | 4 + .../ump/juce_UMPMidi1ToBytestreamTranslator.h | 4 + .../juce_UMPMidi1ToMidi2DefaultTranslator.h | 4 + .../midi/ump/juce_UMPProtocols.h | 4 + .../midi/ump/juce_UMPReceiver.h | 4 + .../midi/ump/juce_UMPSysEx7.cpp | 12 +- .../midi/ump/juce_UMPSysEx7.h | 4 + .../midi/ump/juce_UMPUtils.h | 4 + .../juce_audio_basics/midi/ump/juce_UMPView.h | 4 + .../midi/ump/juce_UMPacket.h | 4 + .../midi/ump/juce_UMPackets.h | 4 + .../native/juce_mac_CoreAudioLayouts.h | 272 +++--- .../synthesisers/juce_Synthesiser.h | 8 - .../juce_audio_basics/utilities/juce_ADSR.h | 1 + .../utilities/juce_ADSR_test.cpp | 14 + .../utilities/juce_SmoothedValue.h | 11 +- .../audio_io/juce_AudioDeviceManager.cpp | 620 +++++++++++- .../audio_io/juce_AudioDeviceManager.h | 28 +- .../audio_io/juce_AudioIODeviceType.h | 6 +- .../juce_audio_devices/juce_audio_devices.h | 2 +- .../midi_io/juce_MidiDevices.h | 16 +- .../ump/juce_UMPBytestreamInputHandler.h | 4 + .../midi_io/ump/juce_UMPU32InputHandler.h | 4 + .../native/juce_android_Audio.cpp | 31 +- .../native/juce_android_Oboe.cpp | 53 +- .../native/juce_android_OpenSL.cpp | 64 +- .../native/juce_ios_Audio.cpp | 45 +- .../native/juce_ios_Audio.h | 1 - .../native/juce_mac_CoreAudio.cpp | 256 ++--- .../native/juce_mac_CoreMidi.mm | 4 +- .../native/juce_win32_ASIO.cpp | 5 +- .../native/juce_win32_Midi.cpp | 2 +- .../native/juce_win32_WASAPI.cpp | 89 +- .../sources/juce_AudioTransportSource.cpp | 26 +- .../sources/juce_AudioTransportSource.h | 4 +- .../containers/juce_AbstractFifo.cpp | 2 - .../juce_core/containers/juce_AbstractFifo.h | 29 +- .../modules/juce_core/containers/juce_Array.h | 6 +- .../juce_core/containers/juce_DynamicObject.h | 5 - .../juce_core/containers/juce_OwnedArray.h | 6 +- .../containers/juce_ReferenceCountedArray.h | 6 +- .../juce_SingleThreadedAbstractFifo.h | 126 +++ .../juce_core/containers/juce_Variant.cpp | 15 +- .../juce_core/containers/juce_Variant.h | 28 +- .../files/juce_DirectoryIterator.cpp | 18 - .../juce_core/files/juce_DirectoryIterator.h | 31 +- .../modules/juce_core/files/juce_File.cpp | 15 +- .../modules/juce_core/files/juce_File.h | 21 +- .../juce_core/files/juce_FileSearchPath.cpp | 3 - .../juce_core/files/juce_FileSearchPath.h | 8 +- .../files/juce_RangedDirectoryIterator.cpp | 12 +- .../files/juce_RangedDirectoryIterator.h | 7 + .../modules/juce_core/juce_core.h | 6 +- .../juce_core/maths/juce_BigInteger.cpp | 4 - .../modules/juce_core/maths/juce_BigInteger.h | 2 +- .../juce_core/maths/juce_MathsFunctions.h | 33 +- .../modules/juce_core/memory/juce_Atomic.h | 7 +- .../modules/juce_core/memory/juce_ByteOrder.h | 2 +- .../modules/juce_core/memory/juce_HeapBlock.h | 6 +- .../juce_core/memory/juce_MemoryBlock.h | 9 +- .../memory/juce_ReferenceCountedObject.h | 7 +- .../modules/juce_core/memory/juce_Reservoir.h | 100 ++ .../juce_core/memory/juce_ScopedPointer.h | 27 +- .../modules/juce_core/memory/juce_Singleton.h | 90 +- .../modules/juce_core/misc/juce_Functional.h | 29 +- .../modules/juce_core/misc/juce_Uuid.h | 2 +- .../juce_core/misc/juce_WindowsRegistry.h | 8 +- .../native/juce_BasicNativeHeaders.h | 2 +- .../native/juce_android_JNIHelpers.h | 9 +- .../juce_core/native/juce_android_Network.cpp | 4 +- .../juce_core/native/juce_curl_Network.cpp | 4 +- .../juce_core/native/juce_linux_Files.cpp | 36 +- .../juce_core/native/juce_linux_Network.cpp | 4 +- .../juce_core/native/juce_mac_CFHelpers.h | 1 + .../juce_core/native/juce_mac_Files.mm | 74 +- .../juce_core/native/juce_mac_Network.mm | 898 +++++++++--------- .../juce_core/native/juce_mac_ObjCHelpers.h | 120 ++- .../juce_core/native/juce_mac_SystemStats.mm | 26 +- .../juce_core/native/juce_posix_NamedPipe.cpp | 26 +- .../juce_core/native/juce_posix_SharedCode.h | 14 +- .../juce_core/native/juce_win32_Files.cpp | 12 +- .../juce_core/native/juce_win32_Network.cpp | 4 +- .../juce_core/network/juce_IPAddress.cpp | 2 +- .../modules/juce_core/network/juce_URL.cpp | 24 +- .../modules/juce_core/network/juce_URL.h | 93 +- .../streams/juce_BufferedInputStream.cpp | 246 +++-- .../streams/juce_BufferedInputStream.h | 4 +- .../streams/juce_MemoryInputStream.cpp | 4 +- .../juce_core/streams/juce_URLInputSource.cpp | 7 +- .../juce_core/system/juce_CompilerSupport.h | 6 +- .../juce_core/system/juce_CompilerWarnings.h | 23 +- .../juce_core/system/juce_PlatformDefs.h | 50 +- .../juce_core/system/juce_StandardHeader.h | 9 +- .../juce_core/system/juce_SystemStats.h | 6 +- .../juce_core/text/juce_CharPointer_UTF16.h | 2 +- .../juce_core/text/juce_CharacterFunctions.h | 11 +- .../juce_core/text/juce_LocalisedStrings.cpp | 4 - .../juce_core/text/juce_LocalisedStrings.h | 2 +- .../modules/juce_core/text/juce_String.cpp | 23 +- .../modules/juce_core/text/juce_String.h | 21 +- .../juce_core/text/juce_StringArray.cpp | 4 - .../modules/juce_core/text/juce_StringArray.h | 2 +- .../juce_core/text/juce_StringPairArray.cpp | 4 - .../juce_core/text/juce_StringPairArray.h | 2 +- .../juce_core/text/juce_StringPool.cpp | 1 - .../modules/juce_core/text/juce_StringPool.h | 3 - .../modules/juce_core/threads/juce_Process.h | 6 +- .../modules/juce_core/threads/juce_Thread.h | 2 +- .../modules/juce_core/time/juce_Time.cpp | 33 +- .../modules/juce_core/xml/juce_XmlElement.h | 50 +- .../zip/juce_GZIPDecompressorInputStream.h | 5 - .../juce_ApplicationProperties.cpp | 4 - .../juce_ApplicationProperties.h | 2 +- .../juce_data_structures.h | 2 +- .../values/juce_ValueTree.cpp | 14 +- .../values/juce_ValueTree.h | 9 +- .../juce_ConnectedChildProcess.cpp | 86 +- .../interprocess/juce_ConnectedChildProcess.h | 130 ++- .../modules/juce_events/juce_events.h | 2 +- .../messages/juce_ApplicationBase.h | 2 +- .../messages/juce_Initialisation.h | 6 +- .../messages/juce_MessageManager.h | 2 +- .../juce_MountedVolumeListChangeDetector.h | 2 +- .../native/juce_mac_MessageManager.mm | 42 +- .../modules/juce_events/timers/juce_Timer.cpp | 4 +- .../juce_graphics/colour/juce_Colour.cpp | 2 +- .../contexts/juce_GraphicsContext.cpp | 8 +- ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 2 +- .../modules/juce_graphics/fonts/juce_Font.cpp | 261 +++-- .../modules/juce_graphics/fonts/juce_Font.h | 14 +- .../fonts/juce_GlyphArrangement.cpp | 4 +- .../juce_graphics/fonts/juce_Typeface.cpp | 6 +- .../geometry/juce_AffineTransform.cpp | 2 +- .../geometry/juce_AffineTransform.h | 13 +- .../juce_graphics/geometry/juce_EdgeTable.cpp | 83 +- .../juce_graphics/geometry/juce_EdgeTable.h | 20 +- .../juce_graphics/geometry/juce_Rectangle.h | 64 +- .../geometry/juce_Rectangle_test.cpp | 51 + .../juce_graphics/images/juce_Image.cpp | 15 +- .../modules/juce_graphics/images/juce_Image.h | 10 +- .../juce_graphics/images/juce_ImageCache.cpp | 4 +- .../juce_graphics/images/juce_ScaledImage.h | 82 ++ .../modules/juce_graphics/juce_graphics.cpp | 4 + .../modules/juce_graphics/juce_graphics.h | 3 +- .../native/juce_RenderingHelpers.h | 4 +- .../native/juce_mac_CoreGraphicsContext.mm | 11 +- .../native/juce_mac_CoreGraphicsHelpers.h | 2 +- .../juce_graphics/native/juce_mac_Fonts.mm | 47 +- .../juce_win32_Direct2DGraphicsContext.cpp | 3 +- .../juce_win32_DirectWriteTypeLayout.cpp | 8 +- .../juce_graphics/native/juce_win32_Fonts.cpp | 2 +- .../juce_gui_basics/buttons/juce_Button.cpp | 60 +- .../juce_gui_basics/buttons/juce_Button.h | 35 +- .../juce_gui_basics/buttons/juce_TextButton.h | 7 +- .../commands/juce_ApplicationCommandManager.h | 6 - .../components/juce_Component.cpp | 102 +- .../components/juce_Component.h | 86 +- .../components/juce_FocusTraverser.cpp | 1 + .../juce_gui_basics/desktop/juce_Desktop.cpp | 9 +- .../juce_gui_basics/desktop/juce_Desktop.h | 66 +- .../juce_gui_basics/desktop/juce_Displays.h | 13 +- .../filebrowser/juce_FileChooser.cpp | 1 - .../juce_FileSearchPathListComponent.cpp | 4 - .../juce_FileSearchPathListComponent.h | 3 - .../juce_gui_basics/juce_gui_basics.cpp | 69 +- .../modules/juce_gui_basics/juce_gui_basics.h | 7 +- .../keyboard/juce_KeyboardFocusTraverser.cpp | 1 + .../juce_gui_basics/layout/juce_FlexBox.cpp | 739 +++++++++----- .../juce_gui_basics/layout/juce_FlexItem.h | 2 +- .../juce_gui_basics/layout/juce_Grid.cpp | 537 ++++++----- .../juce_gui_basics/layout/juce_Grid.h | 4 +- .../layout/juce_MultiDocumentPanel.cpp | 9 +- .../layout/juce_MultiDocumentPanel.h | 15 +- .../juce_gui_basics/layout/juce_Viewport.cpp | 12 +- .../menus/juce_MenuBarComponent.cpp | 8 +- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 111 +-- .../juce_gui_basics/menus/juce_PopupMenu.h | 153 ++- .../misc/juce_DropShadower.cpp | 123 ++- .../juce_gui_basics/misc/juce_DropShadower.h | 4 + .../mouse/juce_DragAndDropContainer.cpp | 109 +-- .../mouse/juce_DragAndDropContainer.h | 37 +- .../mouse/juce_MouseCursor.cpp | 155 +-- .../juce_gui_basics/mouse/juce_MouseCursor.h | 20 +- .../mouse/juce_MouseInputSource.cpp | 4 +- .../mouse/juce_MouseInputSource.h | 14 +- .../accessibility/juce_ios_Accessibility.mm | 182 ++-- .../accessibility/juce_mac_Accessibility.mm | 168 ++-- .../juce_mac_AccessibilitySharedCode.mm | 59 +- .../juce_win32_Accessibility.cpp | 30 +- .../juce_win32_AccessibilityElement.cpp | 15 +- .../juce_win32_AccessibilityElement.h | 2 +- .../juce_win32_WindowsUIAWrapper.h | 4 +- .../native/juce_ScopedDPIAwarenessDisabler.h | 54 ++ .../native/juce_android_Windowing.cpp | 92 +- .../native/juce_ios_ContentSharer.cpp | 6 +- .../native/juce_ios_FileChooser.mm | 78 +- .../native/juce_ios_UIViewComponentPeer.mm | 68 +- .../native/juce_ios_Windowing.mm | 120 ++- .../native/juce_linux_FileChooser.cpp | 15 +- .../native/juce_linux_Windowing.cpp | 159 +++- .../native/juce_mac_FileChooser.mm | 92 +- .../native/juce_mac_MainMenu.mm | 95 +- .../native/juce_mac_MouseCursor.mm | 172 ++-- .../native/juce_mac_NSViewComponentPeer.mm | 485 +++++----- .../native/juce_mac_Windowing.mm | 98 +- .../native/juce_win32_Windowing.cpp | 557 ++++++----- .../native/x11/juce_linux_X11_DragAndDrop.cpp | 45 +- .../native/x11/juce_linux_X11_Symbols.cpp | 2 + .../native/x11/juce_linux_X11_Symbols.h | 8 + .../native/x11/juce_linux_XWindowSystem.cpp | 581 +++++++---- .../native/x11/juce_linux_XWindowSystem.h | 99 +- .../juce_ChoicePropertyComponent.cpp | 10 +- .../juce_gui_basics/widgets/juce_ComboBox.cpp | 34 +- .../juce_gui_basics/widgets/juce_ComboBox.h | 11 +- .../juce_gui_basics/widgets/juce_Label.cpp | 28 +- .../juce_gui_basics/widgets/juce_ListBox.cpp | 45 +- .../juce_gui_basics/widgets/juce_ListBox.h | 16 +- .../juce_gui_basics/widgets/juce_Slider.cpp | 10 +- .../juce_gui_basics/widgets/juce_Slider.h | 29 +- .../widgets/juce_TableHeaderComponent.cpp | 7 +- .../widgets/juce_TableListBox.h | 6 - .../widgets/juce_TextEditor.cpp | 2 +- .../widgets/juce_ToolbarItemComponent.cpp | 2 +- .../juce_gui_basics/widgets/juce_TreeView.cpp | 223 +++-- .../juce_gui_basics/widgets/juce_TreeView.h | 5 - .../windows/juce_AlertWindow.cpp | 6 +- .../windows/juce_AlertWindow.h | 13 +- .../windows/juce_CallOutBox.cpp | 2 +- .../windows/juce_ComponentPeer.cpp | 4 +- .../windows/juce_ComponentPeer.h | 5 + .../windows/juce_DialogWindow.h | 2 +- .../windows/juce_ResizableWindow.h | 22 +- .../windows/juce_TooltipWindow.cpp | 106 ++- .../windows/juce_TooltipWindow.h | 23 +- .../windows/juce_TopLevelWindow.cpp | 4 +- 258 files changed, 8479 insertions(+), 4233 deletions(-) create mode 100644 external/JuceLibraryCode/modules/juce_core/containers/juce_SingleThreadedAbstractFifo.h create mode 100644 external/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h create mode 100644 external/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle_test.cpp create mode 100644 external/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h create mode 100644 external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ScopedDPIAwarenessDisabler.h diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h b/external/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h index c8bbdfb5c..03bef327e 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h @@ -60,6 +60,98 @@ class JUCE_API AudioPlayHead fpsUnknown = 99 }; + /** More descriptive frame rate type. */ + class JUCE_API FrameRate + { + public: + /** Creates a frame rate with a base rate of 0. */ + FrameRate() = default; + + /** Creates a FrameRate instance from a FrameRateType. */ + FrameRate (FrameRateType type) : FrameRate (fromType (type)) {} + + /** Gets the FrameRateType that matches the state of this FrameRate. + + Returns fpsUnknown if this FrameRate cannot be represented by any of the + other enum fields. + */ + FrameRateType getType() const + { + switch (base) + { + case 24: return pulldown ? fps23976 : fps24; + case 25: return fps25; + case 30: return pulldown ? (drop ? fps2997drop : fps2997) + : (drop ? fps30drop : fps30); + case 60: return drop ? fps60drop : fps60; + } + + return fpsUnknown; + } + + /** Returns the plain rate, without taking pulldown into account. */ + int getBaseRate() const { return base; } + + /** Returns true if drop-frame timecode is in use. */ + bool isDrop() const { return drop; } + + /** Returns true if the effective framerate is actually equal to the base rate divided by 1.001 */ + bool isPullDown() const { return pulldown; } + + /** Returns the actual rate described by this object, taking pulldown into account. */ + double getEffectiveRate() const { return pulldown ? (double) base / 1.001 : (double) base; } + + /** Returns a copy of this object with the specified base rate. */ + FrameRate withBaseRate (int x) const { return with (&FrameRate::base, x); } + + /** Returns a copy of this object with drop frames enabled or disabled, as specified. */ + FrameRate withDrop (bool x = true) const { return with (&FrameRate::drop, x); } + + /** Returns a copy of this object with pulldown enabled or disabled, as specified. */ + FrameRate withPullDown (bool x = true) const { return with (&FrameRate::pulldown, x); } + + /** Returns true if this instance is equal to other. */ + bool operator== (const FrameRate& other) const + { + const auto tie = [] (const FrameRate& x) { return std::tie (x.base, x.drop, x.pulldown); }; + return tie (*this) == tie (other); + } + + /** Returns true if this instance is not equal to other. */ + bool operator!= (const FrameRate& other) const { return ! (*this == other); } + + private: + static FrameRate fromType (FrameRateType type) + { + switch (type) + { + case fps23976: return FrameRate().withBaseRate (24).withPullDown(); + case fps24: return FrameRate().withBaseRate (24); + case fps25: return FrameRate().withBaseRate (25); + case fps2997: return FrameRate().withBaseRate (30).withPullDown(); + case fps30: return FrameRate().withBaseRate (30); + case fps2997drop: return FrameRate().withBaseRate (30).withDrop().withPullDown(); + case fps30drop: return FrameRate().withBaseRate (30).withDrop(); + case fps60: return FrameRate().withBaseRate (60); + case fps60drop: return FrameRate().withBaseRate (60).withDrop(); + case fpsUnknown: break; + } + + return {}; + } + + template + FrameRate with (Member&& member, Value&& value) const + { + auto copy = *this; + copy.*member = std::forward (value); + return copy; + } + + int base = 0; + bool drop = false, pulldown = false; + }; + //============================================================================== /** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method. */ @@ -95,7 +187,7 @@ class JUCE_API AudioPlayHead double ppqPositionOfLastBarStart = 0; /** The video frame rate, if applicable. */ - FrameRateType frameRate = FrameRateType::fps23976; + FrameRate frameRate = FrameRateType::fps23976; /** True if the transport is currently playing. */ bool isPlaying = false; @@ -124,7 +216,7 @@ class JUCE_API AudioPlayHead //============================================================================== bool operator== (const CurrentPositionInfo& other) const noexcept { - auto tie = [] (const CurrentPositionInfo& i) + const auto tie = [] (const CurrentPositionInfo& i) { return std::tie (i.timeInSamples, i.ppqPosition, diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp index e7f350605..1c097ccbc 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp @@ -29,7 +29,7 @@ AudioChannelSet::AudioChannelSet (uint32 c) : channels (static_cast (c)) { } -AudioChannelSet::AudioChannelSet (const Array& c) +AudioChannelSet::AudioChannelSet (const std::initializer_list& c) { for (auto channel : c) addChannel (channel); @@ -339,6 +339,8 @@ String AudioChannelSet::getDescription() const if (*this == create5point0()) return "5.0 Surround"; if (*this == create5point1()) return "5.1 Surround"; + if (*this == create5point1point2()) return "5.1.2 Surround"; + if (*this == create5point1point4()) return "5.1.4 Surround"; if (*this == create6point0()) return "6.0 Surround"; if (*this == create6point1()) return "6.1 Surround"; if (*this == create6point0Music()) return "6.0 (Music) Surround"; @@ -348,7 +350,11 @@ String AudioChannelSet::getDescription() const if (*this == create7point0SDDS()) return "7.0 Surround SDDS"; if (*this == create7point1SDDS()) return "7.1 Surround SDDS"; if (*this == create7point0point2()) return "7.0.2 Surround"; + if (*this == create7point0point4()) return "7.0.4 Surround"; if (*this == create7point1point2()) return "7.1.2 Surround"; + if (*this == create7point1point4()) return "7.1.4 Surround"; + if (*this == create7point1point6()) return "7.1.6 Surround"; + if (*this == create9point1point6()) return "9.1.6 Surround"; if (*this == quadraphonic()) return "Quadraphonic"; if (*this == pentagonal()) return "Pentagonal"; @@ -442,29 +448,33 @@ void AudioChannelSet::removeChannel (ChannelType newChannel) } AudioChannelSet AudioChannelSet::disabled() { return {}; } -AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet (1u << centre); } -AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } -AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); } -AudioChannelSet AudioChannelSet::createLRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surround)); } -AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); } -AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); } -AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); } -AudioChannelSet AudioChannelSet::create6point0Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); } -AudioChannelSet AudioChannelSet::create6point1Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); } -AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } -AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } -AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << centreSurround) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround) | (1u << wideLeft) | (1u << wideRight)); } -AudioChannelSet AudioChannelSet::create7point0point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); } -AudioChannelSet AudioChannelSet::create7point1point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); } -AudioChannelSet AudioChannelSet::create7point0point4() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topFrontLeft) | (1u << topFrontRight) | (1u << topRearLeft) | (1u << topRearRight)); } -AudioChannelSet AudioChannelSet::create7point1point4() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topFrontLeft) | (1u << topFrontRight) | (1u << topRearLeft) | (1u << topRearRight)); } +AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet ({ centre }); } +AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ({ left, right }); } +AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ({ left, right, centre }); } +AudioChannelSet AudioChannelSet::createLRS() { return AudioChannelSet ({ left, right, surround }); } +AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ({ left, right, centre, surround }); } +AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround }); } +AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround }); } +AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, centreSurround }); } +AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, centreSurround }); } +AudioChannelSet AudioChannelSet::create6point0Music() { return AudioChannelSet ({ left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide }); } +AudioChannelSet AudioChannelSet::create6point1Music() { return AudioChannelSet ({ left, right, LFE, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide }); } +AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear }); } +AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre }); } +AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear }); } +AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre }); } +AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ({ left, right, leftSurround, rightSurround }); } +AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ({ left, right, centre, leftSurroundRear, rightSurroundRear }); } +AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ({ left, right, centre, centreSurround, leftSurroundRear, rightSurroundRear }); } +AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, centreSurround, wideLeft, wideRight }); } +AudioChannelSet AudioChannelSet::create5point1point2() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight }); } +AudioChannelSet AudioChannelSet::create5point1point4() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create7point0point2() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight }); } +AudioChannelSet AudioChannelSet::create7point1point2() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight }); } +AudioChannelSet AudioChannelSet::create7point0point4() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create7point1point4() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create7point1point6() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create9point1point6() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight }); } AudioChannelSet AudioChannelSet::ambisonic (int order) { diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h index c9c4b03b2..61591cec9 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h @@ -196,6 +196,18 @@ class JUCE_API AudioChannelSet */ static AudioChannelSet JUCE_CALLTYPE create7point1SDDS(); + /** Creates a set for a 5.1.2 surround setup (left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight). + + Is equivalent to: kAudioChannelLayoutTag_Atmos_5_1_2 (CoreAudio). + */ + static AudioChannelSet JUCE_CALLTYPE create5point1point2(); + + /** Creates a set for a 5.1.4 surround setup (left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight). + + Is equivalent to: kAudioChannelLayoutTag_Atmos_5_1_4 (CoreAudio). + */ + static AudioChannelSet JUCE_CALLTYPE create5point1point4(); + /** Creates a set for Dolby Atmos 7.0.2 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight). Is equivalent to: n/a (VST), AAX_eStemFormat_7_0_2 (AAX), n/a (CoreAudio) @@ -204,7 +216,7 @@ class JUCE_API AudioChannelSet /** Creates a set for Dolby Atmos 7.1.2 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topSideLeft, topSideRight). - Is equivalent to: k71_2 (VST), AAX_eStemFormat_7_1_2 (AAX), n/a (CoreAudio) + Is equivalent to: k71_2 (VST), AAX_eStemFormat_7_1_2 (AAX), kAudioChannelLayoutTag_Atmos_7_1_2 (CoreAudio) */ static AudioChannelSet JUCE_CALLTYPE create7point1point2(); @@ -216,10 +228,21 @@ class JUCE_API AudioChannelSet /** Creates a set for Dolby Atmos 7.1.4 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topFrontLeft, topFrontRight, topRearLeft, topRearRight). - Is equivalent to: k71_4 (VST), n/a (AAX), n/a (CoreAudio) + Is equivalent to: k71_4 (VST), n/a (AAX), kAudioChannelLayoutTag_Atmos_7_1_4 (CoreAudio) */ static AudioChannelSet JUCE_CALLTYPE create7point1point4(); + /** Creates a set for Dolby Atmos 7.1.6 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight). + + Is equivalent to: k71_6 (VST), n/a (AAX), n/a (CoreAudio) + */ + static AudioChannelSet JUCE_CALLTYPE create7point1point6(); + + /** Creates a set for a 9.1.6 surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight). + + Is equivalent to: kAudioChannelLayoutTag_Atmos_9_1_6 (CoreAudio). + */ + static AudioChannelSet JUCE_CALLTYPE create9point1point6(); //============================================================================== /** Creates a set for quadraphonic surround setup (left, right, leftSurround, rightSurround) @@ -318,8 +341,8 @@ class JUCE_API AudioChannelSet //============================================================================== // Used by Dolby Atmos 7.0.2 and 7.1.2 - topSideLeft = 28, /**< Lts (AAX), Tsl (VST) channel for Dolby Atmos. */ - topSideRight = 29, /**< Rts (AAX), Tsr (VST) channel for Dolby Atmos. */ + topSideLeft = 28, /**< Lts (AAX), Tsl (VST), Ltm (AU) channel for Dolby Atmos. */ + topSideRight = 29, /**< Rts (AAX), Tsr (VST), Rtm (AU) channel for Dolby Atmos. */ //============================================================================== // Ambisonic ACN formats - all channels are SN3D normalised @@ -487,7 +510,7 @@ class JUCE_API AudioChannelSet //============================================================================== explicit AudioChannelSet (uint32); - explicit AudioChannelSet (const Array&); + explicit AudioChannelSet (const std::initializer_list&); //============================================================================== static int JUCE_CALLTYPE getAmbisonicOrderForNumChannels (int); diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp index cab2c4d95..b2ce01b1b 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp @@ -23,6 +23,9 @@ namespace juce { +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample) { auto maxVal = (double) 0x7fff; @@ -431,35 +434,22 @@ void AudioDataConverters::convertFormatToFloat (DataFormat sourceFormat, const v //============================================================================== void AudioDataConverters::interleaveSamples (const float** source, float* dest, int numSamples, int numChannels) { - for (int chan = 0; chan < numChannels; ++chan) - { - auto i = chan; - auto src = source [chan]; + using Format = AudioData::Format; - for (int j = 0; j < numSamples; ++j) - { - dest [i] = src [j]; - i += numChannels; - } - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { source, numChannels }, + AudioData::InterleavedDest { dest, numChannels }, + numSamples); } void AudioDataConverters::deinterleaveSamples (const float* source, float** dest, int numSamples, int numChannels) { - for (int chan = 0; chan < numChannels; ++chan) - { - auto i = chan; - auto dst = dest [chan]; + using Format = AudioData::Format; - for (int j = 0; j < numSamples; ++j) - { - dst [j] = source [i]; - i += numChannels; - } - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { source, numChannels }, + AudioData::NonInterleavedDest { dest, numChannels }, + numSamples); } - //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -588,6 +578,50 @@ class AudioConversionTests : public UnitTest Test1 ::test (*this, r); beginTest ("Round-trip conversion: Float32"); Test1 ::test (*this, r); + + using Format = AudioData::Format; + + beginTest ("Interleaving"); + { + constexpr auto numChannels = 4; + constexpr auto numSamples = 512; + + AudioBuffer sourceBuffer { numChannels, numSamples }, + destBuffer { 1, numChannels * numSamples }; + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + sourceBuffer.setSample (ch, i, r.nextFloat()); + + AudioData::interleaveSamples (AudioData::NonInterleavedSource { sourceBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { destBuffer.getWritePointer (0), numChannels }, + numSamples); + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + expect (destBuffer.getSample (0, ch + (i * numChannels)) == sourceBuffer.getSample (ch, i)); + } + + beginTest ("Deinterleaving"); + { + constexpr auto numChannels = 4; + constexpr auto numSamples = 512; + + AudioBuffer sourceBuffer { 1, numChannels * numSamples }, + destBuffer { numChannels, numSamples }; + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + sourceBuffer.setSample (0, ch + (i * numChannels), r.nextFloat()); + + AudioData::deinterleaveSamples (AudioData::InterleavedSource { sourceBuffer.getReadPointer (0), numChannels }, + AudioData::NonInterleavedDest { destBuffer.getArrayOfWritePointers(), numChannels }, + numSamples); + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + expect (sourceBuffer.getSample (0, ch + (i * numChannels)) == destBuffer.getSample (ch, i)); + } } }; @@ -595,4 +629,7 @@ static AudioConversionTests audioConversionUnitTests; #endif +JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h index ddcaa7976..c84469043 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h @@ -639,11 +639,152 @@ class JUCE_API AudioData const int sourceChannels, destChannels; }; -}; + //============================================================================== + /** A struct that contains a SampleFormat and Endianness to be used with the source and + destination types when calling the interleaveSamples() and deinterleaveSamples() helpers. + + @see interleaveSamples, deinterleaveSamples + */ + template + struct Format + { + using DataFormat = DataFormatIn; + using Endianness = EndiannessIn; + }; + +private: + template + struct ChannelDataSubtypes; + + template + struct ChannelDataSubtypes + { + using ElementType = std::remove_pointer_t; + using ChannelType = std::conditional_t; + using DataType = std::conditional_t; + using PointerType = Pointer, + std::conditional_t>; + }; + + template + struct ChannelDataSubtypes> + { + using Subtypes = ChannelDataSubtypes; + using DataType = typename Subtypes::DataType; + using PointerType = typename Subtypes::PointerType; + }; + + template + struct ChannelData + { + using Subtypes = ChannelDataSubtypes; + using DataType = typename Subtypes::DataType; + using PointerType = typename Subtypes::PointerType; + DataType data; + int channels; + }; + +public: + //============================================================================== + /** A sequence of interleaved samples used as the source for the deinterleaveSamples() method. */ + template using InterleavedSource = ChannelData; + /** A sequence of interleaved samples used as the destination for the interleaveSamples() method. */ + template using InterleavedDest = ChannelData; + /** A sequence of non-interleaved samples used as the source for the interleaveSamples() method. */ + template using NonInterleavedSource = ChannelData; + /** A sequence of non-interleaved samples used as the destination for the deinterleaveSamples() method. */ + template using NonInterleavedDest = ChannelData; + + /** A helper function for converting a sequence of samples from a non-interleaved source + to an interleaved destination. + + When calling this method you need to specify the source and destination data format and endianness + from the AudioData SampleFormat and Endianness types and provide the data and number of channels + for each. For example, to convert a floating-point stream of big endian samples to an interleaved, + native endian stream of 16-bit integer samples you would do the following: + + @code + using SourceFormat = AudioData::Format; + using DestFormat = AudioData::Format; + + AudioData::interleaveSamples (AudioData::NonInterleavedSource { sourceData, numSourceChannels }, + AudioData::InterleavedDest { destData, numDestChannels }, + numSamples); + @endcode + */ + template + static void interleaveSamples (NonInterleavedSource source, + InterleavedDest dest, + int numSamples) + { + using SourceType = typename decltype (source)::PointerType; + using DestType = typename decltype (dest) ::PointerType; + + for (int i = 0; i < dest.channels; ++i) + { + const DestType destType (addBytesToPointer (dest.data, i * DestType::getBytesPerSample()), dest.channels); + + if (i < source.channels) + { + if (*source.data != nullptr) + { + destType.convertSamples (SourceType { *source.data }, numSamples); + ++source.data; + } + } + else + { + destType.clearSamples (numSamples); + } + } + } + + /** A helper function for converting a sequence of samples from an interleaved source + to a non-interleaved destination. + + When calling this method you need to specify the source and destination data format and endianness + from the AudioData SampleFormat and Endianness types and provide the data and number of channels + for each. For example, to convert a floating-point stream of big endian samples to an non-interleaved, + native endian stream of 16-bit integer samples you would do the following: + + @code + using SourceFormat = AudioData::Format; + using DestFormat = AudioData::Format; + + AudioData::deinterleaveSamples (AudioData::InterleavedSource { sourceData, numSourceChannels }, + AudioData::NonInterleavedDest { destData, numDestChannels }, + numSamples); + @endcode + */ + template + static void deinterleaveSamples (InterleavedSource source, + NonInterleavedDest dest, + int numSamples) + { + using SourceType = typename decltype (source)::PointerType; + using DestType = typename decltype (dest) ::PointerType; + + for (int i = 0; i < dest.channels; ++i) + { + if (auto* targetChan = dest.data[i]) + { + const DestType destType (targetChan); + + if (i < source.channels) + destType.convertSamples (SourceType (addBytesToPointer (source.data, i * SourceType::getBytesPerSample()), source.channels), numSamples); + else + destType.clearSamples (numSamples); + } + } + } +}; //============================================================================== +#ifndef DOXYGEN /** A set of routines to convert buffers of 32-bit floating point data to and from various integer formats. @@ -653,7 +794,7 @@ class JUCE_API AudioData @tags{Audio} */ -class JUCE_API AudioDataConverters +class [[deprecated]] JUCE_API AudioDataConverters { public: //============================================================================== @@ -710,7 +851,7 @@ class JUCE_API AudioDataConverters private: AudioDataConverters(); - JUCE_DECLARE_NON_COPYABLE (AudioDataConverters) }; +#endif } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp index 05f30455d..eb2349e90 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp @@ -23,8 +23,8 @@ namespace juce { -AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() {} -AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() {} +AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() = default; +AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() = default; void AudioProcessLoadMeasurer::reset() { @@ -33,43 +33,55 @@ void AudioProcessLoadMeasurer::reset() void AudioProcessLoadMeasurer::reset (double sampleRate, int blockSize) { - cpuUsageMs = 0; + cpuUsageProportion = 0; xruns = 0; if (sampleRate > 0.0 && blockSize > 0) { - msPerBlock = 1000.0 * blockSize / sampleRate; - timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0; + msPerSample = 1000.0 / sampleRate; + timeToCpuScale = (msPerSample > 0.0) ? (1.0 / msPerSample) : 0.0; } else { - msPerBlock = 0; + msPerSample = 0; timeToCpuScale = 0; } } void AudioProcessLoadMeasurer::registerBlockRenderTime (double milliseconds) { - const double filterAmount = 0.2; - cpuUsageMs += filterAmount * (milliseconds - cpuUsageMs); + registerRenderTime (milliseconds, samplesPerBlock); +} + +void AudioProcessLoadMeasurer::registerRenderTime (double milliseconds, int numSamples) +{ + const auto maxMilliseconds = numSamples * msPerSample; + const auto usedProportion = milliseconds / maxMilliseconds; + const auto filterAmount = 0.2; + cpuUsageProportion += filterAmount * (usedProportion - cpuUsageProportion); - if (milliseconds > msPerBlock) + if (milliseconds > maxMilliseconds) ++xruns; } -double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); } +double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, cpuUsageProportion); } double AudioProcessLoadMeasurer::getLoadAsPercentage() const { return 100.0 * getLoadAsProportion(); } int AudioProcessLoadMeasurer::getXRunCount() const { return xruns; } AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p) - : owner (p), startTime (Time::getMillisecondCounterHiRes()) + : ScopedTimer (p, p.samplesPerBlock) +{ +} + +AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p, int numSamplesInBlock) + : owner (p), startTime (Time::getMillisecondCounterHiRes()), samplesInBlock (numSamplesInBlock) { } AudioProcessLoadMeasurer::ScopedTimer::~ScopedTimer() { - owner.registerBlockRenderTime (Time::getMillisecondCounterHiRes() - startTime); + owner.registerRenderTime (Time::getMillisecondCounterHiRes() - startTime, samplesInBlock); } } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h index d6c729e51..62cf44c6b 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h @@ -72,11 +72,13 @@ class JUCE_API AudioProcessLoadMeasurer struct JUCE_API ScopedTimer { ScopedTimer (AudioProcessLoadMeasurer&); + ScopedTimer (AudioProcessLoadMeasurer&, int numSamplesInBlock); ~ScopedTimer(); private: AudioProcessLoadMeasurer& owner; double startTime; + int samplesInBlock; JUCE_DECLARE_NON_COPYABLE (ScopedTimer) }; @@ -87,9 +89,15 @@ class JUCE_API AudioProcessLoadMeasurer */ void registerBlockRenderTime (double millisecondsTaken); + /** Can be called manually to add the time of a callback to the stats. + Normally you probably would never call this - it's simpler and more robust to + use a ScopedTimer to measure the time using an RAII pattern. + */ + void registerRenderTime (double millisecondsTaken, int numSamples); + private: - double cpuUsageMs = 0, timeToCpuScale = 0, msPerBlock = 0; - int xruns = 0; + double cpuUsageProportion = 0, timeToCpuScale = 0, msPerSample = 0; + int xruns = 0, samplesPerBlock = 0; }; diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index c51c8ec87..7917c325c 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -1051,7 +1051,9 @@ intptr_t JUCE_CALLTYPE FloatVectorOperations::getFpStatusRegister() noexcept void JUCE_CALLTYPE FloatVectorOperations::setFpStatusRegister (intptr_t fpsr) noexcept { #if JUCE_INTEL && JUCE_USE_SSE_INTRINSICS - auto fpsr_w = static_cast (fpsr); + // the volatile keyword here is needed to workaround a bug in AppleClang 13.0 + // which aggressively optimises away the variable otherwise + volatile auto fpsr_w = static_cast (fpsr); _mm_setcsr (fpsr_w); #elif defined (__arm64__) || defined (__aarch64__) || JUCE_USE_ARM_NEON #if defined (__arm64__) || defined (__aarch64__) diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h b/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h index 2e1330483..814f7f297 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h @@ -32,7 +32,7 @@ ID: juce_audio_basics vendor: juce - version: 6.1.2 + version: 6.1.3 name: JUCE audio and MIDI data classes description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp index 987b6fb23..c2d58487f 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp @@ -209,13 +209,14 @@ MidiBufferIterator MidiBuffer::findNextSamplePosition (int samplePosition) const } //============================================================================== +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept : buffer (b), iterator (b.data.begin()) { } -MidiBuffer::Iterator::~Iterator() noexcept {} - void MidiBuffer::Iterator::setNextSamplePosition (int samplePosition) noexcept { iterator = buffer.findNextSamplePosition (samplePosition); @@ -244,6 +245,9 @@ bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePositio return true; } +JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h index 6e1c32d1b..cdcca5afa 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h @@ -273,7 +273,9 @@ class JUCE_API MidiBuffer MidiBufferIterator findNextSamplePosition (int samplePosition) const noexcept; //============================================================================== - /** + #ifndef DOXYGEN + /** This class is now deprecated in favour of MidiBufferIterator. + Used to iterate through the events in a MidiBuffer. Note that altering the buffer while an iterator is using it will produce @@ -281,20 +283,12 @@ class JUCE_API MidiBuffer @see MidiBuffer */ - class JUCE_API Iterator + class [[deprecated]] JUCE_API Iterator { public: //============================================================================== - /** Creates an Iterator for this MidiBuffer. - This class has been deprecated in favour of MidiBufferIterator. - */ - JUCE_DEPRECATED (Iterator (const MidiBuffer&) noexcept); - - /** Creates a copy of an iterator. */ - Iterator (const Iterator&) = default; - - /** Destructor. */ - ~Iterator() noexcept; + /** Creates an Iterator for this MidiBuffer. */ + Iterator (const MidiBuffer& b) noexcept; //============================================================================== /** Repositions the iterator so that the next event retrieved will be the first @@ -336,6 +330,7 @@ class JUCE_API MidiBuffer const MidiBuffer& buffer; MidiBufferIterator iterator; }; + #endif /** The raw data holding this buffer. Obviously access to this data is provided at your own risk. Its internal format could diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp index 87a451687..82f71b51e 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp @@ -264,7 +264,6 @@ namespace MidiFileHelpers //============================================================================== MidiFile::MidiFile() : timeFormat ((short) (unsigned short) 0xe728) {} -MidiFile::~MidiFile() {} MidiFile::MidiFile (const MidiFile& other) : timeFormat (other.timeFormat) { diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h index 1d614a242..c38d343d1 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h @@ -45,9 +45,6 @@ class JUCE_API MidiFile /** Creates an empty MidiFile object. */ MidiFile(); - /** Destructor. */ - ~MidiFile(); - /** Creates a copy of another MidiFile. */ MidiFile (const MidiFile&); diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp index da307e58e..83f84c207 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp @@ -28,10 +28,6 @@ MidiKeyboardState::MidiKeyboardState() zerostruct (noteStates); } -MidiKeyboardState::~MidiKeyboardState() -{ -} - //============================================================================== void MidiKeyboardState::reset() { diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h index f0954b5bb..17e4b72d0 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h @@ -43,7 +43,6 @@ class JUCE_API MidiKeyboardState public: //============================================================================== MidiKeyboardState(); - ~MidiKeyboardState(); //============================================================================== /** Resets the state of the object. diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h index 8d0f1f6c1..ce4fb3945 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h @@ -102,7 +102,8 @@ class JUCE_API MidiMessage double timeStamp = 0, bool sysexHasEmbeddedLength = true); - /** Creates an active-sense message. + /** Creates an empty sysex message. + Since the MidiMessage has to contain a valid message, this default constructor just initialises it with an empty sysex message. */ @@ -856,17 +857,16 @@ class JUCE_API MidiMessage //============================================================================== + #ifndef DOXYGEN /** Reads a midi variable-length integer. - This signature has been deprecated in favour of the safer - readVariableLengthValue. - The `data` argument indicates the data to read the number from, and `numBytesUsed` is used as an out-parameter to indicate the number of bytes that were read. */ - JUCE_DEPRECATED (static int readVariableLengthVal (const uint8* data, - int& numBytesUsed) noexcept); + [[deprecated ("This signature has been deprecated in favour of the safer readVariableLengthValue.")]] + static int readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept; + #endif /** Holds information about a variable-length value which was parsed from a stream of bytes. diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp index 5a1480697..2a01f989a 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp @@ -25,7 +25,6 @@ namespace juce MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm) {} MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {} -MidiMessageSequence::MidiEventHolder::~MidiEventHolder() {} //============================================================================== MidiMessageSequence::MidiMessageSequence() @@ -63,10 +62,6 @@ MidiMessageSequence& MidiMessageSequence::operator= (MidiMessageSequence&& other return *this; } -MidiMessageSequence::~MidiMessageSequence() -{ -} - void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept { list.swapWith (other.list); @@ -309,43 +304,182 @@ void MidiMessageSequence::deleteSysExMessages() } //============================================================================== -void MidiMessageSequence::createControllerUpdatesForTime (int channelNumber, double time, Array& dest) +class OptionalPitchWheel { - bool doneProg = false; - bool donePitchWheel = false; - bool doneControllers[128] = {}; + int value = 0; + bool valid = false; - for (int i = list.size(); --i >= 0;) +public: + void emit (int channel, Array& out) const + { + if (valid) + out.add (MidiMessage::pitchWheel (channel, value)); + } + + void set (int v) + { + value = v; + valid = true; + } +}; + +class OptionalControllerValues +{ + int values[128]; + +public: + OptionalControllerValues() + { + std::fill (std::begin (values), std::end (values), -1); + } + + void emit (int channel, Array& out) const + { + for (auto it = std::begin (values); it != std::end (values); ++it) + if (*it != -1) + out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), *it)); + } + + void set (int controller, int value) + { + values[controller] = value; + } +}; + +class OptionalProgramChange +{ + int value = -1, bankLSB = -1, bankMSB = -1; + +public: + void emit (int channel, double time, Array& out) const { - auto& mm = list.getUnchecked(i)->message; + if (value == -1) + return; - if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time) + if (bankLSB != -1 && bankMSB != -1) { - if (mm.isProgramChange() && ! doneProg) - { - doneProg = true; - dest.add (MidiMessage (mm, 0.0)); - } - else if (mm.isPitchWheel() && ! donePitchWheel) + out.add (MidiMessage::controllerEvent (channel, 0x00, bankMSB).withTimeStamp (time)); + out.add (MidiMessage::controllerEvent (channel, 0x20, bankLSB).withTimeStamp (time)); + } + + out.add (MidiMessage::programChange (channel, value).withTimeStamp (time)); + } + + // Returns true if this is a bank number change, and false otherwise. + bool trySetBank (int controller, int v) + { + switch (controller) + { + case 0x00: bankMSB = v; return true; + case 0x20: bankLSB = v; return true; + } + + return false; + } + + void setProgram (int v) { value = v; } +}; + +class ParameterNumberState +{ + enum class Kind { rpn, nrpn }; + + int newestRpnLsb = -1, newestRpnMsb = -1, newestNrpnLsb = -1, newestNrpnMsb = -1; + int lastSentLsb = -1, lastSentMsb = -1; + Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn; + +public: + // If the effective parameter number has changed since the last time this function was called, + // this will emit the current parameter in full (MSB and LSB). + // This should be called before each data message (entry, increment, decrement: 0x06, 0x26, 0x60, 0x61) + // to ensure that the data message operates on the correct parameter number. + void sendIfNecessary (int channel, double time, Array& out) + { + const auto newestMsb = newestKind == Kind::rpn ? newestRpnMsb : newestNrpnMsb; + const auto newestLsb = newestKind == Kind::rpn ? newestRpnLsb : newestNrpnLsb; + + auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb); + const auto newest = std::tie (newestKind, newestMsb, newestLsb); + + if (lastSent == newest || newestMsb == -1 || newestLsb == -1) + return; + + out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, newestMsb).withTimeStamp (time)); + out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x64 : 0x62, newestLsb).withTimeStamp (time)); + + lastSent = newest; + } + + // Returns true if this is a parameter number change, and false otherwise. + bool trySetProgramNumber (int controller, int value) + { + switch (controller) + { + case 0x65: newestRpnMsb = value; newestKind = Kind::rpn; return true; + case 0x64: newestRpnLsb = value; newestKind = Kind::rpn; return true; + case 0x63: newestNrpnMsb = value; newestKind = Kind::nrpn; return true; + case 0x62: newestNrpnLsb = value; newestKind = Kind::nrpn; return true; + } + + return false; + } +}; + +void MidiMessageSequence::createControllerUpdatesForTime (int channel, double time, Array& dest) +{ + OptionalProgramChange programChange; + OptionalControllerValues controllers; + OptionalPitchWheel pitchWheel; + ParameterNumberState parameterNumberState; + + for (const auto& item : list) + { + const auto& mm = item->message; + + if (! (mm.isForChannel (channel) && mm.getTimeStamp() <= time)) + continue; + + if (mm.isController()) + { + const auto num = mm.getControllerNumber(); + + if (parameterNumberState.trySetProgramNumber (num, mm.getControllerValue())) + continue; + + if (programChange.trySetBank (num, mm.getControllerValue())) + continue; + + constexpr int passthroughs[] { 0x06, 0x26, 0x60, 0x61 }; + + if (std::find (std::begin (passthroughs), std::end (passthroughs), num) != std::end (passthroughs)) { - donePitchWheel = true; - dest.add (MidiMessage (mm, 0.0)); + parameterNumberState.sendIfNecessary (channel, mm.getTimeStamp(), dest); + dest.add (mm); } - else if (mm.isController()) + else { - auto controllerNumber = mm.getControllerNumber(); - jassert (isPositiveAndBelow (controllerNumber, 128)); - - if (! doneControllers[controllerNumber]) - { - doneControllers[controllerNumber] = true; - dest.add (MidiMessage (mm, 0.0)); - } + controllers.set (num, mm.getControllerValue()); } } + else if (mm.isProgramChange()) + { + programChange.setProgram (mm.getProgramChangeNumber()); + } + else if (mm.isPitchWheel()) + { + pitchWheel.set (mm.getPitchWheelValue()); + } } -} + pitchWheel.emit (channel, dest); + controllers.emit (channel, dest); + + // Also emits bank change messages if necessary. + programChange.emit (channel, time, dest); + + // Set the parameter number to its final state. + parameterNumberState.sendIfNecessary (channel, time, dest); +} //============================================================================== //============================================================================== @@ -402,6 +536,338 @@ struct MidiMessageSequenceTest : public UnitTest expectEquals (s.getNumEvents(), 7); expectEquals (s.getIndexOfMatchingKeyUp (0), -1); // Truncated note, should be no note off expectEquals (s.getTimeOfMatchingKeyUp (1), 5.0); + + struct ControlValue { int control, value; }; + + struct DataEntry + { + int controllerBase, channel, parameter, value; + double time; + + std::array getControlValues() const + { + return { { { controllerBase + 1, (parameter >> 7) & 0x7f }, + { controllerBase + 0, (parameter >> 0) & 0x7f }, + { 0x06, (value >> 7) & 0x7f }, + { 0x26, (value >> 0) & 0x7f } } }; + } + + void addToSequence (MidiMessageSequence& s) const + { + for (const auto& pair : getControlValues()) + s.addEvent (MidiMessage::controllerEvent (channel, pair.control, pair.value), time); + } + + bool matches (const MidiMessage* begin, const MidiMessage* end) const + { + const auto isEqual = [this] (const ControlValue& cv, const MidiMessage& msg) + { + return msg.getTimeStamp() == time + && msg.isController() + && msg.getChannel() == channel + && msg.getControllerNumber() == cv.control + && msg.getControllerValue() == cv.value; + }; + + const auto pairs = getControlValues(); + return std::equal (pairs.begin(), pairs.end(), begin, end, isEqual); + } + }; + + const auto addNrpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0) + { + DataEntry { 0x62, channel, parameter, value, time }.addToSequence (seq); + }; + + const auto addRpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0) + { + DataEntry { 0x64, channel, parameter, value, time }.addToSequence (seq); + }; + + const auto checkNrpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0) + { + expect (DataEntry { 0x62, channel, parameter, value, time }.matches (begin, end)); + }; + + const auto checkRpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0) + { + expect (DataEntry { 0x64, channel, parameter, value, time }.matches (begin, end)); + }; + + beginTest ("createControllerUpdatesForTime should emit (N)RPN components in the correct order"); + { + const auto channel = 1; + const auto number = 200; + const auto value = 300; + + MidiMessageSequence sequence; + addNrpn (sequence, channel, number, value); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + checkNrpn (m.begin(), m.end(), channel, number, value); + } + + beginTest ("createControllerUpdatesForTime ignores (N)RPNs after the final requested time"); + { + const auto channel = 2; + const auto number = 123; + const auto value = 456; + + MidiMessageSequence sequence; + addRpn (sequence, channel, number, value, 0.5); + addRpn (sequence, channel, 111, 222, 1.5); + addRpn (sequence, channel, 333, 444, 2.5); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + checkRpn (m.begin(), std::next (m.begin(), 4), channel, number, value, 0.5); + } + + beginTest ("createControllerUpdatesForTime should emit separate (N)RPN messages when appropriate"); + { + const auto channel = 2; + const auto numberA = 1111; + const auto valueA = 9999; + + const auto numberB = 8888; + const auto valueB = 2222; + + const auto numberC = 7777; + const auto valueC = 3333; + + const auto numberD = 6666; + const auto valueD = 4444; + + const auto time = 0.5; + + MidiMessageSequence sequence; + addRpn (sequence, channel, numberA, valueA, time); + addRpn (sequence, channel, numberB, valueB, time); + addNrpn (sequence, channel, numberC, valueC, time); + addNrpn (sequence, channel, numberD, valueD, time); + + Array m; + sequence.createControllerUpdatesForTime (channel, time * 2, m); + + checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), channel, numberA, valueA, time); + checkRpn (std::next (m.begin(), 4), std::next (m.begin(), 8), channel, numberB, valueB, time); + checkNrpn (std::next (m.begin(), 8), std::next (m.begin(), 12), channel, numberC, valueC, time); + checkNrpn (std::next (m.begin(), 12), std::next (m.begin(), 16), channel, numberD, valueD, time); + } + + beginTest ("createControllerUpdatesForTime correctly emits (N)RPN messages on multiple channels"); + { + struct Info { int channel, number, value; }; + + const Info infos[] { { 2, 1111, 9999 }, + { 8, 8888, 2222 }, + { 5, 7777, 3333 }, + { 1, 6666, 4444 } }; + + const auto time = 0.5; + + MidiMessageSequence sequence; + + for (const auto& info : infos) + addRpn (sequence, info.channel, info.number, info.value, time); + + for (const auto& info : infos) + { + Array m; + sequence.createControllerUpdatesForTime (info.channel, time * 2, m); + checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), info.channel, info.number, info.value, time); + } + } + + const auto messagesAreEqual = [] (const MidiMessage& a, const MidiMessage& b) + { + return std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(), + b.getRawData(), b.getRawData() + b.getRawDataSize()); + }; + + beginTest ("createControllerUpdatesForTime sends bank select messages when the next program is in a new bank"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + sequence.addEvent (MidiMessage::programChange (channel, 5), time); + + sequence.addEvent (MidiMessage::controllerEvent (channel, 0x00, 128), time); + sequence.addEvent (MidiMessage::controllerEvent (channel, 0x20, 64), time); + sequence.addEvent (MidiMessage::programChange (channel, 63), time); + + const Array finalEvents { MidiMessage::controllerEvent (channel, 0x00, 50), + MidiMessage::controllerEvent (channel, 0x20, 40), + MidiMessage::programChange (channel, 30) }; + + for (const auto& e : finalEvents) + sequence.addEvent (e); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + expect (std::equal (m.begin(), m.end(), finalEvents.begin(), finalEvents.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime preserves all Data Increment and Data Decrement messages"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x60, 0), + MidiMessage::controllerEvent (channel, 0x06, 100), + MidiMessage::controllerEvent (channel, 0x26, 50), + MidiMessage::controllerEvent (channel, 0x60, 10), + MidiMessage::controllerEvent (channel, 0x61, 10), + MidiMessage::controllerEvent (channel, 0x06, 20), + MidiMessage::controllerEvent (channel, 0x26, 30), + MidiMessage::controllerEvent (channel, 0x61, 10), + MidiMessage::controllerEvent (channel, 0x61, 20) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + expect (std::equal (m.begin(), m.end(), messages.begin(), messages.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime does not emit redundant parameter number changes"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x65, 0), + MidiMessage::controllerEvent (channel, 0x64, 100), + MidiMessage::controllerEvent (channel, 0x63, 50), + MidiMessage::controllerEvent (channel, 0x62, 10), + MidiMessage::controllerEvent (channel, 0x06, 10) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + const Array expected { MidiMessage::controllerEvent (channel, 0x63, 50), + MidiMessage::controllerEvent (channel, 0x62, 10), + MidiMessage::controllerEvent (channel, 0x06, 10) }; + + expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime sets parameter number correctly at end of sequence"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x65, 0), + MidiMessage::controllerEvent (channel, 0x64, 100), + MidiMessage::controllerEvent (channel, 0x63, 50), + MidiMessage::controllerEvent (channel, 0x62, 10), + MidiMessage::controllerEvent (channel, 0x06, 10), + MidiMessage::controllerEvent (channel, 0x64, 5) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + const auto finalTime = 1.0; + + Array m; + sequence.createControllerUpdatesForTime (channel, finalTime, m); + + const Array expected { MidiMessage::controllerEvent (channel, 0x63, 50), + MidiMessage::controllerEvent (channel, 0x62, 10), + MidiMessage::controllerEvent (channel, 0x06, 10), + // Note: we should send both the MSB and LSB! + MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime), + MidiMessage::controllerEvent (channel, 0x64, 5).withTimeStamp (finalTime) }; + + expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime does not emit duplicate parameter number change messages"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x65, 1), + MidiMessage::controllerEvent (channel, 0x64, 2), + MidiMessage::controllerEvent (channel, 0x63, 3), + MidiMessage::controllerEvent (channel, 0x62, 4), + MidiMessage::controllerEvent (channel, 0x06, 10), + MidiMessage::controllerEvent (channel, 0x63, 30), + MidiMessage::controllerEvent (channel, 0x62, 40), + MidiMessage::controllerEvent (channel, 0x63, 3), + MidiMessage::controllerEvent (channel, 0x62, 4), + MidiMessage::controllerEvent (channel, 0x60, 5), + MidiMessage::controllerEvent (channel, 0x65, 10) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + const auto finalTime = 1.0; + + Array m; + sequence.createControllerUpdatesForTime (channel, finalTime, m); + + const Array expected { MidiMessage::controllerEvent (channel, 0x63, 3), + MidiMessage::controllerEvent (channel, 0x62, 4), + MidiMessage::controllerEvent (channel, 0x06, 10), + // Parameter number is set to (30, 40) then back to (3, 4), + // so there is no need to resend it + MidiMessage::controllerEvent (channel, 0x60, 5), + // Set parameter number to final value + MidiMessage::controllerEvent (channel, 0x65, 10).withTimeStamp (finalTime), + MidiMessage::controllerEvent (channel, 0x64, 2) .withTimeStamp (finalTime) }; + + expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime emits bank change messages immediately before program change"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x00, 1), + MidiMessage::controllerEvent (channel, 0x20, 2), + MidiMessage::controllerEvent (channel, 0x65, 0), + MidiMessage::controllerEvent (channel, 0x64, 0), + MidiMessage::programChange (channel, 5) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + const auto finalTime = 1.0; + + Array m; + sequence.createControllerUpdatesForTime (channel, finalTime, m); + + const Array expected { MidiMessage::controllerEvent (channel, 0x00, 1), + MidiMessage::controllerEvent (channel, 0x20, 2), + MidiMessage::programChange (channel, 5), + MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime), + MidiMessage::controllerEvent (channel, 0x64, 0).withTimeStamp (finalTime) }; + + + expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual)); + } } }; diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h index 6cef1e7aa..9d2d77fd8 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h @@ -53,9 +53,6 @@ class JUCE_API MidiMessageSequence /** Move assignment operator */ MidiMessageSequence& operator= (MidiMessageSequence&&) noexcept; - /** Destructor. */ - ~MidiMessageSequence(); - //============================================================================== /** Structure used to hold midi events in the sequence. @@ -68,9 +65,6 @@ class JUCE_API MidiMessageSequence { public: //============================================================================== - /** Destructor. */ - ~MidiEventHolder(); - /** The message itself, whose timestamp is used to specify the event's time. */ MidiMessage message; @@ -277,6 +271,21 @@ class JUCE_API MidiMessageSequence As well as controllers, it will also recreate the midi program number and pitch bend position. + This function has special handling for the "bank select" and "data entry" + controllers (0x00, 0x20, 0x06, 0x26, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65). + + If the sequence contains multiple bank select and program change messages, + only the bank select messages immediately preceding the final program change + message will be kept. + + All "data increment" and "data decrement" messages will be retained. Some hardware will + ignore the requested increment/decrement values, so retaining all messages is the only + way to ensure compatibility with all hardware. + + "Parameter number" changes will be slightly condensed. Only the parameter number + events immediately preceding each data entry event will be kept. The parameter number + will also be set to its final value at the end of the sequence, if necessary. + @param channelNumber the midi channel to look for, in the range 1 to 16. Controllers for other channels will be ignored. @param time the time at which you want to find out the state - there are diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h index 07a855c2f..55ed172cf 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h @@ -37,7 +37,11 @@ #include "juce_UMPDispatcher.h" #include "juce_UMPReceiver.h" +#ifndef DOXYGEN + namespace juce { namespace ump = universal_midi_packets; } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h index 8b2ef45d5..5a76099f5 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -324,3 +326,5 @@ struct Conversion } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h index ee99d6a30..1665f71d7 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -163,3 +165,5 @@ namespace universal_midi_packets }; } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h index 792c9a38b..2852c95b7 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -196,3 +198,5 @@ class ToBytestreamDispatcher } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h index f55f40213..09e7e7620 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -532,3 +534,5 @@ struct Factory } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h index efc78a954..02a129995 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -124,3 +126,5 @@ class Iterator } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h index 11c127c56..13a2012c7 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -211,3 +213,5 @@ class Midi1ToBytestreamTranslator } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h index 9d993aab5..f34515adf 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -185,3 +187,5 @@ class Midi1ToMidi2DefaultTranslator } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h index f465166fb..adb00157e 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -42,3 +44,5 @@ enum class MidiProtocol } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h index 3b22d88ee..e72ec7c27 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -40,3 +42,5 @@ struct Receiver } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp index 43e6c2205..99f237af2 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp @@ -39,12 +39,12 @@ SysEx7::PacketBytes SysEx7::getDataBytes (const PacketX2& packet) return { - { packet.getU8<2>(), - packet.getU8<3>(), - packet.getU8<4>(), - packet.getU8<5>(), - packet.getU8<6>(), - packet.getU8<7>() }, + { { packet.getU8<2>(), + packet.getU8<3>(), + packet.getU8<4>(), + packet.getU8<5>(), + packet.getU8<6>(), + packet.getU8<7>() } }, jmin (numBytes, maxBytes) }; } diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h index bb2492453..179a6ed7f 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -71,3 +73,5 @@ struct SysEx7 } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h index 9fd727bd6..fa57b7461 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -111,3 +113,5 @@ struct Utils } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.h index 4e451dc38..a6ef0a5da 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -86,3 +88,5 @@ class View } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPacket.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPacket.h index 603dbd461..138465414 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPacket.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPacket.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -187,3 +189,5 @@ using PacketX4 = Packet<4>; } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPackets.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPackets.h index 33e2dc234..f8eaaefae 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPackets.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPackets.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -90,3 +92,5 @@ class Packets } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h b/external/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h index c91d7d9a6..3c246790d 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h @@ -23,10 +23,140 @@ namespace juce { -#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS) +#if ! defined (DOXYGEN) && (JUCE_MAC || JUCE_IOS) struct CoreAudioLayouts { + //============================================================================== + struct LayoutTagSpeakerList + { + AudioChannelLayoutTag tag; + AudioChannelSet::ChannelType channelTypes[16]; + }; + + //============================================================================== + // This list has been derived from https://pastebin.com/24dQ4BPJ + // Apple channel labels have been replaced by JUCE channel names + // This means that some layouts will be identical in JUCE but not in CoreAudio + + // In Apple's official definition the following tags exist with the same speaker layout and order + // even when *not* represented in JUCE channels + // kAudioChannelLayoutTag_Binaural = kAudioChannelLayoutTag_Stereo + // kAudioChannelLayoutTag_MPEG_5_0_B = kAudioChannelLayoutTag_Pentagonal + // kAudioChannelLayoutTag_ITU_2_2 = kAudioChannelLayoutTag_Quadraphonic + // kAudioChannelLayoutTag_AudioUnit_6_0 = kAudioChannelLayoutTag_Hexagonal + struct SpeakerLayoutTable : AudioChannelSet // save us some typing + { + template + static constexpr auto getArray (Items... items) + { + return std::array { { items... } }; + } + + static constexpr auto get() + { + using List = LayoutTagSpeakerList; + + return getArray (List { kAudioChannelLayoutTag_Mono, { centre } }, + List { kAudioChannelLayoutTag_Stereo, { left, right } }, + List { kAudioChannelLayoutTag_MPEG_3_0_A, { left, right, centre } }, + List { kAudioChannelLayoutTag_ITU_2_1, { left, right, centreSurround } }, + List { kAudioChannelLayoutTag_MPEG_4_0_A, { left, right, centre, centreSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_0_A, { left, right, centre, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_1_A, { left, right, centre, LFE, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_AudioUnit_6_0, { left, right, leftSurround, rightSurround, centre, centreSurround } }, + List { kAudioChannelLayoutTag_MPEG_6_1_A, { left, right, centre, LFE, leftSurround, rightSurround, centreSurround } }, + List { kAudioChannelLayoutTag_DTS_6_0_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DTS_6_1_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_AudioUnit_7_0, { left, right, leftSurroundSide, rightSurroundSide, centre, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_AudioUnit_7_0_Front, { left, right, leftSurround, rightSurround, centre, leftCentre, rightCentre } }, + List { kAudioChannelLayoutTag_MPEG_7_1_C, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_MPEG_7_1_A, { left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre } }, + List { kAudioChannelLayoutTag_Ambisonic_B_Format, { ambisonicW, ambisonicX, ambisonicY, ambisonicZ } }, + List { kAudioChannelLayoutTag_Quadraphonic, { left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_Pentagonal, { left, right, leftSurroundRear, rightSurroundRear, centre } }, + List { kAudioChannelLayoutTag_Hexagonal, { left, right, leftSurroundRear, rightSurroundRear, centre, centreSurround } }, + List { kAudioChannelLayoutTag_Octagonal, { left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight } }, + + #if defined (MAC_OS_VERSION_11_0) + List { kAudioChannelLayoutTag_Atmos_5_1_4, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, + List { kAudioChannelLayoutTag_Atmos_7_1_2, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight } }, + #endif + + #if defined (MAC_OS_X_VERSION_10_15) + List { kAudioChannelLayoutTag_Atmos_5_1_2, { left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight } }, + List { kAudioChannelLayoutTag_Atmos_7_1_4, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, + List { kAudioChannelLayoutTag_Atmos_9_1_6, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight } }, + #endif + + // More uncommon layouts... + List { kAudioChannelLayoutTag_StereoHeadphones, { left, right } }, + List { kAudioChannelLayoutTag_MatrixStereo, { left, right } }, + List { kAudioChannelLayoutTag_MidSide, { centre, discreteChannel0 } }, + List { kAudioChannelLayoutTag_XY, { ambisonicX, ambisonicY } }, + List { kAudioChannelLayoutTag_Binaural, { left, right } }, + List { kAudioChannelLayoutTag_Cube, { left, right, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, + List { kAudioChannelLayoutTag_MPEG_3_0_B, { centre, left, right } }, + List { kAudioChannelLayoutTag_MPEG_4_0_B, { centre, left, right, centreSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_0_B, { left, right, leftSurround, rightSurround, centre } }, + List { kAudioChannelLayoutTag_MPEG_5_0_C, { left, centre, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_0_D, { centre, left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_1_B, { left, right, leftSurround, rightSurround, centre, LFE } }, + List { kAudioChannelLayoutTag_MPEG_5_1_C, { left, centre, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_MPEG_5_1_D, { centre, left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_MPEG_7_1_B, { centre, leftCentre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_Emagic_Default_7_1, { left, right, leftSurround, rightSurround, centre, LFE, leftCentre, rightCentre } }, + List { kAudioChannelLayoutTag_SMPTE_DTV, { left, right, centre, LFE, leftSurround, rightSurround, discreteChannel0 /* leftMatrixTotal */, (ChannelType) (discreteChannel0 + 1) /* rightMatrixTotal */} }, + List { kAudioChannelLayoutTag_ITU_2_2, { left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DVD_4, { left, right, LFE } }, + List { kAudioChannelLayoutTag_DVD_5, { left, right, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_DVD_6, { left, right, LFE, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DVD_10, { left, right, centre, LFE } }, + List { kAudioChannelLayoutTag_DVD_11, { left, right, centre, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_DVD_18, { left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_AAC_6_0, { centre, left, right, leftSurround, rightSurround, centreSurround } }, + List { kAudioChannelLayoutTag_AAC_6_1, { centre, left, right, leftSurround, rightSurround, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_AAC_7_0, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_AAC_7_1_B, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, + List { kAudioChannelLayoutTag_AAC_7_1_C, { centre, left, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, + List { kAudioChannelLayoutTag_AAC_Octagonal, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, centreSurround } }, + List { kAudioChannelLayoutTag_TMH_10_2_std, { left, right, centre, topFrontCentre, leftSurroundSide, rightSurroundSide, leftSurround, rightSurround, topFrontLeft, topFrontRight, wideLeft, wideRight, topRearCentre, centreSurround, LFE, LFE2 } }, + List { kAudioChannelLayoutTag_AC3_1_0_1, { centre, LFE } }, + List { kAudioChannelLayoutTag_AC3_3_0, { left, centre, right } }, + List { kAudioChannelLayoutTag_AC3_3_1, { left, centre, right, centreSurround } }, + List { kAudioChannelLayoutTag_AC3_3_0_1, { left, centre, right, LFE } }, + List { kAudioChannelLayoutTag_AC3_2_1_1, { left, right, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_AC3_3_1_1, { left, centre, right, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_EAC_6_0_A, { left, centre, right, leftSurround, rightSurround, centreSurround } }, + List { kAudioChannelLayoutTag_EAC_7_0_A, { left, centre, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_EAC3_6_1_A, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_EAC3_6_1_B, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_EAC3_6_1_C, { left, centre, right, leftSurround, rightSurround, LFE, topFrontCentre } }, + List { kAudioChannelLayoutTag_EAC3_7_1_A, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_EAC3_7_1_B, { left, centre, right, leftSurround, rightSurround, LFE, leftCentre, rightCentre } }, + List { kAudioChannelLayoutTag_EAC3_7_1_C, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundSide, rightSurroundSide } }, + List { kAudioChannelLayoutTag_EAC3_7_1_D, { left, centre, right, leftSurround, rightSurround, LFE, wideLeft, wideRight } }, + List { kAudioChannelLayoutTag_EAC3_7_1_E, { left, centre, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, + List { kAudioChannelLayoutTag_EAC3_7_1_F, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topMiddle } }, + List { kAudioChannelLayoutTag_EAC3_7_1_G, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, + List { kAudioChannelLayoutTag_EAC3_7_1_H, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, + List { kAudioChannelLayoutTag_DTS_3_1, { centre, left, right, LFE } }, + List { kAudioChannelLayoutTag_DTS_4_1, { centre, left, right, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_DTS_6_0_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround } }, + List { kAudioChannelLayoutTag_DTS_6_0_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_DTS_6_1_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_DTS_6_1_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear, LFE } }, + List { kAudioChannelLayoutTag_DTS_6_1_D, { centre, left, right, leftSurround, rightSurround, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_DTS_7_0, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DTS_7_1, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_DTS_8_0_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_DTS_8_0_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DTS_8_1_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, + List { kAudioChannelLayoutTag_DTS_8_1_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround, LFE } }); + } + }; + +public: //============================================================================== enum { @@ -61,16 +191,16 @@ struct CoreAudioLayouts if (set.getAmbisonicOrder() >= 0) return coreAudioHOASN3DLayoutTag | static_cast (set.size()); - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) + for (const auto& item : SpeakerLayoutTable::get()) { AudioChannelSet caSet; - for (int i = 0; i < numElementsInArray (tbl->channelTypes) - && tbl->channelTypes[i] != AudioChannelSet::unknown; ++i) - caSet.addChannel (tbl->channelTypes[i]); + for (int i = 0; i < numElementsInArray (item.channelTypes) + && item.channelTypes[i] != AudioChannelSet::unknown; ++i) + caSet.addChannel (item.channelTypes[i]); if (caSet == set) - return tbl->tag; + return item.tag; } return kAudioChannelLayoutTag_DiscreteInOrder | static_cast (set.size()); @@ -121,13 +251,13 @@ struct CoreAudioLayouts Array speakers; - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) + for (const auto& item : SpeakerLayoutTable::get()) { - if (tag == tbl->tag) + if (tag == item.tag) { - for (int i = 0; i < numElementsInArray (tbl->channelTypes) - && tbl->channelTypes[i] != AudioChannelSet::unknown; ++i) - speakers.add (tbl->channelTypes[i]); + for (int i = 0; i < numElementsInArray (item.channelTypes) + && item.channelTypes[i] != AudioChannelSet::unknown; ++i) + speakers.add (item.channelTypes[i]); return speakers; } @@ -150,19 +280,12 @@ struct CoreAudioLayouts } private: - //============================================================================== - struct LayoutTagSpeakerList - { - AudioChannelLayoutTag tag; - AudioChannelSet::ChannelType channelTypes[16]; - }; - static Array createKnownCoreAudioTags() { Array tags; - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) - tags.addIfNotAlreadyThere (tbl->tag); + for (const auto& item : SpeakerLayoutTable::get()) + tags.addIfNotAlreadyThere (item.tag); for (unsigned order = 0; order <= 5; ++order) tags.addIfNotAlreadyThere (coreAudioHOASN3DLayoutTag | ((order + 1) * (order + 1))); @@ -170,115 +293,6 @@ struct CoreAudioLayouts return tags; } - //============================================================================== - // This list has been derived from https://pastebin.com/24dQ4BPJ - // Apple channel labels have been replaced by JUCE channel names - // This means that some layouts will be identical in JUCE but not in CoreAudio - - // In Apple's official definition the following tags exist with the same speaker layout and order - // even when *not* represented in JUCE channels - // kAudioChannelLayoutTag_Binaural = kAudioChannelLayoutTag_Stereo - // kAudioChannelLayoutTag_MPEG_5_0_B = kAudioChannelLayoutTag_Pentagonal - // kAudioChannelLayoutTag_ITU_2_2 = kAudioChannelLayoutTag_Quadraphonic - // kAudioChannelLayoutTag_AudioUnit_6_0 = kAudioChannelLayoutTag_Hexagonal - struct SpeakerLayoutTable : AudioChannelSet // save us some typing - { - static LayoutTagSpeakerList* get() noexcept - { - static LayoutTagSpeakerList tbl[] = { - // list layouts for which there is a corresponding named AudioChannelSet first - { kAudioChannelLayoutTag_Mono, { centre } }, - { kAudioChannelLayoutTag_Stereo, { left, right } }, - { kAudioChannelLayoutTag_MPEG_3_0_A, { left, right, centre } }, - { kAudioChannelLayoutTag_ITU_2_1, { left, right, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_4_0_A, { left, right, centre, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_A, { left, right, centre, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_1_A, { left, right, centre, LFE, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_AudioUnit_6_0, { left, right, leftSurround, rightSurround, centre, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_6_1_A, { left, right, centre, LFE, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_DTS_6_0_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_6_1_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_AudioUnit_7_0, { left, right, leftSurroundSide, rightSurroundSide, centre, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_AudioUnit_7_0_Front, { left, right, leftSurround, rightSurround, centre, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_MPEG_7_1_C, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_MPEG_7_1_A, { left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_Ambisonic_B_Format, { ambisonicW, ambisonicX, ambisonicY, ambisonicZ } }, - { kAudioChannelLayoutTag_Quadraphonic, { left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_Pentagonal, { left, right, leftSurroundRear, rightSurroundRear, centre } }, - { kAudioChannelLayoutTag_Hexagonal, { left, right, leftSurroundRear, rightSurroundRear, centre, centreSurround } }, - { kAudioChannelLayoutTag_Octagonal, { left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight } }, - - // more uncommon layouts - { kAudioChannelLayoutTag_StereoHeadphones, { left, right } }, - { kAudioChannelLayoutTag_MatrixStereo, { left, right } }, - { kAudioChannelLayoutTag_MidSide, { centre, discreteChannel0 } }, - { kAudioChannelLayoutTag_XY, { ambisonicX, ambisonicY } }, - { kAudioChannelLayoutTag_Binaural, { left, right } }, - { kAudioChannelLayoutTag_Cube, { left, right, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, - { kAudioChannelLayoutTag_MPEG_3_0_B, { centre, left, right } }, - { kAudioChannelLayoutTag_MPEG_4_0_B, { centre, left, right, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_B, { left, right, leftSurround, rightSurround, centre } }, - { kAudioChannelLayoutTag_MPEG_5_0_C, { left, centre, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_D, { centre, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_1_B, { left, right, leftSurround, rightSurround, centre, LFE } }, - { kAudioChannelLayoutTag_MPEG_5_1_C, { left, centre, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_MPEG_5_1_D, { centre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_MPEG_7_1_B, { centre, leftCentre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_Emagic_Default_7_1, { left, right, leftSurround, rightSurround, centre, LFE, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_SMPTE_DTV, { left, right, centre, LFE, leftSurround, rightSurround, discreteChannel0 /* leftMatrixTotal */, (ChannelType) (discreteChannel0 + 1) /* rightMatrixTotal */} }, - { kAudioChannelLayoutTag_ITU_2_2, { left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DVD_4, { left, right, LFE } }, - { kAudioChannelLayoutTag_DVD_5, { left, right, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DVD_6, { left, right, LFE, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DVD_10, { left, right, centre, LFE } }, - { kAudioChannelLayoutTag_DVD_11, { left, right, centre, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DVD_18, { left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_AAC_6_0, { centre, left, right, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_AAC_6_1, { centre, left, right, leftSurround, rightSurround, centreSurround, LFE } }, - { kAudioChannelLayoutTag_AAC_7_0, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_AAC_7_1_B, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_AAC_7_1_C, { centre, left, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, - { kAudioChannelLayoutTag_AAC_Octagonal, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, centreSurround } }, - { kAudioChannelLayoutTag_TMH_10_2_std, { left, right, centre, topFrontCentre, leftSurroundSide, rightSurroundSide, leftSurround, rightSurround, topFrontLeft, topFrontRight, wideLeft, wideRight, topRearCentre, centreSurround, LFE, LFE2 } }, - { kAudioChannelLayoutTag_AC3_1_0_1, { centre, LFE } }, - { kAudioChannelLayoutTag_AC3_3_0, { left, centre, right } }, - { kAudioChannelLayoutTag_AC3_3_1, { left, centre, right, centreSurround } }, - { kAudioChannelLayoutTag_AC3_3_0_1, { left, centre, right, LFE } }, - { kAudioChannelLayoutTag_AC3_2_1_1, { left, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_AC3_3_1_1, { left, centre, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_EAC_6_0_A, { left, centre, right, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_EAC_7_0_A, { left, centre, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_EAC3_6_1_A, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_EAC3_6_1_B, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_EAC3_6_1_C, { left, centre, right, leftSurround, rightSurround, LFE, topFrontCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_A, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_EAC3_7_1_B, { left, centre, right, leftSurround, rightSurround, LFE, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_C, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundSide, rightSurroundSide } }, - { kAudioChannelLayoutTag_EAC3_7_1_D, { left, centre, right, leftSurround, rightSurround, LFE, wideLeft, wideRight } }, - { kAudioChannelLayoutTag_EAC3_7_1_E, { left, centre, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, - { kAudioChannelLayoutTag_EAC3_7_1_F, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topMiddle } }, - { kAudioChannelLayoutTag_EAC3_7_1_G, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_H, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, - { kAudioChannelLayoutTag_DTS_3_1, { centre, left, right, LFE } }, - { kAudioChannelLayoutTag_DTS_4_1, { centre, left, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_6_0_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround } }, - { kAudioChannelLayoutTag_DTS_6_0_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_DTS_6_1_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_6_1_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_DTS_6_1_D, { centre, left, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DTS_7_0, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_7_1, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_8_0_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_DTS_8_0_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_8_1_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_DTS_8_1_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround, LFE } }, - { 0, {} } - }; - - return tbl; - } - }; - //============================================================================== static AudioChannelSet::ChannelType getChannelTypeFromAudioChannelLabel (AudioChannelLabel label) noexcept { diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/external/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index 218cfe6b6..1934b6eae 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -631,14 +631,6 @@ class JUCE_API Synthesiser template void processNextBlock (AudioBuffer&, const MidiBuffer&, int startSample, int numSamples); - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note the new parameters for these methods. - virtual int findFreeVoice (const bool) const { return 0; } - virtual int noteOff (int, int, int) { return 0; } - virtual int findFreeVoice (SynthesiserSound*, const bool) { return 0; } - virtual int findVoiceToSteal (SynthesiserSound*) const { return 0; } - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) }; diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h b/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h index 45a27d306..8c9a7f3ce 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h @@ -124,6 +124,7 @@ class JUCE_API ADSR } else { + envelopeVal = parameters.sustain; state = State::sustain; } } diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp index f03809ab1..a6603c988 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp @@ -152,6 +152,20 @@ struct ADSRTests : public UnitTest expect (isSustained (buffer, parameters.sustain)); } + beginTest ("Zero-length attack and decay releases correctly"); + { + adsr.reset(); + adsr.setParameters ({ 0.0f, 0.0f, parameters.sustain, parameters.release }); + + adsr.noteOn(); + adsr.noteOff(); + + auto buffer = getTestBuffer (sampleRate, parameters.release); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isDecreasing (buffer)); + } + beginTest ("Zero-length release resets to idle"); { adsr.reset(); diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h b/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h index 6f126c828..9cf36225e 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h @@ -330,9 +330,8 @@ class SmoothedValue : public SmoothedValueBase lsv.setTargetValue (x); lsv.setValue (x, true); -> lsv.setCurrentAndTargetValue (x); @@ -340,7 +339,8 @@ class SmoothedValue : public SmoothedValueBase +static auto getSetupInfo (Setup& s, bool isInput) +{ + struct SetupInfo + { + // double brackets so that we get the expression type, i.e. a (possibly const) reference + decltype ((s.inputDeviceName)) name; + decltype ((s.inputChannels)) channels; + decltype ((s.useDefaultInputChannels)) useDefault; + }; + + return isInput ? SetupInfo { s.inputDeviceName, s.inputChannels, s.useDefaultInputChannels } + : SetupInfo { s.outputDeviceName, s.outputChannels, s.useDefaultOutputChannels }; +} + +static auto tie (const AudioDeviceManager::AudioDeviceSetup& s) +{ + return std::tie (s.outputDeviceName, + s.inputDeviceName, + s.sampleRate, + s.bufferSize, + s.inputChannels, + s.useDefaultInputChannels, + s.outputChannels, + s.useDefaultOutputChannels); +} + bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const { - return outputDeviceName == other.outputDeviceName - && inputDeviceName == other.inputDeviceName - && sampleRate == other.sampleRate - && bufferSize == other.bufferSize - && inputChannels == other.inputChannels - && useDefaultInputChannels == other.useDefaultInputChannels - && outputChannels == other.outputChannels - && useDefaultOutputChannels == other.useDefaultOutputChannels; + return tie (*this) == tie (other); } bool AudioDeviceManager::AudioDeviceSetup::operator!= (const AudioDeviceManager::AudioDeviceSetup& other) const { - return ! operator== (other); + return tie (*this) != tie (other); } //============================================================================== @@ -109,11 +129,33 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() types.clear (false); - if (auto* first = availableDeviceTypes.getFirst()) - currentDeviceType = first->getTypeName(); + for (auto* type : availableDeviceTypes) + type->scanForDevices(); + + pickCurrentDeviceTypeWithDevices(); } } +void AudioDeviceManager::pickCurrentDeviceTypeWithDevices() +{ + const auto deviceTypeHasDevices = [] (const AudioIODeviceType* ptr) + { + return ! ptr->getDeviceNames (true) .isEmpty() + || ! ptr->getDeviceNames (false).isEmpty(); + }; + + if (auto* type = findType (currentDeviceType)) + if (deviceTypeHasDevices (type)) + return; + + const auto iter = std::find_if (availableDeviceTypes.begin(), + availableDeviceTypes.end(), + deviceTypeHasDevices); + + if (iter != availableDeviceTypes.end()) + currentDeviceType = (*iter)->getTypeName(); +} + const OwnedArray& AudioDeviceManager::getAvailableDeviceTypes() { scanDevicesIfNeeded(); @@ -244,6 +286,7 @@ String AudioDeviceManager::initialise (const int numInputChannelsNeeded, const AudioDeviceSetup* preferredSetupOptions) { scanDevicesIfNeeded(); + pickCurrentDeviceTypeWithDevices(); numInputChansNeeded = numInputChannelsNeeded; numOutputChansNeeded = numOutputChannelsNeeded; @@ -267,26 +310,67 @@ String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDevi } else if (preferredDefaultDeviceName.isNotEmpty()) { - for (auto* type : availableDeviceTypes) + const auto nameMatches = [&preferredDefaultDeviceName] (const String& name) + { + return name.matchesWildcard (preferredDefaultDeviceName, true); + }; + + struct WildcardMatch + { + String value; + bool successful; + }; + + const auto getWildcardMatch = [&nameMatches] (const StringArray& names) + { + const auto iter = std::find_if (names.begin(), names.end(), nameMatches); + return WildcardMatch { iter != names.end() ? *iter : String(), iter != names.end() }; + }; + + struct WildcardMatches + { + WildcardMatch input, output; + }; + + const auto getMatchesForType = [&getWildcardMatch] (const AudioIODeviceType* type) { - for (auto& out : type->getDeviceNames (false)) + return WildcardMatches { getWildcardMatch (type->getDeviceNames (true)), + getWildcardMatch (type->getDeviceNames (false)) }; + }; + + struct SearchResult + { + String type, input, output; + }; + + const auto result = [&] + { + // First, look for a device type with an input and output which matches the preferred name + for (auto* type : availableDeviceTypes) { - if (out.matchesWildcard (preferredDefaultDeviceName, true)) - { - setup.outputDeviceName = out; - break; - } + const auto matches = getMatchesForType (type); + + if (matches.input.successful && matches.output.successful) + return SearchResult { type->getTypeName(), matches.input.value, matches.output.value }; } - for (auto& in : type->getDeviceNames (true)) + // No device type has matching ins and outs, so fall back to a device where either the + // input or output match + for (auto* type : availableDeviceTypes) { - if (in.matchesWildcard (preferredDefaultDeviceName, true)) - { - setup.inputDeviceName = in; - break; - } + const auto matches = getMatchesForType (type); + + if (matches.input.successful || matches.output.successful) + return SearchResult { type->getTypeName(), matches.input.value, matches.output.value }; } - } + + // No devices match the query, so just use the default devices from the current type + return SearchResult { currentDeviceType, {}, {} }; + }(); + + currentDeviceType = result.type; + setup.inputDeviceName = result.input; + setup.outputDeviceName = result.output; } insertDefaultDeviceNames (setup); @@ -415,11 +499,14 @@ void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) cons { if (auto* type = getCurrentDeviceTypeObject()) { - if (numOutputChansNeeded > 0 && setup.outputDeviceName.isEmpty()) - setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)]; + for (const auto isInput : { false, true }) + { + const auto numChannelsNeeded = isInput ? numInputChansNeeded : numOutputChansNeeded; + const auto info = getSetupInfo (setup, isInput); - if (numInputChansNeeded > 0 && setup.inputDeviceName.isEmpty()) - setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)]; + if (numChannelsNeeded > 0 && info.name.isEmpty()) + info.name = type->getDeviceNames (isInput) [type->getDefaultDeviceIndex (isInput)]; + } } } @@ -565,20 +652,24 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup String error; - if (currentSetup.inputDeviceName != newSetup.inputDeviceName - || currentSetup.outputDeviceName != newSetup.outputDeviceName - || currentAudioDevice == nullptr) + const auto needsNewDevice = currentSetup.inputDeviceName != newSetup.inputDeviceName + || currentSetup.outputDeviceName != newSetup.outputDeviceName + || currentAudioDevice == nullptr; + + if (needsNewDevice) { deleteCurrentDevice(); scanDevicesIfNeeded(); auto* type = getCurrentDeviceTypeObject(); - if (newSetup.outputDeviceName.isNotEmpty() && ! deviceListContains (type, false, newSetup.outputDeviceName)) - return "No such device: " + newSetup.outputDeviceName; + for (const auto isInput : { false, true }) + { + const auto name = getSetupInfo (newSetup, isInput).name; - if (newSetup.inputDeviceName.isNotEmpty() && ! deviceListContains (type, true, newSetup.inputDeviceName)) - return "No such device: " + newSetup.inputDeviceName; + if (name.isNotEmpty() && ! deviceListContains (type, isInput, name)) + return "No such device: " + name; + } currentAudioDevice.reset (type->createDevice (newSetup.outputDeviceName, newSetup.inputDeviceName)); @@ -818,11 +909,10 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat const ScopedLock sl (audioCallbackLock); inputLevelGetter->updateLevel (inputChannelData, numInputChannels, numSamples); - outputLevelGetter->updateLevel (const_cast (outputChannelData), numOutputChannels, numSamples); if (callbacks.size() > 0) { - AudioProcessLoadMeasurer::ScopedTimer timer (loadMeasurer); + AudioProcessLoadMeasurer::ScopedTimer timer (loadMeasurer, numSamples); tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true); @@ -866,6 +956,8 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat if (testSoundPosition >= testSound->getNumSamples()) testSound.reset(); } + + outputLevelGetter->updateLevel (const_cast (outputChannelData), numOutputChannels, numSamples); } void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device) @@ -1182,4 +1274,456 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& name) } } +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class AudioDeviceManagerTests : public UnitTest +{ +public: + AudioDeviceManagerTests() : UnitTest ("AudioDeviceManager", UnitTestCategories::audio) {} + + void runTest() override + { + beginTest ("When the AudioDeviceSetup has non-empty device names, initialise uses the requested devices"); + { + AudioDeviceManager manager; + initialiseManager (manager); + + expectEquals (manager.getAvailableDeviceTypes().size(), 2); + + AudioDeviceManager::AudioDeviceSetup setup; + setup.outputDeviceName = "z"; + setup.inputDeviceName = "c"; + + expect (manager.initialise (2, 2, nullptr, true, String{}, &setup).isEmpty()); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + } + + beginTest ("When the AudioDeviceSetup has empty device names, initialise picks suitable default devices"); + { + AudioDeviceManager manager; + initialiseManager (manager); + + AudioDeviceManager::AudioDeviceSetup setup; + + expect (manager.initialise (2, 2, nullptr, true, String{}, &setup).isEmpty()); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("x")); + expectEquals (newSetup.inputDeviceName, String ("a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + } + + beginTest ("When the preferred device name matches an input and an output on the same type, that type is used"); + { + AudioDeviceManager manager; + initialiseManagerWithDifferentDeviceNames (manager); + + expect (manager.initialise (2, 2, nullptr, true, "bar *").isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), String ("bar")); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("bar out a")); + expectEquals (newSetup.inputDeviceName, String ("bar in a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + beginTest ("When the preferred device name matches either an input and an output, but not both, that type is used"); + { + AudioDeviceManager manager; + initialiseManagerWithDifferentDeviceNames (manager); + + expect (manager.initialise (2, 2, nullptr, true, "bar out b").isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), String ("bar")); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("bar out b")); + expectEquals (newSetup.inputDeviceName, String ("bar in a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + beginTest ("When the preferred device name does not match any inputs or outputs, defaults are used"); + { + AudioDeviceManager manager; + initialiseManagerWithDifferentDeviceNames (manager); + + expect (manager.initialise (2, 2, nullptr, true, "unmatchable").isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), String ("foo")); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("foo out a")); + expectEquals (newSetup.inputDeviceName, String ("foo in a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + beginTest ("When first device type has no devices, a device type with devices is used instead"); + { + AudioDeviceManager manager; + initialiseManagerWithEmptyDeviceType (manager); + + AudioDeviceManager::AudioDeviceSetup setup; + + expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty()); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("x")); + expectEquals (newSetup.inputDeviceName, String ("a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + } + + beginTest ("If a device type has been explicitly set to a type with devices, " + "initialisation should respect this choice"); + { + AudioDeviceManager manager; + initialiseManagerWithEmptyDeviceType (manager); + manager.setCurrentAudioDeviceType (mockBName, true); + + AudioDeviceManager::AudioDeviceSetup setup; + expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), mockBName); + } + + beginTest ("If a device type has been explicitly set to a type without devices, " + "initialisation should pick a type with devices instead"); + { + AudioDeviceManager manager; + initialiseManagerWithEmptyDeviceType (manager); + manager.setCurrentAudioDeviceType (emptyName, true); + + AudioDeviceManager::AudioDeviceSetup setup; + expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), mockAName); + } + + beginTest ("Carry out a long sequence of configuration changes"); + { + AudioDeviceManager manager; + initialiseManagerWithEmptyDeviceType (manager); + initialiseWithDefaultDevices (manager); + disableInputChannelsButLeaveDeviceOpen (manager); + selectANewInputDevice (manager); + disableInputDevice (manager); + reenableInputDeviceWithNoChannels (manager); + enableInputChannels (manager); + disableInputChannelsButLeaveDeviceOpen (manager); + switchDeviceType (manager); + enableInputChannels (manager); + closeDeviceByRequestingEmptyNames (manager); + } + } + +private: + void initialiseWithDefaultDevices (AudioDeviceManager& manager) + { + manager.initialiseWithDefaultDevices (2, 2); + const auto& setup = manager.getAudioDeviceSetup(); + + expectEquals (setup.inputChannels.countNumberOfSetBits(), 2); + expectEquals (setup.outputChannels.countNumberOfSetBits(), 2); + + expect (setup.useDefaultInputChannels); + expect (setup.useDefaultOutputChannels); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void disableInputChannelsButLeaveDeviceOpen (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputChannels.clear(); + setup.useDefaultInputChannels = false; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void selectANewInputDevice (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = "b"; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void disableInputDevice (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = ""; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void reenableInputDeviceWithNoChannels (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = "a"; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void enableInputChannels (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = manager.getCurrentDeviceTypeObject()->getDeviceNames (true)[0]; + setup.inputChannels = 3; + setup.useDefaultInputChannels = false; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void switchDeviceType (AudioDeviceManager& manager) + { + const auto oldSetup = manager.getAudioDeviceSetup(); + + expectEquals (manager.getCurrentAudioDeviceType(), String (mockAName)); + + manager.setCurrentAudioDeviceType (mockBName, true); + + expectEquals (manager.getCurrentAudioDeviceType(), String (mockBName)); + + const auto newSetup = manager.getAudioDeviceSetup(); + + expect (newSetup.outputDeviceName.isNotEmpty()); + // We had no channels enabled, which means we don't need to open a new input device + expect (newSetup.inputDeviceName.isEmpty()); + + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void closeDeviceByRequestingEmptyNames (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = ""; + setup.outputDeviceName = ""; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (newSetup.inputDeviceName.isEmpty()); + expect (newSetup.outputDeviceName.isEmpty()); + + expect (manager.getCurrentAudioDevice() == nullptr); + } + + const String mockAName = "mockA"; + const String mockBName = "mockB"; + const String emptyName = "empty"; + + class MockDevice : public AudioIODevice + { + public: + MockDevice (String typeNameIn, String outNameIn, String inNameIn) + : AudioIODevice ("mock", typeNameIn), outName (outNameIn), inName (inNameIn) {} + + StringArray getOutputChannelNames() override { return { "o1", "o2", "o3" }; } + StringArray getInputChannelNames() override { return { "i1", "i2", "i3" }; } + + Array getAvailableSampleRates() override { return { 44100.0, 48000.0 }; } + Array getAvailableBufferSizes() override { return { 128, 256 }; } + int getDefaultBufferSize() override { return 128; } + + String open (const BigInteger& inputs, const BigInteger& outputs, double sr, int bs) override + { + inChannels = inputs; + outChannels = outputs; + sampleRate = sr; + blockSize = bs; + on = true; + return {}; + } + + void close() override { on = false; } + bool isOpen() override { return on; } + + void start (AudioIODeviceCallback*) override { playing = true; } + void stop() override { playing = false; } + bool isPlaying() override { return playing; } + + String getLastError() override { return {}; } + int getCurrentBufferSizeSamples() override { return blockSize; } + double getCurrentSampleRate() override { return sampleRate; } + int getCurrentBitDepth() override { return 16; } + + BigInteger getActiveOutputChannels() const override { return outChannels; } + BigInteger getActiveInputChannels() const override { return inChannels; } + + int getOutputLatencyInSamples() override { return 0; } + int getInputLatencyInSamples() override { return 0; } + + private: + String outName, inName; + BigInteger outChannels, inChannels; + double sampleRate = 0.0; + int blockSize = 0; + bool on = false, playing = false; + }; + + class MockDeviceType : public AudioIODeviceType + { + public: + explicit MockDeviceType (String kind) + : MockDeviceType (std::move (kind), { "a", "b", "c" }, { "x", "y", "z" }) {} + + MockDeviceType (String kind, StringArray inputNames, StringArray outputNames) + : AudioIODeviceType (std::move (kind)), + inNames (std::move (inputNames)), + outNames (std::move (outputNames)) {} + + void scanForDevices() override {} + + StringArray getDeviceNames (bool isInput) const override + { + return getNames (isInput); + } + + int getDefaultDeviceIndex (bool) const override { return 0; } + + int getIndexOfDevice (AudioIODevice* device, bool isInput) const override + { + return getNames (isInput).indexOf (device->getName()); + } + + bool hasSeparateInputsAndOutputs() const override { return true; } + + AudioIODevice* createDevice (const String& outputName, const String& inputName) override + { + if (inNames.contains (inputName) || outNames.contains (outputName)) + return new MockDevice (getTypeName(), outputName, inputName); + + return nullptr; + } + + private: + const StringArray& getNames (bool isInput) const { return isInput ? inNames : outNames; } + + const StringArray inNames, outNames; + }; + + void initialiseManager (AudioDeviceManager& manager) + { + manager.addAudioDeviceType (std::make_unique (mockAName)); + manager.addAudioDeviceType (std::make_unique (mockBName)); + } + + void initialiseManagerWithEmptyDeviceType (AudioDeviceManager& manager) + { + manager.addAudioDeviceType (std::make_unique (emptyName, StringArray{}, StringArray{})); + initialiseManager (manager); + } + + void initialiseManagerWithDifferentDeviceNames (AudioDeviceManager& manager) + { + manager.addAudioDeviceType (std::make_unique ("foo", + StringArray { "foo in a", "foo in b" }, + StringArray { "foo out a", "foo out b" })); + + manager.addAudioDeviceType (std::make_unique ("bar", + StringArray { "bar in a", "bar in b" }, + StringArray { "bar out a", "bar out b" })); + } +}; + +static AudioDeviceManagerTests audioDeviceManagerTests; + +#endif + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h b/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h index 0d4082f9b..8b5fde835 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -93,13 +93,11 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster The name has to be one of the ones listed by the AudioDeviceManager's currently selected device type. This may be the same as the input device. - An empty string indicates the default device. */ String outputDeviceName; /** The name of the audio device used for input. This may be the same as the output device. - An empty string indicates the default device. */ String inputDeviceName; @@ -174,7 +172,10 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster @param preferredSetupOptions if this is non-null, the structure will be used as the set of preferred settings when opening the device. If you use this parameter, the preferredDefaultDeviceName - field will be ignored + field will be ignored. If you set the outputDeviceName + or inputDeviceName data members of the AudioDeviceSetup + to empty strings, then a default device will be used. + @returns an error message if anything went wrong, or an empty string if it worked ok. */ @@ -219,7 +220,11 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster settings, then tweak the appropriate fields in the AudioDeviceSetup structure, and pass it back into this method to apply the new settings. - @param newSetup the settings that you'd like to use + @param newSetup the settings that you'd like to use. + If you don't need an input or output device, set the + inputDeviceName or outputDeviceName data members respectively + to empty strings. Note that this behaviour differs from + the behaviour of initialise(). @param treatAsChosenDevice if this is true and if the device opens correctly, these new settings will be taken as having been explicitly chosen by the user, and the next time createStateXml() is called, these settings @@ -466,18 +471,20 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster int getXRunCount() const noexcept; //============================================================================== - /** Deprecated. */ + #ifndef DOXYGEN + [[deprecated ("Use setMidiInputDeviceEnabled instead.")]] void setMidiInputEnabled (const String&, bool); - /** Deprecated. */ + [[deprecated ("Use isMidiInputDeviceEnabled instead.")]] bool isMidiInputEnabled (const String&) const; - /** Deprecated. */ + [[deprecated ("Use addMidiInputDeviceCallback instead.")]] void addMidiInputCallback (const String&, MidiInputCallback*); - /** Deprecated. */ + [[deprecated ("Use removeMidiInputDeviceCallback instead.")]] void removeMidiInputCallback (const String&, MidiInputCallback*); - /** Deprecated. */ + [[deprecated ("Use setDefaultMidiOutputDevice instead.")]] void setDefaultMidiOutput (const String&); - /** Deprecated. */ + [[deprecated ("Use getDefaultMidiOutputIdentifier instead.")]] const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputDeviceInfo.name; } + #endif private: //============================================================================== @@ -546,6 +553,7 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster AudioIODeviceType* findType (const String& inputName, const String& outputName); AudioIODeviceType* findType (const String& typeName); + void pickCurrentDeviceTypeWithDevices(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager) }; diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h b/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h index 95bb73a67..2aa1d6985 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h @@ -167,8 +167,10 @@ class JUCE_API AudioIODeviceType /** Creates a Bela device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_Bela(); - /** This method has been deprecated. You should call the method which takes a WASAPIDeviceMode instead. */ - JUCE_DEPRECATED (static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode)); + #ifndef DOXYGEN + [[deprecated ("You should call the method which takes a WASAPIDeviceMode instead.")]] + static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode); + #endif protected: explicit AudioIODeviceType (const String& typeName); diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h b/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h index 06e3e672d..881578eaf 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h @@ -32,7 +32,7 @@ ID: juce_audio_devices vendor: juce - version: 6.1.2 + version: 6.1.3 name: JUCE audio and MIDI I/O device classes description: Classes to play and record from audio and MIDI I/O devices website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h b/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h index f3f3c7143..bfc1ee565 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h @@ -157,12 +157,14 @@ class JUCE_API MidiInput final void setName (const String& newName) noexcept { deviceInfo.name = newName; } //============================================================================== - /** Deprecated. */ + #ifndef DOXYGEN + [[deprecated ("Use getAvailableDevices instead.")]] static StringArray getDevices(); - /** Deprecated. */ + [[deprecated ("Use getDefaultDevice instead.")]] static int getDefaultDeviceIndex(); - /** Deprecated. */ + [[deprecated ("Use openDevice that takes a device identifier instead.")]] static std::unique_ptr openDevice (int, MidiInputCallback*); + #endif /** @internal */ class Pimpl; @@ -347,12 +349,14 @@ class JUCE_API MidiOutput final : private Thread bool isBackgroundThreadRunning() const noexcept { return isThreadRunning(); } //============================================================================== - /** Deprecated. */ + #ifndef DOXYGEN + [[deprecated ("Use getAvailableDevices instead.")]] static StringArray getDevices(); - /** Deprecated. */ + [[deprecated ("Use getDefaultDevice instead.")]] static int getDefaultDeviceIndex(); - /** Deprecated. */ + [[deprecated ("Use openDevice that takes a device identifier instead.")]] static std::unique_ptr openDevice (int); + #endif /** @internal */ class Pimpl; diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h b/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h index 69dd916cc..9afbca8e2 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -138,3 +140,5 @@ struct BytestreamToUMPHandler : public BytestreamInputHandler } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h b/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h index 9eea51937..e47e895d9 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h @@ -20,6 +20,8 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { namespace universal_midi_packets @@ -149,3 +151,5 @@ struct U32ToUMPHandler : public U32InputHandler } } + +#endif diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp index 88b298903..241028cbc 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp @@ -326,6 +326,9 @@ class AndroidAudioIODevice : public AudioIODevice, JNIEnv* env = getEnv(); jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels)); + using NativeInt16 = AudioData::Format; + using NativeFloat32 = AudioData::Format; + while (! threadShouldExit()) { if (inputDevice != nullptr) @@ -339,20 +342,9 @@ class AndroidAudioIODevice : public AudioIODevice, jshort* const src = env->GetShortArrayElements (audioBuffer, nullptr); - for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan) - { - AudioData::Pointer d (inputChannelBuffer.getWritePointer (chan)); - - if (chan < numDeviceInputChannels) - { - AudioData::Pointer s (src + chan, numDeviceInputChannels); - d.convertSamples (s, actualBufferSize); - } - else - { - d.clearSamples (actualBufferSize); - } - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { reinterpret_cast (src), numDeviceInputChannels }, + AudioData::NonInterleavedDest { inputChannelBuffer.getArrayOfWritePointers(), inputChannelBuffer.getNumChannels() }, + actualBufferSize); env->ReleaseShortArrayElements (audioBuffer, src, 0); } @@ -382,14 +374,9 @@ class AndroidAudioIODevice : public AudioIODevice, jshort* const dest = env->GetShortArrayElements (audioBuffer, nullptr); - for (int chan = 0; chan < numDeviceOutputChannels; ++chan) - { - AudioData::Pointer d (dest + chan, numDeviceOutputChannels); - - const float* const sourceChanData = outputChannelBuffer.getReadPointer (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); - AudioData::Pointer s (sourceChanData); - d.convertSamples (s, actualBufferSize); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { outputChannelBuffer.getArrayOfReadPointers(), outputChannelBuffer.getNumChannels() }, + AudioData::InterleavedDest { reinterpret_cast (dest), numDeviceOutputChannels }, + actualBufferSize); env->ReleaseShortArrayElements (audioBuffer, dest, 0); jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels); diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp index 4833cd55d..3ff5abff8 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp @@ -44,30 +44,25 @@ struct OboeAudioIODeviceBufferHelpers static bool referAudioBufferDirectlyToOboeIfPossible (int16*, AudioBuffer&, int) { return false; } + using NativeInt16 = AudioData::Format; + using NativeFloat32 = AudioData::Format; + static void convertFromOboe (const int16* srcInterleaved, AudioBuffer& audioBuffer, int numSamples) { - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + const auto numChannels = audioBuffer.getNumChannels(); - DstSampleType dstData (audioBuffer.getWritePointer (i)); - SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels()); - dstData.convertSamples (srcData, numSamples); - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { reinterpret_cast (srcInterleaved), numChannels }, + AudioData::NonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + numSamples); } static void convertToOboe (const AudioBuffer& audioBuffer, int16* dstInterleaved, int numSamples) { - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + const auto numChannels = audioBuffer.getNumChannels(); - DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels()); - SrcSampleType srcData (audioBuffer.getReadPointer (i)); - dstData.convertSamples (srcData, numSamples); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { audioBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { reinterpret_cast (dstInterleaved), numChannels }, + numSamples); } }; @@ -89,6 +84,8 @@ struct OboeAudioIODeviceBufferHelpers return false; } + using Format = AudioData::Format; + static void convertFromOboe (const float* srcInterleaved, AudioBuffer& audioBuffer, int numSamples) { auto numChannels = audioBuffer.getNumChannels(); @@ -98,15 +95,9 @@ struct OboeAudioIODeviceBufferHelpers // No need to convert, we instructed the buffer to point to the src data directly already jassert (audioBuffer.getWritePointer (0) != srcInterleaved); - for (int i = 0; i < numChannels; ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - DstSampleType dstData (audioBuffer.getWritePointer (i)); - SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels()); - dstData.convertSamples (srcData, numSamples); - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { srcInterleaved, numChannels }, + AudioData::NonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + numSamples); } } @@ -119,15 +110,9 @@ struct OboeAudioIODeviceBufferHelpers // No need to convert, we instructed the buffer to point to the src data directly already jassert (audioBuffer.getReadPointer (0) != dstInterleaved); - for (int i = 0; i < numChannels; ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels()); - SrcSampleType srcData (audioBuffer.getReadPointer (i)); - dstData.convertSamples (srcData, numSamples); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { audioBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { dstInterleaved, numChannels }, + numSamples); } } }; diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index 928eaf5e5..c95cdb5b0 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -194,31 +194,25 @@ struct BufferHelpers static void prepareCallbackBuffer (AudioBuffer&, int16*) {} + using LittleEndianInt16 = AudioData::Format; + using NativeFloat32 = AudioData::Format; + static void convertFromOpenSL (const int16* srcInterleaved, AudioBuffer& audioBuffer) { - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + const auto numChannels = audioBuffer.getNumChannels(); - DstSampleType dstData (audioBuffer.getWritePointer (i)); - SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels()); - dstData.convertSamples (srcData, audioBuffer.getNumSamples()); - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { reinterpret_cast (srcInterleaved), numChannels }, + AudioData::NonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + audioBuffer.getNumSamples()); } static void convertToOpenSL (const AudioBuffer& audioBuffer, int16* dstInterleaved) { - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels()); - SrcSampleType srcData (audioBuffer.getReadPointer (i)); + const auto numChannels = audioBuffer.getNumChannels(); - dstData.convertSamples (srcData, audioBuffer.getNumSamples()); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { audioBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { reinterpret_cast (dstInterleaved), numChannels }, + audioBuffer.getNumSamples()); } }; @@ -247,43 +241,37 @@ struct BufferHelpers audioBuffer.setDataToReferTo (&native, 1, audioBuffer.getNumSamples()); } + using LittleEndianFloat32 = AudioData::Format; + using NativeFloat32 = AudioData::Format; + static void convertFromOpenSL (const float* srcInterleaved, AudioBuffer& audioBuffer) { - if (audioBuffer.getNumChannels() == 1) + const auto numChannels = audioBuffer.getNumChannels(); + + if (numChannels == 1) { jassert (srcInterleaved == audioBuffer.getWritePointer (0)); return; } - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - DstSampleType dstData (audioBuffer.getWritePointer (i)); - SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels()); - dstData.convertSamples (srcData, audioBuffer.getNumSamples()); - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { srcInterleaved, numChannels }, + AudioData::nonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + audioBuffer.getNumSamples()); } static void convertToOpenSL (const AudioBuffer& audioBuffer, float* dstInterleaved) { - if (audioBuffer.getNumChannels() == 1) + const auto numChannels = audioBuffer.getNumChannels(); + + if (numChannels == 1) { jassert (dstInterleaved == audioBuffer.getReadPointer (0)); return; } - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels()); - SrcSampleType srcData (audioBuffer.getReadPointer (i)); - - dstData.convertSamples (srcData, audioBuffer.getNumSamples()); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { audioBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { dstInterleaved, numChannels }, + audioBuffer.getNumSamples()); } }; diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp index 121cdae15..5fab73567 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -25,7 +25,13 @@ namespace juce class iOSAudioIODevice; -static const char* const iOSAudioDeviceName = "iOS Audio"; +constexpr const char* const iOSAudioDeviceName = "iOS Audio"; + +#ifndef JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES + #define JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES +#endif + +constexpr std::initializer_list iOSExplicitSampleRates { JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES }; //============================================================================== struct AudioSessionHolder @@ -58,6 +64,8 @@ static const char* getRoutingChangeReason (AVAudioSessionRouteChangeReason reaso } } +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") + bool getNotificationValueForKey (NSNotification* notification, NSString* key, NSUInteger& value) noexcept { if (notification != nil) @@ -76,6 +84,8 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS return false; } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } // namespace juce //============================================================================== @@ -278,9 +288,15 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, #endif if (category == AVAudioSessionCategoryPlayAndRecord) + { options |= (AVAudioSessionCategoryOptionDefaultToSpeaker - | AVAudioSessionCategoryOptionAllowBluetooth - | AVAudioSessionCategoryOptionAllowBluetoothA2DP); + | AVAudioSessionCategoryOptionAllowBluetooth); + + #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + if (@available (iOS 10.0, *)) + options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP; + #endif + } JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setCategory: category withOptions: options @@ -356,6 +372,12 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, // depending on whether the headphones are plugged in or not! void updateAvailableSampleRates() { + if (iOSExplicitSampleRates.size() != 0) + { + availableSampleRates = Array (iOSExplicitSampleRates); + return; + } + availableSampleRates.clear(); AudioUnitRemovePropertyListenerWithUserData (audioUnit, @@ -700,11 +722,20 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, &dataSize); if (err == noErr) { - #if (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) - [[UIApplication sharedApplication] openURL: (NSURL*)hostUrl]; - #else - [[UIApplication sharedApplication] openURL: (NSURL*)hostUrl options: @{} completionHandler: nil]; + #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + if (@available (iOS 10.0, *)) + { + [[UIApplication sharedApplication] openURL: (NSURL*) hostUrl + options: @{} + completionHandler: nil]; + + return; + } #endif + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + [[UIApplication sharedApplication] openURL: (NSURL*) hostUrl]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } } diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h index 45d3ef267..2ada61d97 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h @@ -84,7 +84,6 @@ class iOSAudioIODevice : public AudioIODevice friend struct AudioSessionHolder; struct Pimpl; - friend struct Pimpl; std::unique_ptr pimpl; JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index 268f87850..c6920b10d 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -39,6 +39,12 @@ constexpr auto juceAudioObjectPropertyElementMain = #endif //============================================================================== +struct AsyncRestarter +{ + virtual ~AsyncRestarter() = default; + virtual void restartAsync() = 0; +}; + struct SystemVol { SystemVol (AudioObjectPropertySelector selector) noexcept @@ -151,14 +157,15 @@ class CoreAudioIODeviceType; class CoreAudioIODevice; //============================================================================== -class CoreAudioInternal : private Timer +class CoreAudioInternal : private Timer, + private AsyncUpdater { public: CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id, bool input, bool output) - : owner (d), - deviceID (id), - isInputDevice (input), - isOutputDevice (output) + : owner (d), + deviceID (id), + isInputDevice (input), + isOutputDevice (output) { jassert (deviceID != 0); @@ -178,6 +185,9 @@ class CoreAudioInternal : private Timer ~CoreAudioInternal() override { + stopTimer(); + cancelPendingUpdate(); + AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; pa.mScope = kAudioObjectPropertyScopeWildcard; @@ -669,7 +679,7 @@ class CoreAudioInternal : private Timer return error; } - bool start() + bool start (AudioIODeviceCallback* callbackToNotify) { const ScopedLock sl (callbackLock); @@ -692,22 +702,26 @@ class CoreAudioInternal : private Timer } } } + + if (started) + { + callback = callbackToNotify; + + if (callback != nullptr) + callback->audioDeviceAboutToStart (&owner); + } } - return started; - } + playing = started && callback != nullptr; - void setCallback (AudioIODeviceCallback* cb) - { - const ScopedLock sl (callbackLock); - callback = cb; + return started; } - void stop (bool leaveInterruptRunning) + AudioIODeviceCallback* stop (bool leaveInterruptRunning) { const ScopedLock sl (callbackLock); - callback = nullptr; + auto result = std::exchange (callback, nullptr); if (started && (deviceID != 0) && ! leaveInterruptRunning) { @@ -726,7 +740,10 @@ class CoreAudioInternal : private Timer OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); audioProcID = {}; started = false; + playing = false; } + + return result; } double getSampleRate() const { return sampleRate; } @@ -795,27 +812,22 @@ class CoreAudioInternal : private Timer } } - // called by callbacks + // called by callbacks (possibly off the main thread) void deviceDetailsChanged() { if (callbacksAllowed.get() == 1) startTimer (100); } - void timerCallback() override + // called by callbacks (possibly off the main thread) + void deviceRequestedRestart() { - JUCE_COREAUDIOLOG ("Device changed"); - - stopTimer(); - auto oldSampleRate = sampleRate; - auto oldBufferSize = bufferSize; - - if (! updateDetailsFromDevice()) - owner.stopInternal(); - else if ((oldBufferSize != bufferSize || oldSampleRate != sampleRate) && owner.shouldRestartDevice()) - owner.restart(); + owner.restart(); + triggerAsyncUpdate(); } + bool isPlaying() const { return playing.load(); } + //============================================================================== CoreAudioIODevice& owner; int inputLatency = 0; @@ -826,13 +838,14 @@ class CoreAudioInternal : private Timer StringArray inChanNames, outChanNames; Array sampleRates; Array bufferSizes; - AudioIODeviceCallback* callback = nullptr; AudioDeviceIOProcID audioProcID = {}; private: + AudioIODeviceCallback* callback = nullptr; CriticalSection callbackLock; AudioDeviceID deviceID; bool started = false, audioDeviceStopPending = false; + std::atomic playing { false }; double sampleRate = 0; int bufferSize = 512; HeapBlock audioBuffer; @@ -845,6 +858,26 @@ class CoreAudioInternal : private Timer HeapBlock tempInputBuffers, tempOutputBuffers; //============================================================================== + void timerCallback() override + { + JUCE_COREAUDIOLOG ("Device changed"); + + stopTimer(); + auto oldSampleRate = sampleRate; + auto oldBufferSize = bufferSize; + + if (! updateDetailsFromDevice()) + owner.stopInternal(); + else if ((oldBufferSize != bufferSize || oldSampleRate != sampleRate) && owner.shouldRestartDevice()) + owner.restart(); + } + + void handleAsyncUpdate() override + { + if (owner.deviceType != nullptr) + owner.deviceType->audioDeviceListChanged(); + } + static OSStatus audioIOProc (AudioDeviceID /*inDevice*/, const AudioTimeStamp* /*inNow*/, const AudioBufferList* inInputData, @@ -879,10 +912,7 @@ class CoreAudioInternal : private Timer case kAudioDevicePropertyDeviceHasChanged: case kAudioObjectPropertyOwnedObjects: - intern->owner.restart(); - - if (intern->owner.deviceType != nullptr) - intern->owner.deviceType->triggerAsyncAudioDeviceListChange(); + intern->deviceRequestedRestart(); break; case kAudioDevicePropertyBufferSizeRange: @@ -950,20 +980,18 @@ class CoreAudioIODevice : public AudioIODevice, inputIndex (inputIndex_), outputIndex (outputIndex_) { - CoreAudioInternal* device = nullptr; - - if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) - { - jassert (inputDeviceId != 0); - device = new CoreAudioInternal (*this, inputDeviceId, true, outputDeviceId != 0); - } - else + internal = [this, &inputDeviceId, &outputDeviceId] { - device = new CoreAudioInternal (*this, outputDeviceId, false, true); - } + if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) + { + jassert (inputDeviceId != 0); + return std::make_unique (*this, inputDeviceId, true, outputDeviceId != 0); + } - jassert (device != nullptr); - internal.reset (device); + return std::make_unique (*this, outputDeviceId, false, true); + }(); + + jassert (internal != nullptr); AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; @@ -1059,49 +1087,35 @@ class CoreAudioIODevice : public AudioIODevice, void start (AudioIODeviceCallback* callback) override { - if (! isStarted) - { - if (callback != nullptr) - callback->audioDeviceAboutToStart (this); - - isStarted = internal->start(); - - if (isStarted) - { - internal->setCallback (callback); - previousCallback = callback; - } - } + if (internal->start (callback)) + previousCallback = callback; } void stop() override { restartDevice = false; + stopAndGetLastCallback(); + } - if (isStarted) - { - auto lastCallback = internal->callback; + AudioIODeviceCallback* stopAndGetLastCallback() const + { + auto* lastCallback = internal->stop (true); - isStarted = false; - internal->stop (true); + if (lastCallback != nullptr) + lastCallback->audioDeviceStopped(); - if (lastCallback != nullptr) - lastCallback->audioDeviceStopped(); - } + return lastCallback; } - void stopInternal() + AudioIODeviceCallback* stopInternal() { - stop(); restartDevice = true; + return stopAndGetLastCallback(); } bool isPlaying() override { - if (internal->callback == nullptr) - isStarted = false; - - return isStarted; + return internal->isPlaying(); } String getLastError() override @@ -1115,28 +1129,21 @@ class CoreAudioIODevice : public AudioIODevice, deviceType->audioDeviceListChanged(); } + // called by callbacks (possibly off the main thread) void restart() { - if (deviceWrapperRestartCallback != nullptr) + if (restarter != nullptr) { - deviceWrapperRestartCallback(); + restarter->restartAsync(); + return; } - else - { - { - const ScopedLock sl (closeLock); - if (isStarted) - { - if (internal->callback != nullptr) - previousCallback = internal->callback; - - stopInternal(); - } - } - - startTimer (100); + { + const ScopedLock sl (closeLock); + previousCallback = stopInternal(); } + + startTimer (100); } bool setCurrentSampleRate (double newSampleRate) @@ -1144,9 +1151,9 @@ class CoreAudioIODevice : public AudioIODevice, return internal->setNominalSampleRate (newSampleRate); } - void setDeviceWrapperRestartCallback (const std::function& cb) + void setAsyncRestarter (AsyncRestarter* restarterIn) { - deviceWrapperRestartCallback = cb; + restarter = restarterIn; } bool shouldRestartDevice() const noexcept { return restartDevice; } @@ -1156,10 +1163,10 @@ class CoreAudioIODevice : public AudioIODevice, private: std::unique_ptr internal; - bool isOpen_ = false, isStarted = false, restartDevice = true; + bool isOpen_ = false, restartDevice = true; String lastError; AudioIODeviceCallback* previousCallback = nullptr; - std::function deviceWrapperRestartCallback = nullptr; + AsyncRestarter* restarter = nullptr; BigInteger inputChannelsRequested, outputChannelsRequested; CriticalSection closeLock; @@ -1198,6 +1205,7 @@ class CoreAudioIODevice : public AudioIODevice, //============================================================================== class AudioIODeviceCombiner : public AudioIODevice, + private AsyncRestarter, private Thread, private Timer { @@ -1215,18 +1223,20 @@ class AudioIODeviceCombiner : public AudioIODevice, devices.clear(); } - void addDevice (CoreAudioIODevice* device, bool useInputs, bool useOutputs) + void addDevice (std::unique_ptr device, bool useInputs, bool useOutputs) { jassert (device != nullptr); jassert (! isOpen()); jassert (! device->isOpen()); - devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs)); + auto* devicePtr = device.get(); + + devices.add (std::make_unique (*this, std::move (device), useInputs, useOutputs)); if (currentSampleRate == 0) - currentSampleRate = device->getCurrentSampleRate(); + currentSampleRate = devicePtr->getCurrentSampleRate(); if (currentBufferSize == 0) - currentBufferSize = device->getCurrentBufferSizeSamples(); + currentBufferSize = devicePtr->getCurrentBufferSizeSamples(); } Array getDevices() const @@ -1454,7 +1464,7 @@ class AudioIODeviceCombiner : public AudioIODevice, start (cb); } - void restartAsync() + void restartAsync() override { { const ScopedLock sl (closeLock); @@ -1531,7 +1541,13 @@ class AudioIODeviceCombiner : public AudioIODevice, void start (AudioIODeviceCallback* newCallback) override { - if (callback != newCallback) + const auto shouldStart = [&] + { + const ScopedLock sl (callbackLock); + return callback != newCallback; + }(); + + if (shouldStart) { stop(); fifos.clear(); @@ -1543,8 +1559,7 @@ class AudioIODeviceCombiner : public AudioIODevice, newCallback->audioDeviceAboutToStart (this); const ScopedLock sl (callbackLock); - callback = newCallback; - previousCallback = callback; + previousCallback = std::exchange (callback, newCallback); } } @@ -1801,11 +1816,11 @@ class AudioIODeviceCombiner : public AudioIODevice, //============================================================================== struct DeviceWrapper : private AudioIODeviceCallback { - DeviceWrapper (AudioIODeviceCombiner& cd, CoreAudioIODevice* d, bool useIns, bool useOuts) - : owner (cd), device (d), + DeviceWrapper (AudioIODeviceCombiner& cd, std::unique_ptr d, bool useIns, bool useOuts) + : owner (cd), device (std::move (d)), useInputs (useIns), useOutputs (useOuts) { - d->setDeviceWrapperRestartCallback ([this] { owner.restartAsync(); }); + device->setAsyncRestarter (&owner); } ~DeviceWrapper() override @@ -2027,6 +2042,8 @@ class CoreAudioIODeviceType : public AudioIODeviceType, ~CoreAudioIODeviceType() override { + cancelPendingUpdate(); + AudioObjectPropertyAddress pa; pa.mSelector = kAudioHardwarePropertyDevices; pa.mScope = kAudioObjectPropertyScopeWildcard; @@ -2178,22 +2195,20 @@ class CoreAudioIODeviceType : public AudioIODeviceType, : outputDeviceName; if (inputDeviceID == outputDeviceID) - return new CoreAudioIODevice (this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex); - - std::unique_ptr in, out; + return std::make_unique (this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex).release(); - if (inputDeviceID != 0) - in.reset (new CoreAudioIODevice (this, inputDeviceName, inputDeviceID, inputIndex, 0, -1)); + auto in = inputDeviceID != 0 ? std::make_unique (this, inputDeviceName, inputDeviceID, inputIndex, 0, -1) + : nullptr; - if (outputDeviceID != 0) - out.reset (new CoreAudioIODevice (this, outputDeviceName, 0, -1, outputDeviceID, outputIndex)); + auto out = outputDeviceID != 0 ? std::make_unique (this, outputDeviceName, 0, -1, outputDeviceID, outputIndex) + : nullptr; - if (in == nullptr) return out.release(); + if (in == nullptr) return out.release(); if (out == nullptr) return in.release(); - std::unique_ptr combo (new AudioIODeviceCombiner (combinedName, this)); - combo->addDevice (in.release(), true, false); - combo->addDevice (out.release(), false, true); + auto combo = std::make_unique (combinedName, this); + combo->addDevice (std::move (in), true, false); + combo->addDevice (std::move (out), false, true); return combo.release(); } @@ -2203,11 +2218,6 @@ class CoreAudioIODeviceType : public AudioIODeviceType, callDeviceChangeListeners(); } - void triggerAsyncAudioDeviceListChange() - { - triggerAsyncUpdate(); - } - //============================================================================== private: StringArray inputDeviceNames, outputDeviceNames; @@ -2215,6 +2225,11 @@ class CoreAudioIODeviceType : public AudioIODeviceType, bool hasScanned = false; + void handleAsyncUpdate() override + { + audioDeviceListChanged(); + } + static int getNumChannels (AudioDeviceID deviceID, bool input) { int total = 0; @@ -2244,15 +2259,10 @@ class CoreAudioIODeviceType : public AudioIODeviceType, static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) { - static_cast (clientData)->triggerAsyncAudioDeviceListChange(); + static_cast (clientData)->triggerAsyncUpdate(); return noErr; } - void handleAsyncUpdate() override - { - audioDeviceListChanged(); - } - JUCE_DECLARE_WEAK_REFERENCEABLE (CoreAudioIODeviceType) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType) }; diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm index f482d0e9e..967a64696 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm @@ -82,7 +82,7 @@ static bool checkError (OSStatus err, int lineNum) struct Sender; #if JUCE_HAS_NEW_COREMIDI_API - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new") + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") template <> struct Sender : public SenderBase @@ -829,7 +829,7 @@ void handlePackets (const EventList& list) struct CreatorFunctions; #if JUCE_HAS_NEW_COREMIDI_API - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new") + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") template <> struct CreatorFunctions diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index 2aa8efaaf..2a96b4c7e 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -951,15 +951,18 @@ class ASIOAudioIODevice : public AudioIODevice, { JUCE_ASIO_LOG ("rate change: " + String (currentSampleRate) + " to " + String (newRate)); auto err = asioObject->setSampleRate (newRate); + JUCE_ASIO_LOG_ERROR ("setSampleRate", err); + Thread::sleep (10); if (err == ASE_NoClock && numClockSources > 0) { JUCE_ASIO_LOG ("trying to set a clock source.."); - Thread::sleep (10); err = asioObject->setClockSource (clocks[0].index); JUCE_ASIO_LOG_ERROR ("setClockSource2", err); Thread::sleep (10); err = asioObject->setSampleRate (newRate); + JUCE_ASIO_LOG_ERROR ("setSampleRate", err); + Thread::sleep (10); } if (err == 0) diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp index 025a13e01..b7d6270b5 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp @@ -258,7 +258,7 @@ struct Win32MidiService : public MidiServiceType, struct MidiHeader { - MidiHeader() {} + MidiHeader() = default; void prepare (HMIDIIN device) { diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index bc32b5627..2e9c4d385 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -884,8 +884,7 @@ class WASAPIInputDevice : public WASAPIDeviceBase closeClient(); captureClient = nullptr; reservoir.reset(); - reservoirReadPos = 0; - reservoirWritePos = 0; + queue = SingleThreadedAbstractFifo(); } template @@ -903,13 +902,12 @@ class WASAPIInputDevice : public WASAPIDeviceBase else updateFormatWithType ((AudioData::Int16*) nullptr); } - bool start (int userBufferSize) + bool start (int userBufferSizeIn) { - reservoirSize = (int) (actualBufferSize + (UINT32) userBufferSize); - reservoirMask = nextPowerOfTwo (reservoirSize) - 1; - reservoir.setSize ((size_t) ((reservoirMask + 1) * bytesPerFrame), true); - reservoirReadPos = 0; - reservoirWritePos = 0; + const auto reservoirSize = nextPowerOfTwo ((int) (actualBufferSize + (UINT32) userBufferSizeIn)); + + queue = SingleThreadedAbstractFifo (reservoirSize); + reservoir.setSize ((size_t) (queue.getSize() * bytesPerFrame), true); xruns = 0; if (! check (client->Start())) @@ -927,93 +925,80 @@ class WASAPIInputDevice : public WASAPIDeviceBase UINT32 numSamplesAvailable; DWORD flags; - while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr) - != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */) + while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr) != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */) captureClient->ReleaseBuffer (numSamplesAvailable); } - int getNumSamplesInReservoir() const noexcept { return reservoirWritePos.load() - reservoirReadPos.load(); } + int getNumSamplesInReservoir() const noexcept { return queue.getNumReadable(); } void handleDeviceBuffer() { if (numChannels <= 0) return; - uint8* inputData; - UINT32 numSamplesAvailable; - DWORD flags; + uint8* inputData = nullptr; + UINT32 numSamplesAvailable = 0; + DWORD flags = 0; while (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)) && numSamplesAvailable > 0) { if ((flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) xruns++; - int samplesLeft = (int) numSamplesAvailable; + if (numSamplesAvailable > (UINT32) queue.getRemainingSpace()) + { + captureClient->ReleaseBuffer (0); + return; + } + + auto offset = 0; - while (samplesLeft > 0) + for (const auto& block : queue.write ((int) numSamplesAvailable)) { - auto localWrite = reservoirWritePos.load() & reservoirMask; - auto samplesToDo = jmin (samplesLeft, reservoirMask + 1 - localWrite); - auto samplesToDoBytes = samplesToDo * bytesPerFrame; + const auto samplesToDoBytes = block.getLength() * bytesPerFrame; - void* reservoirPtr = addBytesToPointer (reservoir.getData(), localWrite * bytesPerFrame); + auto* reservoirPtr = addBytesToPointer (reservoir.getData(), block.getStart() * bytesPerFrame); if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0) zeromem (reservoirPtr, (size_t) samplesToDoBytes); else - memcpy (reservoirPtr, inputData, (size_t) samplesToDoBytes); + memcpy (reservoirPtr, inputData + offset * bytesPerFrame, (size_t) samplesToDoBytes); - reservoirWritePos += samplesToDo; - inputData += samplesToDoBytes; - samplesLeft -= samplesToDo; + offset += block.getLength(); } - if (getNumSamplesInReservoir() > reservoirSize) - reservoirReadPos = reservoirWritePos.load() - reservoirSize; - captureClient->ReleaseBuffer (numSamplesAvailable); } } - void copyBuffersFromReservoir (float** destBuffers, int numDestBuffers, int bufferSize) + void copyBuffersFromReservoir (float* const* destBuffers, const int numDestBuffers, const int bufferSize) { if ((numChannels <= 0 && bufferSize == 0) || reservoir.isEmpty()) return; - int offset = jmax (0, bufferSize - getNumSamplesInReservoir()); + auto offset = jmax (0, bufferSize - queue.getNumReadable()); if (offset > 0) - { for (int i = 0; i < numDestBuffers; ++i) zeromem (destBuffers[i], (size_t) offset * sizeof (float)); - bufferSize -= offset; - reservoirReadPos -= offset / 2; - } - - while (bufferSize > 0) + for (const auto& block : queue.read (jmin (queue.getNumReadable(), bufferSize))) { - auto localRead = reservoirReadPos.load() & reservoirMask; - auto samplesToDo = jmin (bufferSize, getNumSamplesInReservoir(), reservoirMask + 1 - localRead); - - if (samplesToDo <= 0) - break; - - auto reservoirOffset = localRead * bytesPerFrame; - - for (int i = 0; i < numDestBuffers; ++i) - converter->convertSamples (destBuffers[i] + offset, 0, addBytesToPointer (reservoir.getData(), reservoirOffset), channelMaps.getUnchecked(i), samplesToDo); - - bufferSize -= samplesToDo; - offset += samplesToDo; - reservoirReadPos += samplesToDo; + for (auto i = 0; i < numDestBuffers; ++i) + converter->convertSamples (destBuffers[i] + offset, + 0, + addBytesToPointer (reservoir.getData(), block.getStart() * bytesPerFrame), + channelMaps.getUnchecked (i), + block.getLength()); + + offset += block.getLength(); } } ComSmartPtr captureClient; MemoryBlock reservoir; - int reservoirSize, reservoirMask, xruns; - std::atomic reservoirReadPos, reservoirWritePos; + SingleThreadedAbstractFifo queue; + int xruns = 0; std::unique_ptr converter; @@ -1095,7 +1080,7 @@ class WASAPIOutputDevice : public WASAPIDeviceBase return (int) actualBufferSize; } - void copyBuffers (const float** srcBuffers, int numSrcBuffers, int bufferSize, + void copyBuffers (const float* const* srcBuffers, int numSrcBuffers, int bufferSize, WASAPIInputDevice* inputDevice, Thread& thread) { if (numChannels <= 0) diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp index 5bddda3ce..c1bb5c05f 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp @@ -45,9 +45,6 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly } - readAheadBufferSize = readAheadSize; - sourceSampleRate = sourceSampleRateToCorrectFor; - ResamplingAudioSource* newResamplerSource = nullptr; BufferingAudioSource* newBufferingSource = nullptr; PositionableAudioSource* newPositionableSource = nullptr; @@ -82,8 +79,8 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, if (isPrepared) { - if (newResamplerSource != nullptr && sourceSampleRate > 0 && sampleRate > 0) - newResamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); + if (newResamplerSource != nullptr && sourceSampleRateToCorrectFor > 0 && sampleRate > 0) + newResamplerSource->setResamplingRatio (sourceSampleRateToCorrectFor / sampleRate); newMasterSource->prepareToPlay (blockSize, sampleRate); } @@ -97,8 +94,9 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, bufferingSource = newBufferingSource; masterSource = newMasterSource; positionableSource = newPositionableSource; + readAheadBufferSize = readAheadSize; + sourceSampleRate = sourceSampleRateToCorrectFor; - inputStreamEOF = false; playing = false; } @@ -114,7 +112,6 @@ void AudioTransportSource::start() const ScopedLock sl (callbackLock); playing = true; stopped = false; - inputStreamEOF = false; } sendChangeMessage(); @@ -157,6 +154,12 @@ double AudioTransportSource::getLengthInSeconds() const return 0.0; } +bool AudioTransportSource::hasStreamFinished() const noexcept +{ + return positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 + && ! positionableSource->isLooping(); +} + void AudioTransportSource::setNextReadPosition (int64 newPosition) { if (positionableSource != nullptr) @@ -168,13 +171,13 @@ void AudioTransportSource::setNextReadPosition (int64 newPosition) if (resamplerSource != nullptr) resamplerSource->flushBuffers(); - - inputStreamEOF = false; } } int64 AudioTransportSource::getNextReadPosition() const { + const ScopedLock sl (callbackLock); + if (positionableSource != nullptr) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; @@ -221,7 +224,6 @@ void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double ne if (resamplerSource != nullptr && sourceSampleRate > 0) resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); - inputStreamEOF = false; isPrepared = true; } @@ -258,11 +260,9 @@ void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info info.buffer->clear (info.startSample + 256, info.numSamples - 256); } - if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 - && ! positionableSource->isLooping()) + if (hasStreamFinished()) { playing = false; - inputStreamEOF = true; sendChangeMessage(); } diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h b/external/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h index e91b14303..c36356cd4 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h @@ -102,7 +102,7 @@ class JUCE_API AudioTransportSource : public PositionableAudioSource, double getLengthInSeconds() const; /** Returns true if the player has stopped because its input stream ran out of data. */ - bool hasStreamFinished() const noexcept { return inputStreamEOF; } + bool hasStreamFinished() const noexcept; //============================================================================== /** Starts playing (if a source has been selected). @@ -170,7 +170,7 @@ class JUCE_API AudioTransportSource : public PositionableAudioSource, std::atomic playing { false }, stopped { true }; double sampleRate = 44100.0, sourceSampleRate = 0; int blockSize = 128, readAheadBufferSize = 0; - bool isPrepared = false, inputStreamEOF = false; + bool isPrepared = false; void releaseMasterResources(); diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp b/external/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp index ce32272a4..d06cfc62a 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp @@ -28,8 +28,6 @@ AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity) jassert (bufferSize > 0); } -AbstractFifo::~AbstractFifo() {} - int AbstractFifo::getTotalSize() const noexcept { return bufferSize; } int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; } diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h b/external/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h index 3a1b75e0b..0f6e82bbe 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h @@ -42,30 +42,24 @@ namespace juce { void addToFifo (const int* someData, int numItems) { - int start1, size1, start2, size2; - abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2); + const auto scope = abstractFifo.write (numItems); - if (size1 > 0) - copySomeData (myBuffer + start1, someData, size1); + if (scope.blockSize1 > 0) + copySomeData (myBuffer + scope.startIndex1, someData, scope.blockSize1); - if (size2 > 0) - copySomeData (myBuffer + start2, someData + size1, size2); - - abstractFifo.finishedWrite (size1 + size2); + if (scope.blockSize2 > 0) + copySomeData (myBuffer + scope.startIndex2, someData, scope.blockSize2); } void readFromFifo (int* someData, int numItems) { - int start1, size1, start2, size2; - abstractFifo.prepareToRead (numItems, start1, size1, start2, size2); + const auto scope = abstractFifo.read (numItems); - if (size1 > 0) - copySomeData (someData, myBuffer + start1, size1); - - if (size2 > 0) - copySomeData (someData + size1, myBuffer + start2, size2); + if (scope.blockSize1 > 0) + copySomeData (someData, myBuffer + scope.startIndex1, scope.blockSize1); - abstractFifo.finishedRead (size1 + size2); + if (scope.blockSize2 > 0) + copySomeData (someData + scope.blockSize1, myBuffer + scope.startIndex2, scope.blockSize2); } AbstractFifo abstractFifo { 1024 }; @@ -82,9 +76,6 @@ class JUCE_API AbstractFifo /** Creates a FIFO to manage a buffer with the specified capacity. */ AbstractFifo (int capacity) noexcept; - /** Destructor */ - ~AbstractFifo(); - //============================================================================== /** Returns the total size of the buffer being managed. */ int getTotalSize() const noexcept; diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_Array.h b/external/JuceLibraryCode/modules/juce_core/containers/juce_Array.h index 973a35160..924173de8 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_Array.h +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_Array.h @@ -1125,9 +1125,9 @@ class Array //============================================================================== #ifndef DOXYGEN - // Note that the swapWithArray method has been replaced by a more flexible templated version, - // and renamed "swapWith" to be more consistent with the names used in other classes. - JUCE_DEPRECATED_WITH_BODY (void swapWithArray (Array& other) noexcept, { swapWith (other); }) + [[deprecated ("This method has been replaced by a more flexible templated version and renamed " + "to swapWith to be more consistent with the names used in other classes.")]] + void swapWithArray (Array& other) noexcept { swapWith (other); } #endif private: diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h b/external/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h index d97764e74..1a221b257 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h @@ -121,11 +121,6 @@ class JUCE_API DynamicObject : public ReferenceCountedObject //============================================================================== NamedValueSet properties; - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This method has been deprecated - use var::invoke instead - virtual void invokeMethod (const Identifier&, const var*, int) {} - #endif - JUCE_LEAK_DETECTOR (DynamicObject) }; diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h b/external/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h index 9a53464ae..c6ba1a2f0 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h @@ -843,9 +843,9 @@ class OwnedArray //============================================================================== #ifndef DOXYGEN - // Note that the swapWithArray method has been replaced by a more flexible templated version, - // and renamed "swapWith" to be more consistent with the names used in other classes. - JUCE_DEPRECATED_WITH_BODY (void swapWithArray (OwnedArray& other) noexcept, { swapWith (other); }) + [[deprecated ("This method has been replaced by a more flexible templated version and renamed " + "to swapWith to be more consistent with the names used in other classes.")]] + void swapWithArray (OwnedArray& other) noexcept { swapWith (other); } #endif private: diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h b/external/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h index 157a5a489..6de4f93e1 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h @@ -876,9 +876,9 @@ class ReferenceCountedArray //============================================================================== #ifndef DOXYGEN - // Note that the swapWithArray method has been replaced by a more flexible templated version, - // and renamed "swapWith" to be more consistent with the names used in other classes. - JUCE_DEPRECATED_WITH_BODY (void swapWithArray (ReferenceCountedArray& other) noexcept, { swapWith (other); }) + [[deprecated ("This method has been replaced by a more flexible templated version and renamed " + "to swapWith to be more consistent with the names used in other classes.")]] + void swapWithArray (ReferenceCountedArray& other) noexcept { swapWith (other); } #endif private: diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_SingleThreadedAbstractFifo.h b/external/JuceLibraryCode/modules/juce_core/containers/juce_SingleThreadedAbstractFifo.h new file mode 100644 index 000000000..681587b2b --- /dev/null +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_SingleThreadedAbstractFifo.h @@ -0,0 +1,126 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +//============================================================================== +/** + Encapsulates the logic for a single-threaded FIFO. + + This might be useful for building buffers which can be written and read in + blocks of different sizes. For example, in an audio effect we might wish to + run some processing on fixed-size blocks of audio input, but the host may + provide input blocks of varying sizes. In this situation, we might want to + store the previous input in a buffer, and extract a fixed-size block + whenever there are enough samples available. The SingleThreadedAbstractFifo + implements logic suitable for this use-case. + + This class is quite similar to AbstractFifo, in that it only keeps track of + the current read/write locations. The user is responsible for providing the + actual buffer that will be read/written. + + The intended usage of this class is as follows: + - Create some backing storage in a vector, AudioBuffer etc. + - Construct a SingleThreadedAbstractFifo to manage the buffer, passing the + number of items in the buffer. + - Each time new input is ready, call write(), passing the number of items + you wish to write into the buffer. This function returns a pair of ranges + describing which indices in the backing storage should be written. + - Call getNumReadable() to find out how many items are ready to read from + the buffer. + - If there are enough items ready to read, call read(), passing the number + of items you require. This function returns a pair of ranges describing + which indices in the backing storage may be read. + + Unlike AbstractFifo, the SingleThreadedAbstractFifo is intended for use + from a single thread. It is not safe to call any non-const member function + of SingleThreadedAbstractFifo concurrently with any other member function. + + @see AbstractFifo + + @tags{Core} +*/ +class SingleThreadedAbstractFifo +{ +public: + /** Creates a SingleThreadedAbstractFifo with no size. */ + SingleThreadedAbstractFifo() = default; + + /** Creates a SingleThreadedAbstractFifo that can manage a buffer of the specified size. */ + explicit SingleThreadedAbstractFifo (int sizeIn) + : size (sizeIn) + { + // This class only works properly when the size is a power of two. + // Use nextPowerOfTwo() to find a good size, and ensure that your + // backing storage is the same size. + jassert (isPowerOfTwo (sizeIn)); + } + + /** Returns the number of unused elements present in the buffer. */ + int getRemainingSpace() const { return size - numReadable; } + + /** Returns the number of pending elements present in the buffer. */ + int getNumReadable() const { return numReadable; } + + /** Returns the size of the managed buffer. */ + int getSize() const { return size; } + + /** Returns two blocks in the buffer where new items may be written. + + Note that if the buffer is running low on free space, the sum of the lengths of + the returned ranges may be less than num! + */ + std::array, 2> write (int num) + { + const auto startPos = (readPos + numReadable) & (size - 1); + const auto maxToWrite = jmin (getRemainingSpace(), num); + const auto firstBlockSize = jmin (maxToWrite, size - startPos); + + numReadable += maxToWrite; + + return { { { startPos, startPos + firstBlockSize }, { 0, maxToWrite - firstBlockSize } } }; + } + + /** Returns two blocks in the buffer from which new items may be read. + + Note that if the buffer doesn't have the requested number of items available, + the sum of the lengths of the returned ranges may be less than num! + */ + std::array, 2> read (int num) + { + const auto startPos = readPos; + const auto maxToRead = jmin (numReadable, num); + const auto firstBlockSize = jmin (maxToRead, size - startPos); + + readPos = (startPos + maxToRead) & (size - 1); + numReadable -= maxToRead; + + return { { { startPos, startPos + firstBlockSize }, { 0, maxToRead - firstBlockSize } } }; + } + +private: + int size = 0, readPos = 0, numReadable = 0; +}; + + +} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp b/external/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp index 6337e3052..d071794e1 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp @@ -510,8 +510,6 @@ var::var() noexcept : type (&Instance::attributesVoid) {} var::var (const VariantType& t) noexcept : type (&t) {} var::~var() noexcept { type->cleanUp (value); } -JUCE_DECLARE_DEPRECATED_STATIC (const var var::null;) - //============================================================================== var::var (const var& valueToCopy) : type (valueToCopy.type) { @@ -895,4 +893,17 @@ var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int { } +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const var var::null; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h b/external/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h index 130df6a0f..776bfd0c6 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h @@ -147,6 +147,17 @@ class JUCE_API var /** Returns true if this var has the same value as the one supplied. Note that this ignores the type, so a string var "123" and an integer var with the value 123 are considered to be equal. + + Note that equality checking depends on the "wrapped" type of the object on which + equals() is called. That means the following code will convert the right-hand-side + argument to a string and compare the string values, because the object on the + left-hand-side was initialised from a string: + @code var ("123").equals (var (123)) @endcode + However, the following code will convert the right-hand-side argument to a double + and compare the values as doubles, because the object on the left-hand-side was + initialised from a double: + @code var (45.6).equals ("45.6000") @endcode + @see equalsWithSameType */ bool equals (const var& other) const noexcept; @@ -271,15 +282,14 @@ class JUCE_API var */ static var readFromStream (InputStream& input); - /* This was a static empty var object, but is now deprecated as it's too easy to accidentally - use it indirectly during a static constructor, leading to hard-to-find order-of-initialisation - problems. - @deprecated If you need a default-constructed var, just use var() or {}. - The only time you might miss having var::null available might be if you need to return an - empty var from a function by reference, but if you need to do that, it's easy enough to use - a function-local static var and return that, avoiding any order-of-initialisation issues. - */ - JUCE_DEPRECATED_STATIC (static const var null;) + //============================================================================== + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) + [[deprecated ("This was a static empty var object, but is now deprecated as it's too easy to accidentally " + "use it indirectly during a static constructor leading to hard-to-find order-of-initialisation " + "problems. Use var() or {} instead. For returning an empty var from a function by reference, " + "use a function-local static var and return that.")]] + static const var null; + #endif private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp b/external/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp index 2c17654cf..e3a7a1289 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp @@ -23,24 +23,6 @@ namespace juce { -DirectoryIterator::DirectoryIterator (const File& directory, bool recursive, - const String& pattern, int type) - : wildCards (parseWildcards (pattern)), - fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern), - wildCard (pattern), - path (File::addTrailingSeparator (directory.getFullPathName())), - whatToLookFor (type), - isRecursive (recursive) -{ - // you have to specify the type of files you're looking for! - jassert ((type & (File::findFiles | File::findDirectories)) != 0); - jassert (type > 0 && type <= 7); -} - -DirectoryIterator::~DirectoryIterator() -{ -} - StringArray DirectoryIterator::parseWildcards (const String& pattern) { StringArray s; diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h b/external/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h index cbf19cc48..b72d46975 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h @@ -23,6 +23,8 @@ namespace juce { +#ifndef DOXYGEN + //============================================================================== /** This class is now deprecated in favour of RangedDirectoryIterator. @@ -50,9 +52,7 @@ class JUCE_API DirectoryIterator final { public: //============================================================================== - /** This class is now deprecated in favour of RangedDirectoryIterator. - - Creates a DirectoryIterator for a given directory. + /** Creates a DirectoryIterator for a given directory. After creating one of these, call its next() method to get the first file - e.g. @code @@ -69,13 +69,22 @@ class JUCE_API DirectoryIterator final @see RangedDirectoryIterator */ - JUCE_DEPRECATED (DirectoryIterator (const File& directory, - bool isRecursive, - const String& wildCard = "*", - int whatToLookFor = File::findFiles)); - - /** Destructor. */ - ~DirectoryIterator(); + [[deprecated ("This class is now deprecated in favour of RangedDirectoryIterator.")]] + DirectoryIterator (const File& directory, + bool recursive, + const String& pattern = "*", + int type = File::findFiles) + : wildCards (parseWildcards (pattern)), + fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern), + wildCard (pattern), + path (File::addTrailingSeparator (directory.getFullPathName())), + whatToLookFor (type), + isRecursive (recursive) + { + // you have to specify the type of files you're looking for! + jassert ((whatToLookFor & (File::findFiles | File::findDirectories)) != 0); + jassert (whatToLookFor > 0 && whatToLookFor <= 7); + } /** Moves the iterator along to the next file. @@ -150,4 +159,6 @@ class JUCE_API DirectoryIterator final JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryIterator) }; +#endif + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_File.cpp b/external/JuceLibraryCode/modules/juce_core/files/juce_File.cpp index 9c4247991..6705dc55d 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_File.cpp +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_File.cpp @@ -63,8 +63,6 @@ File& File::operator= (File&& other) noexcept return *this; } -JUCE_DECLARE_DEPRECATED_STATIC (const File File::nonexistent{};) - //============================================================================== static String removeEllipsis (const String& path) { @@ -1009,6 +1007,19 @@ File File::getLinkedTarget() const } #endif +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const File File::nonexistent{}; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif + //============================================================================== MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode, bool exclusive) : range (0, file.getSize()) diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_File.h b/external/JuceLibraryCode/modules/juce_core/files/juce_File.h index d060d0611..fd702ca22 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_File.h +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_File.h @@ -20,7 +20,7 @@ ============================================================================== */ -#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS) +#if ! defined (DOXYGEN) && (JUCE_MAC || JUCE_IOS) #if __LP64__ using OSType = unsigned int; #else @@ -1074,6 +1074,17 @@ class JUCE_API File final void addToDock() const; #endif + #if JUCE_MAC || JUCE_IOS + /** Returns the path to the container shared by all apps with the provided app group ID. + + You *must* pass one of the app group IDs listed in your app's entitlements file. + + On failure, this function may return a non-existent file, so you should check + that the path exists and is writable before trying to use it. + */ + static File getContainerForSecurityApplicationGroupIdentifier (const String& appGroup); + #endif + //============================================================================== /** Comparator for files */ struct NaturalFileComparator @@ -1095,14 +1106,16 @@ class JUCE_API File final bool foldersFirst; }; + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) /* These static objects are deprecated because it's too easy to accidentally use them indirectly during a static constructor, which leads to very obscure order-of-initialisation bugs. Use File::getSeparatorChar() and File::getSeparatorString(), and instead of File::nonexistent, just use File() or {}. */ - JUCE_DEPRECATED_STATIC (static const juce_wchar separator;) - JUCE_DEPRECATED_STATIC (static const StringRef separatorString;) - JUCE_DEPRECATED_STATIC (static const File nonexistent;) + [[deprecated]] static const juce_wchar separator; + [[deprecated]] static const StringRef separatorString; + [[deprecated]] static const File nonexistent; + #endif private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp b/external/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp index 2c72da280..673c86b9e 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp @@ -23,9 +23,6 @@ namespace juce { -FileSearchPath::FileSearchPath() {} -FileSearchPath::~FileSearchPath() {} - FileSearchPath::FileSearchPath (const String& path) { init (path); diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h b/external/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h index b595300fa..905512741 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h @@ -36,7 +36,10 @@ class JUCE_API FileSearchPath public: //============================================================================== /** Creates an empty search path. */ - FileSearchPath(); + FileSearchPath() = default; + + /** Destructor. */ + ~FileSearchPath() = default; /** Creates a search path from a string of pathnames. @@ -53,9 +56,6 @@ class JUCE_API FileSearchPath /** Copies another search path. */ FileSearchPath& operator= (const FileSearchPath&); - /** Destructor. */ - ~FileSearchPath(); - /** Uses a string containing a list of pathnames to re-initialise this list. This search path is cleared and the semicolon- or comma-separated folders diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.cpp b/external/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.cpp index 10c6b78f0..0e624638b 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.cpp +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.cpp @@ -23,6 +23,9 @@ namespace juce { +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + float DirectoryEntry::getEstimatedProgress() const { if (auto it = iterator.lock()) @@ -31,9 +34,6 @@ float DirectoryEntry::getEstimatedProgress() const return 0.0f; } -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) - // We implement this in terms of the deprecated DirectoryIterator, // but the old DirectoryIterator might go away in the future! RangedDirectoryIterator::RangedDirectoryIterator (const File& directory, @@ -49,9 +49,6 @@ RangedDirectoryIterator::RangedDirectoryIterator (const File& directory, increment(); } -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC - bool RangedDirectoryIterator::next() { const auto result = iterator->next (&entry.directory, @@ -74,4 +71,7 @@ void RangedDirectoryIterator::increment() iterator = nullptr; } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.h b/external/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.h index 3322f786f..9886636f3 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.h +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.h @@ -24,6 +24,9 @@ namespace juce { //============================================================================== +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + /** Describes the attributes of a file or folder. @@ -178,4 +181,8 @@ inline RangedDirectoryIterator begin (const RangedDirectoryIterator& it) { retur */ inline RangedDirectoryIterator end (const RangedDirectoryIterator&) { return {}; } + +JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/juce_core.h b/external/JuceLibraryCode/modules/juce_core/juce_core.h index 119a8b282..9cf87dca4 100644 --- a/external/JuceLibraryCode/modules/juce_core/juce_core.h +++ b/external/JuceLibraryCode/modules/juce_core/juce_core.h @@ -32,7 +32,7 @@ ID: juce_core vendor: juce - version: 6.1.2 + version: 6.1.3 name: JUCE core classes description: The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality. website: http://www.juce.com/juce @@ -244,6 +244,7 @@ JUCE_END_IGNORE_WARNINGS_MSVC #include "memory/juce_ReferenceCountedObject.h" #include "memory/juce_ScopedPointer.h" #include "memory/juce_OptionalScopedPointer.h" +#include "containers/juce_ScopedValueSetter.h" #include "memory/juce_Singleton.h" #include "memory/juce_WeakReference.h" #include "threads/juce_ScopedLock.h" @@ -259,10 +260,10 @@ JUCE_END_IGNORE_WARNINGS_MSVC #include "containers/juce_ListenerList.h" #include "containers/juce_OwnedArray.h" #include "containers/juce_ReferenceCountedArray.h" -#include "containers/juce_ScopedValueSetter.h" #include "containers/juce_SortedSet.h" #include "containers/juce_SparseSet.h" #include "containers/juce_AbstractFifo.h" +#include "containers/juce_SingleThreadedAbstractFifo.h" #include "text/juce_NewLine.h" #include "text/juce_StringPool.h" #include "text/juce_Identifier.h" @@ -340,6 +341,7 @@ JUCE_END_IGNORE_WARNINGS_MSVC #include "containers/juce_PropertySet.h" #include "memory/juce_SharedResourcePointer.h" #include "memory/juce_AllocationHooks.h" +#include "memory/juce_Reservoir.h" #if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS) #include "native/juce_mac_ObjCHelpers.h" diff --git a/external/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp b/external/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp index 6c86681ab..e36fcd882 100644 --- a/external/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp +++ b/external/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp @@ -130,10 +130,6 @@ BigInteger& BigInteger::operator= (BigInteger&& other) noexcept return *this; } -BigInteger::~BigInteger() -{ -} - void BigInteger::swapWith (BigInteger& other) noexcept { for (int i = 0; i < numPreallocatedInts; ++i) diff --git a/external/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h b/external/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h index 5444800a2..5c3389daf 100644 --- a/external/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h +++ b/external/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h @@ -69,7 +69,7 @@ class JUCE_API BigInteger BigInteger& operator= (BigInteger&&) noexcept; /** Destructor. */ - ~BigInteger(); + ~BigInteger() = default; //============================================================================== /** Copies another BigInteger onto this one. */ diff --git a/external/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h b/external/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h index fc6cf86ce..3472b0df6 100644 --- a/external/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h +++ b/external/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h @@ -395,18 +395,12 @@ struct MathConstants }; #ifndef DOXYGEN -/** A double-precision constant for pi. - @deprecated This is deprecated in favour of MathConstants::pi. - The reason is that "double_Pi" was a confusing name, and many people misused it, - wrongly thinking it meant 2 * pi ! -*/ +/** A double-precision constant for pi. */ +[[deprecated ("This is deprecated in favour of MathConstants::pi.")]] const constexpr double double_Pi = MathConstants::pi; -/** A single-precision constant for pi. - @deprecated This is deprecated in favour of MathConstants::pi. - The reason is that "double_Pi" was a confusing name, and many people misused it, - wrongly thinking it meant 2 * pi ! -*/ +/** A single-precision constant for pi. */ +[[deprecated ("This is deprecated in favour of MathConstants::pi.")]] const constexpr float float_Pi = MathConstants::pi; #endif @@ -609,7 +603,7 @@ uint32 readLittleEndianBitsInBuffer (const void* sourceBuffer, uint32 startBit, //============================================================================== -#if JUCE_INTEL || defined (DOXYGEN) +#if JUCE_INTEL || DOXYGEN /** This macro can be applied to a float variable to check whether it contains a denormalised value, and to normalise it if necessary. On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. @@ -637,7 +631,7 @@ namespace TypeHelpers */ template struct ParameterType { using type = const Type&; }; - #if ! DOXYGEN + #ifndef DOXYGEN template struct ParameterType { using type = Type&; }; template struct ParameterType { using type = Type*; }; template <> struct ParameterType { using type = char; }; @@ -662,7 +656,7 @@ namespace TypeHelpers */ template struct SmallestFloatType { using type = float; }; - #if ! DOXYGEN + #ifndef DOXYGEN template <> struct SmallestFloatType { using type = double; }; #endif @@ -673,7 +667,7 @@ namespace TypeHelpers */ template struct UnsignedTypeWithSize {}; - #if ! DOXYGEN + #ifndef DOXYGEN template <> struct UnsignedTypeWithSize<1> { using type = uint8; }; template <> struct UnsignedTypeWithSize<2> { using type = uint16; }; template <> struct UnsignedTypeWithSize<4> { using type = uint32; }; @@ -682,13 +676,10 @@ namespace TypeHelpers } //============================================================================== -#if ! DOXYGEN - // These old functions are deprecated: Just use roundToInt instead. - JUCE_DEPRECATED_ATTRIBUTE inline int roundDoubleToInt (double value) noexcept { return roundToInt (value); } - JUCE_DEPRECATED_ATTRIBUTE inline int roundFloatToInt (float value) noexcept { return roundToInt (value); } - - // This old function isn't needed - just use std::abs() instead - JUCE_DEPRECATED_ATTRIBUTE inline int64 abs64 (int64 n) noexcept { return std::abs (n); } +#ifndef DOXYGEN + [[deprecated ("Use roundToInt instead.")]] inline int roundDoubleToInt (double value) noexcept { return roundToInt (value); } + [[deprecated ("Use roundToInt instead.")]] inline int roundFloatToInt (float value) noexcept { return roundToInt (value); } + [[deprecated ("Use std::abs() instead.")]] inline int64 abs64 (int64 n) noexcept { return std::abs (n); } #endif } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h b/external/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h index bfbf79ecd..438228d92 100644 --- a/external/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h +++ b/external/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h @@ -136,10 +136,9 @@ struct Atomic final //============================================================================== #ifndef DOXYGEN - /* This method has been deprecated as there is no equivalent method in - std::atomic. Use compareAndSetBool instead. - */ - JUCE_DEPRECATED (Type compareAndSetValue (Type, Type) noexcept); + [[deprecated ("This method has been deprecated as there is no equivalent method in " + "std::atomic. Use compareAndSetBool instead.")]] + Type compareAndSetValue (Type, Type) noexcept; #endif }; diff --git a/external/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h b/external/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h index 4ecc99a4a..fc45bb64a 100644 --- a/external/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h +++ b/external/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h @@ -20,7 +20,7 @@ ============================================================================== */ -#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS) +#if ! defined (DOXYGEN) && (JUCE_MAC || JUCE_IOS) #include #endif diff --git a/external/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h b/external/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h index aea56900d..17c179529 100644 --- a/external/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h +++ b/external/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h @@ -23,7 +23,7 @@ namespace juce { -#if ! (defined (DOXYGEN) || JUCE_EXCEPTIONS_DISABLED) +#if ! (DOXYGEN || JUCE_EXCEPTIONS_DISABLED) namespace HeapBlockHelper { template @@ -107,7 +107,7 @@ class HeapBlock If you want an array of zero values, you can use the calloc() method or the other constructor that takes an InitialisationState parameter. */ - template + template ::value, int> = 0> explicit HeapBlock (SizeType numElements) : data (static_cast (std::malloc (static_cast (numElements) * sizeof (ElementType)))) { @@ -119,7 +119,7 @@ class HeapBlock The initialiseToZero parameter determines whether the new memory should be cleared, or left uninitialised. */ - template + template ::value, int> = 0> HeapBlock (SizeType numElements, bool initialiseToZero) : data (static_cast (initialiseToZero ? std::calloc (static_cast (numElements), sizeof (ElementType)) diff --git a/external/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h b/external/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h index db9ad863b..c23f34884 100644 --- a/external/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h +++ b/external/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h @@ -269,13 +269,14 @@ class JUCE_API MemoryBlock bool fromBase64Encoding (StringRef encodedString); //============================================================================== - // This method has been deprecated in favour of the replaceAll() method which will - // also replace the data when `numBytes == 0` - JUCE_DEPRECATED_WITH_BODY (void replaceWith (const void* srcData, size_t numBytes), + #ifndef DOXYGEN + [[deprecated ("Use the replaceAll method instead, which will also replace the data when numBytes == 0.")]] + void replaceWith (const void* srcData, size_t numBytes) { if (numBytes > 0) replaceAll (srcData, numBytes); - }) + } + #endif private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h b/external/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h index 64dfa9973..593b413bb 100644 --- a/external/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h +++ b/external/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h @@ -428,9 +428,10 @@ class ReferenceCountedObjectPtr operator ReferencedType*() const noexcept { return referencedObject; } #endif - - // This old method is deprecated in favour of the shorter and more standard get() method. - JUCE_DEPRECATED_WITH_BODY (ReferencedType* getObject() const, { return get(); }) + #ifndef DOXYGEN + [[deprecated ("Use the get method instead.")]] + ReferencedType* getObject() const { return get(); } + #endif private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h b/external/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h new file mode 100644 index 000000000..d177a1eef --- /dev/null +++ b/external/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h @@ -0,0 +1,100 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +/** + Helper functions for managing buffered readers. +*/ +struct Reservoir +{ + /** Attempts to read the requested range from some kind of input stream, + with intermediate buffering in a 'reservoir'. + + While there are still samples in the requested range left to read, this + function will check whether the next part of the requested range is + already loaded into the reservoir. If the range is available, then + doBufferedRead will call readFromReservoir with the range that should + be copied to the output buffer. If the range is not available, + doBufferedRead will call fillReservoir to request that a new region is + loaded into the reservoir. It will repeat these steps until either the + entire requested region has been read, or the stream ends. + + This will return the range that could not be read successfully, if any. + An empty range implies that the entire read was successful. + + Note that all ranges, including those provided as arguments to the + callbacks, are relative to the original unbuffered input. That is, if + getBufferedRange returns the range [200, 300), then readFromReservoir + might be passed the range [250, 300) in order to copy the final 50 + samples from the reservoir. + + @param rangeToRead the absolute position of the range that should + be read + @param getBufferedRange a function void -> Range that returns + the region currently held in the reservoir + @param readFromReservoir a function Range -> void that can be + used to copy samples from the region in the + reservoir specified in the input range + @param fillReservoir a function Index -> void that is given a + requested read location, and that should + attempt to fill the reservoir starting at this + location. After this function, + getBufferedRange should return the new region + contained in the managed buffer + */ + template + static Range doBufferedRead (Range rangeToRead, + GetBufferedRange&& getBufferedRange, + ReadFromReservoir&& readFromReservoir, + FillReservoir&& fillReservoir) + { + while (! rangeToRead.isEmpty()) + { + const auto bufferedRange = getBufferedRange(); + + if (bufferedRange.contains (rangeToRead.getStart())) + { + const auto rangeToReadInBuffer = rangeToRead.getIntersectionWith (getBufferedRange()); + readFromReservoir (rangeToReadInBuffer); + rangeToRead.setStart (rangeToReadInBuffer.getEnd()); + } + else + { + fillReservoir (rangeToRead.getStart()); + + const auto newRange = getBufferedRange(); + + if (newRange.isEmpty() || ! newRange.contains (rangeToRead.getStart())) + break; + } + } + + return rangeToRead; + } +}; + +} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h b/external/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h index 9d75c35ca..00bd92e52 100644 --- a/external/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h +++ b/external/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h @@ -30,30 +30,28 @@ namespace juce This class is deprecated. You should use std::unique_ptr instead. */ template -class ScopedPointer +class [[deprecated]] ScopedPointer { public: //============================================================================== - // ScopedPointer is deprecated! You should use std::unique_ptr instead. - JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer() = default; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) - // ScopedPointer is deprecated! You should use std::unique_ptr instead. - JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer (decltype (nullptr)) noexcept {} + inline ScopedPointer() {} - // ScopedPointer is deprecated! You should use std::unique_ptr instead. - JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer (ObjectType* objectToTakePossessionOf) noexcept + inline ScopedPointer (decltype (nullptr)) noexcept {} + + inline ScopedPointer (ObjectType* objectToTakePossessionOf) noexcept : object (objectToTakePossessionOf) { } - // ScopedPointer is deprecated! You should use std::unique_ptr instead. ScopedPointer (ScopedPointer& objectToTransferFrom) noexcept : object (objectToTransferFrom.release()) { } - // ScopedPointer is deprecated! You should use std::unique_ptr instead. - JUCE_DEPRECATED_ATTRIBUTE inline ~ScopedPointer() { reset(); } + inline ~ScopedPointer() { reset(); } ScopedPointer& operator= (ScopedPointer& objectToTransferFrom) { @@ -143,9 +141,15 @@ class ScopedPointer ScopedPointer (const ScopedPointer&) = delete; ScopedPointer& operator= (const ScopedPointer&) = delete; #endif + + JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_WARNINGS_GCC_LIKE }; //============================================================================== +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + template bool operator== (ObjectType1* pointer1, const ScopedPointer& pointer2) noexcept { @@ -212,6 +216,9 @@ template void deleteAndZero (ScopedPointer&) { static_assert (sizeof (Type) == 12345, "Attempt to call deleteAndZero() on a ScopedPointer"); } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + } // namespace juce #endif diff --git a/external/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h b/external/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h index c671cd033..1474f72ff 100644 --- a/external/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h +++ b/external/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h @@ -47,53 +47,49 @@ struct SingletonHolder : private MutexType // (inherited so we can use the empt If you're having trouble cleaning up your singletons, perhaps consider using the SharedResourcePointer class instead. */ - jassert (instance == nullptr); + jassert (instance.load() == nullptr); } /** Returns the current instance, or creates a new instance if there isn't one. */ Type* get() { - if (instance == nullptr) + if (auto* ptr = instance.load()) + return ptr; + + typename MutexType::ScopedLockType sl (*this); + + if (auto* ptr = instance.load()) + return ptr; + + auto once = onlyCreateOncePerRun; // (local copy avoids VS compiler warning about this being constant) + + if (once) { - typename MutexType::ScopedLockType sl (*this); + static bool createdOnceAlready = false; - if (instance == nullptr) + if (createdOnceAlready) { - auto once = onlyCreateOncePerRun; // (local copy avoids VS compiler warning about this being constant) - - if (once) - { - static bool createdOnceAlready = false; - - if (createdOnceAlready) - { - // This means that the doNotRecreateAfterDeletion flag was set - // and you tried to create the singleton more than once. - jassertfalse; - return nullptr; - } - - createdOnceAlready = true; - } - - static bool alreadyInside = false; - - if (alreadyInside) - { - // This means that your object's constructor has done something which has - // ended up causing a recursive loop of singleton creation.. - jassertfalse; - } - else - { - alreadyInside = true; - getWithoutChecking(); - alreadyInside = false; - } + // This means that the doNotRecreateAfterDeletion flag was set + // and you tried to create the singleton more than once. + jassertfalse; + return nullptr; } + + createdOnceAlready = true; } - return instance; + static bool alreadyInside = false; + + if (alreadyInside) + { + // This means that your object's constructor has done something which has + // ended up causing a recursive loop of singleton creation. + jassertfalse; + return nullptr; + } + + const ScopedValueSetter scope (alreadyInside, true); + return getWithoutChecking(); } /** Returns the current instance, or creates a new instance if there isn't one, but doesn't do @@ -101,32 +97,30 @@ struct SingletonHolder : private MutexType // (inherited so we can use the empt */ Type* getWithoutChecking() { - if (instance == nullptr) - { - auto newObject = new Type(); // (create into a local so that instance is still null during construction) - instance = newObject; - } + if (auto* p = instance.load()) + return p; - return instance; + auto* newObject = new Type(); // (create into a local so that instance is still null during construction) + instance.store (newObject); + return newObject; } /** Deletes and resets the current instance, if there is one. */ void deleteInstance() { typename MutexType::ScopedLockType sl (*this); - auto old = instance; - instance = nullptr; - delete old; + delete instance.exchange (nullptr); } /** Called by the class's destructor to clear the pointer if it is currently set to the given object. */ void clear (Type* expectedObject) noexcept { - if (instance == expectedObject) - instance = nullptr; + instance.compare_exchange_strong (expectedObject, nullptr); } - Type* instance = nullptr; + // This must be atomic, otherwise a late call to get() may attempt to read instance while it is + // being modified by the very first call to get(). + std::atomic instance { nullptr }; }; diff --git a/external/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h b/external/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h index 03b91b8de..c5452e738 100644 --- a/external/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h +++ b/external/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h @@ -23,24 +23,43 @@ namespace juce { +#ifndef DOXYGEN +namespace detail +{ + template + using Void = void; + + template + struct EqualityComparableToNullptr + : std::false_type {}; + + template + struct EqualityComparableToNullptr() != nullptr)>> + : std::true_type {}; +} // namespace detail +#endif + +//============================================================================== /** Some helper methods for checking a callable object before invoking with the specified arguments. - If the object is a std::function it will check for nullptr before - calling. For a callable object it will invoke the function call operator. + If the object provides a comparison operator for nullptr it will check before + calling. For other objects it will just invoke the function call operator. @tags{Core} */ struct NullCheckedInvocation { - template - static void invoke (std::function&& fn, Args&&... args) + template ::value, int> = 0> + static void invoke (Callable&& fn, Args&&... args) { if (fn != nullptr) fn (std::forward (args)...); } - template + template ::value, int> = 0> static void invoke (Callable&& fn, Args&&... args) { fn (std::forward (args)...); diff --git a/external/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h b/external/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h index 363e5b63f..fb516b03b 100644 --- a/external/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h +++ b/external/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h @@ -136,7 +136,7 @@ class JUCE_API Uuid } // namespace juce -#if ! DOXYGEN +#ifndef DOXYGEN namespace std { template <> struct hash diff --git a/external/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h b/external/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h index b54f7b322..6deaa467c 100644 --- a/external/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h +++ b/external/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h @@ -123,10 +123,12 @@ class JUCE_API WindowsRegistry bool registerForCurrentUserOnly, WoW64Mode mode = WoW64_Default); + #ifndef DOXYGEN // DEPRECATED: use the other methods with a WoW64Mode parameter of WoW64_64bit instead. - JUCE_DEPRECATED (static String getValueWow64 (const String&, const String& defaultValue = String())); - JUCE_DEPRECATED (static bool valueExistsWow64 (const String&)); - JUCE_DEPRECATED (static bool keyExistsWow64 (const String&)); + [[deprecated]] static String getValueWow64 (const String&, const String& defaultValue = String()); + [[deprecated]] static bool valueExistsWow64 (const String&); + [[deprecated]] static bool keyExistsWow64 (const String&); + #endif private: WindowsRegistry() = delete; diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h b/external/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h index 7f364e90c..db28d82bd 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -28,7 +28,7 @@ #if JUCE_MAC || JUCE_IOS #if JUCE_IOS - #if JUCE_MODULE_AVAILABLE_juce_opengl && defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_12_0 + #if JUCE_MODULE_AVAILABLE_juce_opengl && defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0 #define GLES_SILENCE_DEPRECATION 1 #endif diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h b/external/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h index 0103d0eb6..4c8692f9d 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h @@ -503,11 +503,18 @@ DECLARE_JNI_CLASS (AndroidRect, "android/graphics/Rect") #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (getIdentifier, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I") \ - METHOD (openRawResourceFd, "openRawResourceFd", "(I)Landroid/content/res/AssetFileDescriptor;") + METHOD (openRawResourceFd, "openRawResourceFd", "(I)Landroid/content/res/AssetFileDescriptor;") \ + METHOD (getConfiguration, "getConfiguration", "()Landroid/content/res/Configuration;") DECLARE_JNI_CLASS (AndroidResources, "android/content/res/Resources") #undef JNI_CLASS_MEMBERS +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + FIELD (uiMode, "uiMode", "I") \ + +DECLARE_JNI_CLASS (AndroidConfiguration, "android/content/res/Configuration") +#undef JNI_CLASS_MEMBERS + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (getHeight, "getHeight", "()I") \ METHOD (getWidth, "getWidth", "()I") diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp index 8a250fc25..fb34599be 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp @@ -554,9 +554,9 @@ class WebInputStream::Pimpl JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; -std::unique_ptr URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp index e0da17948..635322df2 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp @@ -648,9 +648,9 @@ class WebInputStream::Pimpl JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; -std::unique_ptr URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); } } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp index 9c96c90f9..b27d697f8 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp @@ -139,7 +139,7 @@ File File::getSpecialLocation (const SpecialLocationType type) case invokedExecutableFile: if (juce_argv != nullptr && juce_argc > 0) - return File (CharPointer_UTF8 (juce_argv[0])); + return File (String (CharPointer_UTF8 (juce_argv[0]))); // Falls through JUCE_FALLTHROUGH @@ -199,27 +199,31 @@ static bool isFileExecutable (const String& filename) bool Process::openDocument (const String& fileName, const String& parameters) { - auto cmdString = fileName.replace (" ", "\\ ", false); - cmdString << " " << parameters; - - if (cmdString.startsWithIgnoreCase ("file:") - || File::createFileWithoutCheckingPath (fileName).isDirectory() - || ! isFileExecutable (fileName)) + const auto cmdString = [&] { - StringArray cmdLines; - - for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", - "google-chrome", "chromium-browser", "opera", "konqueror" }) + if (fileName.startsWithIgnoreCase ("file:") + || File::createFileWithoutCheckingPath (fileName).isDirectory() + || ! isFileExecutable (fileName)) { - cmdLines.add (String (browserName) + " " + cmdString.trim().quoted()); + const auto singleCommand = fileName.trim().quoted(); + + StringArray cmdLines; + + for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", + "google-chrome", "chromium-browser", "opera", "konqueror" }) + { + cmdLines.add (String (browserName) + " " + singleCommand); + } + + return cmdLines.joinIntoString (" || "); } - cmdString = cmdLines.joinIntoString (" || "); - } + return (fileName.replace (" ", "\\ ", false) + " " + parameters).trim(); + }(); - const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr }; + const char* const argv[] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr }; - auto cpid = fork(); + const auto cpid = fork(); if (cpid == 0) { diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp index fe87afa7c..b2b378c30 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp @@ -607,9 +607,9 @@ class WebInputStream::Pimpl JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; -std::unique_ptr URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); } #endif diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_CFHelpers.h b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_CFHelpers.h index dcb084e65..b68df2873 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_CFHelpers.h +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_CFHelpers.h @@ -43,6 +43,7 @@ template struct CFObjectHolder { CFObjectHolder() = default; + explicit CFObjectHolder (CFType obj) : object (obj) {} CFObjectHolder (const CFObjectHolder&) = delete; CFObjectHolder (CFObjectHolder&&) = delete; diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm index 77a4f11ab..fd69765e8 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm @@ -201,7 +201,7 @@ static bool launchExecutable (const String& pathAndArguments) case invokedExecutableFile: if (juce_argv != nullptr && juce_argc > 0) - return File::getCurrentWorkingDirectory().getChildFile (CharPointer_UTF8 (juce_argv[0])); + return File::getCurrentWorkingDirectory().getChildFile (String (CharPointer_UTF8 (juce_argv[0]))); // deliberate fall-through... JUCE_FALLTHROUGH @@ -285,13 +285,17 @@ static bool launchExecutable (const String& pathAndArguments) JUCE_AUTORELEASEPOOL { - #if (defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0) \ - || (defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8) - NSError* error = nil; - return [[NSFileManager defaultManager] trashItemAtURL: createNSURLFromFile (*this) - resultingItemURL: nil - error: &error]; - #elif JUCE_IOS + #if JUCE_MAC || (JUCE_IOS && (defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)) + if (@available (macOS 10.8, iOS 11.0, *)) + { + NSError* error = nil; + return [[NSFileManager defaultManager] trashItemAtURL: createNSURLFromFile (*this) + resultingItemURL: nil + error: &error]; + } + #endif + + #if JUCE_IOS return deleteFile(); #else [[NSWorkspace sharedWorkspace] recycleURLs: [NSArray arrayWithObject: createNSURLFromFile (*this)] @@ -406,12 +410,20 @@ bool next (String& filenameFound, #if JUCE_IOS ignoreUnused (parameters); - #if (! defined __IPHONE_OS_VERSION_MIN_REQUIRED) || (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) - return [[UIApplication sharedApplication] openURL: filenameAsURL]; - #else - [[UIApplication sharedApplication] openURL: filenameAsURL options: @{} completionHandler: nil]; - return true; + #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + if (@available (iOS 10.0, *)) + { + [[UIApplication sharedApplication] openURL: filenameAsURL + options: @{} + completionHandler: nil]; + + return true; + } #endif + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + return [[UIApplication sharedApplication] openURL: filenameAsURL]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #else NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; @@ -430,16 +442,23 @@ bool next (String& filenameFound, for (int i = 0; i < params.size(); ++i) [paramArray addObject: juceStringToNS (params[i])]; - #if (defined MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15 - auto config = [NSWorkspaceOpenConfiguration configuration]; - [config setCreatesNewApplicationInstance: YES]; - config.arguments = paramArray; + #if defined (MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 + if (@available (macOS 10.15, *)) + { + auto config = [NSWorkspaceOpenConfiguration configuration]; + [config setCreatesNewApplicationInstance: YES]; + config.arguments = paramArray; + + [workspace openApplicationAtURL: filenameAsURL + configuration: config + completionHandler: nil]; + + return true; + } + #endif + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") - [workspace openApplicationAtURL: filenameAsURL - configuration: config - completionHandler: nil]; - return true; - #else NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease]; [dict setObject: paramArray @@ -449,7 +468,8 @@ bool next (String& filenameFound, options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance configuration: dict error: nil]; - #endif + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } if (file.exists()) @@ -506,4 +526,12 @@ bool next (String& filenameFound, } #endif +File File::getContainerForSecurityApplicationGroupIdentifier (const String& appGroup) +{ + if (auto* url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: juceStringToNS (appGroup)]) + return File (nsStringToJuce ([url path])); + + return File(); +} + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm index 9d0048cbd..cfbc4b8f2 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm @@ -109,18 +109,295 @@ } //============================================================================== -// Unfortunately, we need to have this ugly ifdef here as long as some older OS X versions do not support NSURLSession -#if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) - -//============================================================================== -class URLConnectionState : private Thread +class URLConnectionStateBase : public Thread { public: - URLConnectionState (NSURLRequest* req, const int maxRedirects) + explicit URLConnectionStateBase (NSURLRequest* req, int maxRedirects) : Thread ("http connection"), request ([req retain]), data ([[NSMutableData data] retain]), numRedirectsToFollow (maxRedirects) + { + } + + virtual ~URLConnectionStateBase() = default; + + virtual void cancel() = 0; + virtual bool start (WebInputStream&, WebInputStream::Listener*) = 0; + virtual int read (char* dest, int numBytes) = 0; + + int64 getContentLength() const noexcept { return contentLength; } + NSDictionary* getHeaders() const noexcept { return headers; } + int getStatusCode() const noexcept { return statusCode; } + NSInteger getErrorCode() const noexcept { return nsUrlErrorCode; } + +protected: + CriticalSection dataLock, createConnectionLock; + id delegate = nil; + NSDictionary* headers = nil; + NSURLRequest* request = nil; + NSMutableData* data = nil; + int64 contentLength = -1; + int statusCode = 0; + NSInteger nsUrlErrorCode = 0; + + std::atomic initialised { false }, hasFailed { false }, hasFinished { false }; + const int numRedirectsToFollow; + int numRedirects = 0; + int64 latestTotalBytes = 0; + bool hasBeenCancelled = false; + +private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionStateBase) +}; + +#if JUCE_MAC +// This version is only used for backwards-compatibility with older OSX targets, +// so we'll turn off deprecation warnings. This code will be removed at some point +// in the future. +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated") +class URLConnectionStatePreYosemite : public URLConnectionStateBase +{ +public: + URLConnectionStatePreYosemite (NSURLRequest* req, const int maxRedirects) + : URLConnectionStateBase (req, maxRedirects) + { + static DelegateClass cls; + delegate = [cls.createInstance() init]; + DelegateClass::setState (delegate, this); + } + + ~URLConnectionStatePreYosemite() override + { + stop(); + + [connection release]; + [request release]; + [headers release]; + [delegate release]; + [data release]; + } + + bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) override + { + startThread(); + + while (isThreadRunning() && ! initialised) + { + if (listener != nullptr) + if (! listener->postDataSendProgress (inputStream, (int) latestTotalBytes, (int) [[request HTTPBody] length])) + return false; + + Thread::sleep (1); + } + + return connection != nil && ! hasFailed; + } + + void stop() + { + { + const ScopedLock dLock (dataLock); + const ScopedLock connectionLock (createConnectionLock); + + hasBeenCancelled = true; + + if (connection != nil) + [connection cancel]; + } + + stopThread (10000); + } + + void cancel() override + { + hasFinished = hasFailed = true; + stop(); + } + + int read (char* dest, int numBytes) override + { + int numDone = 0; + + while (numBytes > 0) + { + const ScopedLock sl (dataLock); + auto available = jmin (numBytes, (int) [data length]); + + if (available > 0) + { + [data getBytes: dest length: (NSUInteger) available]; + [data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0]; + + numDone += available; + numBytes -= available; + dest += available; + } + else + { + if (hasFailed || hasFinished) + break; + + const ScopedUnlock sul (dataLock); + Thread::sleep (1); + } + } + + return numDone; + } + + void didReceiveResponse (NSURLResponse* response) + { + { + const ScopedLock sl (dataLock); + [data setLength: 0]; + } + + contentLength = [response expectedContentLength]; + + [headers release]; + headers = nil; + + if ([response isKindOfClass: [NSHTTPURLResponse class]]) + { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response; + headers = [[httpResponse allHeaderFields] retain]; + statusCode = (int) [httpResponse statusCode]; + } + + initialised = true; + } + + NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) + { + if (redirectResponse != nullptr) + { + if (numRedirects >= numRedirectsToFollow) + return nil; // Cancel redirect and allow connection to continue + + ++numRedirects; + } + + return newRequest; + } + + void didFailWithError (NSError* error) + { + DBG (nsStringToJuce ([error description])); ignoreUnused (error); + nsUrlErrorCode = [error code]; + hasFailed = true; + initialised = true; + signalThreadShouldExit(); + } + + void didReceiveData (NSData* newData) + { + const ScopedLock sl (dataLock); + [data appendData: newData]; + initialised = true; + } + + void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) + { + latestTotalBytes = static_cast (totalBytesWritten); + } + + void finishedLoading() + { + hasFinished = true; + initialised = true; + signalThreadShouldExit(); + } + + void run() override + { + { + const ScopedLock lock (createConnectionLock); + + if (hasBeenCancelled) + return; + + connection = [[NSURLConnection alloc] initWithRequest: request + delegate: delegate]; + } + + while (! threadShouldExit()) + { + JUCE_AUTORELEASEPOOL + { + [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + } + } + } + +private: + //============================================================================== + struct DelegateClass : public ObjCClass + { + DelegateClass() : ObjCClass ("JUCENetworkDelegate_") + { + addIvar ("state"); + + addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse); + addMethod (@selector (connection:didFailWithError:), didFailWithError); + addMethod (@selector (connection:didReceiveData:), didReceiveData); + addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), + connectionDidSendBodyData); + addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading); + addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest); + + registerClass(); + } + + static void setState (id self, URLConnectionStatePreYosemite* state) { object_setInstanceVariable (self, "state", state); } + static URLConnectionStatePreYosemite* getState (id self) { return getIvar (self, "state"); } + + private: + static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response) + { + getState (self)->didReceiveResponse (response); + } + + static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error) + { + getState (self)->didFailWithError (error); + } + + static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData) + { + getState (self)->didReceiveData (newData); + } + + static NSURLRequest* willSendRequest (id self, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse* response) + { + return getState (self)->willSendRequest (request, response); + } + + static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) + { + getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); + } + + static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) + { + getState (self)->finishedLoading(); + } + }; + + NSURLConnection* connection = nil; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionStatePreYosemite) +}; +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +#endif + +//============================================================================== +class URLConnectionState : public URLConnectionStateBase +{ +public: + URLConnectionState (NSURLRequest* req, const int maxRedirects) + : URLConnectionStateBase (req, maxRedirects) { static DelegateClass cls; delegate = [cls.createInstance() init]; @@ -151,11 +428,10 @@ [data release]; } - void cancel() + void cancel() override { { - const ScopedLock lock (createTaskLock); - + const ScopedLock lock (createConnectionLock); hasBeenCancelled = true; } @@ -163,10 +439,10 @@ void cancel() stopThread (10000); } - bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) + bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) override { { - const ScopedLock lock (createTaskLock); + const ScopedLock lock (createConnectionLock); if (hasBeenCancelled) return false; @@ -186,7 +462,7 @@ bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) return true; } - int read (char* dest, int numBytes) + int read (char* dest, int numBytes) override { int numDone = 0; @@ -304,7 +580,7 @@ void run() override delegateQueue: [NSOperationQueue currentQueue]] retain]; { - const ScopedLock lock (createTaskLock); + const ScopedLock lock (createConnectionLock); if (! hasBeenCancelled) task = [session dataTaskWithRequest: request]; @@ -323,23 +599,6 @@ void run() override initialised = true; } - int64 contentLength = -1; - CriticalSection dataLock; - id delegate = nil; - NSURLRequest* request = nil; - NSURLSession* session = nil; - NSURLSessionTask* task = nil; - NSMutableData* data = nil; - NSDictionary* headers = nil; - int statusCode = 0; - std::atomic initialised { false }, hasFailed { false }, hasFinished { false }; - bool isBeingDeleted = false; - const int numRedirectsToFollow; - int numRedirects = 0; - int64 latestTotalBytes = 0; - CriticalSection createTaskLock; - bool hasBeenCancelled = false; - private: //============================================================================== struct DelegateClass : public ObjCClass @@ -349,14 +608,14 @@ void run() override addIvar ("state"); addMethod (@selector (URLSession:dataTask:didReceiveResponse:completionHandler:), - didReceiveResponse, "v@:@@@@"); - addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError, "v@:@@"); - addMethod (@selector (URLSession:dataTask:didReceiveData:), didReceiveData, "v@:@@@"); + didReceiveResponse); + addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError); + addMethod (@selector (URLSession:dataTask:didReceiveData:), didReceiveData); addMethod (@selector (URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:), - didSendBodyData, "v@:@@qqq"); + didSendBodyData); addMethod (@selector (URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:), - willPerformHTTPRedirection, "v@:@@@@@"); - addMethod (@selector (URLSession:task:didCompleteWithError:), didCompleteWithError, "v@:@@@"); + willPerformHTTPRedirection); + addMethod (@selector (URLSession:task:didCompleteWithError:), didCompleteWithError); registerClass(); } @@ -403,6 +662,10 @@ static void didCompleteWithError (id self, SEL, NSURLConnection*, NSURLSessionTa } }; + NSURLSession* session = nil; + NSURLSessionTask* task = nil; + bool isBeingDeleted = false; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionState) }; @@ -412,10 +675,8 @@ static void didCompleteWithError (id self, SEL, NSURLConnection*, NSURLSessionTa { BackgroundDownloadTask (const URL& urlToUse, const File& targetLocationToUse, - String extraHeadersToUse, - URL::DownloadTask::Listener* listenerToUse, - bool shouldUsePostRequest) - : listener (listenerToUse), + const URL::DownloadTaskOptions& options) + : listener (options.listener), uniqueIdentifier (String (urlToUse.toString (true).hashCode64()) + String (Random().nextInt64())) { targetLocation = targetLocationToUse; @@ -429,11 +690,11 @@ static void didCompleteWithError (id self, SEL, NSURLConnection*, NSURLSessionTa auto nsUrl = [NSURL URLWithString: juceStringToNS (urlToUse.toString (true))]; NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL: nsUrl]; - if (shouldUsePostRequest) + if (options.usePost) [request setHTTPMethod: @"POST"]; StringArray headerLines; - headerLines.addLines (extraHeadersToUse); + headerLines.addLines (options.extraHeaders); headerLines.removeEmptyStrings (true); for (int i = 0; i < headerLines.size(); ++i) @@ -445,10 +706,14 @@ static void didCompleteWithError (id self, SEL, NSURLConnection*, NSURLSessionTa [request addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; } - session = - [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: juceStringToNS (uniqueIdentifier)] - delegate: delegate - delegateQueue: nullptr]; + auto* configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: juceStringToNS (uniqueIdentifier)]; + + if (options.sharedContainer.isNotEmpty()) + [configuration setSharedContainerIdentifier: juceStringToNS (options.sharedContainer)]; + + session = [NSURLSession sessionWithConfiguration: configuration + delegate: delegate + delegateQueue: nullptr]; if (session != nullptr) downloadTask = [session downloadTaskWithRequest:request]; @@ -479,465 +744,194 @@ static void didCompleteWithError (id self, SEL, NSURLConnection*, NSURLSessionTa bool initOK() { return (downloadTask != nullptr); - } - - bool connect() - { - [downloadTask resume]; - while (downloaded == -1 && finished == false) - connectionEvent.wait(); - - connectFinished = true; - return ! error; - } - - //============================================================================== - URL::DownloadTask::Listener* listener; - NSObject* delegate = nil; - NSURLSession* session = nil; - NSURLSessionDownloadTask* downloadTask = nil; - bool connectFinished = false, hasBeenDestroyed = false; - Atomic calledComplete; - WaitableEvent connectionEvent, destroyEvent; - String uniqueIdentifier; - - static HashMap activeSessions; - - void didWriteData (int64 totalBytesWritten, int64 totalBytesExpectedToWrite) - { - downloaded = totalBytesWritten; - - if (contentLength == -1) - contentLength = totalBytesExpectedToWrite; - - if (connectFinished && error == false && finished == false && listener != nullptr) - listener->progress (this, totalBytesWritten, contentLength); - - connectionEvent.signal(); - } - - void didFinishDownloadingToURL (NSURL* location) - { - NSFileManager* fileManager = [[NSFileManager alloc] init]; - error = ([fileManager moveItemAtURL: location - toURL: createNSURLFromFile (targetLocation) - error: nil] == NO); - httpCode = 200; - finished = true; - - connectionEvent.signal(); - - if (listener != nullptr && calledComplete.exchange (1) == 0) - { - if (contentLength > 0 && downloaded < contentLength) - { - downloaded = contentLength; - listener->progress (this, downloaded, contentLength); - } - - listener->finished (this, !error); - } - } - - static int getHTTPErrorCode (NSError* nsError) - { - // see https://developer.apple.com/reference/foundation/nsurlsessiondownloadtask?language=objc - switch ([nsError code]) - { - case NSURLErrorUserAuthenticationRequired: return 401; - case NSURLErrorNoPermissionsToReadFile: return 403; - case NSURLErrorFileDoesNotExist: return 404; - default: return 500; - } - } - - void didCompleteWithError (NSError* nsError) - { - if (calledComplete.exchange (1) == 0) - { - httpCode = nsError != nil ? getHTTPErrorCode (nsError) : -1; - error = true; - finished = true; - - if (listener != nullptr) - listener->finished (this, ! error); - } - - connectionEvent.signal(); - } - - void didBecomeInvalidWithError() - { - hasBeenDestroyed = true; - destroyEvent.signal(); - } - - //============================================================================== - void notify() - { - if (downloadTask == nullptr) return; - - if (NSError* error = [downloadTask error]) - { - didCompleteWithError (error); - } - else - { - const int64 contentLength = [downloadTask countOfBytesExpectedToReceive]; - - if ([downloadTask state] == NSURLSessionTaskStateCompleted) - didWriteData (contentLength, contentLength); - else - didWriteData ([downloadTask countOfBytesReceived], contentLength); - } - } - - static void invokeNotify (const String& identifier) - { - ScopedLock lock (activeSessions.getLock()); - - if (auto* task = activeSessions[identifier]) - task->notify(); - } - - //============================================================================== - struct DelegateClass : public ObjCClass> - { - DelegateClass() : ObjCClass> ("JUCE_URLDelegate_") - { - addIvar ("state"); - - addMethod (@selector (URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:), - didWriteData, "v@:@@qqq"); - addMethod (@selector (URLSession:downloadTask:didFinishDownloadingToURL:), didFinishDownloadingToURL, "v@:@@@"); - addMethod (@selector (URLSession:task:didCompleteWithError:), didCompleteWithError, "v@:@@@"); - addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError, "v@:@@@"); - - registerClass(); - } - - static void setState (id self, BackgroundDownloadTask* state) { object_setInstanceVariable (self, "state", state); } - static BackgroundDownloadTask* getState (id self) { return getIvar (self, "state"); } - - private: - static void didWriteData (id self, SEL, NSURLSession*, NSURLSessionDownloadTask*, int64_t, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) - { - if (auto state = getState (self)) - state->didWriteData (totalBytesWritten, totalBytesExpectedToWrite); - } - - static void didFinishDownloadingToURL (id self, SEL, NSURLSession*, NSURLSessionDownloadTask*, NSURL* location) - { - if (auto state = getState (self)) - state->didFinishDownloadingToURL (location); - } - - static void didCompleteWithError (id self, SEL, NSURLSession*, NSURLSessionTask*, NSError* nsError) - { - if (auto state = getState (self)) - state->didCompleteWithError (nsError); - } - - static void didBecomeInvalidWithError (id self, SEL, NSURLSession*, NSURLSessionTask*, NSError*) - { - if (auto state = getState (self)) - state->didBecomeInvalidWithError(); - } - }; -}; - -HashMap BackgroundDownloadTask::activeSessions; - -std::unique_ptr URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool usePostRequest) -{ - std::unique_ptr downloadTask (new BackgroundDownloadTask (*this, targetLocation, extraHeaders, listener, usePostRequest)); - - if (downloadTask->initOK() && downloadTask->connect()) - return downloadTask; - - return nullptr; -} - -void URL::DownloadTask::juce_iosURLSessionNotify (const String& identifier) -{ - BackgroundDownloadTask::invokeNotify (identifier); -} -#else -std::unique_ptr URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool usePost) -{ - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, usePost); -} -#endif - -//============================================================================== -#else - -// This version is only used for backwards-compatibility with older OSX targets, -// so we'll turn off deprecation warnings. This code will be removed at some point -// in the future. - -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated") - -//============================================================================== -class URLConnectionState : public Thread -{ -public: - URLConnectionState (NSURLRequest* req, const int maxRedirects) - : Thread ("http connection"), - request ([req retain]), - data ([[NSMutableData data] retain]), - numRedirectsToFollow (maxRedirects) - { - static DelegateClass cls; - delegate = [cls.createInstance() init]; - DelegateClass::setState (delegate, this); - } - - ~URLConnectionState() override - { - stop(); - - [connection release]; - [request release]; - [headers release]; - [delegate release]; - [data release]; - } - - bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) - { - startThread(); - - while (isThreadRunning() && ! initialised) - { - if (listener != nullptr) - if (! listener->postDataSendProgress (inputStream, latestTotalBytes, (int) [[request HTTPBody] length])) - return false; + } - Thread::sleep (1); - } + bool connect() + { + [downloadTask resume]; + while (downloaded == -1 && finished == false) + connectionEvent.wait(); - return connection != nil && ! hasFailed; + connectFinished = true; + return ! error; } - void stop() + //============================================================================== + URL::DownloadTask::Listener* listener; + NSObject* delegate = nil; + NSURLSession* session = nil; + NSURLSessionDownloadTask* downloadTask = nil; + bool connectFinished = false, hasBeenDestroyed = false; + Atomic calledComplete; + WaitableEvent connectionEvent, destroyEvent; + String uniqueIdentifier; + + static HashMap activeSessions; + + void didWriteData (int64 totalBytesWritten, int64 totalBytesExpectedToWrite) { - { - const ScopedLock dLock (dataLock); - const ScopedLock connectionLock (createConnectionLock); + downloaded = totalBytesWritten; - hasBeenCancelled = true; + if (contentLength == -1) + contentLength = totalBytesExpectedToWrite; - if (connection != nil) - [connection cancel]; - } + if (connectFinished && error == false && finished == false && listener != nullptr) + listener->progress (this, totalBytesWritten, contentLength); - stopThread (10000); + connectionEvent.signal(); } - void cancel() + void didFinishDownloadingToURL (NSURL* location) { - hasFinished = hasFailed = true; - stop(); - } + auto* fileManager = [NSFileManager defaultManager]; + error = ([fileManager moveItemAtURL: location + toURL: createNSURLFromFile (targetLocation) + error: nil] == NO); + httpCode = 200; + finished = true; - int read (char* dest, int numBytes) - { - int numDone = 0; + connectionEvent.signal(); - while (numBytes > 0) + if (listener != nullptr && calledComplete.exchange (1) == 0) { - const ScopedLock sl (dataLock); - auto available = jmin (numBytes, (int) [data length]); - - if (available > 0) + if (contentLength > 0 && downloaded < contentLength) { - [data getBytes: dest length: (NSUInteger) available]; - [data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0]; - - numDone += available; - numBytes -= available; - dest += available; + downloaded = contentLength; + listener->progress (this, downloaded, contentLength); } - else - { - if (hasFailed || hasFinished) - break; - const ScopedUnlock sul (dataLock); - Thread::sleep (1); - } + listener->finished (this, !error); } - - return numDone; } - void didReceiveResponse (NSURLResponse* response) + static int getHTTPErrorCode (NSError* nsError) { + // see https://developer.apple.com/reference/foundation/nsurlsessiondownloadtask?language=objc + switch ([nsError code]) { - const ScopedLock sl (dataLock); - [data setLength: 0]; - } - - contentLength = [response expectedContentLength]; - - [headers release]; - headers = nil; - - if ([response isKindOfClass: [NSHTTPURLResponse class]]) - { - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response; - headers = [[httpResponse allHeaderFields] retain]; - statusCode = (int) [httpResponse statusCode]; + case NSURLErrorUserAuthenticationRequired: return 401; + case NSURLErrorNoPermissionsToReadFile: return 403; + case NSURLErrorFileDoesNotExist: return 404; + default: return 500; } - - initialised = true; } - NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) + void didCompleteWithError (NSError* nsError) { - if (redirectResponse != nullptr) + if (calledComplete.exchange (1) == 0) { - if (numRedirects >= numRedirectsToFollow) - return nil; // Cancel redirect and allow connection to continue + httpCode = nsError != nil ? getHTTPErrorCode (nsError) : -1; + error = true; + finished = true; - ++numRedirects; + if (listener != nullptr) + listener->finished (this, ! error); } - return newRequest; - } - - void didFailWithError (NSError* error) - { - DBG (nsStringToJuce ([error description])); ignoreUnused (error); - nsUrlErrorCode = [error code]; - hasFailed = true; - initialised = true; - signalThreadShouldExit(); - } - - void didReceiveData (NSData* newData) - { - const ScopedLock sl (dataLock); - [data appendData: newData]; - initialised = true; + connectionEvent.signal(); } - void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) + void didBecomeInvalidWithError() { - latestTotalBytes = static_cast (totalBytesWritten); + hasBeenDestroyed = true; + destroyEvent.signal(); } - void finishedLoading() + //============================================================================== + void notify() { - hasFinished = true; - initialised = true; - signalThreadShouldExit(); - } + if (downloadTask == nullptr) return; - void run() override - { + if (NSError* error = [downloadTask error]) { - const ScopedLock lock (createConnectionLock); - - if (hasBeenCancelled) - return; - - connection = [[NSURLConnection alloc] initWithRequest: request - delegate: delegate]; + didCompleteWithError (error); } - - while (! threadShouldExit()) + else { - JUCE_AUTORELEASEPOOL - { - [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; - } + const int64 contentLength = [downloadTask countOfBytesExpectedToReceive]; + + if ([downloadTask state] == NSURLSessionTaskStateCompleted) + didWriteData (contentLength, contentLength); + else + didWriteData ([downloadTask countOfBytesReceived], contentLength); } } - int64 contentLength = -1; - CriticalSection dataLock; - NSObject* delegate = nil; - NSURLRequest* request = nil; - NSURLConnection* connection = nil; - NSMutableData* data = nil; - NSDictionary* headers = nil; - NSInteger nsUrlErrorCode = 0; - int statusCode = 0; - std::atomic initialised { false }, hasFailed { false }, hasFinished { false }; - const int numRedirectsToFollow; - int numRedirects = 0; - int latestTotalBytes = 0; - CriticalSection createConnectionLock; - bool hasBeenCancelled = false; + static void invokeNotify (const String& identifier) + { + ScopedLock lock (activeSessions.getLock()); + + if (auto* task = activeSessions[identifier]) + task->notify(); + } -private: //============================================================================== - struct DelegateClass : public ObjCClass + struct DelegateClass : public ObjCClass> { - DelegateClass() : ObjCClass ("JUCENetworkDelegate_") + DelegateClass() : ObjCClass> ("JUCE_URLDelegate_") { - addIvar ("state"); + addIvar ("state"); - addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@"); - addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@"); - addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@"); - addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), - connectionDidSendBodyData, "v@:@iii"); - addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); - addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@@"); + addMethod (@selector (URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:), didWriteData); + addMethod (@selector (URLSession:downloadTask:didFinishDownloadingToURL:), didFinishDownloadingToURL); + addMethod (@selector (URLSession:task:didCompleteWithError:), didCompleteWithError); + addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError); registerClass(); } - static void setState (id self, URLConnectionState* state) { object_setInstanceVariable (self, "state", state); } - static URLConnectionState* getState (id self) { return getIvar (self, "state"); } + static void setState (id self, BackgroundDownloadTask* state) { object_setInstanceVariable (self, "state", state); } + static BackgroundDownloadTask* getState (id self) { return getIvar (self, "state"); } private: - static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response) - { - getState (self)->didReceiveResponse (response); - } - - static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error) - { - getState (self)->didFailWithError (error); - } - - static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData) + static void didWriteData (id self, SEL, NSURLSession*, NSURLSessionDownloadTask*, int64_t, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) { - getState (self)->didReceiveData (newData); + if (auto state = getState (self)) + state->didWriteData (totalBytesWritten, totalBytesExpectedToWrite); } - static NSURLRequest* willSendRequest (id self, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse* response) + static void didFinishDownloadingToURL (id self, SEL, NSURLSession*, NSURLSessionDownloadTask*, NSURL* location) { - return getState (self)->willSendRequest (request, response); + if (auto state = getState (self)) + state->didFinishDownloadingToURL (location); } - static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) + static void didCompleteWithError (id self, SEL, NSURLSession*, NSURLSessionTask*, NSError* nsError) { - getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); + if (auto state = getState (self)) + state->didCompleteWithError (nsError); } - static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) + static void didBecomeInvalidWithError (id self, SEL, NSURLSession*, NSURLSessionTask*, NSError*) { - getState (self)->finishedLoading(); + if (auto state = getState (self)) + state->didBecomeInvalidWithError(); } }; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionState) }; -std::unique_ptr URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) +HashMap BackgroundDownloadTask::activeSessions; + +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); -} + auto downloadTask = std::make_unique (*this, targetLocation, options); -JUCE_END_IGNORE_WARNINGS_GCC_LIKE + if (downloadTask->initOK() && downloadTask->connect()) + return downloadTask; -#endif + return nullptr; +} +void URL::DownloadTask::juce_iosURLSessionNotify (const String& identifier) +{ + BackgroundDownloadTask::invokeNotify (identifier); +} +#else +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) +{ + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); +} +#endif //============================================================================== class WebInputStream::Pimpl @@ -975,28 +969,28 @@ bool connect (WebInputStream::Listener* webInputListener, int numRetries = 0) if (! connection->start (owner, webInputListener)) { - // Workaround for deployment targets below 10.10 where HTTPS POST requests with keep-alive fail with the NSURLErrorNetworkConnectionLost error code. - #if ! (JUCE_IOS || (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10)) - if (numRetries == 0 && connection->nsUrlErrorCode == NSURLErrorNetworkConnectionLost) - { - connection.reset(); + connection.reset(); + + if (@available (macOS 10.10, *)) + return false; + + // Workaround for macOS versions below 10.10 where HTTPS POST requests with keep-alive + // fail with the NSURLErrorNetworkConnectionLost error code. + if (numRetries == 0 && connection->getErrorCode() == NSURLErrorNetworkConnectionLost) return connect (webInputListener, ++numRetries); - } - #endif - connection.reset(); return false; } - if (connection->headers != nil) + if (auto* connectionHeaders = connection->getHeaders()) { - statusCode = connection->statusCode; + statusCode = connection->getStatusCode(); - NSEnumerator* enumerator = [connection->headers keyEnumerator]; + NSEnumerator* enumerator = [connectionHeaders keyEnumerator]; while (NSString* key = [enumerator nextObject]) responseHeaders.set (nsStringToJuce (key), - nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); + nsStringToJuce ((NSString*) [connectionHeaders objectForKey: key])); return true; } @@ -1036,10 +1030,9 @@ void withExtraHeaders (const String& extraHeaders) StringPairArray getResponseHeaders() const { return responseHeaders; } int getStatusCode() const { return statusCode; } - //============================================================================== - bool isError() const { return (connection == nullptr || connection->headers == nullptr); } - int64 getTotalLength() { return connection == nullptr ? -1 : connection->contentLength; } + bool isError() const { return (connection == nullptr || connection->getHeaders() == nullptr); } + int64 getTotalLength() { return connection == nullptr ? -1 : connection->getContentLength(); } bool isExhausted() { return finished; } int64 getPosition() { return position; } @@ -1087,7 +1080,7 @@ bool setPosition (int64 wantedPos) private: WebInputStream& owner; URL url; - std::unique_ptr connection; + std::unique_ptr connection; String headers; MemoryBlock postData; int64 position = 0; @@ -1150,7 +1143,12 @@ void createConnection() // Workaround for an Apple bug. See https://github.com/AFNetworking/AFNetworking/issues/2334 [req HTTPBody]; - connection.reset (new URLConnectionState (req, numRedirectsToFollow)); + if (@available (macOS 10.10, *)) + connection = std::make_unique (req, numRedirectsToFollow); + #if JUCE_MAC + else + connection = std::make_unique (req, numRedirectsToFollow); + #endif } } } diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCHelpers.h b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCHelpers.h index 90015e479..bf28b76b8 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCHelpers.h +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCHelpers.h @@ -252,6 +252,95 @@ struct NSObjectDeleter template using NSUniquePtr = std::unique_ptr; +/* This has very similar semantics to NSUniquePtr, with the main difference that it doesn't + automatically add a pointer to the managed type. This makes it possible to declare + scoped handles to id or block types. +*/ +template +class ObjCObjectHandle +{ +public: + ObjCObjectHandle() = default; + + // Note that this does *not* retain the argument. + explicit ObjCObjectHandle (T ptr) : item (ptr) {} + + ~ObjCObjectHandle() noexcept { reset(); } + + ObjCObjectHandle (const ObjCObjectHandle& other) + : item (other.item) + { + if (item != nullptr) + [item retain]; + } + + ObjCObjectHandle& operator= (const ObjCObjectHandle& other) + { + auto copy = other; + swap (copy); + return *this; + } + + ObjCObjectHandle (ObjCObjectHandle&& other) noexcept { swap (other); } + + ObjCObjectHandle& operator= (ObjCObjectHandle&& other) noexcept + { + reset(); + swap (other); + return *this; + } + + // Note that this does *not* retain the argument. + void reset (T ptr) { *this = ObjCObjectHandle { ptr }; } + + T get() const { return item; } + + void reset() + { + if (item != nullptr) + [item release]; + + item = {}; + } + + bool operator== (const ObjCObjectHandle& other) const { return item == other.item; } + bool operator!= (const ObjCObjectHandle& other) const { return ! (*this == other); } + +private: + void swap (ObjCObjectHandle& other) noexcept { std::swap (other.item, item); } + + T item{}; +}; + +//============================================================================== +namespace detail +{ + constexpr auto makeCompileTimeStr() + { + return std::array { { '\0' } }; + } + + template + constexpr auto joinCompileTimeStrImpl (A&& a, std::index_sequence, + B&& b, std::index_sequence) + { + return std::array { { a[As]..., b[Bs]..., '\0' } }; + } + + template + constexpr auto joinCompileTimeStr (const char (&a)[A], std::array b) + { + return joinCompileTimeStrImpl (a, std::make_index_sequence(), + b, std::make_index_sequence()); + } + + template + constexpr auto makeCompileTimeStr (const char (&v)[A], Others&&... others) + { + return joinCompileTimeStr (v, makeCompileTimeStr (others...)); + } +} // namespace detail + //============================================================================== template inline Type getIvar (id self, const char* name) @@ -294,29 +383,12 @@ struct ObjCClass jassert (b); ignoreUnused (b); } - template - void addMethod (SEL selector, FunctionType callbackFn, const char* signature) - { - BOOL b = class_addMethod (cls, selector, (IMP) callbackFn, signature); - jassert (b); ignoreUnused (b); - } - - template - void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2) - { - addMethod (selector, callbackFn, (String (sig1) + sig2).toUTF8()); - } - - template - void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3) - { - addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3).toUTF8()); - } - - template - void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3, const char* sig4) + template + void addMethod (SEL selector, Result (*callbackFn) (id, SEL, Args...)) { - addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3 + sig4).toUTF8()); + const auto s = detail::makeCompileTimeStr (@encode (Result), @encode (id), @encode (SEL), @encode (Args)...); + const auto b = class_addMethod (cls, selector, (IMP) callbackFn, s.data()); + jassertquiet (b); } void addProtocol (Protocol* protocol) @@ -353,10 +425,10 @@ struct ObjCLifetimeManagedClass : public ObjCClass addIvar ("cppObject"); JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - addMethod (@selector (initWithJuceObject:), initWithJuceObject, "@@:@"); + addMethod (@selector (initWithJuceObject:), initWithJuceObject); JUCE_END_IGNORE_WARNINGS_GCC_LIKE - addMethod (@selector (dealloc), dealloc, "v@:"); + addMethod (@selector (dealloc), dealloc); registerClass(); } diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm index fe05ba7d6..632f75241 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm @@ -69,6 +69,8 @@ hasAVX512VL, hasAVX512VBMI, hasAVX512VPOPCNTDQ); + #elif JUCE_ARM && __ARM_ARCH > 7 + hasNeon = true; #endif numLogicalCPUs = (int) [[NSProcessInfo processInfo] activeProcessorCount]; @@ -89,15 +91,21 @@ static String getOSXVersion() { JUCE_AUTORELEASEPOOL { - const String systemVersionPlist ("/System/Library/CoreServices/SystemVersion.plist"); - - #if (defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13) - NSError* error = nullptr; - NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL: createNSURLFromFile (systemVersionPlist) - error: &error]; - #else - NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile: juceStringToNS (systemVersionPlist)]; - #endif + const auto* dict = [] + { + const String systemVersionPlist ("/System/Library/CoreServices/SystemVersion.plist"); + + #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 + if (@available (macOS 10.13, *)) + { + NSError* error = nullptr; + return [NSDictionary dictionaryWithContentsOfURL: createNSURLFromFile (systemVersionPlist) + error: &error]; + } + #endif + + return [NSDictionary dictionaryWithContentsOfFile: juceStringToNS (systemVersionPlist)]; + }(); if (dict != nullptr) return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]); diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp index 38e7ed66f..87eb90a0c 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp @@ -66,7 +66,9 @@ class NamedPipe::Pimpl if (numRead <= 0) { - if (errno != EWOULDBLOCK || stopReadOperation.load() || hasExpired (timeoutEnd)) + const auto error = errno; + + if (! (error == EWOULDBLOCK || error == EAGAIN) || stopReadOperation.load() || hasExpired (timeoutEnd)) return -1; const int maxWaitingTime = 30; @@ -97,8 +99,20 @@ class NamedPipe::Pimpl auto bytesThisTime = numBytesToWrite - bytesWritten; auto numWritten = (int) ::write (pipeOut, sourceBuffer, (size_t) bytesThisTime); - if (numWritten <= 0) - return -1; + if (numWritten < 0) + { + const auto error = errno; + const int maxWaitingTime = 30; + + if (error == EWOULDBLOCK || error == EAGAIN) + waitToWrite (pipeOut, timeoutEnd == 0 ? maxWaitingTime + : jmin (maxWaitingTime, + (int) (timeoutEnd - Time::getMillisecondCounter()))); + else + return -1; + + numWritten = 0; + } bytesWritten += numWritten; sourceBuffer += numWritten; @@ -178,6 +192,12 @@ class NamedPipe::Pimpl poll (&pfd, 1, timeoutMsecs); } + static void waitToWrite (int handle, int timeoutMsecs) noexcept + { + pollfd pfd { handle, POLLOUT, 0 }; + poll (&pfd, 1, timeoutMsecs); + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h b/external/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h index b802fc0e3..ca63258be 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h @@ -99,8 +99,16 @@ static MaxNumFileHandlesInitialiser maxNumFileHandlesInitialiser; #endif //============================================================================== -JUCE_DECLARE_DEPRECATED_STATIC (const juce_wchar File::separator = '/';) -JUCE_DECLARE_DEPRECATED_STATIC (const StringRef File::separatorString ("/");) +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + +const juce_wchar File::separator = '/'; +const StringRef File::separatorString ("/"); + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + +#endif juce_wchar File::getSeparatorChar() { return '/'; } StringRef File::getSeparatorString() { return "/"; } @@ -1360,7 +1368,7 @@ struct HighResolutionTimer::Pimpl mach_timebase_info (&timebase); const auto ticksPerMs = ((double) timebase.denom * 1000000.0) / (double) timebase.numer; - const auto periodTicks = (uint32_t) (ticksPerMs * periodMs); + const auto periodTicks = (uint32_t) jmin ((double) std::numeric_limits::max(), periodMs * ticksPerMs); thread_time_constraint_policy_data_t policy; policy.period = periodTicks; diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp index 911cb7757..b57bda5e2 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp @@ -162,8 +162,16 @@ namespace WindowsFileHelpers } //============================================================================== -JUCE_DECLARE_DEPRECATED_STATIC (const juce_wchar File::separator = '\\';) -JUCE_DECLARE_DEPRECATED_STATIC (const StringRef File::separatorString ("\\");) +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const juce_wchar File::separator = '\\'; +const StringRef File::separatorString ("\\"); + +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif juce_wchar File::getSeparatorChar() { return '\\'; } StringRef File::getSeparatorString() { return "\\"; } diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp index 9ad4e64dd..d22f8232a 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp @@ -650,9 +650,9 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA return mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS; } -std::unique_ptr URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); } } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp b/external/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp index 770aea0d0..ea6a52892 100644 --- a/external/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp +++ b/external/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp @@ -166,7 +166,7 @@ IPAddress::IPAddress (const String& adr) } IPAddressByteUnion temp; - temp.combined = (uint16) CharacterFunctions::HexParser::parse (tokens[i].getCharPointer()); + temp.combined = CharacterFunctions::HexParser::parse (tokens[i].getCharPointer()); address[i * 2] = temp.split[0]; address[i * 2 + 1] = temp.split[1]; diff --git a/external/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp b/external/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp index 32c422e77..ad93fdb43 100644 --- a/external/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp +++ b/external/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp @@ -106,29 +106,26 @@ struct FallbackDownloadTask : public URL::DownloadTask, JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FallbackDownloadTask) }; -void URL::DownloadTask::Listener::progress (DownloadTask*, int64, int64) {} -URL::DownloadTask::Listener::~Listener() {} +void URL::DownloadTaskListener::progress (DownloadTask*, int64, int64) {} //============================================================================== std::unique_ptr URL::DownloadTask::createFallbackDownloader (const URL& urlToUse, const File& targetFileToUse, - const String& extraHeadersToUse, - Listener* listenerToUse, - bool usePostRequest) + const DownloadTaskOptions& options) { const size_t bufferSize = 0x8000; targetFileToUse.deleteFile(); if (auto outputStream = targetFileToUse.createOutputStream (bufferSize)) { - auto stream = std::make_unique (urlToUse, usePostRequest); - stream->withExtraHeaders (extraHeadersToUse); + auto stream = std::make_unique (urlToUse, options.usePost); + stream->withExtraHeaders (options.extraHeaders); if (stream->connect (nullptr)) return std::make_unique (std::move (outputStream), bufferSize, std::move (stream), - listenerToUse); + options.listener); } return nullptr; @@ -1005,4 +1002,15 @@ std::unique_ptr URL::createInputStream (bool usePostCommand, .withHttpRequestCmd (httpRequestCmd)); } +std::unique_ptr URL::downloadToFile (const File& targetLocation, + String extraHeaders, + DownloadTask::Listener* listener, + bool usePostCommand) +{ + auto options = DownloadTaskOptions().withExtraHeaders (std::move (extraHeaders)) + .withListener (listener) + .withUsePost (usePostCommand); + return downloadToFile (targetLocation, std::move (options)); +} + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/network/juce_URL.h b/external/JuceLibraryCode/modules/juce_core/network/juce_URL.h index 5f73dbb8d..79d99bafc 100644 --- a/external/JuceLibraryCode/modules/juce_core/network/juce_URL.h +++ b/external/JuceLibraryCode/modules/juce_core/network/juce_URL.h @@ -428,29 +428,72 @@ class JUCE_API URL std::unique_ptr createOutputStream() const; //============================================================================== - /** Represents a download task. + class DownloadTask; - Returned by downloadToFile() to allow querying and controlling the download task. + /** Used to receive callbacks for download progress. */ + struct JUCE_API DownloadTaskListener + { + virtual ~DownloadTaskListener() = default; + + /** Called when the download has finished. Be aware that this callback may + come on an arbitrary thread. + */ + virtual void finished (DownloadTask* task, bool success) = 0; + + /** Called periodically by the OS to indicate download progress. + + Beware that this callback may come on an arbitrary thread. + */ + virtual void progress (DownloadTask* task, int64 bytesDownloaded, int64 totalLength); + }; + + /** Holds options that can be specified when starting a new download + with downloadToFile(). */ - class JUCE_API DownloadTask + class DownloadTaskOptions { public: - /** Used to receive callbacks for download progress. */ - struct JUCE_API Listener - { - virtual ~Listener(); + String extraHeaders; + String sharedContainer; + DownloadTaskListener* listener = nullptr; + bool usePost = false; + + /** Specifies headers to add to the request. */ + auto withExtraHeaders (String value) const { return with (&DownloadTaskOptions::extraHeaders, std::move (value)); } + + /** On iOS, specifies the container where the downloaded file will be stored. + + If you initiate a download task from inside an app extension on iOS, + you must supply this option. + + This is currently unused on other platforms. + */ + auto withSharedContainer (String value) const { return with (&DownloadTaskOptions::sharedContainer, std::move (value)); } + + /** Specifies an observer for the download task. */ + auto withListener (DownloadTaskListener* value) const { return with (&DownloadTaskOptions::listener, std::move (value)); } - /** Called when the download has finished. Be aware that this callback may - come on an arbitrary thread. - */ - virtual void finished (URL::DownloadTask* task, bool success) = 0; + /** Specifies whether a post command should be used. */ + auto withUsePost (bool value) const { return with (&DownloadTaskOptions::usePost, value); } - /** Called periodically by the OS to indicate download progress. + private: + template + DownloadTaskOptions with (Member&& member, Value&& value) const + { + auto copy = *this; + copy.*member = std::forward (value); + return copy; + } + }; - Beware that this callback may come on an arbitrary thread. - */ - virtual void progress (URL::DownloadTask* task, int64 bytesDownloaded, int64 totalLength); - }; + /** Represents a download task. + + Returned by downloadToFile() to allow querying and controlling the download task. + */ + class JUCE_API DownloadTask + { + public: + using Listener = DownloadTaskListener; /** Releases the resources of the download task, unregisters the listener and cancels the download if necessary. @@ -493,7 +536,7 @@ class JUCE_API URL private: friend class URL; - static std::unique_ptr createFallbackDownloader (const URL&, const File&, const String&, Listener*, bool); + static std::unique_ptr createFallbackDownloader (const URL&, const File&, const DownloadTaskOptions&); public: #if JUCE_IOS @@ -505,6 +548,13 @@ class JUCE_API URL JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DownloadTask) }; + /** This function is replaced by a new overload accepting a DownloadTaskOptions argument. */ + [[deprecated ("Use the overload with a DownloadTaskOptions argument instead")]] + std::unique_ptr downloadToFile (const File& targetLocation, + String extraHeaders = String(), + DownloadTaskListener* listener = nullptr, + bool usePostCommand = false); + /** Download the URL to a file. This method attempts to download the URL to a given file location. @@ -515,9 +565,7 @@ class JUCE_API URL network re-connections and continuing your download while your app is suspended. */ std::unique_ptr downloadToFile (const File& targetLocation, - String extraHeaders = String(), - DownloadTask::Listener* listener = nullptr, - bool usePostCommand = false); + const DownloadTaskOptions& options); //============================================================================== /** Tries to download the entire contents of this URL into a binary data block. @@ -611,14 +659,14 @@ class JUCE_API URL static URL createWithoutParsing (const String& url); //============================================================================== + #ifndef DOXYGEN using OpenStreamProgressCallback = bool (void* context, int bytesSent, int totalBytes); /** This method has been deprecated. - New code should use the method which takes an InputStreamOptions argument instead. - @see InputStreamOptions */ + [[deprecated ("New code should use the method which takes an InputStreamOptions argument instead.")]] std::unique_ptr createInputStream (bool doPostLikeRequest, OpenStreamProgressCallback* progressCallback = nullptr, void* progressCallbackContext = nullptr, @@ -628,6 +676,7 @@ class JUCE_API URL int* statusCode = nullptr, int numRedirectsToFollow = 5, String httpRequestCmd = {}) const; + #endif private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp b/external/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp index 210521b84..482d480e4 100644 --- a/external/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp +++ b/external/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp @@ -39,22 +39,20 @@ static int calcBufferStreamBufferSize (int requestedSize, InputStream* source) n //============================================================================== BufferedInputStream::BufferedInputStream (InputStream* sourceStream, int size, bool takeOwnership) - : source (sourceStream, takeOwnership), - bufferSize (calcBufferStreamBufferSize (size, sourceStream)), - position (sourceStream->getPosition()), - bufferStart (position) + : source (sourceStream, takeOwnership), + bufferedRange (sourceStream->getPosition(), sourceStream->getPosition()), + position (bufferedRange.getStart()), + bufferLength (calcBufferStreamBufferSize (size, sourceStream)) { - buffer.malloc (bufferSize); + buffer.malloc (bufferLength); } BufferedInputStream::BufferedInputStream (InputStream& sourceStream, int size) - : BufferedInputStream (&sourceStream, size, false) + : BufferedInputStream (&sourceStream, size, false) { } -BufferedInputStream::~BufferedInputStream() -{ -} +BufferedInputStream::~BufferedInputStream() = default; //============================================================================== char BufferedInputStream::peekByte() @@ -62,7 +60,7 @@ char BufferedInputStream::peekByte() if (! ensureBuffered()) return 0; - return position < lastReadPos ? buffer[(int) (position - bufferStart)] : 0; + return position < lastReadPos ? buffer[(int) (position - bufferedRange.getStart())] : 0; } int64 BufferedInputStream::getTotalLength() @@ -90,20 +88,19 @@ bool BufferedInputStream::ensureBuffered() { auto bufferEndOverlap = lastReadPos - bufferOverlap; - if (position < bufferStart || position >= bufferEndOverlap) + if (position < bufferedRange.getStart() || position >= bufferEndOverlap) { - int bytesRead; + int bytesRead = 0; if (position < lastReadPos && position >= bufferEndOverlap - && position >= bufferStart) + && position >= bufferedRange.getStart()) { auto bytesToKeep = (int) (lastReadPos - position); - memmove (buffer, buffer + (int) (position - bufferStart), (size_t) bytesToKeep); + memmove (buffer, buffer + (int) (position - bufferedRange.getStart()), (size_t) bytesToKeep); - bufferStart = position; bytesRead = source->read (buffer + bytesToKeep, - (int) (bufferSize - bytesToKeep)); + (int) (bufferLength - bytesToKeep)); if (bytesRead < 0) return false; @@ -113,75 +110,62 @@ bool BufferedInputStream::ensureBuffered() } else { - bufferStart = position; - - if (! source->setPosition (bufferStart)) + if (! source->setPosition (position)) return false; - bytesRead = source->read (buffer, bufferSize); + bytesRead = (int) source->read (buffer, (size_t) bufferLength); if (bytesRead < 0) return false; - lastReadPos = bufferStart + bytesRead; + lastReadPos = position + bytesRead; } - while (bytesRead < bufferSize) + bufferedRange = Range (position, lastReadPos); + + while (bytesRead < bufferLength) buffer[bytesRead++] = 0; } return true; } -int BufferedInputStream::read (void* destBuffer, int maxBytesToRead) +int BufferedInputStream::read (void* destBuffer, const int maxBytesToRead) { - jassert (destBuffer != nullptr && maxBytesToRead >= 0); + const auto initialPosition = position; - if (position >= bufferStart - && position + maxBytesToRead <= lastReadPos) - { - memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) maxBytesToRead); - position += maxBytesToRead; - return maxBytesToRead; - } + const auto getBufferedRange = [this] { return bufferedRange; }; - if (position < bufferStart || position >= lastReadPos) - if (! ensureBuffered()) - return 0; - - int bytesRead = 0; - - while (maxBytesToRead > 0) + const auto readFromReservoir = [this, &destBuffer, &initialPosition] (const Range rangeToRead) { - auto numToRead = jmin (maxBytesToRead, (int) (lastReadPos - position)); - - if (numToRead > 0) - { - memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) numToRead); - maxBytesToRead -= numToRead; - bytesRead += numToRead; - position += numToRead; - destBuffer = static_cast (destBuffer) + numToRead; - } + memcpy (static_cast (destBuffer) + (rangeToRead.getStart() - initialPosition), + buffer + (rangeToRead.getStart() - bufferedRange.getStart()), + (size_t) rangeToRead.getLength()); + }; - auto oldLastReadPos = lastReadPos; - - if (! ensureBuffered() - || oldLastReadPos == lastReadPos - || isExhausted()) - break; - } - - return bytesRead; + const auto fillReservoir = [this] (int64 requestedStart) + { + position = requestedStart; + ensureBuffered(); + }; + + const auto remaining = Reservoir::doBufferedRead (Range (position, position + maxBytesToRead), + getBufferedRange, + readFromReservoir, + fillReservoir); + + const auto bytesRead = maxBytesToRead - remaining.getLength(); + position = remaining.getStart(); + return (int) bytesRead; } String BufferedInputStream::readString() { - if (position >= bufferStart + if (position >= bufferedRange.getStart() && position < lastReadPos) { auto maxChars = (int) (lastReadPos - position); - auto* src = buffer + (int) (position - bufferStart); + auto* src = buffer + (int) (position - bufferedRange.getStart()); for (int i = 0; i < maxChars; ++i) { @@ -203,71 +187,131 @@ String BufferedInputStream::readString() struct BufferedInputStreamTests : public UnitTest { + template + static void applyImpl (Fn&& fn, std::index_sequence, Values&& values) + { + fn (std::get (values)...); + } + + template + static void apply (Fn&& fn, std::tuple values) + { + applyImpl (fn, std::make_index_sequence(), values); + } + + template + static void allCombinationsImpl (Fn&& fn, Values&& values) + { + apply (fn, values); + } + + template + static void allCombinationsImpl (Fn&& fn, Values&& values, Range&& range, Ranges&&... ranges) + { + for (auto& item : range) + allCombinationsImpl (fn, std::tuple_cat (values, std::tie (item)), ranges...); + } + + template + static void allCombinations (Fn&& fn, Ranges&&... ranges) + { + allCombinationsImpl (fn, std::tie(), ranges...); + } + BufferedInputStreamTests() : UnitTest ("BufferedInputStream", UnitTestCategories::streams) {} void runTest() override { - const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26); - MemoryInputStream mi (data, true); + const MemoryBlock testBufferA ("abcdefghijklmnopqrstuvwxyz", 26); - BufferedInputStream stream (mi, (int) data.getSize()); + const auto testBufferB = [&] + { + MemoryBlock mb { 8192 }; + auto r = getRandom(); - beginTest ("Read"); + std::for_each (mb.begin(), mb.end(), [&] (char& item) + { + item = (char) r.nextInt (std::numeric_limits::max()); + }); - expectEquals (stream.getPosition(), (int64) 0); - expectEquals (stream.getTotalLength(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); - expect (! stream.isExhausted()); + return mb; + }(); - size_t numBytesRead = 0; - MemoryBlock readBuffer (data.getSize()); + const MemoryBlock buffers[] { testBufferA, testBufferB }; + const int readSizes[] { 3, 10, 50 }; + const bool shouldPeek[] { false, true }; - while (numBytesRead < data.getSize()) + const auto runTest = [this] (const MemoryBlock& data, const int readSize, const bool peek) { - expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead)); + MemoryInputStream mi (data, true); - numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3); + BufferedInputStream stream (mi, jmin (200, (int) data.getSize())); - expectEquals (stream.getPosition(), (int64) numBytesRead); - expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); - expect (stream.isExhausted() == (numBytesRead == data.getSize())); - } + beginTest ("Read"); - expectEquals (stream.getPosition(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), (int64) 0); - expect (stream.isExhausted()); + expectEquals (stream.getPosition(), (int64) 0); + expectEquals (stream.getTotalLength(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); + expect (! stream.isExhausted()); - expect (readBuffer == data); + size_t numBytesRead = 0; + MemoryBlock readBuffer (data.getSize()); - beginTest ("Skip"); + while (numBytesRead < data.getSize()) + { + if (peek) + expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead)); + + const auto startingPos = numBytesRead; + numBytesRead += (size_t) stream.read (readBuffer.begin() + numBytesRead, readSize); + + expect (std::equal (readBuffer.begin() + startingPos, + readBuffer.begin() + numBytesRead, + data.begin() + startingPos, + data.begin() + numBytesRead)); + expectEquals (stream.getPosition(), (int64) numBytesRead); + expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); + expect (stream.isExhausted() == (numBytesRead == data.getSize())); + } - stream.setPosition (0); - expectEquals (stream.getPosition(), (int64) 0); - expectEquals (stream.getTotalLength(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); - expect (! stream.isExhausted()); + expectEquals (stream.getPosition(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), (int64) 0); + expect (stream.isExhausted()); - numBytesRead = 0; - const int numBytesToSkip = 5; + expect (readBuffer == data); - while (numBytesRead < data.getSize()) - { - expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead)); + beginTest ("Skip"); - stream.skipNextBytes (numBytesToSkip); - numBytesRead += numBytesToSkip; - numBytesRead = std::min (numBytesRead, data.getSize()); + stream.setPosition (0); + expectEquals (stream.getPosition(), (int64) 0); + expectEquals (stream.getTotalLength(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); + expect (! stream.isExhausted()); - expectEquals (stream.getPosition(), (int64) numBytesRead); - expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); - expect (stream.isExhausted() == (numBytesRead == data.getSize())); - } + numBytesRead = 0; + const int numBytesToSkip = 5; + + while (numBytesRead < data.getSize()) + { + expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead)); + + stream.skipNextBytes (numBytesToSkip); + numBytesRead += numBytesToSkip; + numBytesRead = std::min (numBytesRead, data.getSize()); + + expectEquals (stream.getPosition(), (int64) numBytesRead); + expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); + expect (stream.isExhausted() == (numBytesRead == data.getSize())); + } + + expectEquals (stream.getPosition(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), (int64) 0); + expect (stream.isExhausted()); + }; - expectEquals (stream.getPosition(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), (int64) 0); - expect (stream.isExhausted()); + allCombinations (runTest, buffers, readSizes, shouldPeek); } }; diff --git a/external/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h b/external/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h index 01274ee67..c3de33fbf 100644 --- a/external/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h +++ b/external/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h @@ -79,8 +79,8 @@ class JUCE_API BufferedInputStream : public InputStream private: //============================================================================== OptionalScopedPointer source; - int bufferSize; - int64 position, lastReadPos = 0, bufferStart, bufferOverlap = 128; + Range bufferedRange; + int64 position, bufferLength, lastReadPos = 0, bufferOverlap = 128; HeapBlock buffer; bool ensureBuffered(); diff --git a/external/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp b/external/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp index 1f504d849..6a5312a28 100644 --- a/external/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp +++ b/external/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp @@ -52,9 +52,7 @@ MemoryInputStream::MemoryInputStream (MemoryBlock&& source) dataSize = internalCopy.getSize(); } -MemoryInputStream::~MemoryInputStream() -{ -} +MemoryInputStream::~MemoryInputStream() = default; int64 MemoryInputStream::getTotalLength() { diff --git a/external/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.cpp b/external/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.cpp index be7e686c4..6121cfc1e 100644 --- a/external/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.cpp +++ b/external/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.cpp @@ -39,7 +39,7 @@ URLInputSource::~URLInputSource() InputStream* URLInputSource::createInputStream() { - return u.createInputStream (false).release(); + return u.createInputStream (URL::InputStreamOptions (URL::ParameterHandling::inAddress)).release(); } InputStream* URLInputSource::createInputStreamFor (const String& relatedItemPath) @@ -48,7 +48,10 @@ InputStream* URLInputSource::createInputStreamFor (const String& relatedItemPath auto parent = sub.containsChar (L'/') ? sub.upToLastOccurrenceOf ("/", false, false) : String (); - return u.withNewSubPath (parent).getChildURL (relatedItemPath).createInputStream (false).release(); + return u.withNewSubPath (parent) + .getChildURL (relatedItemPath) + .createInputStream (URL::InputStreamOptions (URL::ParameterHandling::inAddress)) + .release(); } int64 URLInputSource::hashCode() const diff --git a/external/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h b/external/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h index 79dd1f1fb..c49ad3fc0 100644 --- a/external/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h +++ b/external/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h @@ -66,10 +66,6 @@ #define JUCE_CXX14_IS_AVAILABLE (__cplusplus >= 201402L) #define JUCE_CXX17_IS_AVAILABLE (__cplusplus >= 201703L) - #if defined (__OBJC__) - #define JUCE_OBJC_HAS_AVAILABLE_FEATURE (__clang_major__ >= 9) - #endif - #endif //============================================================================== @@ -96,7 +92,7 @@ #endif //============================================================================== -#if ! DOXYGEN +#ifndef DOXYGEN // These are old flags that are now supported on all compatible build targets #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 diff --git a/external/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h b/external/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h index efb1893b0..c0daad136 100644 --- a/external/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h +++ b/external/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h @@ -189,7 +189,7 @@ #define JUCE_IGNORE_MSVC(warnings) __pragma(warning(disable:warnings)) #define JUCE_BEGIN_IGNORE_WARNINGS_LEVEL_MSVC(level, warnings) \ __pragma(warning(push, level)) JUCE_IGNORE_MSVC(warnings) - #define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings) \ + #define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings) \ __pragma(warning(push)) JUCE_IGNORE_MSVC(warnings) #define JUCE_END_IGNORE_WARNINGS_MSVC __pragma(warning(pop)) #else @@ -198,3 +198,24 @@ #define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings) #define JUCE_END_IGNORE_WARNINGS_MSVC #endif + +#if JUCE_MAC || JUCE_IOS + #define JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION 11 +#else + #define JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION 9 +#endif + +/** Disable sanitizers for a range of functions. + + This functionality doesn't seem to exist on GCC yet, so at the moment this only works for clang. +*/ +#if JUCE_CLANG && __clang_major__ >= JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION + #define JUCE_BEGIN_NO_SANITIZE(warnings) \ + _Pragma(JUCE_TO_STRING(clang attribute push(__attribute__((no_sanitize(warnings))), apply_to=function))) + #define JUCE_END_NO_SANITIZE _Pragma(JUCE_TO_STRING(clang attribute pop)) +#else + #define JUCE_BEGIN_NO_SANITIZE(warnings) + #define JUCE_END_NO_SANITIZE +#endif + +#undef JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION diff --git a/external/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h b/external/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h index 6ea28971a..cb30e2146 100644 --- a/external/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h +++ b/external/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h @@ -117,7 +117,7 @@ namespace juce #endif //============================================================================== -#if JUCE_MSVC && ! DOXYGEN +#if JUCE_MSVC && ! defined (DOXYGEN) #define JUCE_BLOCK_WITH_FORCED_SEMICOLON(x) \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ @@ -187,7 +187,7 @@ namespace juce #endif //============================================================================== -#if ! DOXYGEN +#ifndef DOXYGEN #define JUCE_JOIN_MACRO_HELPER(a, b) a ## b #define JUCE_STRINGIFY_MACRO_HELPER(a) #a #endif @@ -298,49 +298,7 @@ namespace juce #endif //============================================================================== -// Cross-compiler deprecation macros.. -#ifdef DOXYGEN - /** This macro can be used to wrap a function which has been deprecated. */ - #define JUCE_DEPRECATED(functionDef) - #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) -#elif JUCE_MSVC && ! JUCE_NO_DEPRECATION_WARNINGS - #define JUCE_DEPRECATED_ATTRIBUTE __declspec(deprecated) - #define JUCE_DEPRECATED(functionDef) JUCE_DEPRECATED_ATTRIBUTE functionDef - #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) JUCE_DEPRECATED_ATTRIBUTE functionDef body -#elif (JUCE_GCC || JUCE_CLANG) && ! JUCE_NO_DEPRECATION_WARNINGS - #define JUCE_DEPRECATED_ATTRIBUTE __attribute__ ((deprecated)) - #define JUCE_DEPRECATED(functionDef) functionDef JUCE_DEPRECATED_ATTRIBUTE - #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef JUCE_DEPRECATED_ATTRIBUTE body -#else - #define JUCE_DEPRECATED_ATTRIBUTE - #define JUCE_DEPRECATED(functionDef) functionDef - #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef body -#endif - -#if JUCE_ALLOW_STATIC_NULL_VARIABLES - #if ! (defined (DOXYGEN) || defined (JUCE_GCC) || (JUCE_MSVC && _MSC_VER <= 1900)) - #define JUCE_DEPRECATED_STATIC(valueDef) JUCE_DEPRECATED_ATTRIBUTE valueDef - - #if JUCE_MSVC - #define JUCE_DECLARE_DEPRECATED_STATIC(valueDef) \ - __pragma(warning(push)) \ - __pragma(warning(disable:4996)) \ - valueDef \ - __pragma(warning(pop)) - #else - #define JUCE_DECLARE_DEPRECATED_STATIC(valueDef) valueDef - #endif - #else - #define JUCE_DEPRECATED_STATIC(valueDef) valueDef - #define JUCE_DECLARE_DEPRECATED_STATIC(valueDef) valueDef - #endif -#else - #define JUCE_DEPRECATED_STATIC(valueDef) - #define JUCE_DECLARE_DEPRECATED_STATIC(valueDef) -#endif - -//============================================================================== -#if JUCE_ANDROID && ! DOXYGEN +#if JUCE_ANDROID && ! defined (DOXYGEN) #define JUCE_MODAL_LOOPS_PERMITTED 0 #elif ! defined (JUCE_MODAL_LOOPS_PERMITTED) /** Some operating environments don't provide a modal loop mechanism, so this flag can be @@ -351,7 +309,7 @@ namespace juce //============================================================================== #if JUCE_GCC || JUCE_CLANG #define JUCE_PACKED __attribute__((packed)) -#elif ! DOXYGEN +#elif ! defined (DOXYGEN) #define JUCE_PACKED #endif diff --git a/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h b/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h index 439fbe170..d0e57cbff 100644 --- a/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h +++ b/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h @@ -29,7 +29,7 @@ */ #define JUCE_MAJOR_VERSION 6 #define JUCE_MINOR_VERSION 1 -#define JUCE_BUILDNUMBER 2 +#define JUCE_BUILDNUMBER 3 /** Current JUCE version number. @@ -153,13 +153,6 @@ JUCE_END_IGNORE_WARNINGS_MSVC /** This macro is added to all JUCE public function declarations. */ #define JUCE_PUBLIC_FUNCTION JUCE_API JUCE_CALLTYPE -#if (! defined (JUCE_CATCH_DEPRECATED_CODE_MISUSE)) && JUCE_DEBUG && ! DOXYGEN - /** This turns on some non-essential bits of code that should prevent old code from compiling - in cases where method signatures have changed, etc. - */ - #define JUCE_CATCH_DEPRECATED_CODE_MISUSE 1 -#endif - #ifndef DOXYGEN #define JUCE_NAMESPACE juce // This old macro is deprecated: you should just use the juce namespace directly. #endif diff --git a/external/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h b/external/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h index c65dfb1e1..ede2c1383 100644 --- a/external/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h +++ b/external/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h @@ -234,8 +234,10 @@ class JUCE_API SystemStats final //============================================================================== - // This method was spelt wrong! Please change your code to use getCpuSpeedInMegahertz() instead - JUCE_DEPRECATED_WITH_BODY (static int getCpuSpeedInMegaherz(), { return getCpuSpeedInMegahertz(); }) + #ifndef DOXYGEN + [[deprecated ("This method was spelt wrong! Please change your code to use getCpuSpeedInMegahertz instead.")]] + static int getCpuSpeedInMegaherz() { return getCpuSpeedInMegahertz(); } + #endif private: SystemStats() = delete; // uses only static methods diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h b/external/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h index beadd9f9e..42681486b 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h @@ -344,7 +344,7 @@ class CharPointer_UTF16 final return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } - #if JUCE_MSVC && ! DOXYGEN + #if JUCE_MSVC && ! defined (DOXYGEN) int compareIgnoreCase (CharPointer_UTF16 other) const noexcept { return _wcsicmp (data, other.data); diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h b/external/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h index 03980720e..d5d5b640c 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h @@ -24,7 +24,7 @@ namespace juce { //============================================================================== -#if JUCE_WINDOWS && ! DOXYGEN +#if JUCE_WINDOWS && ! defined (DOXYGEN) #define JUCE_NATIVE_WCHAR_IS_UTF8 0 #define JUCE_NATIVE_WCHAR_IS_UTF16 1 #define JUCE_NATIVE_WCHAR_IS_UTF32 0 @@ -60,7 +60,7 @@ namespace juce #define T(stringLiteral) JUCE_T(stringLiteral) #endif -#if ! DOXYGEN +#ifndef DOXYGEN //============================================================================== // GNU libstdc++ does not have std::make_unsigned @@ -490,6 +490,9 @@ class JUCE_API CharacterFunctions template struct HexParser { + static_assert (std::is_unsigned::value, "ResultType must be unsigned because " + "left-shifting a negative value is UB"); + template static ResultType parse (CharPointerType t) noexcept { @@ -497,10 +500,10 @@ class JUCE_API CharacterFunctions while (! t.isEmpty()) { - auto hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); + auto hexValue = static_cast (CharacterFunctions::getHexDigitValue (t.getAndAdvance())); if (hexValue >= 0) - result = (result << 4) | hexValue; + result = static_cast (result << 4) | hexValue; } return result; diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp b/external/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp index 164972440..b380d994c 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp @@ -48,10 +48,6 @@ LocalisedStrings& LocalisedStrings::operator= (const LocalisedStrings& other) return *this; } -LocalisedStrings::~LocalisedStrings() -{ -} - //============================================================================== String LocalisedStrings::translate (const String& text) const { diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h b/external/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h index 4ba1e006c..fe73c8ff2 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h @@ -90,7 +90,7 @@ class JUCE_API LocalisedStrings LocalisedStrings& operator= (const LocalisedStrings&); /** Destructor. */ - ~LocalisedStrings(); + ~LocalisedStrings() = default; //============================================================================== /** Selects the current set of mappings to be used by the system. diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_String.cpp b/external/JuceLibraryCode/modules/juce_core/text/juce_String.cpp index b7cc51d41..e859f41de 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_String.cpp +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_String.cpp @@ -237,8 +237,6 @@ class StringHolder } }; -JUCE_DECLARE_DEPRECATED_STATIC (const String String::empty;) - //============================================================================== String::String() noexcept : text (&(emptyString.text)) { @@ -1280,7 +1278,7 @@ String String::replaceSection (int index, int numCharsToReplace, StringRef strin dest += newStringBytes; memcpy (dest, startOfRemainder.getAddress(), remainderBytes); dest += remainderBytes; - CharPointerType ((CharPointerType::CharType*) dest).writeNull(); + CharPointerType (unalignedPointerCast (dest)).writeNull(); return result; } @@ -1959,8 +1957,8 @@ String String::toHexString (const void* const d, const int size, const int group return s; } -int String::getHexValue32() const noexcept { return CharacterFunctions::HexParser ::parse (text); } -int64 String::getHexValue64() const noexcept { return CharacterFunctions::HexParser::parse (text); } +int String::getHexValue32() const noexcept { return (int32) CharacterFunctions::HexParser::parse (text); } +int64 String::getHexValue64() const noexcept { return (int64) CharacterFunctions::HexParser::parse (text); } //============================================================================== static String getStringFromWindows1252Codepage (const char* data, size_t num) @@ -2151,7 +2149,7 @@ String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) JUCE_END_IGNORE_WARNINGS_MSVC //============================================================================== -StringRef::StringRef() noexcept : text ((const String::CharPointerType::CharType*) "\0\0\0") +StringRef::StringRef() noexcept : text (unalignedPointerCast ("\0\0\0")) { } @@ -2195,7 +2193,6 @@ StringRef::StringRef (const String& string) noexcept : text (string.getCharPoi StringRef::StringRef (const std::string& string) : StringRef (string.c_str()) {} //============================================================================== - static String reduceLengthOfFloatString (const String& input) { const auto start = input.getCharPointer(); @@ -2309,6 +2306,18 @@ static String serialiseDouble (double input) return reduceLengthOfFloatString (String (input, numberOfDecimalPlaces)); } +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const String String::empty; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif //============================================================================== //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_String.h b/external/JuceLibraryCode/modules/juce_core/text/juce_String.h index 422b533fa..85695f886 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_String.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_String.h @@ -20,7 +20,7 @@ ============================================================================== */ -#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS) +#if ! defined (DOXYGEN) && (JUCE_MAC || JUCE_IOS) // Annoyingly we can only forward-declare a typedef by forward-declaring the // aliased type #if __has_attribute(objc_bridge) @@ -1326,15 +1326,13 @@ class JUCE_API String final int getReferenceCount() const noexcept; //============================================================================== - /* This was a static empty string object, but is now deprecated as it's too easy to accidentally - use it indirectly during a static constructor, leading to hard-to-find order-of-initialisation - problems. - @deprecated If you need an empty String object, just use String() or {}. - The only time you might miss having String::empty available might be if you need to return an - empty string from a function by reference, but if you need to do that, it's easy enough to use - a function-local static String object and return that, avoiding any order-of-initialisation issues. - */ - JUCE_DEPRECATED_STATIC (static const String empty;) + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) + [[deprecated ("This was a static empty string object, but is now deprecated as it's too easy to accidentally " + "use it indirectly during a static constructor, leading to hard-to-find order-of-initialisation " + "problems. If you need an empty String object, just use String() or {}. For returning an empty " + "String from a function by reference, use a function-local static String object and return that.")]] + static const String empty; + #endif private: //============================================================================== @@ -1349,7 +1347,6 @@ class JUCE_API String final explicit String (const PreallocationBytes&); // This constructor preallocates a certain amount of memory size_t getByteOffsetOfEnd() const noexcept; - JUCE_DEPRECATED (String (const String&, size_t)); // This private cast operator should prevent strings being accidentally cast // to bools (this is possible because the compiler can add an implicit cast @@ -1499,7 +1496,7 @@ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef } // namespace juce -#if ! DOXYGEN +#ifndef DOXYGEN namespace std { template <> struct hash diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp b/external/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp index 54372e4a8..b4ff3667b 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp @@ -89,10 +89,6 @@ StringArray& StringArray::operator= (StringArray&& other) noexcept return *this; } -StringArray::~StringArray() -{ -} - bool StringArray::operator== (const StringArray& other) const noexcept { return strings == other.strings; diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h b/external/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h index 98ed311de..60c823a8c 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h @@ -99,7 +99,7 @@ class JUCE_API StringArray StringArray (const wchar_t* const* strings, int numberOfStrings); /** Destructor. */ - ~StringArray(); + ~StringArray() = default; /** Copies the contents of another string array into this one */ StringArray& operator= (const StringArray&); diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp index 1a0ba555a..7edfb7350 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp @@ -34,10 +34,6 @@ StringPairArray::StringPairArray (const StringPairArray& other) { } -StringPairArray::~StringPairArray() -{ -} - StringPairArray& StringPairArray::operator= (const StringPairArray& other) { keys = other.keys; diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h index 90c71f2d4..2b270acf3 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h @@ -42,7 +42,7 @@ class JUCE_API StringPairArray StringPairArray (const StringPairArray& other); /** Destructor. */ - ~StringPairArray(); + ~StringPairArray() = default; /** Copies the contents of another string array into this one */ StringPairArray& operator= (const StringPairArray& other); diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp index da1b9fdff..6e51add8b 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp @@ -28,7 +28,6 @@ static const uint32 garbageCollectionInterval = 30000; StringPool::StringPool() noexcept : lastGarbageCollectionTime (0) {} -StringPool::~StringPool() {} struct StartEndString { diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h index bd7934cbe..2ed306362 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h @@ -43,9 +43,6 @@ class JUCE_API StringPool /** Creates an empty pool. */ StringPool() noexcept; - /** Destructor */ - ~StringPool(); - //============================================================================== /** Returns a pointer to a shared copy of the string that is passed in. The pool will always return the same String object when asked for a string that matches it. diff --git a/external/JuceLibraryCode/modules/juce_core/threads/juce_Process.h b/external/JuceLibraryCode/modules/juce_core/threads/juce_Process.h index c2ca64b04..2c983541e 100644 --- a/external/JuceLibraryCode/modules/juce_core/threads/juce_Process.h +++ b/external/JuceLibraryCode/modules/juce_core/threads/juce_Process.h @@ -106,8 +106,8 @@ class JUCE_API Process const String& bodyText, const StringArray& filesToAttach); - #if JUCE_WINDOWS || DOXYGEN //============================================================================== + #if JUCE_WINDOWS || DOXYGEN /** WINDOWS ONLY - This returns the HINSTANCE of the current module. The return type is a void* to avoid being dependent on windows.h - just cast @@ -133,14 +133,14 @@ class JUCE_API Process static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) noexcept; #endif - #if (JUCE_MAC && JUCE_MODULE_AVAILABLE_juce_gui_basics) || DOXYGEN //============================================================================== + #if (JUCE_MAC && JUCE_MODULE_AVAILABLE_juce_gui_basics) || DOXYGEN /** OSX ONLY - Shows or hides the OSX dock icon for this app. */ static void setDockIconVisible (bool isVisible); #endif - #if JUCE_MAC || JUCE_LINUX || JUCE_BSD || DOXYGEN //============================================================================== + #if JUCE_MAC || JUCE_LINUX || JUCE_BSD || DOXYGEN /** UNIX ONLY - Attempts to use setrlimit to change the maximum number of file handles that the app can open. Pass 0 or less as the parameter to mean 'infinite'. Returns true if it succeeds. diff --git a/external/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h b/external/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h index 931ac5af6..c74cf3486 100644 --- a/external/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h +++ b/external/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h @@ -338,7 +338,7 @@ class JUCE_API Thread */ static void JUCE_CALLTYPE setCurrentThreadName (const String& newThreadName); - #if JUCE_ANDROID || defined (DOXYGEN) + #if JUCE_ANDROID || DOXYGEN //============================================================================== /** Initialises the JUCE subsystem for projects not created by the Projucer diff --git a/external/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp b/external/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp index 7a9974ce2..15481c4c8 100644 --- a/external/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp +++ b/external/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp @@ -178,6 +178,21 @@ namespace TimeHelpers } static Atomic lastMSCounterValue { (uint32) 0 }; + + static String getUTCOffsetString (int utcOffsetSeconds, bool includeSemiColon) + { + if (const auto seconds = utcOffsetSeconds) + { + auto minutes = seconds / 60; + + return String::formatted (includeSemiColon ? "%+03d:%02d" + : "%+03d%02d", + minutes / 60, + abs (minutes) % 60); + } + + return "Z"; + } } //============================================================================== @@ -406,17 +421,7 @@ int Time::getUTCOffsetSeconds() const noexcept String Time::getUTCOffsetString (bool includeSemiColon) const { - if (auto seconds = getUTCOffsetSeconds()) - { - auto minutes = seconds / 60; - - return String::formatted (includeSemiColon ? "%+03d:%02d" - : "%+03d%02d", - minutes / 60, - minutes % 60); - } - - return "Z"; + return TimeHelpers::getUTCOffsetString (getUTCOffsetSeconds(), includeSemiColon); } String Time::toISO8601 (bool includeDividerCharacters) const @@ -630,6 +635,12 @@ class TimeTests : public UnitTest expect (t.getUTCOffsetString (true) == "Z" || t.getUTCOffsetString (true).length() == 6); expect (t.getUTCOffsetString (false) == "Z" || t.getUTCOffsetString (false).length() == 5); + expect (TimeHelpers::getUTCOffsetString (-(3 * 60 + 15) * 60, true) == "-03:15"); + expect (TimeHelpers::getUTCOffsetString (-(3 * 60 + 30) * 60, true) == "-03:30"); + expect (TimeHelpers::getUTCOffsetString (-(3 * 60 + 45) * 60, true) == "-03:45"); + + expect (TimeHelpers::getUTCOffsetString ((3 * 60 + 15) * 60, true) == "+03:15"); + expect (Time::fromISO8601 (t.toISO8601 (true)) == t); expect (Time::fromISO8601 (t.toISO8601 (false)) == t); diff --git a/external/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h b/external/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h index a9c3c2d54..af3f39063 100644 --- a/external/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h +++ b/external/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h @@ -732,33 +732,31 @@ class JUCE_API XmlElement return Iterator { getChildByName (name), name }; } - /** This allows us to trigger a warning inside deprecated macros. */ #ifndef DOXYGEN - JUCE_DEPRECATED_WITH_BODY (void macroBasedForLoop() const noexcept, {}) + [[deprecated]] void macroBasedForLoop() const noexcept {} + + [[deprecated ("This has been deprecated in favour of the toString method.")]] + String createDocument (StringRef dtdToUse, + bool allOnOneLine = false, + bool includeXmlHeader = true, + StringRef encodingType = "UTF-8", + int lineWrapLength = 60) const; + + [[deprecated ("This has been deprecated in favour of the writeTo method.")]] + void writeToStream (OutputStream& output, + StringRef dtdToUse, + bool allOnOneLine = false, + bool includeXmlHeader = true, + StringRef encodingType = "UTF-8", + int lineWrapLength = 60) const; + + [[deprecated ("This has been deprecated in favour of the writeTo method.")]] + bool writeToFile (const File& destinationFile, + StringRef dtdToUse, + StringRef encodingType = "UTF-8", + int lineWrapLength = 60) const; #endif - //============================================================================== - /** This has been deprecated in favour of the toString() method. */ - JUCE_DEPRECATED (String createDocument (StringRef dtdToUse, - bool allOnOneLine = false, - bool includeXmlHeader = true, - StringRef encodingType = "UTF-8", - int lineWrapLength = 60) const); - - /** This has been deprecated in favour of the writeTo() method. */ - JUCE_DEPRECATED (void writeToStream (OutputStream& output, - StringRef dtdToUse, - bool allOnOneLine = false, - bool includeXmlHeader = true, - StringRef encodingType = "UTF-8", - int lineWrapLength = 60) const); - - /** This has been deprecated in favour of the writeTo() method. */ - JUCE_DEPRECATED (bool writeToFile (const File& destinationFile, - StringRef dtdToUse, - StringRef encodingType = "UTF-8", - int lineWrapLength = 60) const); - private: //============================================================================== struct XmlAttributeNode @@ -801,6 +799,8 @@ class JUCE_API XmlElement }; //============================================================================== +#ifndef DOXYGEN + /** DEPRECATED: A handy macro to make it easy to iterate all the child elements in an XmlElement. New code should avoid this macro, and instead use getChildIterator directly. @@ -852,4 +852,6 @@ class JUCE_API XmlElement #define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \ for (auto* (childElementVariableName) : ((parentXmlElement).macroBasedForLoop(), (parentXmlElement).getChildWithTagNameIterator ((requiredTagName)))) +#endif + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h b/external/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h index 00948e883..e0b764d14 100644 --- a/external/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h +++ b/external/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h @@ -92,11 +92,6 @@ class JUCE_API GZIPDecompressorInputStream : public InputStream class GZIPDecompressHelper; std::unique_ptr helper; - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // The arguments to this method have changed! Please pass a Format enum instead of the old dontWrap bool. - GZIPDecompressorInputStream (InputStream*, bool, bool, int64 x = -1); - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPDecompressorInputStream) }; diff --git a/external/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp b/external/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp index b39fee9d7..beea76668 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp +++ b/external/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp @@ -26,10 +26,6 @@ namespace juce { -ApplicationProperties::ApplicationProperties() -{ -} - ApplicationProperties::~ApplicationProperties() { closeFiles(); diff --git a/external/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h b/external/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h index a55ffac55..77100393c 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h +++ b/external/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h @@ -57,7 +57,7 @@ class JUCE_API ApplicationProperties Before using it, you must call setStorageParameters() to give it the info it needs to create the property files. */ - ApplicationProperties(); + ApplicationProperties() = default; /** Destructor. */ ~ApplicationProperties(); diff --git a/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h b/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h index e05d52626..469f13571 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h +++ b/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h @@ -35,7 +35,7 @@ ID: juce_data_structures vendor: juce - version: 6.1.2 + version: 6.1.3 name: JUCE data model helper classes description: Classes for undo/redo management, and smart data structures. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp index 37009e3d6..2e6f60b0a 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -579,8 +579,6 @@ ValueTree::ValueTree() noexcept { } -JUCE_DECLARE_DEPRECATED_STATIC (const ValueTree ValueTree::invalid;) - ValueTree::ValueTree (const Identifier& type) : object (new ValueTree::SharedObject (type)) { jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name! @@ -1097,6 +1095,18 @@ void ValueTree::Listener::valueTreeChildOrderChanged (ValueTree&, int, int) void ValueTree::Listener::valueTreeParentChanged (ValueTree&) {} void ValueTree::Listener::valueTreeRedirected (ValueTree&) {} +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const ValueTree ValueTree::invalid; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif //============================================================================== //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h index 713f90623..fcfeabf69 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h +++ b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h @@ -606,10 +606,11 @@ class JUCE_API ValueTree final */ int getReferenceCount() const noexcept; - /* An invalid ValueTree that can be used if you need to return one as an error condition, etc. - @deprecated If you need an empty ValueTree object, just use ValueTree() or {}. - */ - JUCE_DEPRECATED_STATIC (static const ValueTree invalid;) + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) + /* An invalid ValueTree that can be used if you need to return one as an error condition, etc. */ + [[deprecated ("If you need an empty ValueTree object, just use ValueTree() or {}.")]] + static const ValueTree invalid; + #endif private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp b/external/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp index 91e9aa016..f14614242 100644 --- a/external/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp +++ b/external/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp @@ -23,7 +23,7 @@ namespace juce { -enum { magicMastSlaveConnectionHeader = 0x712baf04 }; +enum { magicCoordWorkerConnectionHeader = 0x712baf04 }; static const char* startMessage = "__ipc_st"; static const char* killMessage = "__ipc_k_"; @@ -82,11 +82,11 @@ struct ChildProcessPingThread : public Thread, }; //============================================================================== -struct ChildProcessMaster::Connection : public InterprocessConnection, - private ChildProcessPingThread +struct ChildProcessCoordinator::Connection : public InterprocessConnection, + private ChildProcessPingThread { - Connection (ChildProcessMaster& m, const String& pipeName, int timeout) - : InterprocessConnection (false, magicMastSlaveConnectionHeader), + Connection (ChildProcessCoordinator& m, const String& pipeName, int timeout) + : InterprocessConnection (false, magicCoordWorkerConnectionHeader), ChildProcessPingThread (timeout), owner (m) { @@ -103,7 +103,7 @@ struct ChildProcessMaster::Connection : public InterprocessConnection, void connectionMade() override {} void connectionLost() override { owner.handleConnectionLost(); } - bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); } + bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToWorker (m); } void pingFailed() override { connectionLost(); } void messageReceived (const MemoryBlock& m) override @@ -111,25 +111,34 @@ struct ChildProcessMaster::Connection : public InterprocessConnection, pingReceived(); if (m.getSize() != specialMessageSize || ! isMessageType (m, pingMessage)) - owner.handleMessageFromSlave (m); + owner.handleMessageFromWorker (m); } - ChildProcessMaster& owner; + ChildProcessCoordinator& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) }; //============================================================================== -ChildProcessMaster::ChildProcessMaster() {} +ChildProcessCoordinator::ChildProcessCoordinator() = default; -ChildProcessMaster::~ChildProcessMaster() +ChildProcessCoordinator::~ChildProcessCoordinator() { - killSlaveProcess(); + killWorkerProcess(); } -void ChildProcessMaster::handleConnectionLost() {} +void ChildProcessCoordinator::handleConnectionLost() {} + +void ChildProcessCoordinator::handleMessageFromWorker (const MemoryBlock& mb) +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + handleMessageFromSlave (mb); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_WARNINGS_MSVC +} -bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) +bool ChildProcessCoordinator::sendMessageToWorker (const MemoryBlock& mb) { if (connection != nullptr) return connection->sendMessage (mb); @@ -138,10 +147,10 @@ bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) return false; } -bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID, - int timeoutMs, int streamFlags) +bool ChildProcessCoordinator::launchWorkerProcess (const File& executable, const String& commandLineUniqueID, + int timeoutMs, int streamFlags) { - killSlaveProcess(); + killWorkerProcess(); auto pipeName = "p" + String::toHexString (Random().nextInt64()); @@ -157,7 +166,7 @@ bool ChildProcessMaster::launchSlaveProcess (const File& executable, const Strin if (connection->isConnected()) { - sendMessageToSlave ({ startMessage, specialMessageSize }); + sendMessageToWorker ({ startMessage, specialMessageSize }); return true; } @@ -167,11 +176,11 @@ bool ChildProcessMaster::launchSlaveProcess (const File& executable, const Strin return false; } -void ChildProcessMaster::killSlaveProcess() +void ChildProcessCoordinator::killWorkerProcess() { if (connection != nullptr) { - sendMessageToSlave ({ killMessage, specialMessageSize }); + sendMessageToWorker ({ killMessage, specialMessageSize }); connection->disconnect(); connection.reset(); } @@ -180,11 +189,11 @@ void ChildProcessMaster::killSlaveProcess() } //============================================================================== -struct ChildProcessSlave::Connection : public InterprocessConnection, - private ChildProcessPingThread +struct ChildProcessWorker::Connection : public InterprocessConnection, + private ChildProcessPingThread { - Connection (ChildProcessSlave& p, const String& pipeName, int timeout) - : InterprocessConnection (false, magicMastSlaveConnectionHeader), + Connection (ChildProcessWorker& p, const String& pipeName, int timeout) + : InterprocessConnection (false, magicCoordWorkerConnectionHeader), ChildProcessPingThread (timeout), owner (p) { @@ -198,12 +207,12 @@ struct ChildProcessSlave::Connection : public InterprocessConnection, } private: - ChildProcessSlave& owner; + ChildProcessWorker& owner; void connectionMade() override {} void connectionLost() override { owner.handleConnectionLost(); } - bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); } + bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToCoordinator (m); } void pingFailed() override { connectionLost(); } void messageReceived (const MemoryBlock& m) override @@ -219,20 +228,29 @@ struct ChildProcessSlave::Connection : public InterprocessConnection, if (isMessageType (m, startMessage)) return owner.handleConnectionMade(); - owner.handleMessageFromMaster (m); + owner.handleMessageFromCoordinator (m); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) }; //============================================================================== -ChildProcessSlave::ChildProcessSlave() {} -ChildProcessSlave::~ChildProcessSlave() {} +ChildProcessWorker::ChildProcessWorker() = default; +ChildProcessWorker::~ChildProcessWorker() = default; + +void ChildProcessWorker::handleConnectionMade() {} +void ChildProcessWorker::handleConnectionLost() {} -void ChildProcessSlave::handleConnectionMade() {} -void ChildProcessSlave::handleConnectionLost() {} +void ChildProcessWorker::handleMessageFromCoordinator (const MemoryBlock& mb) +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + handleMessageFromMaster (mb); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_WARNINGS_MSVC +} -bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) +bool ChildProcessWorker::sendMessageToCoordinator (const MemoryBlock& mb) { if (connection != nullptr) return connection->sendMessage (mb); @@ -241,9 +259,9 @@ bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) return false; } -bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, - const String& commandLineUniqueID, - int timeoutMs) +bool ChildProcessWorker::initialiseFromCommandLine (const String& commandLine, + const String& commandLineUniqueID, + int timeoutMs) { auto prefix = getCommandLinePrefix (commandLineUniqueID); diff --git a/external/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h b/external/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h index 2979184f2..0107ee03f 100644 --- a/external/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h +++ b/external/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h @@ -25,47 +25,47 @@ namespace juce //============================================================================== /** - Acts as the slave end of a master/slave pair of connected processes. + Acts as the worker end of a coordinator/worker pair of connected processes. - The ChildProcessSlave and ChildProcessMaster classes make it easy for an app + The ChildProcessWorker and ChildProcessCoordinator classes make it easy for an app to spawn a child process, and to manage a 2-way messaging connection to control it. - To use the system, you need to create subclasses of both ChildProcessSlave and - ChildProcessMaster. To instantiate the ChildProcessSlave object, you must + To use the system, you need to create subclasses of both ChildProcessWorker and + ChildProcessCoordinator. To instantiate the ChildProcessWorker object, you must add some code to your main() or JUCEApplication::initialise() function that calls the initialiseFromCommandLine() method to check the app's command-line parameters to see whether it's being launched as a child process. If this returns - true then the slave process can be allowed to run, and its handleMessageFromMaster() + true then the worker process can be allowed to run, and its handleMessageFromCoordinator() method will be called whenever a message arrives. The juce demo app has a good example of this class in action. - @see ChildProcessMaster, InterprocessConnection, ChildProcess + @see ChildProcessCoordinator, InterprocessConnection, ChildProcess @tags{Events} */ -class JUCE_API ChildProcessSlave +class JUCE_API ChildProcessWorker { public: - /** Creates a non-connected slave process. - Use initialiseFromCommandLine to connect to a master process. + /** Creates a non-connected worker process. + Use initialiseFromCommandLine to connect to a coordinator process. */ - ChildProcessSlave(); + ChildProcessWorker(); /** Destructor. */ - virtual ~ChildProcessSlave(); + virtual ~ChildProcessWorker(); /** This checks some command-line parameters to see whether they were generated by - ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process. + ChildProcessCoordinator::launchWorkerProcess(), and if so, connects to that coordinator process. In an exe that can be used as a child process, you should add some code to your main() or JUCEApplication::initialise() that calls this method. The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) - that matches the string passed to ChildProcessMaster::launchSlaveProcess(). + that matches the string passed to ChildProcessCoordinator::launchWorkerProcess(). The timeoutMs parameter lets you specify how long the child process is allowed - to run without receiving a ping from the master before the master is considered to + to run without receiving a ping from the coordinator before the coordinator is considered to have died, and handleConnectionLost() will be called. Passing <= 0 for this timeout makes it use a default value. @@ -76,78 +76,86 @@ class JUCE_API ChildProcessSlave int timeoutMs = 0); //============================================================================== - /** This will be called to deliver messages from the master process. + /** This will be called to deliver messages from the coordinator process. The call will probably be made on a background thread, so be careful with your thread-safety! You may want to respond by sending back a message with - sendMessageToMaster() + sendMessageToCoordinator() */ - virtual void handleMessageFromMaster (const MemoryBlock&) = 0; + virtual void handleMessageFromCoordinator (const MemoryBlock& mb); - /** This will be called when the master process finishes connecting to this slave. + [[deprecated ("Replaced by handleMessageFromCoordinator.")]] + virtual void handleMessageFromMaster (const MemoryBlock&) {} + + /** This will be called when the coordinator process finishes connecting to this worker. The call will probably be made on a background thread, so be careful with your thread-safety! */ virtual void handleConnectionMade(); - /** This will be called when the connection to the master process is lost. + /** This will be called when the connection to the coordinator process is lost. The call may be made from any thread (including the message thread). - Typically, if your process only exists to act as a slave, you should probably exit + Typically, if your process only exists to act as a worker, you should probably exit when this happens. */ virtual void handleConnectionLost(); - /** Tries to send a message to the master process. + /** Tries to send a message to the coordinator process. This returns true if the message was sent, but doesn't check that it actually gets delivered at the other end. If successful, the data will emerge in a call to your - ChildProcessMaster::handleMessageFromSlave(). + ChildProcessCoordinator::handleMessageFromWorker(). */ - bool sendMessageToMaster (const MemoryBlock&); + bool sendMessageToCoordinator (const MemoryBlock&); + + [[deprecated ("Replaced by sendMessageToCoordinator.")]] + bool sendMessageToMaster (const MemoryBlock& mb) { return sendMessageToCoordinator (mb); } private: struct Connection; std::unique_ptr connection; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessWorker) }; +using ChildProcessSlave [[deprecated ("Replaced by ChildProcessWorker.")]] = ChildProcessWorker; + //============================================================================== /** - Acts as the master in a master/slave pair of connected processes. + Acts as the coordinator in a coordinator/worker pair of connected processes. - The ChildProcessSlave and ChildProcessMaster classes make it easy for an app + The ChildProcessWorker and ChildProcessCoordinator classes make it easy for an app to spawn a child process, and to manage a 2-way messaging connection to control it. - To use the system, you need to create subclasses of both ChildProcessSlave and - ChildProcessMaster. When you want your master process to launch the slave, you - just call launchSlaveProcess(), and it'll attempt to launch the executable that + To use the system, you need to create subclasses of both ChildProcessWorker and + ChildProcessCoordinator. When you want your coordinator process to launch the worker, you + just call launchWorkerProcess(), and it'll attempt to launch the executable that you specify (which may be the same exe), and assuming it has been set-up to - correctly parse the command-line parameters (see ChildProcessSlave) then a + correctly parse the command-line parameters (see ChildProcessWorker) then a two-way connection will be created. The juce demo app has a good example of this class in action. - @see ChildProcessSlave, InterprocessConnection, ChildProcess + @see ChildProcessWorker, InterprocessConnection, ChildProcess @tags{Events} */ -class JUCE_API ChildProcessMaster +class JUCE_API ChildProcessCoordinator { public: - /** Creates an uninitialised master process object. - Use launchSlaveProcess to launch and connect to a child process. + /** Creates an uninitialised coordinator process object. + Use launchWorkerProcess to launch and connect to a child process. */ - ChildProcessMaster(); + ChildProcessCoordinator(); /** Destructor. - Note that the destructor calls killSlaveProcess(), but doesn't wait for + Note that the destructor calls killWorkerProcess(), but doesn't wait for the child process to finish terminating. */ - virtual ~ChildProcessMaster(); + virtual ~ChildProcessCoordinator(); - /** Attempts to launch and connect to a slave process. + /** Attempts to launch and connect to a worker process. This will start the given executable, passing it a special command-line parameter based around the commandLineUniqueID string, which must be a short alphanumeric string (no spaces!) that identifies your app. The exe - that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() + that gets launched must respond by calling ChildProcessWorker::initialiseFromCommandLine() in its startup code, and must use a matching ID to commandLineUniqueID. The timeoutMs parameter lets you specify how long the child process is allowed @@ -156,37 +164,55 @@ class JUCE_API ChildProcessMaster it use a default value. If this all works, the method returns true, and you can begin sending and - receiving messages with the slave process. + receiving messages with the worker process. - If a child process is already running, this will call killSlaveProcess() and + If a child process is already running, this will call killWorkerProcess() and start a new one. */ + bool launchWorkerProcess (const File& executableToLaunch, + const String& commandLineUniqueID, + int timeoutMs = 0, + int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr); + + [[deprecated ("Replaced by launchWorkerProcess.")]] bool launchSlaveProcess (const File& executableToLaunch, const String& commandLineUniqueID, int timeoutMs = 0, - int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr); + int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr) + { + return launchWorkerProcess (executableToLaunch, commandLineUniqueID, timeoutMs, streamFlags); + } - /** Sends a kill message to the slave, and disconnects from it. + /** Sends a kill message to the worker, and disconnects from it. Note that this won't wait for it to terminate. */ - void killSlaveProcess(); + void killWorkerProcess(); - /** This will be called to deliver a message from the slave process. + [[deprecated ("Replaced by killWorkerProcess.")]] + void killSlaveProcess() { killWorkerProcess(); } + + /** This will be called to deliver a message from the worker process. The call will probably be made on a background thread, so be careful with your thread-safety! */ - virtual void handleMessageFromSlave (const MemoryBlock&) = 0; + virtual void handleMessageFromWorker (const MemoryBlock&); + + [[deprecated ("Replaced by handleMessageFromWorker")]] + virtual void handleMessageFromSlave (const MemoryBlock&) {} - /** This will be called when the slave process dies or is somehow disconnected. + /** This will be called when the worker process dies or is somehow disconnected. The call will probably be made on a background thread, so be careful with your thread-safety! */ virtual void handleConnectionLost(); - /** Attempts to send a message to the slave process. + /** Attempts to send a message to the worker process. This returns true if the message was dispatched, but doesn't check that it actually gets delivered at the other end. If successful, the data will emerge in a call to - your ChildProcessSlave::handleMessageFromMaster(). + your ChildProcessWorker::handleMessageFromCoordinator(). */ - bool sendMessageToSlave (const MemoryBlock&); + bool sendMessageToWorker (const MemoryBlock&); + + [[deprecated ("Replaced by sendMessageToWorker.")]] + bool sendMessageToSlave (const MemoryBlock& mb) { return sendMessageToWorker (mb); } private: std::unique_ptr childProcess; @@ -194,7 +220,9 @@ class JUCE_API ChildProcessMaster struct Connection; std::unique_ptr connection; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessCoordinator) }; +using ChildProcessMaster [[deprecated ("Replaced by ChildProcessCoordinator.")]] = ChildProcessCoordinator; + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_events/juce_events.h b/external/JuceLibraryCode/modules/juce_events/juce_events.h index 2af2337d5..0adcb314f 100644 --- a/external/JuceLibraryCode/modules/juce_events/juce_events.h +++ b/external/JuceLibraryCode/modules/juce_events/juce_events.h @@ -32,7 +32,7 @@ ID: juce_events vendor: juce - version: 6.1.2 + version: 6.1.3 name: JUCE message and event handling classes description: Classes for running an application's main event loop and sending/receiving messages, timers, etc. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h b/external/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h index 3eddc09c0..66b4cde60 100644 --- a/external/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h +++ b/external/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h @@ -309,7 +309,7 @@ class JUCE_API JUCEApplicationBase //============================================================================== -#if JUCE_CATCH_UNHANDLED_EXCEPTIONS || defined (DOXYGEN) +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS || DOXYGEN /** The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to the JUCEApplicationBase::sendUnhandledException() method. diff --git a/external/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h b/external/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h index 261ac8f33..d4a05ed69 100644 --- a/external/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h +++ b/external/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h @@ -84,7 +84,7 @@ class JUCE_API ScopedJuceInitialiser_GUI final See the JUCEApplication and JUCEApplicationBase class documentation for more details. */ -#ifdef DOXYGEN +#if DOXYGEN #define START_JUCE_APPLICATION(AppClass) #else #if JUCE_WINDOWS && ! defined (_CONSOLE) @@ -157,8 +157,10 @@ class JUCE_API ScopedJuceInitialiser_GUI final #else #define START_JUCE_APPLICATION(AppClass) \ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") \ JUCE_CREATE_APPLICATION_DEFINE(AppClass) \ - JUCE_MAIN_FUNCTION_DEFINITION + JUCE_MAIN_FUNCTION_DEFINITION \ + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #if JUCE_IOS /** diff --git a/external/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h b/external/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h index 83165bd3a..5d226baf7 100644 --- a/external/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h +++ b/external/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h @@ -203,7 +203,7 @@ class JUCE_API MessageManager final Creates a new critical section to exclusively access methods which can only be called when the message manager is locked. - Unlike CrititcalSection, multiple instances of this lock class provide + Unlike CriticalSection, multiple instances of this lock class provide exclusive access to a single resource - the MessageManager. */ Lock(); diff --git a/external/JuceLibraryCode/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h b/external/JuceLibraryCode/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h index fa0c08194..dd45de7fa 100644 --- a/external/JuceLibraryCode/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h +++ b/external/JuceLibraryCode/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h @@ -23,7 +23,7 @@ namespace juce { -#if JUCE_MAC || JUCE_WINDOWS || defined (DOXYGEN) +#if JUCE_MAC || JUCE_WINDOWS || DOXYGEN //============================================================================== /** diff --git a/external/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm b/external/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm index 66f8a30d0..ab4b67e2b 100644 --- a/external/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/external/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm @@ -37,36 +37,36 @@ { AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") { - addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@"); - addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); - addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); - addMethod (@selector (application:openFile:), application_openFile, "c@:@@"); - addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@"); - addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@"); - addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@"); - addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@"); + addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching); + addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate); + addMethod (@selector (applicationWillTerminate:), applicationWillTerminate); + addMethod (@selector (application:openFile:), application_openFile); + addMethod (@selector (application:openFiles:), application_openFiles); + addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive); + addMethod (@selector (applicationDidResignActive:), applicationDidResignActive); + addMethod (@selector (applicationWillUnhide:), applicationWillUnhide); JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@"); - addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@"); - addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@"); - addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); - addMethod (@selector (dummyMethod), dummyMethod, "v@:"); + addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent); + addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback); + addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan); + addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded); + addMethod (@selector (dummyMethod), dummyMethod); JUCE_END_IGNORE_WARNINGS_GCC_LIKE #if JUCE_PUSH_NOTIFICATIONS //============================================================================== addIvar*> ("pushNotificationsDelegate"); - addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@"); + addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching); JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@"); + addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate); JUCE_END_IGNORE_WARNINGS_GCC_LIKE - addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@"); - addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@"); - addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@"); + addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications); + addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications); + addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification); #endif registerClass(); @@ -460,8 +460,8 @@ void initialiseNSApplication() } // Special function used by some plugin classes to re-post carbon events -void __attribute__ ((visibility("default"))) repostCurrentNSEvent(); -void __attribute__ ((visibility("default"))) repostCurrentNSEvent() +void repostCurrentNSEvent(); +void repostCurrentNSEvent() { struct EventReposter : public CallbackMessage { @@ -515,7 +515,7 @@ void messageCallback() override addIvar ("owner"); JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - addMethod (@selector (changed:), changed, "v@:@"); + addMethod (@selector (changed:), changed); JUCE_END_IGNORE_WARNINGS_GCC_LIKE addProtocol (@protocol (NSTextInput)); diff --git a/external/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp b/external/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp index 4a8ef2e79..5c674bb71 100644 --- a/external/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp +++ b/external/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp @@ -186,8 +186,8 @@ class Timer::TimerThread : private Thread, { // Trying to add a timer that's already here - shouldn't get to this point, // so if you get this assertion, let me know! - jassert (std::find_if (timers.begin(), timers.end(), - [t] (TimerCountdown i) { return i.timer == t; }) == timers.end()); + jassert (std::none_of (timers.begin(), timers.end(), + [t] (TimerCountdown i) { return i.timer == t; })); auto pos = timers.size(); diff --git a/external/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp b/external/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp index 40c83f8ec..e9c141744 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp @@ -542,7 +542,7 @@ String Colour::toString() const Colour Colour::fromString (StringRef encodedColourString) { - return Colour ((uint32) CharacterFunctions::HexParser::parse (encodedColourString.text)); + return Colour (CharacterFunctions::HexParser::parse (encodedColourString.text)); } String Colour::toDisplayString (const bool includeAlphaValue) const diff --git a/external/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/external/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index b9bd18a6f..3e7a523d6 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -34,10 +34,10 @@ namespace #if JUCE_DEBUG const int maxVal = 0x3fffffff; - jassert ((int) x >= -maxVal && (int) x <= maxVal - && (int) y >= -maxVal && (int) y <= maxVal - && (int) w >= 0 && (int) w <= maxVal - && (int) h >= 0 && (int) h <= maxVal); + jassertquiet ((int) x >= -maxVal && (int) x <= maxVal + && (int) y >= -maxVal && (int) y <= maxVal + && (int) w >= 0 && (int) w <= maxVal + && (int) h >= 0 && (int) h <= maxVal); #endif return { x, y, w, h }; diff --git a/external/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/external/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index c8d427251..db8b64a46 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -532,7 +532,7 @@ void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const Affin { Path p; Font& font = stateStack.getLast()->font; - font.getTypeface()->getOutlineForGlyph (glyphNumber, p); + font.getTypefacePtr()->getOutlineForGlyph (glyphNumber, p); fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform)); } diff --git a/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp b/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp index 841ca461e..322694f2d 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp @@ -135,7 +135,11 @@ class TypefaceCache : private DeletedAtShutdown return face.typeface; } - Typeface::Ptr defaultFace; + Typeface::Ptr getDefaultFace() const noexcept + { + const ScopedReadLock slr (lock); + return defaultFace; + } private: struct CachedFace @@ -151,6 +155,7 @@ class TypefaceCache : private DeletedAtShutdown Typeface::Ptr typeface; }; + Typeface::Ptr defaultFace; ReadWriteLock lock; Array faces; size_t counter = 0; @@ -182,7 +187,7 @@ class Font::SharedFontInternal : public ReferenceCountedObject { public: SharedFontInternal() noexcept - : typeface (TypefaceCache::getInstance()->defaultFace), + : typeface (TypefaceCache::getInstance()->getDefaultFace()), typefaceName (Font::getDefaultSansSerifFontName()), typefaceStyle (Font::getDefaultStyle()), height (FontValues::defaultFontHeight) @@ -196,7 +201,7 @@ class Font::SharedFontInternal : public ReferenceCountedObject underline ((styleFlags & underlined) != 0) { if (styleFlags == plain) - typeface = TypefaceCache::getInstance()->defaultFace; + typeface = TypefaceCache::getInstance()->getDefaultFace(); } SharedFontInternal (const String& name, int styleFlags, float fontHeight) noexcept @@ -206,7 +211,7 @@ class Font::SharedFontInternal : public ReferenceCountedObject underline ((styleFlags & underlined) != 0) { if (styleFlags == plain && typefaceName.isEmpty()) - typeface = TypefaceCache::getInstance()->defaultFace; + typeface = TypefaceCache::getInstance()->getDefaultFace(); } SharedFontInternal (const String& name, const String& style, float fontHeight) noexcept @@ -248,10 +253,119 @@ class Font::SharedFontInternal : public ReferenceCountedObject && typefaceStyle == other.typefaceStyle; } + /* The typeface and ascent data members may be read/set from multiple threads + simultaneously, e.g. in the case that two Font instances reference the same + SharedFontInternal and call getTypefacePtr() simultaneously. + + We lock in functions that modify the typeface or ascent in order to + ensure thread safety. + */ + + Typeface::Ptr getTypefacePtr (const Font& f) + { + const ScopedLock lock (mutex); + + if (typeface == nullptr) + { + typeface = TypefaceCache::getInstance()->findTypefaceFor (f); + jassert (typeface != nullptr); + } + + return typeface; + } + + void checkTypefaceSuitability (const Font& f) + { + const ScopedLock lock (mutex); + + if (typeface != nullptr && ! typeface->isSuitableForFont (f)) + typeface = nullptr; + } + + float getAscent (const Font& f) + { + const ScopedLock lock (mutex); + + if (ascent == 0.0f) + ascent = getTypefacePtr (f)->getAscent(); + + return height * ascent; + } + + /* We do not need to lock in these functions, as it's guaranteed + that these data members can only change if there is a single Font + instance referencing the shared state. + */ + + String getTypefaceName() const { return typefaceName; } + String getTypefaceStyle() const { return typefaceStyle; } + float getHeight() const { return height; } + float getHorizontalScale() const { return horizontalScale; } + float getKerning() const { return kerning; } + bool getUnderline() const { return underline; } + + /* This shared state may be shared between two or more Font instances that are being + read/modified from multiple threads. + Before modifying a shared instance you *must* call dupeInternalIfShared to + ensure that only one Font instance is pointing to the SharedFontInternal instance + during the modification. + */ + + void setTypeface (Typeface::Ptr x) + { + jassert (getReferenceCount() == 1); + typeface = std::move (x); + } + + void setTypefaceName (String x) + { + jassert (getReferenceCount() == 1); + typefaceName = std::move (x); + } + + void setTypefaceStyle (String x) + { + jassert (getReferenceCount() == 1); + typefaceStyle = std::move (x); + } + + void setHeight (float x) + { + jassert (getReferenceCount() == 1); + height = x; + } + + void setHorizontalScale (float x) + { + jassert (getReferenceCount() == 1); + horizontalScale = x; + } + + void setKerning (float x) + { + jassert (getReferenceCount() == 1); + kerning = x; + } + + void setAscent (float x) + { + jassert (getReferenceCount() == 1); + ascent = x; + } + + void setUnderline (bool x) + { + jassert (getReferenceCount() == 1); + underline = x; + } + +private: Typeface::Ptr typeface; String typefaceName, typefaceStyle; - float height, horizontalScale = 1.0f, kerning = 0, ascent = 0; + float height = 0.0f, horizontalScale = 1.0f, kerning = 0.0f, ascent = 0.0f; bool underline = false; + + CriticalSection mutex; }; //============================================================================== @@ -291,9 +405,7 @@ Font& Font::operator= (Font&& other) noexcept return *this; } -Font::~Font() noexcept -{ -} +Font::~Font() noexcept = default; bool Font::operator== (const Font& other) const noexcept { @@ -314,8 +426,7 @@ void Font::dupeInternalIfShared() void Font::checkTypefaceSuitability() { - if (font->typeface != nullptr && ! font->typeface->isSuitableForFont (*this)) - font->typeface = nullptr; + font->checkTypefaceSuitability (*this); } //============================================================================== @@ -346,30 +457,30 @@ const String& Font::getDefaultSerifFontName() { return getFontPlacehol const String& Font::getDefaultMonospacedFontName() { return getFontPlaceholderNames().mono; } const String& Font::getDefaultStyle() { return getFontPlaceholderNames().regular; } -const String& Font::getTypefaceName() const noexcept { return font->typefaceName; } -const String& Font::getTypefaceStyle() const noexcept { return font->typefaceStyle; } +String Font::getTypefaceName() const noexcept { return font->getTypefaceName(); } +String Font::getTypefaceStyle() const noexcept { return font->getTypefaceStyle(); } void Font::setTypefaceName (const String& faceName) { - if (faceName != font->typefaceName) + if (faceName != font->getTypefaceName()) { jassert (faceName.isNotEmpty()); dupeInternalIfShared(); - font->typefaceName = faceName; - font->typeface = nullptr; - font->ascent = 0; + font->setTypefaceName (faceName); + font->setTypeface (nullptr); + font->setAscent (0); } } void Font::setTypefaceStyle (const String& typefaceStyle) { - if (typefaceStyle != font->typefaceStyle) + if (typefaceStyle != font->getTypefaceStyle()) { dupeInternalIfShared(); - font->typefaceStyle = typefaceStyle; - font->typeface = nullptr; - font->ascent = 0; + font->setTypefaceStyle (typefaceStyle); + font->setTypeface (nullptr); + font->setAscent (0); } } @@ -382,18 +493,17 @@ Font Font::withTypefaceStyle (const String& newStyle) const StringArray Font::getAvailableStyles() const { - return findAllTypefaceStyles (getTypeface()->getName()); + return findAllTypefaceStyles (getTypefacePtr()->getName()); } -Typeface* Font::getTypeface() const +Typeface::Ptr Font::getTypefacePtr() const { - if (font->typeface == nullptr) - { - font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this); - jassert (font->typeface != nullptr); - } + return font->getTypefacePtr (*this); +} - return font->typeface.get(); +Typeface* Font::getTypeface() const +{ + return getTypefacePtr().get(); } //============================================================================== @@ -435,7 +545,7 @@ Font Font::withHeight (const float newHeight) const float Font::getHeightToPointsFactor() const { - return getTypeface()->getHeightToPointsFactor(); + return getTypefacePtr()->getHeightToPointsFactor(); } Font Font::withPointHeight (float heightInPoints) const @@ -449,10 +559,10 @@ void Font::setHeight (float newHeight) { newHeight = FontValues::limitFontHeight (newHeight); - if (font->height != newHeight) + if (font->getHeight() != newHeight) { dupeInternalIfShared(); - font->height = newHeight; + font->setHeight (newHeight); checkTypefaceSuitability(); } } @@ -461,18 +571,18 @@ void Font::setHeightWithoutChangingWidth (float newHeight) { newHeight = FontValues::limitFontHeight (newHeight); - if (font->height != newHeight) + if (font->getHeight() != newHeight) { dupeInternalIfShared(); - font->horizontalScale *= (font->height / newHeight); - font->height = newHeight; + font->setHorizontalScale (font->getHorizontalScale() * (font->getHeight() / newHeight)); + font->setHeight (newHeight); checkTypefaceSuitability(); } } int Font::getStyleFlags() const noexcept { - int styleFlags = font->underline ? underlined : plain; + int styleFlags = font->getUnderline() ? underlined : plain; if (isBold()) styleFlags |= bold; if (isItalic()) styleFlags |= italic; @@ -492,10 +602,10 @@ void Font::setStyleFlags (const int newFlags) if (getStyleFlags() != newFlags) { dupeInternalIfShared(); - font->typeface = nullptr; - font->typefaceStyle = FontStyleHelpers::getStyleName (newFlags); - font->underline = (newFlags & underlined) != 0; - font->ascent = 0; + font->setTypeface (nullptr); + font->setTypefaceStyle (FontStyleHelpers::getStyleName (newFlags)); + font->setUnderline ((newFlags & underlined) != 0); + font->setAscent (0); } } @@ -506,14 +616,14 @@ void Font::setSizeAndStyle (float newHeight, { newHeight = FontValues::limitFontHeight (newHeight); - if (font->height != newHeight - || font->horizontalScale != newHorizontalScale - || font->kerning != newKerningAmount) + if (font->getHeight() != newHeight + || font->getHorizontalScale() != newHorizontalScale + || font->getKerning() != newKerningAmount) { dupeInternalIfShared(); - font->height = newHeight; - font->horizontalScale = newHorizontalScale; - font->kerning = newKerningAmount; + font->setHeight (newHeight); + font->setHorizontalScale (newHorizontalScale); + font->setKerning (newKerningAmount); checkTypefaceSuitability(); } @@ -527,14 +637,14 @@ void Font::setSizeAndStyle (float newHeight, { newHeight = FontValues::limitFontHeight (newHeight); - if (font->height != newHeight - || font->horizontalScale != newHorizontalScale - || font->kerning != newKerningAmount) + if (font->getHeight() != newHeight + || font->getHorizontalScale() != newHorizontalScale + || font->getKerning() != newKerningAmount) { dupeInternalIfShared(); - font->height = newHeight; - font->horizontalScale = newHorizontalScale; - font->kerning = newKerningAmount; + font->setHeight (newHeight); + font->setHorizontalScale (newHorizontalScale); + font->setKerning (newKerningAmount); checkTypefaceSuitability(); } @@ -551,18 +661,18 @@ Font Font::withHorizontalScale (const float newHorizontalScale) const void Font::setHorizontalScale (const float scaleFactor) { dupeInternalIfShared(); - font->horizontalScale = scaleFactor; + font->setHorizontalScale (scaleFactor); checkTypefaceSuitability(); } float Font::getHorizontalScale() const noexcept { - return font->horizontalScale; + return font->getHorizontalScale(); } float Font::getExtraKerningFactor() const noexcept { - return font->kerning; + return font->getKerning(); } Font Font::withExtraKerningFactor (const float extraKerning) const @@ -575,16 +685,16 @@ Font Font::withExtraKerningFactor (const float extraKerning) const void Font::setExtraKerningFactor (const float extraKerning) { dupeInternalIfShared(); - font->kerning = extraKerning; + font->setKerning (extraKerning); checkTypefaceSuitability(); } Font Font::boldened() const { return withStyle (getStyleFlags() | bold); } Font Font::italicised() const { return withStyle (getStyleFlags() | italic); } -bool Font::isBold() const noexcept { return FontStyleHelpers::isBold (font->typefaceStyle); } -bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->typefaceStyle); } -bool Font::isUnderlined() const noexcept { return font->underline; } +bool Font::isBold() const noexcept { return FontStyleHelpers::isBold (font->getTypefaceStyle()); } +bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->getTypefaceStyle()); } +bool Font::isUnderlined() const noexcept { return font->getUnderline(); } void Font::setBold (const bool shouldBeBold) { @@ -603,20 +713,17 @@ void Font::setItalic (const bool shouldBeItalic) void Font::setUnderline (const bool shouldBeUnderlined) { dupeInternalIfShared(); - font->underline = shouldBeUnderlined; + font->setUnderline (shouldBeUnderlined); checkTypefaceSuitability(); } float Font::getAscent() const { - if (font->ascent == 0.0f) - font->ascent = getTypeface()->getAscent(); - - return font->height * font->ascent; + return font->getAscent (*this); } -float Font::getHeight() const noexcept { return font->height; } -float Font::getDescent() const { return font->height - getAscent(); } +float Font::getHeight() const noexcept { return font->getHeight(); } +float Font::getDescent() const { return font->getHeight() - getAscent(); } float Font::getHeightInPoints() const { return getHeight() * getHeightToPointsFactor(); } float Font::getAscentInPoints() const { return getAscent() * getHeightToPointsFactor(); } @@ -629,35 +736,27 @@ int Font::getStringWidth (const String& text) const float Font::getStringWidthFloat (const String& text) const { - // This call isn't thread-safe when there's a message thread running - jassert (MessageManager::getInstanceWithoutCreating() == nullptr - || MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager()); - - auto w = getTypeface()->getStringWidth (text); + auto w = getTypefacePtr()->getStringWidth (text); - if (font->kerning != 0.0f) - w += font->kerning * (float) text.length(); + if (font->getKerning() != 0.0f) + w += font->getKerning() * (float) text.length(); - return w * font->height * font->horizontalScale; + return w * font->getHeight() * font->getHorizontalScale(); } void Font::getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) const { - // This call isn't thread-safe when there's a message thread running - jassert (MessageManager::getInstanceWithoutCreating() == nullptr - || MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager()); - - getTypeface()->getGlyphPositions (text, glyphs, xOffsets); + getTypefacePtr()->getGlyphPositions (text, glyphs, xOffsets); if (auto num = xOffsets.size()) { - auto scale = font->height * font->horizontalScale; + auto scale = font->getHeight() * font->getHorizontalScale(); auto* x = xOffsets.getRawDataPointer(); - if (font->kerning != 0.0f) + if (font->getKerning() != 0.0f) { for (int i = 0; i < num; ++i) - x[i] = (x[i] + (float) i * font->kerning) * scale; + x[i] = (x[i] + (float) i * font->getKerning()) * scale; } else { diff --git a/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h b/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h index d4c18f56a..bae028c44 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h +++ b/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h @@ -121,7 +121,7 @@ class JUCE_API Font final or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font family names, but are generic font family names that are used to represent the various default fonts. If you need to know the exact typeface font family being used, you can call - Font::getTypeface()->getName(), which will give you the platform-specific font family. + Font::getTypefacePtr()->getName(), which will give you the platform-specific font family. If a suitable font isn't found on the machine, it'll just use a default instead. */ @@ -136,15 +136,15 @@ class JUCE_API Font final but are generic font family names that are used to represent the various default fonts. If you need to know the exact typeface font family being used, you can call - Font::getTypeface()->getName(), which will give you the platform-specific font family. + Font::getTypefacePtr()->getName(), which will give you the platform-specific font family. */ - const String& getTypefaceName() const noexcept; + String getTypefaceName() const noexcept; //============================================================================== /** Returns the font style of the typeface that this font uses. @see withTypefaceStyle, getAvailableStyles() */ - const String& getTypefaceStyle() const noexcept; + String getTypefaceStyle() const noexcept; /** Changes the font style of the typeface. @see getAvailableStyles() @@ -395,12 +395,18 @@ class JUCE_API Font final void getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) const; //============================================================================== + #ifndef DOXYGEN /** Returns the typeface used by this font. Note that the object returned may go out of scope if this font is deleted or has its style changed. */ + [[deprecated ("This method is unsafe, use getTypefacePtr() instead.")]] Typeface* getTypeface() const; + #endif + + /** Returns the typeface used by this font. */ + Typeface::Ptr getTypefacePtr() const; /** Creates an array of Font objects to represent all the fonts on the system. diff --git a/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp b/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp index 4bf10fe4d..32225b575 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp @@ -63,7 +63,7 @@ void PositionedGlyph::createPath (Path& path) const { if (! isWhitespace()) { - if (auto* t = font.getTypeface()) + if (auto t = font.getTypefacePtr()) { Path p; t->getOutlineForGlyph (glyph, p); @@ -78,7 +78,7 @@ bool PositionedGlyph::hitTest (float px, float py) const { if (getBounds().contains (px, py) && ! isWhitespace()) { - if (auto* t = font.getTypeface()) + if (auto t = font.getTypefacePtr()) { Path p; t->getOutlineForGlyph (glyph, p); diff --git a/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp b/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp index 730b25145..a76cc423e 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp @@ -110,14 +110,12 @@ Typeface::Typeface (const String& faceName, const String& styleName) noexcept { } -Typeface::~Typeface() -{ -} +Typeface::~Typeface() = default; Typeface::Ptr Typeface::getFallbackTypeface() { const Font fallbackFont (Font::getFallbackFontName(), Font::getFallbackFontStyle(), 10.0f); - return Typeface::Ptr (fallbackFont.getTypeface()); + return fallbackFont.getTypefacePtr(); } EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight) diff --git a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp index d33234a6e..c26a779dd 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp @@ -59,7 +59,7 @@ bool AffineTransform::isIdentity() const noexcept && mat11 == 1.0f; } -JUCE_DECLARE_DEPRECATED_STATIC (const AffineTransform AffineTransform::identity (1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);) +const AffineTransform AffineTransform::identity (1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); //============================================================================== AffineTransform AffineTransform::followedBy (const AffineTransform& other) const noexcept diff --git a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h index 6ee23855a..cce680f9f 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h +++ b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h @@ -266,6 +266,8 @@ class JUCE_API AffineTransform final /** Returns the determinant of the transform. */ float getDeterminant() const noexcept; + //============================================================================== + #ifndef DOXYGEN /** This method has been deprecated. You can calculate the scale factor using: @@ -279,12 +281,13 @@ class JUCE_API AffineTransform final Obviously a length may be scaled by entirely different amounts depending on its direction, so this is only appropriate as a rough guide. */ - JUCE_DEPRECATED (float getScaleFactor() const noexcept); + [[deprecated ("This method produces incorrect values for transforms containing rotations. " + "See the method docs for a code example on how to calculate the correct scale factor.")]] + float getScaleFactor() const noexcept; - /* A ready-to-use identity transform - now deprecated. - @deprecated If you need an identity transform, just use AffineTransform() or {}. - */ - JUCE_DEPRECATED_STATIC (static const AffineTransform identity;) + [[deprecated ("If you need an identity transform, just use AffineTransform() or {}.")]] + static const AffineTransform identity; + #endif //============================================================================== /* The transform matrix is: diff --git a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp index 9043d01fd..46706eee7 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp @@ -28,15 +28,12 @@ namespace juce JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6263 6386) -const int juce_edgeTableDefaultEdgesPerLine = 32; - -//============================================================================== EdgeTable::EdgeTable (Rectangle area, const Path& path, const AffineTransform& transform) : bounds (area), // this is a very vague heuristic to make a rough guess at a good table size // for a given path, such that it's big enough to mostly avoid remapping, but also // not so big that it's wasteful for simple paths. - maxEdgesPerLine (jmax (juce_edgeTableDefaultEdgesPerLine / 2, + maxEdgesPerLine (jmax (defaultEdgesPerLine / 2, 4 * (int) std::sqrt (path.data.size()))), lineStrideElements (maxEdgesPerLine * 2 + 1) { @@ -49,10 +46,10 @@ EdgeTable::EdgeTable (Rectangle area, const Path& path, const AffineTransfo t += lineStrideElements; } - auto leftLimit = bounds.getX() * 256; - auto topLimit = bounds.getY() * 256; - auto rightLimit = bounds.getRight() * 256; - auto heightLimit = bounds.getHeight() * 256; + auto leftLimit = scale * bounds.getX(); + auto topLimit = scale * bounds.getY(); + auto rightLimit = scale * bounds.getRight(); + auto heightLimit = scale * bounds.getHeight(); PathFlatteningIterator iter (path, transform); @@ -97,7 +94,7 @@ EdgeTable::EdgeTable (Rectangle area, const Path& path, const AffineTransfo else if (x >= rightLimit) x = rightLimit - 1; - addEdgePoint (x, y1 >> 8, direction * step); + addEdgePoint (x, y1 / scale, direction * step); y1 += step; } while (y1 < y2); @@ -110,14 +107,14 @@ EdgeTable::EdgeTable (Rectangle area, const Path& path, const AffineTransfo EdgeTable::EdgeTable (Rectangle rectangleToAdd) : bounds (rectangleToAdd), - maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1) + maxEdgesPerLine (defaultEdgesPerLine), + lineStrideElements (defaultEdgesPerLine * 2 + 1) { allocate(); table[0] = 0; - auto x1 = rectangleToAdd.getX() << 8; - auto x2 = rectangleToAdd.getRight() << 8; + auto x1 = scale * rectangleToAdd.getX(); + auto x2 = scale * rectangleToAdd.getRight(); int* t = table; for (int i = rectangleToAdd.getHeight(); --i >= 0;) @@ -133,8 +130,8 @@ EdgeTable::EdgeTable (Rectangle rectangleToAdd) EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) : bounds (rectanglesToAdd.getBounds()), - maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), + maxEdgesPerLine (defaultEdgesPerLine), + lineStrideElements (defaultEdgesPerLine * 2 + 1), needToCheckEmptiness (true) { allocate(); @@ -142,8 +139,8 @@ EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) for (auto& r : rectanglesToAdd) { - auto x1 = r.getX() << 8; - auto x2 = r.getRight() << 8; + auto x1 = scale * r.getX(); + auto x2 = scale * r.getRight(); auto y = r.getY() - bounds.getY(); for (int j = r.getHeight(); --j >= 0;) @@ -164,17 +161,17 @@ EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) for (auto& r : rectanglesToAdd) { - auto x1 = roundToInt (r.getX() * 256.0f); - auto x2 = roundToInt (r.getRight() * 256.0f); + auto x1 = roundToInt ((float) scale * r.getX()); + auto x2 = roundToInt ((float) scale * r.getRight()); - auto y1 = roundToInt (r.getY() * 256.0f) - (bounds.getY() << 8); - auto y2 = roundToInt (r.getBottom() * 256.0f) - (bounds.getY() << 8); + auto y1 = roundToInt ((float) scale * r.getY()) - (bounds.getY() * scale); + auto y2 = roundToInt ((float) scale * r.getBottom()) - (bounds.getY() * scale); if (x2 <= x1 || y2 <= y1) continue; - auto y = y1 >> 8; - auto lastLine = y2 >> 8; + auto y = y1 / scale; + auto lastLine = y2 / scale; if (y == lastLine) { @@ -197,20 +194,20 @@ EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) EdgeTable::EdgeTable (Rectangle rectangleToAdd) : bounds ((int) std::floor (rectangleToAdd.getX()), - roundToInt (rectangleToAdd.getY() * 256.0f) >> 8, + roundToInt (rectangleToAdd.getY() * 256.0f) / scale, 2 + (int) rectangleToAdd.getWidth(), 2 + (int) rectangleToAdd.getHeight()), - maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine * 2) + 1) + maxEdgesPerLine (defaultEdgesPerLine), + lineStrideElements ((defaultEdgesPerLine * 2) + 1) { jassert (! rectangleToAdd.isEmpty()); allocate(); table[0] = 0; - auto x1 = roundToInt (rectangleToAdd.getX() * 256.0f); - auto x2 = roundToInt (rectangleToAdd.getRight() * 256.0f); - auto y1 = roundToInt (rectangleToAdd.getY() * 256.0f) - (bounds.getY() << 8); - auto y2 = roundToInt (rectangleToAdd.getBottom() * 256.0f) - (bounds.getY() << 8); + auto x1 = roundToInt ((float) scale * rectangleToAdd.getX()); + auto x2 = roundToInt ((float) scale * rectangleToAdd.getRight()); + auto y1 = roundToInt ((float) scale * rectangleToAdd.getY()) - (bounds.getY() * scale); + auto y2 = roundToInt ((float) scale * rectangleToAdd.getBottom()) - (bounds.getY() * scale); jassert (y1 < 256); if (x2 <= x1 || y2 <= y1) @@ -222,7 +219,7 @@ EdgeTable::EdgeTable (Rectangle rectangleToAdd) int lineY = 0; int* t = table; - if ((y1 >> 8) == (y2 >> 8)) + if ((y1 / scale) == (y2 / scale)) { t[0] = 2; t[1] = x1; @@ -242,7 +239,7 @@ EdgeTable::EdgeTable (Rectangle rectangleToAdd) ++lineY; t += lineStrideElements; - while (lineY < (y2 >> 8)) + while (lineY < (y2 / scale)) { t[0] = 2; t[1] = x1; @@ -361,7 +358,7 @@ void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept auto corrected = std::abs (level); - if (corrected >> 8) + if (corrected / scale) { if (useNonZeroWinding) { @@ -371,7 +368,7 @@ void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept { corrected &= 511; - if (corrected >> 8) + if (corrected / scale) corrected = 511 - corrected; } } @@ -497,7 +494,7 @@ void EdgeTable::multiplyLevels (float amount) while (--numPoints > 0) { - item->level = jmin (255, (item->level * multiplier) >> 8); + item->level = jmin (255, (item->level * multiplier) / scale); ++item; } } @@ -521,7 +518,7 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* const otherL return; } - auto right = bounds.getRight() << 8; + auto right = bounds.getRight() * scale; // optimise for the common case where our line lies entirely within a // single pair of points, as happens when clipping to a simple rect. @@ -576,7 +573,7 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* const otherL lastX = nextX; - auto nextLevel = (level1 * (level2 + 1)) >> 8; + auto nextLevel = (level1 * (level2 + 1)) / scale; jassert (isPositiveAndBelow (nextLevel, 256)); if (nextLevel != lastLevel) @@ -702,8 +699,8 @@ void EdgeTable::clipToRectangle (Rectangle r) if (clipped.getX() > bounds.getX() || clipped.getRight() < bounds.getRight()) { - auto x1 = clipped.getX() << 8; - auto x2 = jmin (bounds.getRight(), clipped.getRight()) << 8; + auto x1 = scale * clipped.getX(); + auto x2 = scale * jmin (bounds.getRight(), clipped.getRight()); int* line = table + lineStrideElements * top; for (int i = bottom - top; --i >= 0;) @@ -729,8 +726,8 @@ void EdgeTable::excludeRectangle (Rectangle r) auto bottom = clipped.getBottom() - bounds.getY(); const int rectLine[] = { 4, std::numeric_limits::min(), 255, - clipped.getX() << 8, 0, - clipped.getRight() << 8, 255, + scale * clipped.getX(), 0, + scale * clipped.getRight(), 255, std::numeric_limits::max(), 0 }; for (int i = top; i < bottom; ++i) @@ -800,7 +797,7 @@ void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, if (alpha != lastLevel) { - tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = (x * scale); tempLine[++destIndex] = alpha; lastLevel = alpha; } @@ -810,7 +807,7 @@ void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, if (lastLevel > 0) { - tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = (x * scale); tempLine[++destIndex] = 0; } diff --git a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h index 8ac923465..8dacd12e8 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h +++ b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h @@ -122,7 +122,7 @@ class JUCE_API EdgeTable if (--numPoints > 0) { int x = *++line; - jassert ((x >> 8) >= bounds.getX() && (x >> 8) < bounds.getRight()); + jassert ((x / scale) >= bounds.getX() && (x / scale) < bounds.getRight()); int levelAccumulator = 0; iterationCallback.setEdgeTableYPos (bounds.getY() + y); @@ -130,12 +130,12 @@ class JUCE_API EdgeTable while (--numPoints >= 0) { const int level = *++line; - jassert (isPositiveAndBelow (level, 256)); + jassert (isPositiveAndBelow (level, scale)); const int endX = *++line; jassert (endX >= x); - const int endOfRun = (endX >> 8); + const int endOfRun = (endX / scale); - if (endOfRun == (x >> 8)) + if (endOfRun == (x / scale)) { // small segment within the same pixel, so just save it for the next // time round.. @@ -146,8 +146,8 @@ class JUCE_API EdgeTable // plot the fist pixel of this segment, including any accumulated // levels from smaller segments that haven't been drawn yet levelAccumulator += (0x100 - (x & 0xff)) * level; - levelAccumulator >>= 8; - x >>= 8; + levelAccumulator /= scale; + x /= scale; if (levelAccumulator > 0) { @@ -174,11 +174,11 @@ class JUCE_API EdgeTable x = endX; } - levelAccumulator >>= 8; + levelAccumulator /= scale; if (levelAccumulator > 0) { - x >>= 8; + x /= scale; jassert (x >= bounds.getX() && x < bounds.getRight()); if (levelAccumulator >= 255) @@ -191,6 +191,10 @@ class JUCE_API EdgeTable } private: + //============================================================================== + static constexpr auto defaultEdgesPerLine = 32; + static constexpr auto scale = 256; + //============================================================================== // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc struct LineItem diff --git a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h index 3aeb0ce4d..abad0ccad 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h +++ b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h @@ -26,6 +26,34 @@ namespace juce { +#ifndef DOXYGEN +namespace detail +{ + +template struct Tag {}; + +inline auto getNumericValue (StringRef s, Tag) { return s.text.getIntValue32(); } +inline auto getNumericValue (StringRef s, Tag) { return s.text.getDoubleValue(); } +inline auto getNumericValue (StringRef s, Tag) { return static_cast (s.text.getDoubleValue()); } + +template +ValueType parseAfterSpace (StringRef s) noexcept +{ + return static_cast (getNumericValue (s.text.findEndOfWhitespace(), + Tag{})); +} + +inline int floorAsInt (int n) noexcept { return n; } +inline int floorAsInt (float n) noexcept { return n > (float) std::numeric_limits::min() ? (int) std::floor (n) : std::numeric_limits::min(); } +inline int floorAsInt (double n) noexcept { return n > (double) std::numeric_limits::min() ? (int) std::floor (n) : std::numeric_limits::min(); } + +inline int ceilAsInt (int n) noexcept { return n; } +inline int ceilAsInt (float n) noexcept { return n < (float) std::numeric_limits::max() ? (int) std::ceil (n) : std::numeric_limits::max(); } +inline int ceilAsInt (double n) noexcept { return n < (double) std::numeric_limits::max() ? (int) std::ceil (n) : std::numeric_limits::max(); } + +} // namespace detail +#endif + //============================================================================== /** Manages a rectangle and allows geometric operations to be performed on it. @@ -811,10 +839,10 @@ class Rectangle */ Rectangle getSmallestIntegerContainer() const noexcept { - return Rectangle::leftTopRightBottom (floorAsInt (pos.x), - floorAsInt (pos.y), - ceilAsInt (pos.x + w), - ceilAsInt (pos.y + h)); + return Rectangle::leftTopRightBottom (detail::floorAsInt (pos.x), + detail::floorAsInt (pos.y), + detail::ceilAsInt (pos.x + w), + detail::ceilAsInt (pos.y + h)); } /** Casts this rectangle to a Rectangle. @@ -937,7 +965,7 @@ class Rectangle /** Parses a string containing a rectangle's details. - The string should contain 4 integer tokens, in the form "x y width height". They + The string should contain 4 numeric tokens, in the form "x y width height". They can be comma or whitespace separated. This method is intended to go with the toString() method, to form an easy way @@ -950,15 +978,15 @@ class Rectangle StringArray toks; toks.addTokens (stringVersion.text.findEndOfWhitespace(), ",; \t\r\n", ""); - return { parseIntAfterSpace (toks[0]), - parseIntAfterSpace (toks[1]), - parseIntAfterSpace (toks[2]), - parseIntAfterSpace (toks[3]) }; + return { detail::parseAfterSpace (toks[0]), + detail::parseAfterSpace (toks[1]), + detail::parseAfterSpace (toks[2]), + detail::parseAfterSpace (toks[3]) }; } #ifndef DOXYGEN - // This has been renamed by transformedBy, in order to match the method names used in the Point class. - JUCE_DEPRECATED_WITH_BODY (Rectangle transformed (const AffineTransform& t) const noexcept, { return transformedBy (t); }) + [[deprecated ("This has been renamed to transformedBy in order to match the method names used in the Point class.")]] + Rectangle transformed (const AffineTransform& t) const noexcept { return transformedBy (t); } #endif private: @@ -967,19 +995,9 @@ class Rectangle Point pos; ValueType w {}, h {}; - static ValueType parseIntAfterSpace (StringRef s) noexcept - { return static_cast (s.text.findEndOfWhitespace().getIntValue32()); } - - void copyWithRounding (Rectangle& result) const noexcept { result = getSmallestIntegerContainer(); } - void copyWithRounding (Rectangle& result) const noexcept { result = toFloat(); } + void copyWithRounding (Rectangle& result) const noexcept { result = getSmallestIntegerContainer(); } + void copyWithRounding (Rectangle& result) const noexcept { result = toFloat(); } void copyWithRounding (Rectangle& result) const noexcept { result = toDouble(); } - - static int floorAsInt (int n) noexcept { return n; } - static int floorAsInt (float n) noexcept { return n > (float) std::numeric_limits::min() ? (int) std::floor (n) : std::numeric_limits::min(); } - static int floorAsInt (double n) noexcept { return n > (double) std::numeric_limits::min() ? (int) std::floor (n) : std::numeric_limits::min(); } - static int ceilAsInt (int n) noexcept { return n; } - static int ceilAsInt (float n) noexcept { return n < (float) std::numeric_limits::max() ? (int) std::ceil (n) : std::numeric_limits::max(); } - static int ceilAsInt (double n) noexcept { return n < (double) std::numeric_limits::max() ? (int) std::ceil (n) : std::numeric_limits::max(); } }; } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle_test.cpp b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle_test.cpp new file mode 100644 index 000000000..6fb4bb03e --- /dev/null +++ b/external/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle_test.cpp @@ -0,0 +1,51 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +struct RectangleUnitTest : public UnitTest +{ + RectangleUnitTest() : UnitTest ("Rectangle", UnitTestCategories::graphics) {} + + void runTest() override + { + beginTest ("Rectangle/string conversions can be round-tripped"); + { + const Rectangle a (0.1f, 0.2f, 0.3f, 0.4f); + expect (Rectangle::fromString (a.toString()) == a); + + const Rectangle b (0.1, 0.2, 0.3, 0.4); + expect (Rectangle::fromString (b.toString()) == b); + + const Rectangle c (1, 2, 3, 4); + expect (Rectangle::fromString (c.toString()) == c); + } + } +}; + +static RectangleUnitTest rectangleUnitTest; + +} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp b/external/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp index be93290f3..8a4e362ff 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp @@ -265,8 +265,6 @@ Image::~Image() { } -JUCE_DECLARE_DEPRECATED_STATIC (const Image Image::null;) - int Image::getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getSharedCount(); } int Image::getWidth() const noexcept { return image == nullptr ? 0 : image->width; } int Image::getHeight() const noexcept { return image == nullptr ? 0 : image->height; } @@ -684,4 +682,17 @@ void Image::moveImageSection (int dx, int dy, } } +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const Image Image::null; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h b/external/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h index 4083b0d13..223346217 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h +++ b/external/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h @@ -413,10 +413,12 @@ class JUCE_API Image final /** @internal */ explicit Image (ReferenceCountedObjectPtr) noexcept; - /* A null Image object that can be used when you need to return an invalid image. - @deprecated If you need a default-constructed var, just use Image() or {}. - */ - JUCE_DEPRECATED_STATIC (static const Image null;) + //============================================================================== + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) + /* A null Image object that can be used when you need to return an invalid image. */ + [[deprecated ("If you need a default-constructed var, just use Image() or {}.")]] + static const Image null; + #endif private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp b/external/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp index 2123f43bd..338c2cbf4 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp @@ -29,10 +29,10 @@ namespace juce struct ImageCache::Pimpl : private Timer, private DeletedAtShutdown { - Pimpl() {} + Pimpl() = default; ~Pimpl() override { clearSingletonInstance(); } - JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (ImageCache::Pimpl) + JUCE_DECLARE_SINGLETON (ImageCache::Pimpl, false) Image getFromHashCode (const int64 hashCode) noexcept { diff --git a/external/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h b/external/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h new file mode 100644 index 000000000..e2974d3ec --- /dev/null +++ b/external/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h @@ -0,0 +1,82 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +/** + An image that will be resampled before it is drawn. + + A plain Image only stores plain pixels, but does not store any information + about how these pixels correspond to points. This means that if the image's + dimensions are interpreted as points, then the image will be blurry when + drawn on high resolution displays. If the image's dimensions are instead + interpreted as corresponding to exact pixel positions, then the logical + size of the image will change depending on the scale factor of the screen + used to draw it. + + The ScaledImage class is designed to store an image alongside a scale + factor that informs a renderer how to convert between the image's pixels + and points. +*/ +class JUCE_API ScaledImage +{ +public: + /** Creates a ScaledImage with an invalid image and unity scale. + */ + ScaledImage() = default; + + /** Creates a ScaledImage from an Image, where the dimensions of the image + in pixels are exactly equal to its dimensions in points. + */ + explicit ScaledImage (const Image& imageIn) + : ScaledImage (imageIn, 1.0) {} + + /** Creates a ScaledImage from an Image, using a custom scale factor. + + A scale of 1.0 means that the image's dimensions in pixels is equal to + its dimensions in points. + + A scale of 2.0 means that the image contains 2 pixels per point in each + direction. + */ + ScaledImage (const Image& imageIn, double scaleIn) + : image (imageIn), scaleFactor (scaleIn) {} + + /** Returns the image at its original dimentions. */ + Image getImage() const { return image; } + + /** Returns the image's scale. */ + double getScale() const { return scaleFactor; } + + /** Returns the bounds of this image expressed in points. */ + Rectangle getScaledBounds() const { return image.getBounds().toDouble() / scaleFactor; } + +private: + Image image; + double scaleFactor = 1.0; +}; + +} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp index 784ae859f..5c0adb6e7 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp @@ -126,6 +126,10 @@ #include "effects/juce_DropShadowEffect.cpp" #include "effects/juce_GlowEffect.cpp" +#if JUCE_UNIT_TESTS + #include "geometry/juce_Rectangle_test.cpp" +#endif + #if JUCE_USE_FREETYPE #include "native/juce_freetype_Fonts.cpp" #endif diff --git a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h index 2ed76108b..50152712f 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h +++ b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h @@ -35,7 +35,7 @@ ID: juce_graphics vendor: juce - version: 6.1.2 + version: 6.1.3 name: JUCE graphics classes description: Classes for 2D vector graphics, image loading/saving, font handling, etc. website: http://www.juce.com/juce @@ -140,6 +140,7 @@ namespace juce #include "contexts/juce_GraphicsContext.h" #include "contexts/juce_LowLevelGraphicsContext.h" #include "images/juce_Image.h" +#include "images/juce_ScaledImage.h" #include "colour/juce_FillType.h" #include "native/juce_RenderingHelpers.h" #include "contexts/juce_LowLevelGraphicsSoftwareRenderer.h" diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h b/external/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h index 719bb484e..36b61e4f4 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -289,7 +289,7 @@ class CachedGlyphEdgeTable : public ReferenceCountedObject void generate (const Font& newFont, int glyphNumber) { font = newFont; - auto* typeface = newFont.getTypeface(); + auto typeface = newFont.getTypefacePtr(); snapToIntegerCoordinate = typeface->isHinted(); glyph = glyphNumber; @@ -2567,7 +2567,7 @@ class SoftwareRendererSavedState : public SavedStateBase et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t, fontHeight)); + std::unique_ptr et (font.getTypefacePtr()->getEdgeTableForGlyph (glyphNumber, t, fontHeight)); if (et != nullptr) fillShape (*new EdgeTableRegionType (*et), false); diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index 62a43de0a..e40229ff3 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -610,7 +610,9 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) state->fontRef = nullptr; state->font = newFont; - if (auto osxTypeface = dynamic_cast (state->font.getTypeface())) + auto typeface = state->font.getTypefacePtr(); + + if (auto osxTypeface = dynamic_cast (typeface.get())) { state->fontRef = osxTypeface->fontRef; CGContextSetFont (context.get(), state->fontRef); @@ -667,7 +669,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) { Path p; auto& f = state->font; - f.getTypeface()->getOutlineForGlyph (glyphNumber, p); + f.getTypefacePtr()->getOutlineForGlyph (glyphNumber, p); fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()) .followedBy (transform)); @@ -927,8 +929,11 @@ Image juce_createImageFromUIImage (UIImage* img) #endif #if JUCE_MAC - NSImage* imageToNSImage (const Image& image, float scaleFactor) + NSImage* imageToNSImage (const ScaledImage& scaled) { + const auto image = scaled.getImage(); + const auto scaleFactor = scaled.getScale(); + JUCE_AUTORELEASEPOOL { NSImage* im = [[NSImage alloc] init]; diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h index bf7d9b76b..61b1056d8 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h @@ -102,7 +102,7 @@ CGContextRef juce_getImageContext (const Image&); #endif #if JUCE_MAC - NSImage* imageToNSImage (const Image& image, float scaleFactor = 1.0f); + NSImage* imageToNSImage (const ScaledImage& image); #endif } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm index 129039fdc..49a41799b 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -159,20 +159,30 @@ static float getHeightToPointsFactor (CTFontRef font) //============================================================================== static CTTextAlignment getTextAlignment (const AttributedString& text) { - switch (text.getJustification().getOnlyHorizontalFlags()) + const auto flags = text.getJustification().getOnlyHorizontalFlags(); + + if (@available (macOS 10.8, *)) + { + switch (flags) + { + case Justification::right: return kCTTextAlignmentRight; + case Justification::horizontallyCentred: return kCTTextAlignmentCenter; + case Justification::horizontallyJustified: return kCTTextAlignmentJustified; + default: return kCTTextAlignmentLeft; + } + } + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + + switch (flags) { - #if defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - case Justification::right: return kCTTextAlignmentRight; - case Justification::horizontallyCentred: return kCTTextAlignmentCenter; - case Justification::horizontallyJustified: return kCTTextAlignmentJustified; - default: return kCTTextAlignmentLeft; - #else case Justification::right: return kCTRightTextAlignment; case Justification::horizontallyCentred: return kCTCenterTextAlignment; case Justification::horizontallyJustified: return kCTJustifiedTextAlignment; default: return kCTLeftTextAlignment; - #endif } + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } static CTLineBreakMode getLineBreakMode (const AttributedString& text) @@ -572,10 +582,8 @@ Font result (String::fromCFString (cfsFontFamily.get()), if (fontRef != nullptr) { - #if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8 - if (SystemStats::getOperatingSystemType() >= SystemStats::OperatingSystemType::MacOSX_10_11) + if (@available (macOS 10.11, *)) canBeUsedForLayout = CTFontManagerRegisterGraphicsFont (fontRef, nullptr); - #endif ctFontRef.reset (CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr)); @@ -618,10 +626,9 @@ void initialiseMetrics() { if (fontRef != nullptr) { - #if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8 - if (dataCopy.getSize() != 0) - CTFontManagerUnregisterGraphicsFont (fontRef, nullptr); - #endif + if (@available (macOS 10.8, *)) + if (dataCopy.getSize() != 0) + CTFontManagerUnregisterGraphicsFont (fontRef, nullptr); CGFontRelease (fontRef); } @@ -748,7 +755,9 @@ static void pathApplier (void* info, const CGPathElement* element) CTFontRef getCTFontFromTypeface (const Font& f) { - if (auto* tf = dynamic_cast (f.getTypeface())) + const auto typeface = f.getTypefacePtr(); + + if (auto* tf = dynamic_cast (typeface.get())) return tf->ctFontRef.get(); return {}; @@ -848,7 +857,7 @@ CTFontRef getCTFontFromTypeface (const Font& f) static DefaultFontNames defaultNames; auto newFont = font; - auto& faceName = font.getTypefaceName(); + auto faceName = font.getTypefaceName(); if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans); else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif); @@ -866,7 +875,9 @@ static bool canAllTypefacesBeUsedInLayout (const AttributedString& text) for (int i = 0; i < numCharacterAttributes; ++i) { - if (auto tf = dynamic_cast (text.getAttribute (i).font.getTypeface())) + auto typeface = text.getAttribute (i).font.getTypefacePtr(); + + if (auto tf = dynamic_cast (typeface.get())) if (tf->canBeUsedForLayout) continue; diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp index 4db399dcb..dcad8d3f4 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp @@ -370,7 +370,8 @@ struct Direct2DLowLevelGraphicsContext::SavedState { if (currentFontFace == nullptr) { - auto* typeface = dynamic_cast (font.getTypeface()); + auto typefacePtr = font.getTypefacePtr(); + auto* typeface = dynamic_cast (typefacePtr.get()); currentFontFace = typeface->getIDWriteFontFace(); fontHeightToEmSizeFactor = typeface->getUnitsToHeightScaleFactor(); } diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp index 992ad513c..07f5db1de 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp @@ -192,8 +192,9 @@ namespace DirectWriteTypeLayout for (int i = 0; i < attributedString.getNumAttributes(); ++i) { auto& font = attributedString.getAttribute(i).font; + auto typeface = font.getTypefacePtr(); - if (auto* wt = dynamic_cast (font.getTypeface())) + if (auto* wt = dynamic_cast (typeface.get())) if (wt->getIDWriteFontFace() == glyphRun.fontFace) return font.withHeight (fontHeight); } @@ -334,7 +335,7 @@ namespace DirectWriteTypeLayout Font defaultFont; BOOL fontFound = false; uint32 fontIndex; - fontCollection.FindFamilyName (defaultFont.getTypeface()->getName().toWideCharPointer(), &fontIndex, &fontFound); + fontCollection.FindFamilyName (defaultFont.getTypefacePtr()->getName().toWideCharPointer(), &fontIndex, &fontFound); if (! fontFound) fontIndex = 0; @@ -443,8 +444,9 @@ static bool canAllTypefacesAndFontsBeUsedInLayout (const AttributedString& text) for (int i = 0; i < numCharacterAttributes; ++i) { const auto& font = text.getAttribute (i).font; + auto typeface = font.getTypefacePtr(); - if (font.getHorizontalScale() != 1.0f || dynamic_cast (font.getTypeface()) == nullptr) + if (font.getHorizontalScale() != 1.0f || dynamic_cast (typeface.get()) == nullptr) return false; } diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp index 5befa5eff..3cd21b94d 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp @@ -300,7 +300,7 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) static DefaultFontNames defaultNames; Font newFont (font); - auto& faceName = font.getTypefaceName(); + auto faceName = font.getTypefaceName(); if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans); else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp index 041dffb18..b9f769480 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp @@ -141,6 +141,20 @@ void Button::setConnectedEdges (int newFlags) } //============================================================================== +void Button::checkToggleableState (bool wasToggleable) +{ + if (isToggleable() != wasToggleable) + invalidateAccessibilityHandler(); +} + +void Button::setToggleable (bool isNowToggleable) +{ + const auto wasToggleable = isToggleable(); + + canBeToggled = isNowToggleable; + checkToggleableState (wasToggleable); +} + void Button::setToggleState (bool shouldBeOn, NotificationType notification) { setToggleState (shouldBeOn, notification, notification); @@ -201,7 +215,10 @@ void Button::setToggleState (bool shouldBeOn, bool sendChange) void Button::setClickingTogglesState (bool shouldToggle) noexcept { + const auto wasToggleable = isToggleable(); + clickTogglesState = shouldToggle; + checkToggleableState (wasToggleable); // if you've got clickTogglesState turned on, you shouldn't also connect the button // up to be a command invoker. Instead, your command handler must flip the state of whatever @@ -210,11 +227,6 @@ void Button::setClickingTogglesState (bool shouldToggle) noexcept jassert (commandManagerToUse == nullptr || ! clickTogglesState); } -bool Button::getClickingTogglesState() const noexcept -{ - return clickTogglesState; -} - void Button::setRadioGroupId (int newGroupId, NotificationType notification) { if (radioGroupId != newGroupId) @@ -224,6 +236,7 @@ void Button::setRadioGroupId (int newGroupId, NotificationType notification) if (lastToggleState) turnOffOtherButtonsInGroup (notification, notification); + setToggleable (true); invalidateAccessibilityHandler(); } } @@ -709,7 +722,8 @@ class ButtonAccessibilityHandler : public AccessibilityHandler explicit ButtonAccessibilityHandler (Button& buttonToWrap, AccessibilityRole roleIn) : AccessibilityHandler (buttonToWrap, isRadioButton (buttonToWrap) ? AccessibilityRole::radioButton : roleIn, - getAccessibilityActions (buttonToWrap, roleIn)), + getAccessibilityActions (buttonToWrap), + getAccessibilityInterfaces (buttonToWrap)), button (buttonToWrap) { } @@ -718,7 +732,7 @@ class ButtonAccessibilityHandler : public AccessibilityHandler { auto state = AccessibilityHandler::getCurrentState(); - if (isToggleButton (getRole()) || isRadioButton (button)) + if (button.isToggleable()) { state = state.withCheckable(); @@ -742,28 +756,50 @@ class ButtonAccessibilityHandler : public AccessibilityHandler String getHelp() const override { return button.getTooltip(); } private: - static bool isToggleButton (AccessibilityRole role) noexcept + class ButtonValueInterface : public AccessibilityTextValueInterface { - return role == AccessibilityRole::toggleButton; - } + public: + explicit ButtonValueInterface (Button& buttonToWrap) + : button (buttonToWrap) + { + } + + bool isReadOnly() const override { return true; } + String getCurrentValueAsString() const override { return button.getToggleState() ? "On" : "Off"; } + void setValueAsString (const String&) override {} + + private: + Button& button; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonValueInterface) + }; static bool isRadioButton (const Button& button) noexcept { return button.getRadioGroupId() != 0; } - static AccessibilityActions getAccessibilityActions (Button& button, AccessibilityRole role) + static AccessibilityActions getAccessibilityActions (Button& button) { auto actions = AccessibilityActions().addAction (AccessibilityActionType::press, [&button] { button.triggerClick(); }); - if (isToggleButton (role)) + if (button.isToggleable()) actions = actions.addAction (AccessibilityActionType::toggle, [&button] { button.setToggleState (! button.getToggleState(), sendNotification); }); return actions; } + static Interfaces getAccessibilityInterfaces (Button& button) + { + if (button.isToggleable()) + return { std::make_unique (button) }; + + return {}; + } + Button& button; //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h b/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h index d98bf4df6..c48ccc05e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h @@ -79,6 +79,26 @@ class JUCE_API Button : public Component, bool isOver() const noexcept; //============================================================================== + /** Indicates that the button's on/off state is toggleable. + + By default this is false, and will only be true for ToggleButtons, buttons that + are a part of a radio button group, and buttons for which + getClickingTogglesState() == true, however you can use this method to manually + indicate that a button is toggleable. + + This will present the button as toggleable to accessibility clients and add an + accessible "toggle" action for the button that invokes setToggleState(). + + @see ToggleButton, isToggleable, setToggleState, setClickingTogglesState, setRadioGroupId + */ + void setToggleable (bool shouldBeToggleable); + + /** Returns true if the button's on/off state is toggleable. + + @see setToggleable, setClickingTogglesState + */ + bool isToggleable() const noexcept { return canBeToggled || clickTogglesState; } + /** A button has an on/off state associated with it, and this changes that. By default buttons are 'off' and for simple buttons that you click to perform @@ -120,14 +140,16 @@ class JUCE_API Button : public Component, the button is clicked. If set to true, then before the clicked() callback occurs, the toggle-state - of the button is flipped. + of the button is flipped. This will also cause isToggleable() to return true. + + @see isToggleable */ void setClickingTogglesState (bool shouldAutoToggleOnClick) noexcept; /** Returns true if this button is set to be an automatic toggle-button. This returns the last value that was passed to setClickingTogglesState(). */ - bool getClickingTogglesState() const noexcept; + bool getClickingTogglesState() const noexcept { return clickTogglesState; } //============================================================================== /** Enables the button to act as a member of a mutually-exclusive group @@ -394,8 +416,11 @@ class JUCE_API Button : public Component, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0; }; - // This method's parameters have changed - see the new version. - JUCE_DEPRECATED (void setToggleState (bool, bool)); + //============================================================================== + #ifndef DOXYGEN + [[deprecated ("This method's parameters have changed.")]] + void setToggleState (bool, bool); + #endif protected: //============================================================================== @@ -488,6 +513,7 @@ class JUCE_API Button : public Component, ButtonState buttonState = buttonNormal, lastStatePainted = buttonNormal; Value isOn; + bool canBeToggled = false; bool lastToggleState = false; bool clickTogglesState = false; bool needsToRelease = false; @@ -497,6 +523,7 @@ class JUCE_API Button : public Component, bool generateTooltip = false; std::unique_ptr createAccessibilityHandler() override; + void checkToggleableState (bool wasToggleable); void repeatTimerCallback(); bool keyStateChangedCallback(); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h b/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h index 5f6ec9349..7f94aa742 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h @@ -102,12 +102,7 @@ class JUCE_API TextButton : public Button /** @internal */ void colourChanged() override; -private: - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note that this method has been removed - instead, see LookAndFeel::getTextButtonFont() - virtual int getFont() { return 0; } - #endif - + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextButton) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h b/external/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h index e61ce45ad..998219a78 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h @@ -312,12 +312,6 @@ class JUCE_API ApplicationCommandManager : private AsyncUpdater, void globalFocusChanged (Component*) override; ApplicationCommandInfo* getMutableCommandForID (CommandID) const noexcept; - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This is just here to cause a compile error in old code that hasn't been changed to use the new - // version of this method. - virtual short getFirstCommandTarget() { return 0; } - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp index 360bffb90..9e26cccd8 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp @@ -301,11 +301,11 @@ struct Component::ComponentHelpers } //============================================================================== - static bool hitTest (Component& comp, Point localPoint) + static bool hitTest (Component& comp, Point localPoint) { - return isPositiveAndBelow (localPoint.x, comp.getWidth()) - && isPositiveAndBelow (localPoint.y, comp.getHeight()) - && comp.hitTest (localPoint.x, localPoint.y); + const auto intPoint = localPoint.roundToInt(); + return Rectangle { comp.getWidth(), comp.getHeight() }.toFloat().contains (localPoint) + && comp.hitTest (intPoint.x, intPoint.y); } // converts an unscaled position within a peer to the local position within that peer's component @@ -470,6 +470,24 @@ struct Component::ComponentHelpers for (auto* child : c.childComponentList) releaseAllCachedImageResources (*child); } + + //============================================================================== + static bool modalWouldBlockComponent (const Component& maybeBlocked, Component* modal) + { + return modal != nullptr + && modal != &maybeBlocked + && ! modal->isParentOf (&maybeBlocked) + && ! modal->canModalEventBeSentToComponent (&maybeBlocked); + } + + template + static void sendMouseEventToComponentsThatAreBlockedByModal (Component& modal, Function&& function) + { + for (auto& ms : Desktop::getInstance().getMouseSources()) + if (auto* c = ms.getComponentUnderMouse()) + if (modalWouldBlockComponent (*c, &modal)) + (c->*function) (ms, ms.getScreenPosition(), Time::getCurrentTime()); + } }; //============================================================================== @@ -1358,7 +1376,7 @@ bool Component::hitTest (int x, int y) auto& child = *childComponentList.getUnchecked (i); if (child.isVisible() - && ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point (x, y)))) + && ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point (x, y).toFloat()))) return true; } } @@ -1382,15 +1400,15 @@ void Component::getInterceptsMouseClicks (bool& allowsClicksOnThisComponent, bool Component::contains (Point point) { - return containsInternal (point.toFloat()); + return contains (point.toFloat()); } -bool Component::containsInternal (Point point) +bool Component::contains (Point point) { - if (ComponentHelpers::hitTest (*this, point.roundToInt())) + if (ComponentHelpers::hitTest (*this, point)) { if (parentComponent != nullptr) - return parentComponent->containsInternal (ComponentHelpers::convertToParentSpace (*this, point)); + return parentComponent->contains (ComponentHelpers::convertToParentSpace (*this, point)); if (flags.hasHeavyweightPeerFlag) if (auto* peer = getPeer()) @@ -1402,34 +1420,34 @@ bool Component::containsInternal (Point point) bool Component::reallyContains (Point point, bool returnTrueIfWithinAChild) { - return reallyContainsInternal (point.toFloat(), returnTrueIfWithinAChild); + return reallyContains (point.toFloat(), returnTrueIfWithinAChild); } -bool Component::reallyContainsInternal (Point point, bool returnTrueIfWithinAChild) +bool Component::reallyContains (Point point, bool returnTrueIfWithinAChild) { - if (! containsInternal (point)) + if (! contains (point)) return false; auto* top = getTopLevelComponent(); - auto* compAtPosition = top->getComponentAtInternal (top->getLocalPoint (this, point)); + auto* compAtPosition = top->getComponentAt (top->getLocalPoint (this, point)); return (compAtPosition == this) || (returnTrueIfWithinAChild && isParentOf (compAtPosition)); } Component* Component::getComponentAt (Point position) { - return getComponentAtInternal (position.toFloat()); + return getComponentAt (position.toFloat()); } -Component* Component::getComponentAtInternal (Point position) +Component* Component::getComponentAt (Point position) { - if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position.roundToInt())) + if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position)) { for (int i = childComponentList.size(); --i >= 0;) { auto* child = childComponentList.getUnchecked (i); - child = child->getComponentAtInternal (ComponentHelpers::convertFromParentSpace (*child, position)); + child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, position)); if (child != nullptr) return child; @@ -1443,7 +1461,7 @@ Component* Component::getComponentAtInternal (Point position) Component* Component::getComponentAt (int x, int y) { - return getComponentAt ({ x, y }); + return getComponentAt (Point { x, y }); } //============================================================================== @@ -1720,6 +1738,11 @@ void Component::enterModalState (bool shouldTakeKeyboardFocus, if (! isCurrentlyModal (false)) { + // While this component is in modal state it may block other components from receiving + // mouseExit events. To keep mouseEnter and mouseExit calls balanced on these components, + // we must manually force the mouse to "leave" blocked components. + ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*this, &Component::internalMouseExit); + auto& mcm = *ModalComponentManager::getInstance(); mcm.startModal (this, deleteWhenDismissed); mcm.attachCallback (this, callback); @@ -1738,6 +1761,8 @@ void Component::enterModalState (bool shouldTakeKeyboardFocus, void Component::exitModalState (int returnValue) { + WeakReference deletionChecker (this); + if (isCurrentlyModal (false)) { if (MessageManager::getInstance()->isThisTheMessageThread()) @@ -1746,10 +1771,11 @@ void Component::exitModalState (int returnValue) mcm.endModal (this, returnValue); mcm.bringModalComponentsToFront(); - // If any of the mouse sources are over another Component when we exit the modal state then send a mouse enter event - for (auto& ms : Desktop::getInstance().getMouseSources()) - if (auto* c = ms.getComponentUnderMouse()) - c->internalMouseEnter (ms, ms.getScreenPosition(), Time::getCurrentTime()); + // While this component is in modal state it may block other components from receiving + // mouseEnter events. To keep mouseEnter and mouseExit calls balanced on these components, + // we must manually force the mouse to "enter" blocked components. + if (deletionChecker != nullptr) + ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*deletionChecker, &Component::internalMouseEnter); } else { @@ -1772,10 +1798,7 @@ bool Component::isCurrentlyModal (bool onlyConsiderForemostModalComponent) const bool Component::isCurrentlyBlockedByAnotherModalComponent() const { - auto* mc = getCurrentlyModalComponent(); - - return ! (mc == nullptr || mc == this || mc->isParentOf (this) - || mc->canModalEventBeSentToComponent (this)); + return ComponentHelpers::modalWouldBlockComponent (*this, getCurrentlyModalComponent()); } int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() noexcept @@ -2247,14 +2270,14 @@ void Component::mouseDoubleClick (const MouseEvent&) {} void Component::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel) { // the base class just passes this event up to its parent.. - if (parentComponent != nullptr) + if (parentComponent != nullptr && parentComponent->isEnabled()) parentComponent->mouseWheelMove (e.getEventRelativeTo (parentComponent), wheel); } void Component::mouseMagnify (const MouseEvent& e, float magnifyAmount) { // the base class just passes this event up to its parent.. - if (parentComponent != nullptr) + if (parentComponent != nullptr && parentComponent->isEnabled()) parentComponent->mouseMagnify (e.getEventRelativeTo (parentComponent), magnifyAmount); } @@ -2687,13 +2710,17 @@ void Component::internalKeyboardFocusGain (FocusChangeType cause, { focusGained (cause); - if (safePointer != nullptr) - { + if (safePointer == nullptr) + return; + + if (hasKeyboardFocus (false)) if (auto* handler = getAccessibilityHandler()) handler->grabFocus(); - internalChildKeyboardFocusChange (cause, safePointer); - } + if (safePointer == nullptr) + return; + + internalChildKeyboardFocusChange (cause, safePointer); } void Component::internalKeyboardFocusLoss (FocusChangeType cause) @@ -3003,6 +3030,15 @@ void Component::setEnabled (bool shouldBeEnabled) BailOutChecker checker (this); componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentEnablementChanged (*this); }); + + if (! shouldBeEnabled && hasKeyboardFocus (true)) + { + if (parentComponent != nullptr) + parentComponent->grabKeyboardFocus(); + + // ensure that keyboard focus is given away if it wasn't taken by parent + giveAwayKeyboardFocus(); + } } } @@ -3041,7 +3077,7 @@ bool Component::isMouseOver (bool includeChildren) const if (c != nullptr && (c == this || (includeChildren && isParentOf (c)))) if (ms.isDragging() || ! (ms.isTouch() || ms.isPen())) - if (c->reallyContainsInternal (c->getLocalPoint (nullptr, ms.getScreenPosition()), false)) + if (c->reallyContains (c->getLocalPoint (nullptr, ms.getScreenPosition()), false)) return true; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h b/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h index e28786013..d4613264e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h @@ -936,6 +936,19 @@ class JUCE_API Component : public MouseListener */ bool contains (Point localPoint); + /** Returns true if a given point lies within this component or one of its children. + + Never override this method! Use hitTest to create custom hit regions. + + @param localPoint the coordinate to test, relative to this component's top-left. + @returns true if the point is within the component's hit-test area, but only if + that part of the component isn't clipped by its parent component. Note + that this won't take into account any overlapping sibling components + which might be in the way - for that, see reallyContains() + @see hitTest, reallyContains, getComponentAt + */ + bool contains (Point localPoint); + /** Returns true if a given point lies in this component, taking any overlapping siblings into account. @@ -946,6 +959,16 @@ class JUCE_API Component : public MouseListener */ bool reallyContains (Point localPoint, bool returnTrueIfWithinAChild); + /** Returns true if a given point lies in this component, taking any overlapping + siblings into account. + + @param localPoint the coordinate to test, relative to this component's top-left. + @param returnTrueIfWithinAChild if the point actually lies within a child of this component, + this determines whether that is counted as a hit. + @see contains, getComponentAt + */ + bool reallyContains (Point localPoint, bool returnTrueIfWithinAChild); + /** Returns the component at a certain point within this one. @param x the x coordinate to test, relative to this component's left edge. @@ -969,6 +992,17 @@ class JUCE_API Component : public MouseListener */ Component* getComponentAt (Point position); + /** Returns the component at a certain point within this one. + + @param position the coordinate to test, relative to this component's top-left. + @returns the component that is at this position - which may be 0, this component, + or one of its children. Note that overlapping siblings that might actually + be in the way are not taken into account by this method - to account for these, + instead call getComponentAt on the top-level parent of this component. + @see hitTest, contains, reallyContains + */ + Component* getComponentAt (Point position); + //============================================================================== /** Marks the whole component as needing to be redrawn. @@ -1227,7 +1261,7 @@ class JUCE_API Component : public MouseListener */ int getExplicitFocusOrder() const; - /** A focus container type that can be passed to setFocusContainer(). + /** A focus container type that can be passed to setFocusContainerType(). If a component is marked as a focus container or keyboard focus container then it will act as the top-level component within which focus or keyboard focus is @@ -1284,25 +1318,25 @@ class JUCE_API Component : public MouseListener /** Returns true if this component has been marked as a focus container. - @see setFocusContainer + @see setFocusContainerType */ bool isFocusContainer() const noexcept; /** Returns true if this component has been marked as a keyboard focus container. - @see setFocusContainer + @see setFocusContainerType */ bool isKeyboardFocusContainer() const noexcept; /** Returns the focus container for this component. - @see isFocusContainer, setFocusContainer + @see isFocusContainer, setFocusContainerType */ Component* findFocusContainer() const; /** Returns the keyboard focus container for this component. - @see isFocusContainer, setFocusContainer + @see isFocusContainer, setFocusContainerType */ Component* findKeyboardFocusContainer() const; @@ -1352,7 +1386,7 @@ class JUCE_API Component : public MouseListener by calling getWantsKeyboardFocus), it gets it. - if the component itself doesn't want focus, it will try to pass it on to whichever of its children is the default component, as determined by - the getDefaultComponent() implemetation of the ComponentTraverser returned + the getDefaultComponent() implementation of the ComponentTraverser returned by createKeyboardFocusTraverser(). - if none of its children want focus at all, it will pass it up to its parent instead, unless it's a top-level component without a parent, @@ -1393,14 +1427,14 @@ class JUCE_API Component : public MouseListener /** Tries to move the keyboard focus to one of this component's siblings. This will try to move focus to either the next or previous component, as - determined by the getNextComponent() and getPreviousComponent() implemetations + determined by the getNextComponent() and getPreviousComponent() implementations of the ComponentTraverser returned by createKeyboardFocusTraverser(). This is the method that is used when shifting focus by pressing the tab key. @param moveToNext if true, the focus will move forwards; if false, it will move backwards - @see grabKeyboardFocus, giveAwayKeyboardFocus, setFocusContainer, setWantsKeyboardFocus + @see grabKeyboardFocus, giveAwayKeyboardFocus, setFocusContainerType, setWantsKeyboardFocus */ void moveKeyboardFocusToSibling (bool moveToNext); @@ -1418,8 +1452,8 @@ class JUCE_API Component : public MouseListener passed from this component. The default implementation of this method will return an instance of FocusTraverser - if this component is a focus container (as determined by the setFocusContainer() method). - If the component isn't a focus container, then it will recursively call + if this component is a focus container (as determined by the setFocusContainerType() + method). If the component isn't a focus container, then it will recursively call createFocusTraverser() on its parents. If you override this to return a custom traverser object, then this component and @@ -1432,8 +1466,8 @@ class JUCE_API Component : public MouseListener The default implementation of this method will return an instance of KeyboardFocusTraverser if this component is a keyboard focus container (as determined by - the setFocusContainer() method). If the component isn't a keyboard focus container, then - it will recursively call createKeyboardFocusTraverser() on its parents. + the setFocusContainerType() method). If the component isn't a keyboard focus container, + then it will recursively call createKeyboardFocusTraverser() on its parents. If you override this to return a custom traverser object, then this component and all its sub-components will use the new object to make their keyboard focusing @@ -2442,13 +2476,15 @@ class JUCE_API Component : public MouseListener //============================================================================== #ifndef DOXYGEN - // This method has been deprecated in favour of the setFocusContainerType() method - // that takes a more descriptive enum. - JUCE_DEPRECATED_WITH_BODY (void setFocusContainer (bool shouldBeFocusContainer) noexcept, + [[deprecated ("Use the setFocusContainerType that takes a more descriptive enum.")]] + void setFocusContainer (bool shouldBeFocusContainer) noexcept { setFocusContainerType (shouldBeFocusContainer ? FocusContainerType::keyboardFocusContainer : FocusContainerType::none); - }) + } + + [[deprecated ("Use the contains that takes a Point.")]] + void contains (int, int) = delete; #endif private: @@ -2472,7 +2508,6 @@ class JUCE_API Component : public MouseListener //============================================================================== friend class ComponentPeer; - friend class MouseInputSource; friend class MouseInputSourceInternal; #ifndef DOXYGEN @@ -2572,10 +2607,6 @@ class JUCE_API Component : public MouseListener void sendEnablementChangeMessage(); void sendVisibilityChangeMessage(); - bool containsInternal (Point); - bool reallyContainsInternal (Point, bool); - Component* getComponentAtInternal (Point); - struct ComponentHelpers; friend struct ComponentHelpers; @@ -2584,19 +2615,6 @@ class JUCE_API Component : public MouseListener */ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Component) - //============================================================================== - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This is included here just to cause a compile error if your code is still handling - // drag-and-drop with this method. If so, just update it to use the new FileDragAndDropTarget - // class, which is easy (just make your class inherit from FileDragAndDropTarget, and - // implement its methods instead of this Component method). - virtual void filesDropped (const StringArray&, int, int) {} - - // This is included here to cause an error if you use or overload it - it has been deprecated in - // favour of contains (Point) - void contains (int, int) = delete; - #endif - protected: //============================================================================== /** @internal */ diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_FocusTraverser.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_FocusTraverser.cpp index adea70636..70b33582d 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_FocusTraverser.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_FocusTraverser.cpp @@ -172,6 +172,7 @@ struct FocusTraverserTests : public UnitTest void runTest() override { ScopedJuceInitialiser_GUI libraryInitialiser; + const MessageManagerLock mml; beginTest ("Basic traversal"); { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp index 0aacdfe32..1e3ca9bf8 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp @@ -28,7 +28,8 @@ namespace juce Desktop::Desktop() : mouseSources (new MouseInputSource::SourceList()), - masterScaleFactor ((float) getDefaultMasterScale()) + masterScaleFactor ((float) getDefaultMasterScale()), + nativeDarkModeChangeDetectorImpl (createNativeDarkModeChangeDetectorImpl()) { displays.reset (new Displays (*this)); } @@ -198,6 +199,12 @@ void Desktop::handleAsyncUpdate() }); } +//============================================================================== +void Desktop::addDarkModeSettingListener (DarkModeSettingListener* l) { darkModeSettingListeners.add (l); } +void Desktop::removeDarkModeSettingListener (DarkModeSettingListener* l) { darkModeSettingListeners.remove (l); } + +void Desktop::darkModeChanged() { darkModeSettingListeners.call ([] (DarkModeSettingListener& l) { l.darkModeSettingChanged(); }); } + //============================================================================== void Desktop::resetTimer() { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h index f253866d0..250f60b91 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h @@ -45,6 +45,26 @@ class JUCE_API FocusChangeListener virtual void globalFocusChanged (Component* focusedComponent) = 0; }; +//============================================================================== +/** + Classes can implement this interface and register themselves with the Desktop class + to receive callbacks when the operating system dark mode setting changes. The + Desktop::isDarkModeActive() method can then be used to query the current setting. + + @see Desktop::addDarkModeSettingListener, Desktop::removeDarkModeSettingListener, + Desktop::isDarkModeActive + + @tags{GUI} +*/ +class JUCE_API DarkModeSettingListener +{ +public: + /** Destructor. */ + virtual ~DarkModeSettingListener() = default; + + /** Callback to indicate that the dark mode setting has changed. */ + virtual void darkModeSettingChanged() = 0; +}; //============================================================================== /** @@ -135,8 +155,7 @@ class JUCE_API Desktop : private DeletedAtShutdown, */ void addGlobalMouseListener (MouseListener* listener); - /** Unregisters a MouseListener that was added with the addGlobalMouseListener() - method. + /** Unregisters a MouseListener that was added with addGlobalMouseListener(). @see addGlobalMouseListener */ @@ -150,13 +169,36 @@ class JUCE_API Desktop : private DeletedAtShutdown, */ void addFocusChangeListener (FocusChangeListener* listener); - /** Unregisters a FocusChangeListener that was added with the addFocusChangeListener() - method. + /** Unregisters a FocusChangeListener that was added with addFocusChangeListener(). @see addFocusChangeListener */ void removeFocusChangeListener (FocusChangeListener* listener); + //============================================================================== + /** Registers a DarkModeSettingListener that will receive a callback when the + operating system dark mode setting changes. To query whether dark mode is on + use the isDarkModeActive() method. + + @see isDarkModeActive, removeDarkModeSettingListener + */ + void addDarkModeSettingListener (DarkModeSettingListener* listener); + + /** Unregisters a DarkModeSettingListener that was added with addDarkModeSettingListener(). + + @see addDarkModeSettingListener + */ + void removeDarkModeSettingListener (DarkModeSettingListener* listener); + + /** True if the operating system "dark mode" is active. + + To receive a callback when this setting changes implement the DarkModeSettingListener + interface and use the addDarkModeSettingListener() to register a listener. + + @see addDarkModeSettingListener, removeDarkModeSettingListener + */ + bool isDarkModeActive() const; + //============================================================================== /** Takes a component and makes it full-screen, removing the taskbar, dock, etc. @@ -352,9 +394,10 @@ class JUCE_API Desktop : private DeletedAtShutdown, /** True if the OS supports semitransparent windows */ static bool canUseSemiTransparentWindows() noexcept; - #if JUCE_MAC - /** OSX-specific function to check for the "dark" title-bar and menu mode. */ - static bool isOSXDarkModeActive(); + #if JUCE_MAC && ! defined (DOXYGEN) + [[deprecated ("This macOS-specific method has been deprecated in favour of the cross-platform " + " isDarkModeActive() method.")]] + static bool isOSXDarkModeActive() { return Desktop::getInstance().isDarkModeActive(); } #endif //============================================================================== @@ -376,6 +419,7 @@ class JUCE_API Desktop : private DeletedAtShutdown, ListenerList mouseListeners; ListenerList focusListeners; + ListenerList darkModeSettingListeners; Array desktopComponents; Array peers; @@ -423,6 +467,14 @@ class JUCE_API Desktop : private DeletedAtShutdown, Desktop(); ~Desktop() override; + //============================================================================== + class NativeDarkModeChangeDetectorImpl; + std::unique_ptr nativeDarkModeChangeDetectorImpl; + + static std::unique_ptr createNativeDarkModeChangeDetectorImpl(); + void darkModeChanged(); + + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Displays.h b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Displays.h index 2b358ae01..c24a6682e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Displays.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Displays.h @@ -168,15 +168,16 @@ class JUCE_API Displays void refresh(); /** @internal */ ~Displays() = default; - // This method has been deprecated - use the getDisplayForPoint() or getDisplayForRect() methods instead - // as they can deal with converting between logical and physical pixels - JUCE_DEPRECATED (const Display& getDisplayContaining (Point position) const noexcept); + + [[deprecated ("Use the getDisplayForPoint or getDisplayForRect methods instead " + "as they can deal with converting between logical and physical pixels.")]] + const Display& getDisplayContaining (Point position) const noexcept; // These methods have been deprecated - use the methods which return a Display* instead as they will return // nullptr on headless systems with no connected displays - JUCE_DEPRECATED (const Display& findDisplayForRect (Rectangle, bool isPhysical = false) const noexcept); - JUCE_DEPRECATED (const Display& findDisplayForPoint (Point, bool isPhysical = false) const noexcept); - JUCE_DEPRECATED (const Display& getMainDisplay() const noexcept); + [[deprecated]] const Display& findDisplayForRect (Rectangle, bool isPhysical = false) const noexcept; + [[deprecated]] const Display& findDisplayForPoint (Point, bool isPhysical = false) const noexcept; + [[deprecated]] const Display& getMainDisplay() const noexcept; #endif private: diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp index e9cf13b68..c003b73ce 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp @@ -183,7 +183,6 @@ void FileChooser::launchAsync (int flags, std::functionlaunch(); } - std::shared_ptr FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp) { results.clear(); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp index d87ddfd3f..32bbb8d7d 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp @@ -81,10 +81,6 @@ FileSearchPathListComponent::FileSearchPathListComponent() updateButtons(); } -FileSearchPathListComponent::~FileSearchPathListComponent() -{ -} - void FileSearchPathListComponent::updateButtons() { const bool anythingSelected = listBox.getNumSelectedRows() > 0; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h b/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h index fb18141ed..67194ed0b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h @@ -45,9 +45,6 @@ class JUCE_API FileSearchPathListComponent : public Component, /** Creates an empty FileSearchPathListComponent. */ FileSearchPathListComponent(); - /** Destructor. */ - ~FileSearchPathListComponent() override; - //============================================================================== /** Returns the path as it is currently shown. */ const FileSearchPath& getPath() const noexcept { return path; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp index 1bb3c6395..abaadc2f9 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp @@ -122,7 +122,15 @@ namespace juce { return Process::isForegroundProcess() || isEmbeddedInForegroundProcess (viewComponent); } -} + + bool isWindowOnCurrentVirtualDesktop (void*); + + struct CustomMouseCursorInfo + { + ScaledImage image; + Point hotspot; + }; +} // namespace juce #include "accessibility/juce_AccessibilityHandler.cpp" #include "components/juce_Component.cpp" @@ -134,7 +142,6 @@ namespace juce #include "components/juce_ModalComponentManager.cpp" #include "mouse/juce_ComponentDragger.cpp" #include "mouse/juce_DragAndDropContainer.cpp" -#include "mouse/juce_MouseCursor.cpp" #include "mouse/juce_MouseEvent.cpp" #include "mouse/juce_MouseInactivityDetector.cpp" #include "mouse/juce_MouseListener.cpp" @@ -371,3 +378,61 @@ namespace juce } #endif } + +//============================================================================== +#if JUCE_WINDOWS +bool juce::isWindowOnCurrentVirtualDesktop (void* x) +{ + if (x == nullptr) + return false; + + static auto* desktopManager = [] + { + // IVirtualDesktopManager Copied from ShObjdl_core.h, because it may not be defined + MIDL_INTERFACE ("a5cd92ff-29be-454c-8d04-d82879fb3f1b") + juce_IVirtualDesktopManager : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE IsWindowOnCurrentVirtualDesktop( + __RPC__in HWND topLevelWindow, + __RPC__out BOOL * onCurrentDesktop) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetWindowDesktopId( + __RPC__in HWND topLevelWindow, + __RPC__out GUID * desktopId) = 0; + + virtual HRESULT STDMETHODCALLTYPE MoveWindowToDesktop( + __RPC__in HWND topLevelWindow, + __RPC__in REFGUID desktopId) = 0; + }; + + juce_IVirtualDesktopManager* result = nullptr; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + + class DECLSPEC_UUID("aa509086-5ca9-4c25-8f95-589d3c07b48a") juce_VirtualDesktopManager; + + if (SUCCEEDED (CoCreateInstance (__uuidof (juce_VirtualDesktopManager), nullptr, CLSCTX_ALL, IID_PPV_ARGS (&result)))) + return result; + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return static_cast (nullptr); + }(); + + BOOL current = false; + + if (auto* dm = desktopManager) + if (SUCCEEDED (dm->IsWindowOnCurrentVirtualDesktop (static_cast (x), ¤t))) + return current != false; + + return true; +} +#else + bool juce::isWindowOnCurrentVirtualDesktop (void*) { return true; } + juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); } + juce::ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() {} +#endif + +// Depends on types defined in platform-specific windowing files +#include "mouse/juce_MouseCursor.cpp" diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h index a278709a7..7e0e2e551 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h @@ -35,7 +35,7 @@ ID: juce_gui_basics vendor: juce - version: 6.1.2 + version: 6.1.3 name: JUCE GUI core classes description: Basic user-interface components and related classes. website: http://www.juce.com/juce @@ -161,6 +161,10 @@ namespace juce class FlexBox; class Grid; + + #if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX + Image createSnapshotOfNativeWindow (void* nativeWindowHandle); + #endif } #include "mouse/juce_MouseCursor.h" @@ -366,3 +370,4 @@ namespace juce #include "layout/juce_GridItem.h" #include "layout/juce_Grid.h" +#include "native/juce_ScopedDPIAwarenessDisabler.h" diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp index a398dc6af..ffbf0732b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp @@ -103,6 +103,7 @@ struct KeyboardFocusTraverserTests : public UnitTest void runTest() override { ScopedJuceInitialiser_GUI libraryInitialiser; + const MessageManagerLock mml; beginTest ("No child wants keyboard focus"); { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexBox.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexBox.cpp index f8b885ebc..4183283c1 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexBox.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexBox.cpp @@ -30,11 +30,13 @@ struct FlexBoxLayoutCalculation { using Coord = double; + enum class Axis { main, cross }; + FlexBoxLayoutCalculation (FlexBox& fb, Coord w, Coord h) - : owner (fb), parentWidth (w), parentHeight (h), numItems (owner.items.size()), - isRowDirection (fb.flexDirection == FlexBox::Direction::row - || fb.flexDirection == FlexBox::Direction::rowReverse), - containerLineLength (isRowDirection ? parentWidth : parentHeight) + : owner (fb), parentWidth (w), parentHeight (h), numItems (owner.items.size()), + isRowDirection (fb.flexDirection == FlexBox::Direction::row + || fb.flexDirection == FlexBox::Direction::rowReverse), + containerLineLength (getContainerSize (Axis::main)) { lineItems.calloc (numItems * numItems); lineInfo.calloc (numItems); @@ -59,22 +61,6 @@ struct FlexBoxLayoutCalculation lockedMarginTop = getValueOrZeroIfAuto (item->margin.top); lockedMarginBottom = getValueOrZeroIfAuto (item->margin.bottom); } - - void setWidthChecked (Coord newWidth) noexcept - { - if (isAssigned (item->maxWidth)) newWidth = jmin (newWidth, static_cast (item->maxWidth)); - if (isAssigned (item->minWidth)) newWidth = jmax (newWidth, static_cast (item->minWidth)); - - lockedWidth = newWidth; - } - - void setHeightChecked (Coord newHeight) noexcept - { - if (isAssigned (item->maxHeight)) newHeight = jmin (newHeight, (Coord) item->maxHeight); - if (isAssigned (item->minHeight)) newHeight = jmax (newHeight, (Coord) item->minHeight); - - lockedHeight = newHeight; - } }; struct RowInfo @@ -102,6 +88,65 @@ struct FlexBoxLayoutCalculation static bool isAssigned (Coord value) noexcept { return value != FlexItem::notAssigned; } static Coord getValueOrZeroIfAuto (Coord value) noexcept { return isAuto (value) ? Coord() : value; } + //============================================================================== + bool isSingleLine() const { return owner.flexWrap == FlexBox::Wrap::noWrap; } + + template + Value& pickForAxis (Axis axis, Value& x, Value& y) const + { + return (isRowDirection ? axis == Axis::main : axis == Axis::cross) ? x : y; + } + + auto& getStartMargin (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.item->margin.left, item.item->margin.top); + } + + auto& getEndMargin (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.item->margin.right, item.item->margin.bottom); + } + + auto& getStartLockedMargin (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.lockedMarginLeft, item.lockedMarginTop); + } + + auto& getEndLockedMargin (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.lockedMarginRight, item.lockedMarginBottom); + } + + auto& getLockedSize (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.lockedWidth, item.lockedHeight); + } + + auto& getPreferredSize (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.preferredWidth, item.preferredHeight); + } + + Coord getContainerSize (Axis axis) const + { + return pickForAxis (axis, parentWidth, parentHeight); + } + + auto& getItemSize (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.item->width, item.item->height); + } + + auto& getMinSize (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.item->minWidth, item.item->minHeight); + } + + auto& getMaxSize (Axis axis, ItemWithState& item) const + { + return pickForAxis (axis, item.item->maxWidth, item.item->maxHeight); + } + //============================================================================== void createStates() { @@ -115,14 +160,14 @@ struct FlexBoxLayoutCalculation for (auto& item : itemStates) { - item.preferredWidth = getPreferredWidth (item); - item.preferredHeight = getPreferredHeight (item); + for (auto& axis : { Axis::main, Axis::cross }) + getPreferredSize (axis, item) = computePreferredSize (axis, item); } } void initialiseItems() noexcept { - if (owner.flexWrap == FlexBox::Wrap::noWrap) // for single-line, all items go in line 1 + if (isSingleLine()) // for single-line, all items go in line 1 { lineInfo[0].numItems = numItems; int i = 0; @@ -143,7 +188,7 @@ struct FlexBoxLayoutCalculation { item.resetItemLockedSize(); - const auto flexitemLength = getItemLength (item); + const auto flexitemLength = getItemMainSize (item); if (flexitemLength > currentLength) { @@ -195,19 +240,11 @@ struct FlexBoxLayoutCalculation { auto& item = getItem (column, row); - if (isRowDirection) - { - if (isAuto (item.item->margin.left)) ++allFlexGrow; - if (isAuto (item.item->margin.right)) ++allFlexGrow; - } - else - { - if (isAuto (item.item->margin.top)) ++allFlexGrow; - if (isAuto (item.item->margin.bottom)) ++allFlexGrow; - } + if (isAuto (getStartMargin (Axis::main, item))) ++allFlexGrow; + if (isAuto (getEndMargin (Axis::main, item))) ++allFlexGrow; } - auto changeUnitWidth = remainingLength / allFlexGrow; + const auto changeUnitWidth = remainingLength / allFlexGrow; if (changeUnitWidth > 0) { @@ -215,16 +252,11 @@ struct FlexBoxLayoutCalculation { auto& item = getItem (column, row); - if (isRowDirection) - { - if (isAuto (item.item->margin.left)) item.lockedMarginLeft = changeUnitWidth; - if (isAuto (item.item->margin.right)) item.lockedMarginRight = changeUnitWidth; - } - else - { - if (isAuto (item.item->margin.top)) item.lockedMarginTop = changeUnitWidth; - if (isAuto (item.item->margin.bottom)) item.lockedMarginBottom = changeUnitWidth; - } + if (isAuto (getStartMargin (Axis::main, item))) + getStartLockedMargin (Axis::main, item) = changeUnitWidth; + + if (isAuto (getEndMargin (Axis::main, item))) + getEndLockedMargin (Axis::main, item) = changeUnitWidth; } } } @@ -232,20 +264,25 @@ struct FlexBoxLayoutCalculation void calculateCrossSizesByLine() noexcept { - for (int row = 0; row < numberOfRows; ++row) + // https://www.w3.org/TR/css-flexbox-1/#algo-cross-line + // If the flex container is single-line and has a definite cross size, the cross size of the + // flex line is the flex container’s inner cross size. + if (isSingleLine()) { - Coord maxSize = 0; - const auto numColumns = lineInfo[row].numItems; - - for (int column = 0; column < numColumns; ++column) + lineInfo[0].crossSize = getContainerSize (Axis::cross); + } + else + { + for (int row = 0; row < numberOfRows; ++row) { - auto& item = getItem (column, row); + Coord maxSize = 0; + const auto numColumns = lineInfo[row].numItems; - maxSize = jmax (maxSize, isRowDirection ? item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom - : item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight); - } + for (int column = 0; column < numColumns; ++column) + maxSize = jmax (maxSize, getItemCrossSize (getItem (column, row))); - lineInfo[row].crossSize = maxSize; + lineInfo[row].crossSize = maxSize; + } } } @@ -270,7 +307,7 @@ struct FlexBoxLayoutCalculation void alignLinesPerAlignContent() noexcept { - containerCrossLength = isRowDirection ? parentHeight : parentWidth; + containerCrossLength = getContainerSize (Axis::cross); if (owner.alignContent == FlexBox::AlignContent::flexStart) { @@ -347,26 +384,22 @@ struct FlexBoxLayoutCalculation { auto& item = getItem (column, row); - if (isRowDirection) + getStartLockedMargin (Axis::cross, item) = [&] { - if (isAuto (item.item->margin.top) && isAuto (item.item->margin.bottom)) - item.lockedMarginTop = (crossSizeForLine - item.lockedHeight) / 2; - else if (isAuto (item.item->margin.top)) - item.lockedMarginTop = crossSizeForLine - item.lockedHeight - item.item->margin.bottom; - } - else if (isAuto (item.item->margin.left) && isAuto (item.item->margin.right)) - { - item.lockedMarginLeft = jmax (Coord(), (crossSizeForLine - item.lockedWidth) / 2); - } - else if (isAuto (item.item->margin.top)) - { - item.lockedMarginLeft = jmax (Coord(), crossSizeForLine - item.lockedHeight - item.item->margin.bottom); - } + if (isAuto (getStartMargin (Axis::cross, item)) && isAuto (getEndMargin (Axis::cross, item))) + return (crossSizeForLine - getLockedSize (Axis::cross, item)) / 2; + + if (isAuto (getStartMargin (Axis::cross, item))) + return crossSizeForLine - getLockedSize (Axis::cross, item) - getEndMargin (Axis::cross, item); + + return getStartLockedMargin (Axis::cross, item); + }(); } } } - void alignItemsInCrossAxisInLinesPerAlignItems() noexcept + // Align all flex items along the cross-axis per align-self, if neither of the item’s cross-axis margins are auto. + void alignItemsInCrossAxisInLinesPerAlignSelf() noexcept { for (int row = 0; row < numberOfRows; ++row) { @@ -377,86 +410,65 @@ struct FlexBoxLayoutCalculation { auto& item = getItem (column, row); - if (item.item->alignSelf == FlexItem::AlignSelf::autoAlign) - { - if (owner.alignItems == FlexBox::AlignItems::stretch) - { - item.lockedMarginTop = item.item->margin.top; + if (isAuto (getStartMargin (Axis::cross, item)) || isAuto (getEndMargin (Axis::cross, item))) + continue; - if (isRowDirection) - item.setHeightChecked (lineSize - item.item->margin.top - item.item->margin.bottom); - else - item.setWidthChecked (lineSize - item.item->margin.left - item.item->margin.right); - } - else if (owner.alignItems == FlexBox::AlignItems::flexStart) - { - item.lockedMarginTop = item.item->margin.top; - } - else if (owner.alignItems == FlexBox::AlignItems::flexEnd) - { - if (isRowDirection) - item.lockedMarginTop = lineSize - item.lockedHeight - item.item->margin.bottom; - else - item.lockedMarginLeft = lineSize - item.lockedWidth - item.item->margin.right; - } - else if (owner.alignItems == FlexBox::AlignItems::center) + const auto alignment = [&] + { + switch (item.item->alignSelf) { - if (isRowDirection) - item.lockedMarginTop = (lineSize - item.lockedHeight - item.item->margin.top - item.item->margin.bottom) / 2; - else - item.lockedMarginLeft = (lineSize - item.lockedWidth - item.item->margin.left - item.item->margin.right) / 2; + case FlexItem::AlignSelf::stretch: return FlexBox::AlignItems::stretch; + case FlexItem::AlignSelf::flexStart: return FlexBox::AlignItems::flexStart; + case FlexItem::AlignSelf::flexEnd: return FlexBox::AlignItems::flexEnd; + case FlexItem::AlignSelf::center: return FlexBox::AlignItems::center; + case FlexItem::AlignSelf::autoAlign: break; } - } - } - } - } - void alignLinesPerAlignSelf() noexcept - { - for (int row = 0; row < numberOfRows; ++row) - { - const auto numColumns = lineInfo[row].numItems; - const auto lineSize = lineInfo[row].crossSize; - - for (int column = 0; column < numColumns; ++column) - { - auto& item = getItem (column, row); + return owner.alignItems; + }(); - if (! isAuto (item.item->margin.top)) + getStartLockedMargin (Axis::cross, item) = [&] { - if (item.item->alignSelf == FlexItem::AlignSelf::flexStart) - { - if (isRowDirection) - item.lockedMarginTop = item.item->margin.top; - else - item.lockedMarginLeft = item.item->margin.left; - } - else if (item.item->alignSelf == FlexItem::AlignSelf::flexEnd) + switch (alignment) { - if (isRowDirection) - item.lockedMarginTop = lineSize - item.lockedHeight - item.item->margin.bottom; - else - item.lockedMarginLeft = lineSize - item.lockedWidth - item.item->margin.right; - } - else if (item.item->alignSelf == FlexItem::AlignSelf::center) - { - if (isRowDirection) - item.lockedMarginTop = item.item->margin.top + (lineSize - item.lockedHeight - item.item->margin.top - item.item->margin.bottom) / 2; - else - item.lockedMarginLeft = item.item->margin.left + (lineSize - item.lockedWidth - item.item->margin.left - item.item->margin.right) / 2; - } - else if (item.item->alignSelf == FlexItem::AlignSelf::stretch) - { - item.lockedMarginTop = item.item->margin.top; - item.lockedMarginLeft = item.item->margin.left; - - if (isRowDirection) - item.setHeightChecked (isAssigned (item.item->height) ? getPreferredHeight (item) - : lineSize - item.item->margin.top - item.item->margin.bottom); - else - item.setWidthChecked (isAssigned (item.item->width) ? getPreferredWidth (item) - : lineSize - item.item->margin.left - item.item->margin.right); + // https://www.w3.org/TR/css-flexbox-1/#valdef-align-items-flex-start + // The cross-start margin edge of the flex item is placed flush with the + // cross-start edge of the line. + case FlexBox::AlignItems::flexStart: + return (Coord) getStartMargin (Axis::cross, item); + + // https://www.w3.org/TR/css-flexbox-1/#valdef-align-items-flex-end + // The cross-end margin edge of the flex item is placed flush with the cross-end + // edge of the line. + case FlexBox::AlignItems::flexEnd: + return lineSize - getLockedSize (Axis::cross, item) - getEndMargin (Axis::cross, item); + + // https://www.w3.org/TR/css-flexbox-1/#valdef-align-items-center + // The flex item’s margin box is centered in the cross axis within the line. + case FlexBox::AlignItems::center: + return getStartMargin (Axis::cross, item) + (lineSize - getLockedSize (Axis::cross, item) - getStartMargin (Axis::cross, item) - getEndMargin (Axis::cross, item)) / 2; + + // https://www.w3.org/TR/css-flexbox-1/#valdef-align-items-stretch + case FlexBox::AlignItems::stretch: + return (Coord) getStartMargin (Axis::cross, item); } + + jassertfalse; + return 0.0; + }(); + + if (alignment == FlexBox::AlignItems::stretch) + { + auto newSize = isAssigned (getItemSize (Axis::cross, item)) ? computePreferredSize (Axis::cross, item) + : lineSize - getStartMargin (Axis::cross, item) - getEndMargin (Axis::cross, item); + + if (isAssigned (getMaxSize (Axis::cross, item))) + newSize = jmin (newSize, (Coord) getMaxSize (Axis::cross, item)); + + if (isAssigned (getMinSize (Axis::cross, item))) + newSize = jmax (newSize, (Coord) getMinSize (Axis::cross, item)); + + getLockedSize (Axis::cross, item) = newSize; } } } @@ -496,20 +508,15 @@ struct FlexBoxLayoutCalculation { auto& item = getItem (column, row); - if (isRowDirection) - { - item.lockedMarginLeft += additionalMarginLeft; - item.lockedMarginRight += additionalMarginRight; - item.item->currentBounds.setPosition ((float) (x + item.lockedMarginLeft), (float) item.lockedMarginTop); - x += item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight; - } - else - { - item.lockedMarginTop += additionalMarginLeft; - item.lockedMarginBottom += additionalMarginRight; - item.item->currentBounds.setPosition ((float) item.lockedMarginLeft, (float) (x + item.lockedMarginTop)); - x += item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom; - } + getStartLockedMargin (Axis::main, item) += additionalMarginLeft; + getEndLockedMargin (Axis::main, item) += additionalMarginRight; + + item.item->currentBounds.setPosition (isRowDirection ? (float) (x + item.lockedMarginLeft) + : (float) item.lockedMarginLeft, + isRowDirection ? (float) item.lockedMarginTop + : (float) (x + item.lockedMarginTop)); + + x += getItemMainSize (item); } } } @@ -564,8 +571,9 @@ struct FlexBoxLayoutCalculation void resetItem (ItemWithState& item) noexcept { item.locked = false; - item.lockedWidth = getPreferredWidth (item); - item.lockedHeight = getPreferredHeight (item); + + for (auto& axis : { Axis::main, Axis::cross }) + getLockedSize (axis, item) = computePreferredSize (axis, item); } bool layoutRowItems (const int row) noexcept @@ -580,11 +588,11 @@ struct FlexBoxLayoutCalculation if (item.locked) { - flexContainerLength -= getItemLength (item); + flexContainerLength -= getItemMainSize (item); } else { - totalItemsLength += getItemLength (item); + totalItemsLength += getItemMainSize (item); totalFlexGrow += item.item->flexGrow; totalFlexShrink += item.item->flexShrink; } @@ -628,12 +636,7 @@ struct FlexBoxLayoutCalculation const auto numColumns = lineInfo[row].numItems; for (int column = 0; column < numColumns; ++column) - { - const auto& item = getItem (column, row); - - lineInfo[row].totalLength += isRowDirection ? item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight - : item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom; - } + lineInfo[row].totalLength += getItemMainSize (getItem (column, row)); } } @@ -668,7 +671,7 @@ struct FlexBoxLayoutCalculation } } - Coord getItemLength (const ItemWithState& item) const noexcept + Coord getItemMainSize (const ItemWithState& item) const noexcept { return isRowDirection ? item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight : item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom; @@ -684,84 +687,59 @@ struct FlexBoxLayoutCalculation { bool ok = false; - if (isRowDirection) - { - const auto prefWidth = getPreferredWidth (item); + const auto prefSize = computePreferredSize (Axis::main, item); - if (isAssigned (item.item->maxWidth) && item.item->maxWidth < prefWidth + length) - { - item.lockedWidth = item.item->maxWidth; - item.locked = true; - } - else if (isAssigned (prefWidth) && item.item->minWidth > prefWidth + length) - { - item.lockedWidth = item.item->minWidth; - item.locked = true; - } - else - { - ok = true; - item.lockedWidth = prefWidth + length; - } + const auto pickForMainAxis = [this] (auto& a, auto& b) -> auto& { return pickForAxis (Axis::main, a, b); }; - lineInfo[row].totalLength += item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight; + if (isAssigned (pickForMainAxis (item.item->maxWidth, item.item->maxHeight)) + && pickForMainAxis (item.item->maxWidth, item.item->maxHeight) < prefSize + length) + { + pickForMainAxis (item.lockedWidth, item.lockedHeight) = pickForMainAxis (item.item->maxWidth, item.item->maxHeight); + item.locked = true; + } + else if (isAssigned (prefSize) && pickForMainAxis (item.item->minWidth, item.item->minHeight) > prefSize + length) + { + pickForMainAxis (item.lockedWidth, item.lockedHeight) = pickForMainAxis (item.item->minWidth, item.item->minHeight); + item.locked = true; } else { - const auto prefHeight = getPreferredHeight (item); - - if (isAssigned (item.item->maxHeight) && item.item->maxHeight < prefHeight + length) - { - item.lockedHeight = item.item->maxHeight; - item.locked = true; - } - else if (isAssigned (prefHeight) && item.item->minHeight > prefHeight + length) - { - item.lockedHeight = item.item->minHeight; - item.locked = true; - } - else - { - ok = true; - item.lockedHeight = prefHeight + length; - } - - lineInfo[row].totalLength += item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom; + ok = true; + pickForMainAxis (item.lockedWidth, item.lockedHeight) = prefSize + length; } + lineInfo[row].totalLength += pickForMainAxis (item.lockedWidth, item.lockedHeight) + + pickForMainAxis (item.lockedMarginLeft, item.lockedMarginTop) + + pickForMainAxis (item.lockedMarginRight, item.lockedMarginBottom); + return ok; } - Coord getPreferredWidth (const ItemWithState& itemWithState) const noexcept + Coord computePreferredSize (Axis axis, ItemWithState& itemWithState) const noexcept { const auto& item = *itemWithState.item; - auto preferredWidth = (item.flexBasis > 0 && isRowDirection) - ? item.flexBasis - : (isAssigned (item.width) ? item.width : item.minWidth); - if (isAssigned (item.minWidth) && preferredWidth < item.minWidth) return item.minWidth; - if (isAssigned (item.maxWidth) && preferredWidth > item.maxWidth) return item.maxWidth; + auto preferredSize = (item.flexBasis > 0 && axis == Axis::main) ? item.flexBasis + : (isAssigned (getItemSize (axis, itemWithState)) ? getItemSize (axis, itemWithState) + : getMinSize (axis, itemWithState)); - return preferredWidth; - } + const auto minSize = getMinSize (axis, itemWithState); - Coord getPreferredHeight (const ItemWithState& itemWithState) const noexcept - { - const auto& item = *itemWithState.item; - auto preferredHeight = (item.flexBasis > 0 && ! isRowDirection) - ? item.flexBasis - : (isAssigned (item.height) ? item.height : item.minHeight); + if (isAssigned (minSize) && preferredSize < minSize) + return minSize; + + const auto maxSize = getMaxSize (axis, itemWithState); - if (isAssigned (item.minHeight) && preferredHeight < item.minHeight) return item.minHeight; - if (isAssigned (item.maxHeight) && preferredHeight > item.maxHeight) return item.maxHeight; + if (isAssigned (maxSize) && maxSize < preferredSize) + return maxSize; - return preferredHeight; + return preferredSize; } }; //============================================================================== -FlexBox::FlexBox() noexcept {} -FlexBox::~FlexBox() noexcept {} +FlexBox::FlexBox() noexcept = default; +FlexBox::~FlexBox() noexcept = default; FlexBox::FlexBox (JustifyContent jc) noexcept : justifyContent (jc) {} @@ -784,8 +762,7 @@ void FlexBox::performLayout (Rectangle targetArea) layout.calculateCrossSizeOfAllItems(); layout.alignLinesPerAlignContent(); layout.resolveAutoMarginsOnCrossAxis(); - layout.alignItemsInCrossAxisInLinesPerAlignItems(); - layout.alignLinesPerAlignSelf(); + layout.alignItemsInCrossAxisInLinesPerAlignSelf(); layout.alignItemsByJustifyContent(); layout.layoutAllItems(); @@ -856,4 +833,312 @@ FlexItem FlexItem::withMargin (Margin m) const noexcept { auto fi = FlexItem FlexItem::withOrder (int newOrder) const noexcept { auto fi = *this; fi.order = newOrder; return fi; } FlexItem FlexItem::withAlignSelf (AlignSelf a) const noexcept { auto fi = *this; fi.alignSelf = a; return fi; } +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class FlexBoxTests : public UnitTest +{ +public: + FlexBoxTests() : UnitTest ("FlexBox", UnitTestCategories::gui) {} + + void runTest() override + { + using AlignSelf = FlexItem::AlignSelf; + using Direction = FlexBox::Direction; + + const Rectangle rect (10.0f, 20.0f, 300.0f, 200.0f); + const auto doLayout = [&rect] (Direction direction, Array items) + { + juce::FlexBox flex; + flex.flexDirection = direction; + flex.items = std::move (items); + flex.performLayout (rect); + return flex; + }; + + beginTest ("flex item with mostly auto properties"); + { + const auto test = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem{}.withAlignSelf (alignment) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() }); + test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() }); + test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f }); + test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom(), 0.0f, 0.0f }); + test (Direction::row, AlignSelf::center, { rect.getX(), rect.getCentreY(), 0.0f, 0.0f }); + + test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f }); + test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f }); + test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f }); + test (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getY(), 0.0f, 0.0f }); + test (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getY(), 0.0f, 0.0f }); + } + + beginTest ("flex item with specified width and height"); + { + constexpr auto w = 50.0f; + constexpr auto h = 60.0f; + const auto test = [&] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withWidth (w) + .withHeight (h) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), w, h }); + test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), w, h }); + test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), w, h }); + test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom() - h, w, h }); + test (Direction::row, AlignSelf::center, { rect.getX(), rect.getY() + (rect.getHeight() - h) * 0.5f, w, h }); + + test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), w, h }); + test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), w, h }); + test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), w, h }); + test (Direction::column, AlignSelf::flexEnd, { rect.getRight() - w, rect.getY(), w, h }); + test (Direction::column, AlignSelf::center, { rect.getX() + (rect.getWidth() - w) * 0.5f, rect.getY(), w, h }); + } + + beginTest ("flex item with oversized width and height"); + { + const auto w = rect.getWidth() * 2; + const auto h = rect.getHeight() * 2; + const auto test = [this, &doLayout, &w, &h] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withWidth (w) + .withHeight (h) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + const Rectangle baseRow (rect.getX(), rect.getY(), rect.getWidth(), h); + test (Direction::row, AlignSelf::autoAlign, baseRow); + test (Direction::row, AlignSelf::stretch, baseRow); + test (Direction::row, AlignSelf::flexStart, baseRow); + test (Direction::row, AlignSelf::flexEnd, baseRow.withBottomY (rect.getBottom())); + test (Direction::row, AlignSelf::center, baseRow.withCentre (rect.getCentre())); + + const Rectangle baseColumn (rect.getX(), rect.getY(), w, rect.getHeight()); + test (Direction::column, AlignSelf::autoAlign, baseColumn); + test (Direction::column, AlignSelf::stretch, baseColumn); + test (Direction::column, AlignSelf::flexStart, baseColumn); + test (Direction::column, AlignSelf::flexEnd, baseColumn.withRightX (rect.getRight())); + test (Direction::column, AlignSelf::center, baseColumn.withCentre (rect.getCentre())); + } + + beginTest ("flex item with minimum width and height"); + { + constexpr auto w = 50.0f; + constexpr auto h = 60.0f; + const auto test = [&] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withMinWidth (w) + .withMinHeight (h) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), w, rect.getHeight() }); + test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), w, rect.getHeight() }); + test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), w, h }); + test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom() - h, w, h }); + test (Direction::row, AlignSelf::center, { rect.getX(), rect.getY() + (rect.getHeight() - h) * 0.5f, w, h }); + + test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), h }); + test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), h }); + test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), w, h }); + test (Direction::column, AlignSelf::flexEnd, { rect.getRight() - w, rect.getY(), w, h }); + test (Direction::column, AlignSelf::center, { rect.getX() + (rect.getWidth() - w) * 0.5f, rect.getY(), w, h }); + } + + beginTest ("flex item with maximum width and height"); + { + constexpr auto w = 50.0f; + constexpr auto h = 60.0f; + const auto test = [&] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withMaxWidth (w) + .withMaxHeight (h) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), 0.0f, h }); + test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), 0.0f, h }); + test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f }); + test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom(), 0.0f, 0.0f }); + test (Direction::row, AlignSelf::center, { rect.getX(), rect.getCentreY(), 0.0f, 0.0f }); + + test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), w, 0.0f }); + test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), w, 0.0f }); + test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f }); + test (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getY(), 0.0f, 0.0f }); + test (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getY(), 0.0f, 0.0f }); + } + + beginTest ("flex item with specified flex"); + { + const auto test = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment).withFlex (1.0f) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() }); + test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() }); + test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f }); + test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom(), rect.getWidth(), 0.0f }); + test (Direction::row, AlignSelf::center, { rect.getX(), rect.getCentreY(), rect.getWidth(), 0.0f }); + + test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() }); + test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() }); + test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() }); + test (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getY(), 0.0f, rect.getHeight() }); + test (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getY(), 0.0f, rect.getHeight() }); + } + + beginTest ("flex item with margin"); + { + const FlexItem::Margin margin (10.0f, 20.0f, 30.0f, 40.0f); + + const auto test = [this, &doLayout, &margin] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment).withMargin (margin) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + const auto remainingHeight = rect.getHeight() - margin.top - margin.bottom; + const auto remainingWidth = rect.getWidth() - margin.left - margin.right; + + test (Direction::row, AlignSelf::autoAlign, { rect.getX() + margin.left, rect.getY() + margin.top, 0.0f, remainingHeight }); + test (Direction::row, AlignSelf::stretch, { rect.getX() + margin.left, rect.getY() + margin.top, 0.0f, remainingHeight }); + test (Direction::row, AlignSelf::flexStart, { rect.getX() + margin.left, rect.getY() + margin.top, 0.0f, 0.0f }); + test (Direction::row, AlignSelf::flexEnd, { rect.getX() + margin.left, rect.getBottom() - margin.bottom, 0.0f, 0.0f }); + test (Direction::row, AlignSelf::center, { rect.getX() + margin.left, rect.getY() + margin.top + remainingHeight * 0.5f, 0.0f, 0.0f }); + + test (Direction::column, AlignSelf::autoAlign, { rect.getX() + margin.left, rect.getY() + margin.top, remainingWidth, 0.0f }); + test (Direction::column, AlignSelf::stretch, { rect.getX() + margin.left, rect.getY() + margin.top, remainingWidth, 0.0f }); + test (Direction::column, AlignSelf::flexStart, { rect.getX() + margin.left, rect.getY() + margin.top, 0.0f, 0.0f }); + test (Direction::column, AlignSelf::flexEnd, { rect.getRight() - margin.right, rect.getY() + margin.top, 0.0f, 0.0f }); + test (Direction::column, AlignSelf::center, { rect.getX() + margin.left + remainingWidth * 0.5f, rect.getY() + margin.top, 0.0f, 0.0f }); + } + + const AlignSelf alignments[] { AlignSelf::autoAlign, + AlignSelf::stretch, + AlignSelf::flexStart, + AlignSelf::flexEnd, + AlignSelf::center }; + + beginTest ("flex item with auto margin"); + { + for (const auto& alignment : alignments) + { + for (const auto& direction : { Direction::row, Direction::column }) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withMargin ((float) FlexItem::autoValue) }); + expect (flex.items.getFirst().currentBounds == Rectangle (rect.getCentre(), rect.getCentre())); + } + } + + const auto testTop = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withMargin ({ (float) FlexItem::autoValue, 0.0f, 0.0f, 0.0f }) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + for (const auto& alignment : alignments) + testTop (Direction::row, alignment, { rect.getX(), rect.getBottom(), 0.0f, 0.0f }); + + testTop (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getBottom(), rect.getWidth(), 0.0f }); + testTop (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getBottom(), rect.getWidth(), 0.0f }); + testTop (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getBottom(), 0.0f, 0.0f }); + testTop (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getBottom(), 0.0f, 0.0f }); + testTop (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getBottom(), 0.0f, 0.0f }); + + const auto testBottom = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withMargin ({ 0.0f, 0.0f, (float) FlexItem::autoValue, 0.0f }) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + for (const auto& alignment : alignments) + testBottom (Direction::row, alignment, { rect.getX(), rect.getY(), 0.0f, 0.0f }); + + testBottom (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f }); + testBottom (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f }); + testBottom (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f }); + testBottom (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getY(), 0.0f, 0.0f }); + testBottom (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getY(), 0.0f, 0.0f }); + + const auto testLeft = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withMargin ({ 0.0f, 0.0f, 0.0f, (float) FlexItem::autoValue }) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + testLeft (Direction::row, AlignSelf::autoAlign, { rect.getRight(), rect.getY(), 0.0f, rect.getHeight() }); + testLeft (Direction::row, AlignSelf::stretch, { rect.getRight(), rect.getY(), 0.0f, rect.getHeight() }); + testLeft (Direction::row, AlignSelf::flexStart, { rect.getRight(), rect.getY(), 0.0f, 0.0f }); + testLeft (Direction::row, AlignSelf::flexEnd, { rect.getRight(), rect.getBottom(), 0.0f, 0.0f }); + testLeft (Direction::row, AlignSelf::center, { rect.getRight(), rect.getCentreY(), 0.0f, 0.0f }); + + for (const auto& alignment : alignments) + testLeft (Direction::column, alignment, { rect.getRight(), rect.getY(), 0.0f, 0.0f }); + + const auto testRight = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle expectedBounds) + { + const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment) + .withMargin ({ 0.0f, (float) FlexItem::autoValue, 0.0f, 0.0f }) }); + expect (flex.items.getFirst().currentBounds == expectedBounds); + }; + + testRight (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() }); + testRight (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() }); + testRight (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f }); + testRight (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom(), 0.0f, 0.0f }); + testRight (Direction::row, AlignSelf::center, { rect.getX(), rect.getCentreY(), 0.0f, 0.0f }); + + for (const auto& alignment : alignments) + testRight (Direction::column, alignment, { rect.getX(), rect.getY(), 0.0f, 0.0f }); + } + + beginTest ("in a multiline layout, items too large to fit on the main axis are given a line to themselves"); + { + const auto spacer = 10.0f; + + for (const auto alignment : alignments) + { + juce::FlexBox flex; + flex.flexWrap = FlexBox::Wrap::wrap; + flex.items = { FlexItem().withAlignSelf (alignment) + .withWidth (spacer) + .withHeight (spacer), + FlexItem().withAlignSelf (alignment) + .withWidth (rect.getWidth() * 2) + .withHeight (rect.getHeight()), + FlexItem().withAlignSelf (alignment) + .withWidth (spacer) + .withHeight (spacer) }; + flex.performLayout (rect); + + expect (flex.items[0].currentBounds == Rectangle (rect.getX(), rect.getY(), spacer, spacer)); + expect (flex.items[1].currentBounds == Rectangle (rect.getX(), rect.getY() + spacer, rect.getWidth(), rect.getHeight())); + expect (flex.items[2].currentBounds == Rectangle (rect.getX(), rect.getBottom() + spacer, 10.0f, 10.0f)); + } + } + } +}; + +static FlexBoxTests flexBoxTests; + +#endif + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexItem.h b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexItem.h index 735a97e38..47b83c2fd 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexItem.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexItem.h @@ -104,7 +104,7 @@ class JUCE_API FlexItem final This determines the alignment of the item along the cross-axis (perpendicular to the direction of flow). */ - AlignSelf alignSelf = AlignSelf::stretch; + AlignSelf alignSelf = AlignSelf::autoAlign; //============================================================================== /** This constant can be used for sizes to indicate that 'auto' mode should be used. */ diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.cpp index 7a3b2dfb2..ca6fee680 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.cpp @@ -26,9 +26,20 @@ namespace juce { +struct AllTracksIncludingImplicit +{ + Array items; + int numImplicitLeading; // The number of implicit items before the explicit items +}; + +struct Tracks +{ + AllTracksIncludingImplicit columns, rows; +}; + struct Grid::SizeCalculation { - static float getTotalAbsoluteSize (const Array& tracks, Px gapSize) noexcept + static float getTotalAbsoluteSize (const Array& tracks, Px gapSize) noexcept { float totalCellSize = 0.0f; @@ -42,7 +53,7 @@ struct Grid::SizeCalculation return totalCellSize + totalGap; } - static float getRelativeUnitSize (float size, float totalAbsolute, const Array& tracks) noexcept + static float getRelativeUnitSize (float size, float totalAbsolute, const Array& tracks) noexcept { const float totalRelative = jlimit (0.0f, size, size - totalAbsolute); float factorsSum = 0.0f; @@ -56,50 +67,47 @@ struct Grid::SizeCalculation } //============================================================================== - static float getTotalAbsoluteHeight (const Array& rowTracks, Px rowGap) + static float getTotalAbsoluteHeight (const Array& rowTracks, Px rowGap) { return getTotalAbsoluteSize (rowTracks, rowGap); } - static float getTotalAbsoluteWidth (const Array& columnTracks, Px columnGap) + static float getTotalAbsoluteWidth (const Array& columnTracks, Px columnGap) { return getTotalAbsoluteSize (columnTracks, columnGap); } - static float getRelativeWidthUnit (float gridWidth, Px columnGap, const Array& columnTracks) + static float getRelativeWidthUnit (float gridWidth, Px columnGap, const Array& columnTracks) { return getRelativeUnitSize (gridWidth, getTotalAbsoluteWidth (columnTracks, columnGap), columnTracks); } - static float getRelativeHeightUnit (float gridHeight, Px rowGap, const Array& rowTracks) + static float getRelativeHeightUnit (float gridHeight, Px rowGap, const Array& rowTracks) { return getRelativeUnitSize (gridHeight, getTotalAbsoluteHeight (rowTracks, rowGap), rowTracks); } //============================================================================== - static bool hasAnyFractions (const Array& tracks) + static bool hasAnyFractions (const Array& tracks) { - for (auto& t : tracks) - if (t.isFractional()) - return true; - - return false; + return std::any_of (tracks.begin(), + tracks.end(), + [] (const auto& t) { return t.isFractional(); }); } void computeSizes (float gridWidth, float gridHeight, Px columnGapToUse, Px rowGapToUse, - const Array& columnTracks, - const Array& rowTracks) + const Tracks& tracks) { - if (hasAnyFractions (columnTracks)) - relativeWidthUnit = getRelativeWidthUnit (gridWidth, columnGapToUse, columnTracks); + if (hasAnyFractions (tracks.columns.items)) + relativeWidthUnit = getRelativeWidthUnit (gridWidth, columnGapToUse, tracks.columns.items); else - remainingWidth = gridWidth - getTotalAbsoluteSize (columnTracks, columnGapToUse); + remainingWidth = gridWidth - getTotalAbsoluteSize (tracks.columns.items, columnGapToUse); - if (hasAnyFractions (rowTracks)) - relativeHeightUnit = getRelativeHeightUnit (gridHeight, rowGapToUse, rowTracks); + if (hasAnyFractions (tracks.rows.items)) + relativeHeightUnit = getRelativeHeightUnit (gridHeight, rowGapToUse, tracks.rows.items); else - remainingHeight = gridHeight - getTotalAbsoluteSize (rowTracks, rowGapToUse); + remainingHeight = gridHeight - getTotalAbsoluteSize (tracks.rows.items, rowGapToUse); } float relativeWidthUnit = 0.0f; @@ -126,7 +134,7 @@ struct Grid::PlacementHelpers }; //============================================================================== - static Array getArrayOfLinesFromTracks (const Array& tracks) + static Array getArrayOfLinesFromTracks (const Array& tracks) { // fill line info array Array lines; @@ -168,7 +176,7 @@ struct Grid::PlacementHelpers //============================================================================== static int deduceAbsoluteLineNumberFromLineName (GridItem::Property prop, - const Array& tracks) + const Array& tracks) { jassert (prop.hasAbsolute()); @@ -195,19 +203,27 @@ struct Grid::PlacementHelpers } static int deduceAbsoluteLineNumber (GridItem::Property prop, - const Array& tracks) + const Array& tracks) { jassert (prop.hasAbsolute()); if (prop.hasName()) return deduceAbsoluteLineNumberFromLineName (prop, tracks); - return prop.getNumber() > 0 ? prop.getNumber() : tracks.size() + 2 + prop.getNumber(); + if (prop.getNumber() > 0) + return prop.getNumber(); + + if (prop.getNumber() < 0) + return tracks.size() + 2 + prop.getNumber(); + + // An integer value of 0 is invalid + jassertfalse; + return 1; } static int deduceAbsoluteLineNumberFromNamedSpan (int startLineNumber, GridItem::Property propertyWithSpan, - const Array& tracks) + const Array& tracks) { jassert (propertyWithSpan.hasSpan()); @@ -235,7 +251,7 @@ struct Grid::PlacementHelpers static int deduceAbsoluteLineNumberBasedOnSpan (int startLineNumber, GridItem::Property propertyWithSpan, - const Array& tracks) + const Array& tracks) { jassert (propertyWithSpan.hasSpan()); @@ -246,10 +262,8 @@ struct Grid::PlacementHelpers } //============================================================================== - static LineRange deduceLineRange (GridItem::StartAndEndProperty prop, const Array& tracks) + static LineRange deduceLineRange (GridItem::StartAndEndProperty prop, const Array& tracks) { - LineRange s; - jassert (! (prop.start.hasAuto() && prop.end.hasAuto())); if (prop.start.hasAbsolute() && prop.end.hasAuto()) @@ -261,27 +275,30 @@ struct Grid::PlacementHelpers prop.start = GridItem::Span (1); } - if (prop.start.hasAbsolute() && prop.end.hasAbsolute()) - { - s.start = deduceAbsoluteLineNumber (prop.start, tracks); - s.end = deduceAbsoluteLineNumber (prop.end, tracks); - } - else if (prop.start.hasAbsolute() && prop.end.hasSpan()) - { - s.start = deduceAbsoluteLineNumber (prop.start, tracks); - s.end = deduceAbsoluteLineNumberBasedOnSpan (s.start, prop.end, tracks); - } - else if (prop.start.hasSpan() && prop.end.hasAbsolute()) - { - s.start = deduceAbsoluteLineNumber (prop.end, tracks); - s.end = deduceAbsoluteLineNumberBasedOnSpan (s.start, prop.start, tracks); - } - else + auto s = [&]() -> LineRange { + if (prop.start.hasAbsolute() && prop.end.hasAbsolute()) + { + return { deduceAbsoluteLineNumber (prop.start, tracks), + deduceAbsoluteLineNumber (prop.end, tracks) }; + } + + if (prop.start.hasAbsolute() && prop.end.hasSpan()) + { + const auto start = deduceAbsoluteLineNumber (prop.start, tracks); + return { start, deduceAbsoluteLineNumberBasedOnSpan (start, prop.end, tracks) }; + } + + if (prop.start.hasSpan() && prop.end.hasAbsolute()) + { + const auto start = deduceAbsoluteLineNumber (prop.end, tracks); + return { start, deduceAbsoluteLineNumberBasedOnSpan (start, prop.start, tracks) }; + } + // Can't have an item with spans on both start and end. jassertfalse; - s.start = s.end = {}; - } + return {}; + }(); // swap if start overtakes end if (s.start > s.end) @@ -388,81 +405,77 @@ struct Grid::PlacementHelpers } //============================================================================== - static float getCoord (int trackNumber, float relativeUnit, Px gap, const Array& tracks) + static float getCoord (int trackNumber, float relativeUnit, Px gap, const Array& tracks) { float c = 0; - for (const auto* it = tracks.begin(); it != tracks.begin() + trackNumber - 1; ++it) + for (const auto* it = tracks.begin(); it != tracks.begin() + trackNumber; ++it) c += it->getAbsoluteSize (relativeUnit) + static_cast (gap.pixels); return c; } static Rectangle getCellBounds (int columnNumber, int rowNumber, - const Array& columnTracks, - const Array& rowTracks, - Grid::SizeCalculation calculation, - Px columnGap, Px rowGap) + const Tracks& tracks, + SizeCalculation calculation, + Px columnGap, Px rowGap) { - jassert (columnNumber >= 1 && columnNumber <= columnTracks.size()); - jassert (rowNumber >= 1 && rowNumber <= rowTracks.size()); - - const auto x = getCoord (columnNumber, calculation.relativeWidthUnit, columnGap, columnTracks); - const auto y = getCoord (rowNumber, calculation.relativeHeightUnit, rowGap, rowTracks); + const auto correctedColumn = columnNumber - 1 + tracks.columns.numImplicitLeading; + const auto correctedRow = rowNumber - 1 + tracks.rows .numImplicitLeading; - const auto& columnTrackInfo = columnTracks.getReference (columnNumber - 1); - const float width = columnTrackInfo.getAbsoluteSize (calculation.relativeWidthUnit); + jassert (isPositiveAndBelow (correctedColumn, tracks.columns.items.size())); + jassert (isPositiveAndBelow (correctedRow, tracks.rows .items.size())); - const auto& rowTrackInfo = rowTracks.getReference (rowNumber - 1); - const float height = rowTrackInfo.getAbsoluteSize (calculation.relativeHeightUnit); - - return { x, y, width, height }; + return { getCoord (correctedColumn, calculation.relativeWidthUnit, columnGap, tracks.columns.items), + getCoord (correctedRow, calculation.relativeHeightUnit, rowGap, tracks.rows .items), + tracks.columns.items.getReference (correctedColumn).getAbsoluteSize (calculation.relativeWidthUnit), + tracks.rows .items.getReference (correctedRow) .getAbsoluteSize (calculation.relativeHeightUnit) }; } static Rectangle alignCell (Rectangle area, - int columnNumber, int rowNumber, - int numberOfColumns, int numberOfRows, - Grid::SizeCalculation calculation, - Grid::AlignContent alignContent, - Grid::JustifyContent justifyContent) + int columnNumber, int rowNumber, + int numberOfColumns, int numberOfRows, + SizeCalculation calculation, + AlignContent alignContent, + JustifyContent justifyContent) { - if (alignContent == Grid::AlignContent::end) + if (alignContent == AlignContent::end) area.setY (area.getY() + calculation.remainingHeight); - if (justifyContent == Grid::JustifyContent::end) + if (justifyContent == JustifyContent::end) area.setX (area.getX() + calculation.remainingWidth); - if (alignContent == Grid::AlignContent::center) + if (alignContent == AlignContent::center) area.setY (area.getY() + calculation.remainingHeight / 2); - if (justifyContent == Grid::JustifyContent::center) + if (justifyContent == JustifyContent::center) area.setX (area.getX() + calculation.remainingWidth / 2); - if (alignContent == Grid::AlignContent::spaceBetween) + if (alignContent == AlignContent::spaceBetween) { const auto shift = ((float) (rowNumber - 1) * (calculation.remainingHeight / float(numberOfRows - 1))); area.setY (area.getY() + shift); } - if (justifyContent == Grid::JustifyContent::spaceBetween) + if (justifyContent == JustifyContent::spaceBetween) { const auto shift = ((float) (columnNumber - 1) * (calculation.remainingWidth / float(numberOfColumns - 1))); area.setX (area.getX() + shift); } - if (alignContent == Grid::AlignContent::spaceEvenly) + if (alignContent == AlignContent::spaceEvenly) { const auto shift = ((float) rowNumber * (calculation.remainingHeight / float(numberOfRows + 1))); area.setY (area.getY() + shift); } - if (justifyContent == Grid::JustifyContent::spaceEvenly) + if (justifyContent == JustifyContent::spaceEvenly) { const auto shift = ((float) columnNumber * (calculation.remainingWidth / float(numberOfColumns + 1))); area.setX (area.getX() + shift); } - if (alignContent == Grid::AlignContent::spaceAround) + if (alignContent == AlignContent::spaceAround) { const auto inbetweenShift = calculation.remainingHeight / float(numberOfRows); const auto sidesShift = inbetweenShift / 2; @@ -471,7 +484,7 @@ struct Grid::PlacementHelpers area.setY (area.getY() + shift); } - if (justifyContent == Grid::JustifyContent::spaceAround) + if (justifyContent == JustifyContent::spaceAround) { const auto inbetweenShift = calculation.remainingWidth / float(numberOfColumns); const auto sidesShift = inbetweenShift / 2; @@ -483,50 +496,49 @@ struct Grid::PlacementHelpers return area; } - static Rectangle getAreaBounds (int columnLineNumberStart, int columnLineNumberEnd, - int rowLineNumberStart, int rowLineNumberEnd, - const Array& columnTracks, - const Array& rowTracks, - Grid::SizeCalculation calculation, - Grid::AlignContent alignContent, - Grid::JustifyContent justifyContent, - Px columnGap, Px rowGap) + static Rectangle getAreaBounds (PlacementHelpers::LineRange columnRange, + PlacementHelpers::LineRange rowRange, + const Tracks& tracks, + SizeCalculation calculation, + AlignContent alignContent, + JustifyContent justifyContent, + Px columnGap, Px rowGap) { - auto startCell = getCellBounds (columnLineNumberStart, rowLineNumberStart, - columnTracks, rowTracks, - calculation, - columnGap, rowGap); - - auto endCell = getCellBounds (columnLineNumberEnd - 1, rowLineNumberEnd - 1, - columnTracks, rowTracks, - calculation, - columnGap, rowGap); - - startCell = alignCell (startCell, - columnLineNumberStart, rowLineNumberStart, - columnTracks.size(), rowTracks.size(), - calculation, - alignContent, - justifyContent); - - endCell = alignCell (endCell, - columnLineNumberEnd - 1, rowLineNumberEnd - 1, - columnTracks.size(), rowTracks.size(), - calculation, - alignContent, - justifyContent); - - auto horizontalRange = startCell.getHorizontalRange().getUnionWith (endCell.getHorizontalRange()); - auto verticalRange = startCell.getVerticalRange().getUnionWith (endCell.getVerticalRange()); + const auto findAlignedCell = [&] (int column, int row) + { + const auto cell = getCellBounds (column, row, tracks, calculation, columnGap, rowGap); + return alignCell (cell, + column, + row, + tracks.columns.items.size(), + tracks.rows.items.size(), + calculation, + alignContent, + justifyContent); + }; + + const auto startCell = findAlignedCell (columnRange.start, rowRange.start); + const auto endCell = findAlignedCell (columnRange.end - 1, rowRange.end - 1); + + const auto horizontalRange = startCell.getHorizontalRange().getUnionWith (endCell.getHorizontalRange()); + const auto verticalRange = startCell.getVerticalRange() .getUnionWith (endCell.getVerticalRange()); return { horizontalRange.getStart(), verticalRange.getStart(), horizontalRange.getLength(), verticalRange.getLength() }; } }; +template +static Array operator+ (const Array& a, const Array& b) +{ + auto copy = a; + copy.addArray (b); + return copy; +} + //============================================================================== struct Grid::AutoPlacement { - using ItemPlacementArray = Array>; + using ItemPlacementArray = Array>; //============================================================================== struct OccupancyPlane @@ -538,7 +550,7 @@ struct Grid::AutoPlacement columnFirst (isColumnFirst) {} - Grid::PlacementHelpers::LineArea setCell (Cell cell, int columnSpan, int rowSpan) + PlacementHelpers::LineArea setCell (Cell cell, int columnSpan, int rowSpan) { for (int i = 0; i < columnSpan; i++) for (int j = 0; j < rowSpan; j++) @@ -547,7 +559,7 @@ struct Grid::AutoPlacement return { { cell.column, cell.column + columnSpan }, { cell.row, cell.row + rowSpan } }; } - Grid::PlacementHelpers::LineArea setCell (Cell start, Cell end) + PlacementHelpers::LineArea setCell (Cell start, Cell end) { return setCell (start, std::abs (end.column - start.column), std::abs (end.row - start.row)); @@ -703,16 +715,16 @@ struct Grid::AutoPlacement } //============================================================================== - static bool hasDenseAutoFlow (Grid::AutoFlow autoFlow) + static bool hasDenseAutoFlow (AutoFlow autoFlow) { - return autoFlow == Grid::AutoFlow::columnDense - || autoFlow == Grid::AutoFlow::rowDense; + return autoFlow == AutoFlow::columnDense + || autoFlow == AutoFlow::rowDense; } - static bool isColumnAutoFlow (Grid::AutoFlow autoFlow) + static bool isColumnAutoFlow (AutoFlow autoFlow) { - return autoFlow == Grid::AutoFlow::column - || autoFlow == Grid::AutoFlow::columnDense; + return autoFlow == AutoFlow::column + || autoFlow == AutoFlow::columnDense; } //============================================================================== @@ -730,7 +742,7 @@ struct Grid::AutoPlacement //============================================================================== ItemPlacementArray deduceAllItems (Grid& grid) const { - const auto namedAreas = Grid::PlacementHelpers::deduceNamedAreas (grid.templateAreas); + const auto namedAreas = PlacementHelpers::deduceNamedAreas (grid.templateAreas); OccupancyPlane plane (jmax (grid.templateColumns.size() + 1, 2), jmax (grid.templateRows.size() + 1, 2), @@ -750,7 +762,7 @@ struct Grid::AutoPlacement { if (hasFullyFixedPlacement (*item)) { - const auto a = Grid::PlacementHelpers::deduceLineArea (*item, grid, namedAreas); + const auto a = PlacementHelpers::deduceLineArea (*item, grid, namedAreas); plane.setCell ({ a.column.start, a.row.start }, { a.column.end, a.row.end }); itemPlacementArray.add ({ item, a }); } @@ -764,7 +776,7 @@ struct Grid::AutoPlacement { if (isFixed (item->column)) { - const auto p = Grid::PlacementHelpers::deduceLineRange (item->column, grid.templateColumns); + const auto p = PlacementHelpers::deduceLineRange (item->column, grid.templateColumns); const auto columnSpan = std::abs (p.start - p.end); const auto rowSpan = getSpanFromAuto (item->row); @@ -778,7 +790,7 @@ struct Grid::AutoPlacement } else if (isFixed (item->row)) { - const auto p = Grid::PlacementHelpers::deduceLineRange (item->row, grid.templateRows); + const auto p = PlacementHelpers::deduceLineRange (item->row, grid.templateRows); const auto columnSpan = getSpanFromAuto (item->column); const auto rowSpan = std::abs (p.start - p.end); @@ -818,72 +830,84 @@ struct Grid::AutoPlacement } //============================================================================== - static std::pair getHighestEndLinesNumbers (const ItemPlacementArray& items) + template + static PlacementHelpers::LineRange findFullLineRange (const ItemPlacementArray& items, Accessor&& accessor) { - int columnEndLine = 1; - int rowEndLine = 1; + if (items.isEmpty()) + return { 1, 1 }; - for (auto& item : items) + const auto combine = [&accessor] (const auto& acc, const auto& item) { - const auto p = item.second; - columnEndLine = std::max (p.column.end, columnEndLine); - rowEndLine = std::max (p.row.end, rowEndLine); - } + const auto newRange = accessor (item); + return PlacementHelpers::LineRange { std::min (acc.start, newRange.start), + std::max (acc.end, newRange.end) }; + }; - return { columnEndLine, rowEndLine }; + return std::accumulate (std::next (items.begin()), items.end(), accessor (*items.begin()), combine); } - static std::pair, Array> createImplicitTracks (const Grid& grid, - const ItemPlacementArray& items) + static PlacementHelpers::LineArea findFullLineArea (const ItemPlacementArray& items) { - const auto columnAndRowLineEnds = getHighestEndLinesNumbers (items); - - Array implicitColumnTracks, implicitRowTracks; - - for (int i = grid.templateColumns.size() + 1; i < columnAndRowLineEnds.first; i++) - implicitColumnTracks.add (grid.autoColumns); - - for (int i = grid.templateRows.size() + 1; i < columnAndRowLineEnds.second; i++) - implicitRowTracks.add (grid.autoRows); + return { findFullLineRange (items, [] (const auto& item) { return item.second.column; }), + findFullLineRange (items, [] (const auto& item) { return item.second.row; }) }; + } - return { implicitColumnTracks, implicitRowTracks }; + template + static Array repeated (int repeats, const Item& item) + { + Array result; + result.insertMultiple (-1, item, repeats); + return result; } - //============================================================================== - static void applySizeForAutoTracks (Array& columns, - Array& rows, - const ItemPlacementArray& itemPlacementArray) + static Tracks createImplicitTracks (const Grid& grid, const ItemPlacementArray& items) { - auto isSpan = [] (Grid::PlacementHelpers::LineRange r) -> bool { return std::abs (r.end - r.start) > 1; }; + const auto fullArea = findFullLineArea (items); - auto getHighestItemOnRow = [isSpan] (int rowNumber, const ItemPlacementArray& itemPlacementArrayToUse) -> float - { - float highestRowSize = 0.0f; + const auto leadingColumns = std::max (0, 1 - fullArea.column.start); + const auto leadingRows = std::max (0, 1 - fullArea.row.start); - for (const auto& i : itemPlacementArrayToUse) - if (! isSpan (i.second.row) && i.second.row.start == rowNumber) - highestRowSize = std::max (highestRowSize, i.first->height + i.first->margin.top + i.first->margin.bottom); + const auto trailingColumns = std::max (0, fullArea.column.end - grid.templateColumns.size() - 1); + const auto trailingRows = std::max (0, fullArea.row .end - grid.templateRows .size() - 1); - return highestRowSize; - }; + return { { repeated (leadingColumns, grid.autoColumns) + grid.templateColumns + repeated (trailingColumns, grid.autoColumns), + leadingColumns }, + { repeated (leadingRows, grid.autoRows) + grid.templateRows + repeated (trailingRows, grid.autoRows), + leadingRows } }; + } - auto getHighestItemOnColumn = [isSpan] (int rowNumber, const ItemPlacementArray& itemPlacementArrayToUse) -> float + //============================================================================== + static void applySizeForAutoTracks (Tracks& tracks, const ItemPlacementArray& placements) + { + const auto setSizes = [&placements] (auto& tracksInDirection, const auto& getItem, const auto& getItemSize) { - float highestColumnSize = 0.0f; - for (const auto& i : itemPlacementArrayToUse) - if (! isSpan (i.second.column) && i.second.column.start == rowNumber) - highestColumnSize = std::max (highestColumnSize, i.first->width + i.first->margin.left + i.first->margin.right); + auto& array = tracksInDirection.items; - return highestColumnSize; + for (int index = 0; index < array.size(); ++index) + { + if (array.getReference (index).isAuto()) + { + const auto combiner = [&] (const auto acc, const auto& element) + { + const auto item = getItem (element.second); + const auto isNotSpan = std::abs (item.end - item.start) <= 1; + return isNotSpan && item.start == index + 1 - tracksInDirection.numImplicitLeading + ? std::max (acc, getItemSize (*element.first)) + : acc; + }; + + array.getReference (index).size = std::accumulate (placements.begin(), placements.end(), 0.0f, combiner); + } + } }; - for (int i = 0; i < rows.size(); i++) - if (rows.getReference (i).isAuto()) - rows.getReference (i).size = getHighestItemOnRow (i + 1, itemPlacementArray); + setSizes (tracks.rows, + [] (const auto& i) { return i.row; }, + [] (const auto& i) { return i.height + i.margin.top + i.margin.bottom; }); - for (int i = 0; i < columns.size(); i++) - if (columns.getReference (i).isAuto()) - columns.getReference (i).size = getHighestItemOnColumn (i + 1, itemPlacementArray); + setSizes (tracks.columns, + [] (const auto& i) { return i.column; }, + [] (const auto& i) { return i.width + i.margin.left + i.margin.right; }); } }; @@ -891,22 +915,17 @@ struct Grid::AutoPlacement struct Grid::BoxAlignment { static Rectangle alignItem (const GridItem& item, - const Grid& grid, - Rectangle area) + const Grid& grid, + Rectangle area) { // if item align is auto, inherit value from grid - Grid::AlignItems alignType = Grid::AlignItems::start; - Grid::JustifyItems justifyType = Grid::JustifyItems::start; + const auto alignType = item.alignSelf == GridItem::AlignSelf::autoValue + ? grid.alignItems + : static_cast (item.alignSelf); - if (item.alignSelf == GridItem::AlignSelf::autoValue) - alignType = grid.alignItems; - else - alignType = static_cast (item.alignSelf); - - if (item.justifySelf == GridItem::JustifySelf::autoValue) - justifyType = grid.justifyItems; - else - justifyType = static_cast (item.justifySelf); + const auto justifyType = item.justifySelf == GridItem::JustifySelf::autoValue + ? grid.justifyItems + : static_cast (item.justifySelf); // subtract margin from area area = BorderSize (item.margin.top, item.margin.left, item.margin.bottom, item.margin.right) @@ -922,13 +941,13 @@ struct Grid::BoxAlignment if (item.maxHeight != (float) GridItem::notAssigned) r.setHeight (jmin (item.maxHeight, r.getHeight())); if (item.minHeight > 0.0f) r.setHeight (jmax (item.minHeight, r.getHeight())); - if (alignType == Grid::AlignItems::start && justifyType == Grid::JustifyItems::start) + if (alignType == AlignItems::start && justifyType == JustifyItems::start) return r; - if (alignType == Grid::AlignItems::end) r.setY (r.getY() + (area.getHeight() - r.getHeight())); - if (justifyType == Grid::JustifyItems::end) r.setX (r.getX() + (area.getWidth() - r.getWidth())); - if (alignType == Grid::AlignItems::center) r.setCentre (r.getCentreX(), area.getCentreY()); - if (justifyType == Grid::JustifyItems::center) r.setCentre (area.getCentreX(), r.getCentreY()); + if (alignType == AlignItems::end) r.setY (r.getY() + (area.getHeight() - r.getHeight())); + if (justifyType == JustifyItems::end) r.setX (r.getX() + (area.getWidth() - r.getWidth())); + if (alignType == AlignItems::center) r.setCentre (r.getCentreX(), area.getCentreY()); + if (justifyType == JustifyItems::center) r.setCentre (area.getCentreX(), r.getCentreY()); return r; } @@ -937,90 +956,83 @@ struct Grid::BoxAlignment //============================================================================== Grid::TrackInfo::TrackInfo() noexcept : hasKeyword (true) {} -Grid::TrackInfo::TrackInfo (Px sizeInPixels) noexcept : size (static_cast (sizeInPixels.pixels)), isFraction (false) {} +Grid::TrackInfo::TrackInfo (Px sizeInPixels) noexcept + : size (static_cast (sizeInPixels.pixels)), isFraction (false) {} -Grid::TrackInfo::TrackInfo (Fr fractionOfFreeSpace) noexcept : size ((float)fractionOfFreeSpace.fraction), isFraction (true) {} +Grid::TrackInfo::TrackInfo (Fr fractionOfFreeSpace) noexcept + : size ((float)fractionOfFreeSpace.fraction), isFraction (true) {} -Grid::TrackInfo::TrackInfo (Px sizeInPixels, const String& endLineNameToUse) noexcept : Grid::TrackInfo (sizeInPixels) +Grid::TrackInfo::TrackInfo (Px sizeInPixels, const String& endLineNameToUse) noexcept + : TrackInfo (sizeInPixels) { endLineName = endLineNameToUse; } -Grid::TrackInfo::TrackInfo (Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept : Grid::TrackInfo (fractionOfFreeSpace) +Grid::TrackInfo::TrackInfo (Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept + : TrackInfo (fractionOfFreeSpace) { endLineName = endLineNameToUse; } -Grid::TrackInfo::TrackInfo (const String& startLineNameToUse, Px sizeInPixels) noexcept : Grid::TrackInfo (sizeInPixels) +Grid::TrackInfo::TrackInfo (const String& startLineNameToUse, Px sizeInPixels) noexcept + : TrackInfo (sizeInPixels) { startLineName = startLineNameToUse; } -Grid::TrackInfo::TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace) noexcept : Grid::TrackInfo (fractionOfFreeSpace) +Grid::TrackInfo::TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace) noexcept + : TrackInfo (fractionOfFreeSpace) { startLineName = startLineNameToUse; } Grid::TrackInfo::TrackInfo (const String& startLineNameToUse, Px sizeInPixels, const String& endLineNameToUse) noexcept - : Grid::TrackInfo (startLineNameToUse, sizeInPixels) + : TrackInfo (startLineNameToUse, sizeInPixels) { endLineName = endLineNameToUse; } Grid::TrackInfo::TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept - : Grid::TrackInfo (startLineNameToUse, fractionOfFreeSpace) + : TrackInfo (startLineNameToUse, fractionOfFreeSpace) { endLineName = endLineNameToUse; } float Grid::TrackInfo::getAbsoluteSize (float relativeFractionalUnit) const { - if (isFractional()) - return size * relativeFractionalUnit; - else - return size; + return isFractional() ? size * relativeFractionalUnit : size; } -//============================================================================== -Grid::Grid() noexcept {} -Grid::~Grid() noexcept {} - //============================================================================== void Grid::performLayout (Rectangle targetArea) { - const auto itemsAndAreas = Grid::AutoPlacement().deduceAllItems (*this); + const auto itemsAndAreas = AutoPlacement().deduceAllItems (*this); - const auto implicitTracks = Grid::AutoPlacement::createImplicitTracks (*this, itemsAndAreas); - auto columnTracks = templateColumns; - auto rowTracks = templateRows; - columnTracks.addArray (implicitTracks.first); - rowTracks.addArray (implicitTracks.second); + auto implicitTracks = AutoPlacement::createImplicitTracks (*this, itemsAndAreas); - Grid::AutoPlacement::applySizeForAutoTracks (columnTracks, rowTracks, itemsAndAreas); + AutoPlacement::applySizeForAutoTracks (implicitTracks, itemsAndAreas); - Grid::SizeCalculation calculation; + SizeCalculation calculation; calculation.computeSizes (targetArea.toFloat().getWidth(), targetArea.toFloat().getHeight(), columnGap, rowGap, - columnTracks, - rowTracks); + implicitTracks); for (auto& itemAndArea : itemsAndAreas) { const auto a = itemAndArea.second; - const auto areaBounds = Grid::PlacementHelpers::getAreaBounds (a.column.start, a.column.end, - a.row.start, a.row.end, - columnTracks, - rowTracks, - calculation, - alignContent, - justifyContent, - columnGap, - rowGap); + const auto areaBounds = PlacementHelpers::getAreaBounds (a.column, + a.row, + implicitTracks, + calculation, + alignContent, + justifyContent, + columnGap, + rowGap); auto* item = itemAndArea.first; - item->currentBounds = Grid::BoxAlignment::alignItem (*item, *this, areaBounds) + item->currentBounds = BoxAlignment::alignItem (*item, *this, areaBounds) + targetArea.toFloat().getPosition(); if (auto* c = item->associatedComponent) @@ -1043,6 +1055,13 @@ struct GridTests : public UnitTest using Tr = Grid::TrackInfo; using Rect = Rectangle; + beginTest ("Layout calculation of an empty grid is a no-op"); + { + const Rectangle bounds { 100, 200 }; + Grid grid; + grid.performLayout (bounds); + } + { Grid grid; @@ -1164,6 +1183,23 @@ struct GridTests : public UnitTest expect (grid.items[4].currentBounds == Rect (0.0f, 0.0f, 140.0f, 80.0f)); } + { + Grid grid; + + grid.templateColumns = { Tr ("first", 20_px, "in"), Tr ("in", 1_fr, "in"), Tr (20_px, "last") }; + grid.templateRows = { Tr (1_fr), Tr (20_px) }; + + beginTest ("Grid items placement tests: integer, counting backward"); + + grid.items = { GridItem{}.withColumn ({ -2, -1 }).withRow ({ 1, 3 }), + GridItem{}.withColumn ({ -10, -1 }).withRow ({ 1, -1 }) }; + + grid.performLayout ({ 140, 100 }); + + expect (grid.items[0].currentBounds == Rect (120.0f, 0.0f, 20.0f, 100.0f)); + expect (grid.items[1].currentBounds == Rect (0.0f, 0.0f, 140.0f, 100.0f)); + } + { beginTest ("Grid items placement tests: areas"); @@ -1259,6 +1295,55 @@ struct GridTests : public UnitTest expect (grid.items[4].currentBounds == Rect (250.f, 150.f, 100.f, 100.f)); } + { + beginTest ("Grid implicit rows and columns: triggered by out-of-bounds indices"); + + Grid grid; + + grid.templateColumns = { Tr (1_fr), Tr (1_fr) }; + grid.templateRows = { Tr (60_px), Tr (60_px) }; + + grid.autoColumns = Tr (20_px); + grid.autoRows = Tr (1_fr); + + grid.items = { GridItem{}.withColumn ({ 5, 8 }).withRow ({ -5, -4 }), + GridItem{}.withColumn ({ 4, 7 }).withRow ({ -4, -3 }), + GridItem{}.withColumn ({ -2, -1 }).withRow ({ 4, 5 }) }; + + grid.performLayout ({ 500, 400 }); + + // -3 -2 -1 + // 1 2 3 4 5 6 7 8 + // -5 +---+---+---+---+---+---+---+ 0 + // | | | | | 0 | 0 | 0 | + // -4 +---+---+---+---+---+---+---+ 70 + // | | | | 1 | 1 | 1 | | + // -3 1 +---+---+---+---+---+---+---+ 140 + // | x | x | | | | | | + // -2 2 +---+---+---+---+---+---+---+ 200 y positions + // | x | x | | | | | | + // -1 3 +---+---+---+---+---+---+---+ 260 + // | | | | | | | | + // 4 +---+---+---+---+---+---+---+ 330 + // | | 2 | | | | | | + // 5 +---+---+---+---+---+---+---+ 400 + // + // 0 200 400 420 440 460 480 500 + // x positions + // + // The cells marked "x" are the explicit cells specified by the template rows + // and columns. + // + // The cells marked 0/1/2 correspond to the GridItems at those indices in the + // items array. + // + // Note that negative indices count back from the last explicit line + // number in that direction, so "2" and "-2" both correspond to the same line. + + expect (grid.items[0].currentBounds == Rect (440.0f, 0.0f, 60.0f, 70.0f)); + expect (grid.items[1].currentBounds == Rect (420.0f, 70.0f, 60.0f, 70.0f)); + expect (grid.items[2].currentBounds == Rect (200.0f, 330.0f, 200.0f, 70.0f)); + } } }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.h b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.h index 0abca3fa5..412c05529 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.h @@ -154,10 +154,10 @@ class JUCE_API Grid final //============================================================================== /** Creates an empty Grid container with default parameters. */ - Grid() noexcept; + Grid() = default; /** Destructor */ - ~Grid() noexcept; + ~Grid() noexcept = default; //============================================================================== /** Specifies the alignment of content inside the items along the rows. */ diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp index 6df22ce1e..76a2c5345 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp @@ -95,7 +95,12 @@ MultiDocumentPanel::MultiDocumentPanel() setOpaque (true); } -MultiDocumentPanel::~MultiDocumentPanel() = default; +MultiDocumentPanel::~MultiDocumentPanel() +{ + for (int i = components.size(); --i >= 0;) + if (auto* component = components[i]) + closeDocumentInternal (component); +} //============================================================================== namespace MultiDocHelpers @@ -407,6 +412,8 @@ void MultiDocumentPanel::closeDocumentAsync (Component* component, return; } + + closeDocumentInternal (component); } else { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h index bcbf61c50..00fe20b78 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h @@ -100,10 +100,11 @@ class JUCE_API MultiDocumentPanel : public Component, /** Destructor. - When deleted, this will call closeAllDocuments (false) to make sure all its + When deleted, this will call close all open documents to make sure all its components are deleted. If you need to make sure all documents are saved - before closing, then you should call closeAllDocuments (true) and check that - it returns true before deleting the panel. + before closing, then you should call closeAllDocumentsAsync() with + checkItsOkToCloseFirst == true and check the provided callback result is true + before deleting the panel. */ ~MultiDocumentPanel() override; @@ -133,7 +134,7 @@ class JUCE_API MultiDocumentPanel : public Component, If checkItsOkToCloseFirst is false, then all documents will be closed unconditionally. - @see closeDocument + @see closeDocumentAsync */ void closeAllDocumentsAsync (bool checkItsOkToCloseFirst, std::function callback); @@ -151,8 +152,8 @@ class JUCE_API MultiDocumentPanel : public Component, @param component the component to add @param backgroundColour the background colour to use to fill the component's window or tab - @param deleteWhenRemoved if true, then when the component is removed by closeDocument() - or closeAllDocuments(), then it will be deleted. If false, then + @param deleteWhenRemoved if true, then when the component is removed by closeDocumentAsync() + or closeAllDocumentsAsync(), then it will be deleted. If false, then the caller must handle the component's deletion */ bool addDocument (Component* component, @@ -190,7 +191,7 @@ class JUCE_API MultiDocumentPanel : public Component, The component will be deleted if the deleteWhenRemoved parameter was set to true when it was added with addDocument. - @see addDocument, closeAllDocuments + @see addDocument, closeAllDocumentsAsync */ void closeDocumentAsync (Component* component, bool checkItsOkToCloseFirst, diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp index c3cd8046e..a0a61a047 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp @@ -222,7 +222,7 @@ struct Viewport::DragToScrollListener : private MouseListener, (int) offsetY.getPosition())); } - void mouseDown (const MouseEvent&) override + void mouseDown (const MouseEvent& e) override { if (! isGlobalMouseListener) { @@ -235,12 +235,15 @@ struct Viewport::DragToScrollListener : private MouseListener, Desktop::getInstance().addGlobalMouseListener (this); isGlobalMouseListener = true; + + scrollSource = e.source; } } void mouseDrag (const MouseEvent& e) override { - if (Desktop::getInstance().getNumDraggingMouseSources() == 1 && ! doesMouseEventComponentBlockViewportDrag (e.eventComponent)) + if (e.source == scrollSource + && ! doesMouseEventComponentBlockViewportDrag (e.eventComponent)) { auto totalOffset = e.getOffsetFromDragStart().toFloat(); @@ -263,9 +266,9 @@ struct Viewport::DragToScrollListener : private MouseListener, } } - void mouseUp (const MouseEvent&) override + void mouseUp (const MouseEvent& e) override { - if (isGlobalMouseListener && Desktop::getInstance().getNumDraggingMouseSources() == 0) + if (isGlobalMouseListener && e.source == scrollSource) endDragAndClearGlobalMouseListener(); } @@ -293,6 +296,7 @@ struct Viewport::DragToScrollListener : private MouseListener, Viewport& viewport; ViewportDragPosition offsetX, offsetY; Point originalViewPos; + MouseInputSource scrollSource = Desktop::getInstance().getMainMouseSource(); bool isDragging = false; bool isGlobalMouseListener = false; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp index d4aa74721..d7798a73b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp @@ -241,10 +241,16 @@ void MenuBarComponent::showMenu (int index) auto itemBounds = itemComponent->getBounds(); + const auto callback = [ref = SafePointer (this), index] (int result) + { + if (ref != nullptr) + ref->menuDismissed (index, result); + }; + m.showMenuAsync (PopupMenu::Options().withTargetComponent (this) .withTargetScreenArea (localAreaToGlobal (itemBounds)) .withMinimumWidth (itemBounds.getWidth()), - [this, index] (int result) { menuDismissed (index, result); }); + callback); } } } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index a8f6c2922..951542abd 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -300,16 +300,16 @@ struct MenuWindow : public Component MenuWindow (const PopupMenu& menu, MenuWindow* parentWindow, Options opts, bool alignToRectangle, bool shouldDismissOnMouseUp, ApplicationCommandManager** manager, float parentScaleFactor = 1.0f) - : Component ("menu"), - parent (parentWindow), - options (std::move (opts)), - managerOfChosenCommand (manager), - componentAttachedTo (options.getTargetComponent()), - dismissOnMouseUp (shouldDismissOnMouseUp), - windowCreationTime (Time::getMillisecondCounter()), - lastFocusedTime (windowCreationTime), - timeEnteredCurrentChildComp (windowCreationTime), - scaleFactor (parentWindow != nullptr ? parentScaleFactor : 1.0f) + : Component ("menu"), + parent (parentWindow), + options (opts.withParentComponent (getLookAndFeel().getParentComponentForMenuOptions (opts))), + managerOfChosenCommand (manager), + componentAttachedTo (options.getTargetComponent()), + dismissOnMouseUp (shouldDismissOnMouseUp), + windowCreationTime (Time::getMillisecondCounter()), + lastFocusedTime (windowCreationTime), + timeEnteredCurrentChildComp (windowCreationTime), + scaleFactor (parentWindow != nullptr ? parentScaleFactor : 1.0f) { setWantsKeyboardFocus (false); setMouseClickGrabsKeyboardFocus (false); @@ -321,12 +321,9 @@ struct MenuWindow : public Component auto& lf = getLookAndFeel(); - parentComponent = lf.getParentComponentForMenuOptions (options); - const_cast(options) = options.withParentComponent (parentComponent); - - if (parentComponent != nullptr) + if (auto* pc = options.getParentComponent()) { - parentComponent->addChildComponent (this); + pc->addChildComponent (this); } else { @@ -348,7 +345,7 @@ struct MenuWindow : public Component Desktop::getInstance().addGlobalMouseListener (this); } - if (parentComponent == nullptr && parentWindow == nullptr && lf.shouldPopupMenuScaleWithTargetComponent (options)) + if (options.getParentComponent() == nullptr && parentWindow == nullptr && lf.shouldPopupMenuScaleWithTargetComponent (options)) if (auto* targetComponent = options.getTargetComponent()) scaleFactor = Component::getApproximateScaleFactorForComponent (targetComponent); @@ -374,7 +371,6 @@ struct MenuWindow : public Component calculateWindowPos (targetArea, alignToRectangle); setTopLeftPosition (windowPos.getPosition()); - updateYPositions(); if (auto visibleID = options.getItemThatMustBeVisible()) { @@ -382,8 +378,13 @@ struct MenuWindow : public Component { if (item->item.itemID == visibleID) { - auto targetPosition = parentComponent != nullptr ? parentComponent->getLocalPoint (nullptr, targetArea.getTopLeft()) - : targetArea.getTopLeft(); + const auto targetPosition = [&] + { + if (auto* pc = options.getParentComponent()) + return pc->getLocalPoint (nullptr, targetArea.getTopLeft()); + + return targetArea.getTopLeft(); + }(); auto y = targetPosition.getY() - windowPos.getY(); ensureItemComponentIsVisible (*item, isPositiveAndBelow (y, windowPos.getHeight()) ? y : -1); @@ -441,7 +442,7 @@ struct MenuWindow : public Component { auto& lf = getLookAndFeel(); - if (parentComponent != nullptr) + if (options.getParentComponent()) lf.drawResizableFrame (g, getWidth(), getHeight(), BorderSize (getLookAndFeel().getPopupMenuBorderSizeWithOptions (options))); @@ -781,28 +782,26 @@ struct MenuWindow : public Component if (relativeTo != nullptr) targetPoint = relativeTo->localPointToGlobal (targetPoint); - auto parentArea = Desktop::getInstance().getDisplays().getDisplayForPoint (targetPoint * scaleFactor) - #if JUCE_MAC || JUCE_ANDROID - ->userArea; - #else - ->totalArea; // on windows, don't stop the menu overlapping the taskbar - #endif + auto* display = Desktop::getInstance().getDisplays().getDisplayForPoint (targetPoint * scaleFactor); + auto parentArea = display->safeAreaInsets.subtractedFrom (display->totalArea); - if (parentComponent == nullptr) - return parentArea; + if (auto* pc = options.getParentComponent()) + { + return pc->getLocalArea (nullptr, + pc->getScreenBounds() + .reduced (getLookAndFeel().getPopupMenuBorderSizeWithOptions (options)) + .getIntersection (parentArea)); + } - return parentComponent->getLocalArea (nullptr, - parentComponent->getScreenBounds() - .reduced (getLookAndFeel().getPopupMenuBorderSizeWithOptions (options)) - .getIntersection (parentArea)); + return parentArea; } void calculateWindowPos (Rectangle target, const bool alignToRectangle) { auto parentArea = getParentArea (target.getCentre()) / scaleFactor; - if (parentComponent != nullptr) - target = parentComponent->getLocalArea (nullptr, target).getIntersection (parentArea); + if (auto* pc = options.getParentComponent()) + target = pc->getLocalArea (nullptr, target).getIntersection (parentArea); auto maxMenuHeight = parentArea.getHeight() - 24; @@ -1032,7 +1031,7 @@ struct MenuWindow : public Component windowPos.getHeight() - (PopupMenuSettings::scrollZone + itemComp.getHeight())), currentY); - auto parentArea = getParentArea (windowPos.getPosition(), parentComponent) / scaleFactor; + auto parentArea = getParentArea (windowPos.getPosition(), options.getParentComponent()) / scaleFactor; auto deltaY = wantedY - currentY; windowPos.setSize (jmin (windowPos.getWidth(), parentArea.getWidth()), @@ -1163,8 +1162,7 @@ struct MenuWindow : public Component activeSubMenu.reset (new HelperClasses::MenuWindow (*(childComp->item.subMenu), this, options.withTargetScreenArea (childComp->getScreenBounds()) .withMinimumWidth (0) - .withTargetComponent (nullptr) - .withParentComponent (parentComponent), + .withTargetComponent (nullptr), false, dismissOnMouseUp, managerOfChosenCommand, scaleFactor)); activeSubMenu->setVisible (true); // (must be called before enterModalState on Windows to avoid DropShadower confusion) @@ -1267,7 +1265,6 @@ struct MenuWindow : public Component OwnedArray items; ApplicationCommandManager** managerOfChosenCommand; WeakReference componentAttachedTo; - Component* parentComponent = nullptr; Rectangle windowPos; bool hasBeenOver = false, needsToScroll = false; bool dismissOnMouseUp, hideOnExit = false, disableMouseMoves = false, hasAnyJuceCompHadFocus = false; @@ -1932,6 +1929,11 @@ PopupMenu::Options PopupMenu::Options::withTargetScreenArea (Rectangle area return with (*this, &Options::targetArea, area); } +PopupMenu::Options PopupMenu::Options::withMousePosition() const +{ + return withTargetScreenArea (Rectangle{}.withPosition (Desktop::getMousePosition())); +} + PopupMenu::Options PopupMenu::Options::withDeletionCheck (Component& comp) const { return with (with (*this, &Options::isWatchingForDeletion, true), @@ -2011,38 +2013,29 @@ struct PopupMenuCompletionCallback : public ModalComponentManager::Callback if (PopupMenuSettings::menuWasHiddenBecauseOfAppChange) return; - auto* focusComponent = getComponentToPassFocusTo(); - - const auto focusedIsNotMinimised = [focusComponent] + if (auto* focusComponent = Component::getCurrentlyFocusedComponent()) { - if (focusComponent != nullptr) + const auto focusedIsNotMinimised = [focusComponent] + { if (auto* peer = focusComponent->getPeer()) return ! peer->isMinimised(); - return false; - }(); + return false; + }(); - if (focusedIsNotMinimised) - { - if (auto* topLevel = focusComponent->getTopLevelComponent()) - topLevel->toFront (true); + if (focusedIsNotMinimised) + { + if (auto* topLevel = focusComponent->getTopLevelComponent()) + topLevel->toFront (true); - if (focusComponent->isShowing() && ! focusComponent->hasKeyboardFocus (true)) - focusComponent->grabKeyboardFocus(); + if (focusComponent->isShowing() && ! focusComponent->hasKeyboardFocus (true)) + focusComponent->grabKeyboardFocus(); + } } } - Component* getComponentToPassFocusTo() const - { - if (auto* current = Component::getCurrentlyFocusedComponent()) - return current; - - return prevFocused.get(); - } - ApplicationCommandManager* managerOfChosenCommand = nullptr; std::unique_ptr component; - WeakReference prevFocused { Component::getCurrentlyFocusedComponent() }; JUCE_DECLARE_NON_COPYABLE (PopupMenuCompletionCallback) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h index b71cb9806..f731b349c 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h @@ -350,7 +350,7 @@ class JUCE_API PopupMenu menu ID specified in itemResultID. If this is false, the menu item can't be triggered, so itemResultID is not used. - Note that native macOS menus do support custom components. + Note that native macOS menus do not support custom components. */ void addCustomItem (int itemResultID, Component& customComponent, @@ -448,7 +448,9 @@ class JUCE_API PopupMenu class JUCE_API Options { public: + /** By default, the target screen area will be the current mouse position. */ Options(); + Options (const Options&) = default; Options& operator= (const Options&) = default; @@ -459,38 +461,170 @@ class JUCE_API PopupMenu }; //============================================================================== + /** Sets the target component to use when displaying the menu. + + This is normally the button or other control that triggered the menu. + + The target component is primarily used to control the scale of the menu, so + it's important to supply a target component if you'll be using your program + on hi-DPI displays. + + This function will also set the target screen area, so that the menu displays + next to the target component. If you need to display the menu at a specific + location, you should call withTargetScreenArea() after withTargetComponent. + + @see withTargetComponent, withTargetScreenArea + */ Options withTargetComponent (Component* targetComponent) const; Options withTargetComponent (Component& targetComponent) const; + + /** Sets the region of the screen next to which the menu should be displayed. + + To display the menu next to the mouse cursor use withMousePosition(), + which is equivalent to passing the following to this function: + @code + Rectangle{}.withPosition (Desktop::getMousePosition()) + @endcode + + withTargetComponent() will also set the target screen area. If you need + a target component and a target screen area, make sure to call + withTargetScreenArea() after withTargetComponent(). + + @see withMousePosition + */ Options withTargetScreenArea (Rectangle targetArea) const; + + /** Sets the target screen area to match the current mouse position. + + Make sure to call this after withTargetComponent(). + + @see withTargetScreenArea + */ + Options withMousePosition() const; + + /** If the passed component has been deleted when the popup menu exits, + the selected item's action will not be called. + + This is useful for avoiding dangling references inside the action + callback, in the case that the callback needs to access a component that + may be deleted. + */ Options withDeletionCheck (Component& componentToWatchForDeletion) const; + + /** Sets the minimum width of the popup window. */ Options withMinimumWidth (int minWidth) const; + + /** Sets the minimum number of columns in the popup window. */ Options withMinimumNumColumns (int minNumColumns) const; + + /** Sets the maximum number of columns in the popup window. */ Options withMaximumNumColumns (int maxNumColumns) const; + + /** Sets the default height of each item in the popup menu. */ Options withStandardItemHeight (int standardHeight) const; + + /** Sets an item which must be visible when the menu is initially drawn. + + This is useful to ensure that a particular item is shown when the menu + contains too many items to display on a single screen. + */ Options withItemThatMustBeVisible (int idOfItemToBeVisible) const; + + /** Sets a component that the popup menu will be drawn into. + + Some plugin formats, such as AUv3, dislike it when the plugin editor + spawns additional windows. Some AUv3 hosts display pink backgrounds + underneath transparent popup windows, which is confusing and can appear + as though the plugin is malfunctioning. Setting a parent component will + avoid this unwanted behaviour, but with the downside that the menu size + will be constrained by the size of the parent component. + */ Options withParentComponent (Component* parentComponent) const; + + /** Sets the direction of the popup menu relative to the target screen area. */ Options withPreferredPopupDirection (PopupDirection direction) const; + + /** Sets an item to select in the menu. + + This is useful for controls such as combo boxes, where opening the combo box + with the keyboard should ideally highlight the currently-selected item, allowing + the next/previous item to be selected by pressing up/down on the keyboard, rather + than needing to move the highlighted row down from the top of the menu each time + it is opened. + */ Options withInitiallySelectedItem (int idOfItemToBeSelected) const; //============================================================================== + /** Gets the parent component. This may be nullptr if the Component has been deleted. + + @see withParentComponent + */ Component* getParentComponent() const noexcept { return parentComponent; } + + /** Gets the target component. This may be nullptr if the Component has been deleted. + + @see withTargetComponent + */ Component* getTargetComponent() const noexcept { return targetComponent; } + + /** Returns true if the menu was watching a component, and that component has been deleted, and false otherwise. + + @see withDeletionCheck + */ bool hasWatchedComponentBeenDeleted() const noexcept { return isWatchingForDeletion && componentToWatchForDeletion == nullptr; } + + /** Gets the target screen area. + + @see withTargetScreenArea + */ Rectangle getTargetScreenArea() const noexcept { return targetArea; } + + /** Gets the minimum width. + + @see withMinimumWidth + */ int getMinimumWidth() const noexcept { return minWidth; } + + /** Gets the maximum number of columns. + + @see withMaximumNumColumns + */ int getMaximumNumColumns() const noexcept { return maxColumns; } + + /** Gets the minimum number of columns. + + @see withMinimumNumColumns + */ int getMinimumNumColumns() const noexcept { return minColumns; } + + /** Gets the default height of items in the menu. + + @see withStandardItemHeight + */ int getStandardItemHeight() const noexcept { return standardHeight; } + + /** Gets the ID of the item that must be visible when the menu is initially shown. + + @see withItemThatMustBeVisible + */ int getItemThatMustBeVisible() const noexcept { return visibleItemID; } + + /** Gets the preferred popup menu direction. + + @see withPreferredPopupDirection + */ PopupDirection getPreferredPopupDirection() const noexcept { return preferredPopupDirection; } + + /** Gets the ID of the item that must be selected when the menu is initially shown. + + @see withItemThatMustBeVisible + */ int getInitiallySelectedItemId() const noexcept { return initiallySelectedItemId; } private: //============================================================================== Rectangle targetArea; - Component* targetComponent = nullptr; - Component* parentComponent = nullptr; - WeakReference componentToWatchForDeletion; + WeakReference targetComponent, parentComponent, componentToWatchForDeletion; int visibleItemID = 0, minWidth = 0, minColumns = 1, maxColumns = 0, standardHeight = 0, initiallySelectedItemId = 0; bool isWatchingForDeletion = false; PopupDirection preferredPopupDirection = PopupDirection::downwards; @@ -870,6 +1004,12 @@ class JUCE_API PopupMenu virtual int getPopupMenuColumnSeparatorWidthWithOptions (const Options&) = 0; }; + //============================================================================== + #ifndef DOXYGEN + [[deprecated ("Use the new method.")]] + int drawPopupMenuItem (Graphics&, int, int, bool, bool, bool, bool, bool, const String&, const String&, Image*, const Colour*) { return 0; } + #endif + private: //============================================================================== JUCE_PUBLIC_IN_DLL_BUILD (struct HelperClasses) @@ -885,11 +1025,6 @@ class JUCE_API PopupMenu static void setItem (CustomComponent&, const Item*); - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // These methods have new implementations now - see its new definition - int drawPopupMenuItem (Graphics&, int, int, bool, bool, bool, bool, bool, const String&, const String&, Image*, const Colour*) { return 0; } - #endif - JUCE_LEAK_DETECTOR (PopupMenu) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.cpp index f3eb1739a..0bba23d3e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.cpp @@ -75,6 +75,110 @@ class DropShadower::ShadowWindow : public Component JUCE_DECLARE_NON_COPYABLE (ShadowWindow) }; +class DropShadower::ParentVisibilityChangedListener : public ComponentListener, + private Timer +{ +public: + ParentVisibilityChangedListener (Component& r, ComponentListener& l) + : root (&r), listener (&l) + { + if (auto* firstParent = root->getParentComponent()) + updateParentHierarchy (firstParent); + + if ((SystemStats::getOperatingSystemType() & SystemStats::Windows) != 0) + { + isOnVirtualDesktop = isWindowOnCurrentVirtualDesktop (root->getWindowHandle()); + startTimerHz (5); + } + } + + ~ParentVisibilityChangedListener() override + { + for (auto& compEntry : observedComponents) + if (auto* comp = compEntry.get()) + comp->removeComponentListener (this); + } + + void componentVisibilityChanged (Component&) override + { + listener->componentVisibilityChanged (*root); + } + + void componentParentHierarchyChanged (Component& component) override + { + if (root == &component) + if (auto* firstParent = root->getParentComponent()) + updateParentHierarchy (firstParent); + } + + bool isWindowOnVirtualDesktop() const noexcept { return isOnVirtualDesktop; } + +private: + class ComponentWithWeakReference + { + public: + explicit ComponentWithWeakReference (Component& c) + : ptr (&c), ref (&c) {} + + Component* get() const { return ref.get(); } + + bool operator< (const ComponentWithWeakReference& other) const { return ptr < other.ptr; } + + private: + Component* ptr; + WeakReference ref; + }; + + void updateParentHierarchy (Component* rootComponent) + { + const auto lastSeenComponents = std::exchange (observedComponents, [&] + { + std::set result; + + for (auto node = rootComponent; node != nullptr; node = node->getParentComponent()) + result.emplace (*node); + + return result; + }()); + + const auto withDifference = [] (const auto& rangeA, const auto& rangeB, auto&& callback) + { + std::vector result; + std::set_difference (rangeA.begin(), rangeA.end(), rangeB.begin(), rangeB.end(), std::back_inserter (result)); + + for (const auto& item : result) + if (auto* c = item.get()) + callback (*c); + }; + + withDifference (lastSeenComponents, observedComponents, [this] (auto& comp) { comp.removeComponentListener (this); }); + withDifference (observedComponents, lastSeenComponents, [this] (auto& comp) { comp.addComponentListener (this); }); + } + + void timerCallback() override + { + WeakReference deletionChecker { static_cast (listener) }; + + const auto wasOnVirtualDesktop = std::exchange (isOnVirtualDesktop, + isWindowOnCurrentVirtualDesktop (root->getWindowHandle())); + + // on Windows, isWindowOnCurrentVirtualDesktop() may cause synchronous messages to be dispatched + // to the HWND so we need to check if the shadower is still valid after calling + if (deletionChecker == nullptr) + return; + + if (isOnVirtualDesktop != wasOnVirtualDesktop) + listener->componentVisibilityChanged (*root); + } + + Component* root = nullptr; + ComponentListener* listener = nullptr; + std::set observedComponents; + bool isOnVirtualDesktop = true; + + JUCE_DECLARE_NON_COPYABLE (ParentVisibilityChangedListener) + JUCE_DECLARE_NON_MOVEABLE (ParentVisibilityChangedListener) +}; //============================================================================== DropShadower::DropShadower (const DropShadow& ds) : shadow (ds) {} @@ -109,6 +213,11 @@ void DropShadower::setOwner (Component* componentToFollow) updateParent(); owner->addComponentListener (this); + // The visibility of `owner` is transitively affected by the visibility of its parents. Thus we need to trigger the + // componentVisibilityChanged() event in case it changes for any of the parents. + visibilityChangedListener = std::make_unique (*owner, + static_cast (*this)); + updateShadows(); } } @@ -163,15 +272,11 @@ void DropShadower::updateShadows() const ScopedValueSetter setter (reentrant, true); - if (owner == nullptr) - { - shadowWindows.clear(); - return; - } - - if (owner->isShowing() - && owner->getWidth() > 0 && owner->getHeight() > 0 - && (Desktop::canUseSemiTransparentWindows() || owner->getParentComponent() != nullptr)) + if (owner != nullptr + && owner->isShowing() + && owner->getWidth() > 0 && owner->getHeight() > 0 + && (Desktop::canUseSemiTransparentWindows() || owner->getParentComponent() != nullptr) + && (visibilityChangedListener != nullptr && visibilityChangedListener->isWindowOnVirtualDesktop())) { while (shadowWindows.size() < 4) shadowWindows.add (new ShadowWindow (owner, shadow)); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.h b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.h index cecccf59c..e56be3484 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.h @@ -75,7 +75,11 @@ class JUCE_API DropShadower : private ComponentListener void updateParent(); void updateShadows(); + class ParentVisibilityChangedListener; + std::unique_ptr visibilityChangedListener; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DropShadower) + JUCE_DECLARE_WEAK_REFERENCEABLE (DropShadower) }; } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp index a38da7251..5f8529e1a 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp @@ -35,16 +35,17 @@ class DragAndDropContainer::DragImageComponent : public Component, private Timer { public: - DragImageComponent (const Image& im, + DragImageComponent (const ScaledImage& im, const var& desc, Component* const sourceComponent, const MouseInputSource* draggingSource, DragAndDropContainer& ddc, Point offset) : sourceDetails (desc, sourceComponent, Point()), - image (im), owner (ddc), + image (im), + owner (ddc), mouseDragSource (draggingSource->getComponentUnderMouse()), - imageOffset (offset), + imageOffset (transformOffsetCoordinates (sourceComponent, offset)), originalInputSourceIndex (draggingSource->getIndex()), originalInputSourceType (draggingSource->getType()) { @@ -83,7 +84,7 @@ class DragAndDropContainer::DragImageComponent : public Component, g.fillAll (Colours::white); g.setOpacity (1.0f); - g.drawImageAt (image, 0, 0); + g.drawImage (image.getImage(), getLocalBounds().toFloat()); } void mouseUp (const MouseEvent& e) override @@ -164,7 +165,7 @@ class DragAndDropContainer::DragImageComponent : public Component, forceMouseCursorUpdate(); } - void updateImage (const Image& newImage) + void updateImage (const ScaledImage& newImage) { image = newImage; updateSize(); @@ -218,7 +219,7 @@ class DragAndDropContainer::DragImageComponent : public Component, DragAndDropTarget::SourceDetails sourceDetails; private: - Image image; + ScaledImage image; DragAndDropContainer& owner; WeakReference mouseDragSource, currentlyOverComp; const Point imageOffset; @@ -229,7 +230,8 @@ class DragAndDropContainer::DragImageComponent : public Component, void updateSize() { - setSize (image.getWidth(), image.getHeight()); + const auto bounds = image.getScaledBounds().toNearestInt(); + setSize (bounds.getWidth(), bounds.getHeight()); } void forceMouseCursorUpdate() @@ -263,6 +265,11 @@ class DragAndDropContainer::DragImageComponent : public Component, return nullptr; } + Point transformOffsetCoordinates (const Component* const sourceComponent, Point offsetInSource) const + { + return getLocalPoint (sourceComponent, offsetInSource) - getLocalPoint (sourceComponent, Point()); + } + DragAndDropTarget* findTarget (Point screenPos, Point& relativePos, Component*& resultComponent) const { @@ -383,17 +390,13 @@ class DragAndDropContainer::DragImageComponent : public Component, //============================================================================== -DragAndDropContainer::DragAndDropContainer() -{ -} +DragAndDropContainer::DragAndDropContainer() = default; -DragAndDropContainer::~DragAndDropContainer() -{ -} +DragAndDropContainer::~DragAndDropContainer() = default; void DragAndDropContainer::startDragging (const var& sourceDescription, Component* sourceComponent, - Image dragImage, + const ScaledImage& dragImage, const bool allowDraggingToExternalWindows, const Point* imageOffsetFromMouse, const MouseInputSource* inputSourceCausingDrag) @@ -409,55 +412,53 @@ void DragAndDropContainer::startDragging (const var& sourceDescription, return; } - auto lastMouseDown = draggingSource->getLastMouseDownPosition().roundToInt(); - Point imageOffset; + const auto lastMouseDown = draggingSource->getLastMouseDownPosition().roundToInt(); - if (dragImage.isNull()) + struct ImageAndOffset { - dragImage = sourceComponent->createComponentSnapshot (sourceComponent->getLocalBounds()) - .convertedToFormat (Image::ARGB); + ScaledImage image; + Point offset; + }; - dragImage.multiplyAllAlphas (0.6f); + const auto imageToUse = [&]() -> ImageAndOffset + { + if (! dragImage.getImage().isNull()) + return { dragImage, imageOffsetFromMouse != nullptr ? dragImage.getScaledBounds().getConstrainedPoint (-imageOffsetFromMouse->toDouble()) + : dragImage.getScaledBounds().getCentre() }; - auto lo = 150; - auto hi = 400; + const auto scaleFactor = 2.0; + auto image = sourceComponent->createComponentSnapshot (sourceComponent->getLocalBounds(), true, (float) scaleFactor) + .convertedToFormat (Image::ARGB); + image.multiplyAllAlphas (0.6f); - auto relPos = sourceComponent->getLocalPoint (nullptr, lastMouseDown); - auto clipped = dragImage.getBounds().getConstrainedPoint (relPos); - Random random; + const auto relPos = sourceComponent->getLocalPoint (nullptr, lastMouseDown).toDouble(); + const auto clipped = (image.getBounds().toDouble() / scaleFactor).getConstrainedPoint (relPos); - for (auto y = dragImage.getHeight(); --y >= 0;) - { - auto dy = (y - clipped.getY()) * (y - clipped.getY()); + Image fade (Image::SingleChannel, image.getWidth(), image.getHeight(), true); + Graphics fadeContext (fade); - for (auto x = dragImage.getWidth(); --x >= 0;) - { - auto dx = x - clipped.getX(); - auto distance = roundToInt (std::sqrt (dx * dx + dy)); + ColourGradient gradient; + gradient.isRadial = true; + gradient.point1 = clipped.toFloat() * scaleFactor; + gradient.point2 = gradient.point1 + Point (0.0f, scaleFactor * 400.0f); + gradient.addColour (0.0, Colours::white); + gradient.addColour (0.375, Colours::white); + gradient.addColour (1.0, Colours::transparentWhite); - if (distance > lo) - { - auto alpha = (distance > hi) ? 0 - : (float) (hi - distance) / (float) (hi - lo) - + random.nextFloat() * 0.008f; + fadeContext.setGradientFill (gradient); + fadeContext.fillAll(); - dragImage.multiplyAlphaAt (x, y, alpha); - } - } - } + Image composite (Image::ARGB, image.getWidth(), image.getHeight(), true); + Graphics compositeContext (composite); - imageOffset = clipped; - } - else - { - if (imageOffsetFromMouse == nullptr) - imageOffset = dragImage.getBounds().getCentre(); - else - imageOffset = dragImage.getBounds().getConstrainedPoint (-*imageOffsetFromMouse); - } + compositeContext.reduceClipRegion (fade, {}); + compositeContext.drawImageAt (image, 0, 0); + + return { ScaledImage (composite, scaleFactor), clipped }; + }(); - auto* dragImageComponent = dragImageComponents.add (new DragImageComponent (dragImage, sourceDescription, sourceComponent, - draggingSource, *this, imageOffset)); + auto* dragImageComponent = dragImageComponents.add (new DragImageComponent (imageToUse.image, sourceDescription, sourceComponent, + draggingSource, *this, imageToUse.offset.roundToInt())); if (allowDraggingToExternalWindows) { @@ -522,7 +523,7 @@ var DragAndDropContainer::getDragDescriptionForIndex (int index) const return dragImageComponents.getUnchecked (index)->sourceDetails.description; } -void DragAndDropContainer::setCurrentDragImage (const Image& newImage) +void DragAndDropContainer::setCurrentDragImage (const ScaledImage& newImage) { // If you are performing drag and drop in a multi-touch environment then // you should use the setDragImageForIndex() method instead! @@ -531,7 +532,7 @@ void DragAndDropContainer::setCurrentDragImage (const Image& newImage) dragImageComponents[0]->updateImage (newImage); } -void DragAndDropContainer::setDragImageForIndex (int index, const Image& newImage) +void DragAndDropContainer::setDragImageForIndex (int index, const ScaledImage& newImage) { if (isPositiveAndBelow (index, dragImageComponents.size())) dragImageComponents.getUnchecked (index)->updateImage (newImage); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h index 06057f7fd..3aecf4d7c 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h @@ -94,11 +94,27 @@ class JUCE_API DragAndDropContainer */ void startDragging (const var& sourceDescription, Component* sourceComponent, - Image dragImage = Image(), + const ScaledImage& dragImage = ScaledImage(), bool allowDraggingToOtherJuceWindows = false, const Point* imageOffsetFromMouse = nullptr, const MouseInputSource* inputSourceCausingDrag = nullptr); + [[deprecated ("This overload does not allow the image's scale to be specified. Use the other overload of startDragging instead.")]] + void startDragging (const var& sourceDescription, + Component* sourceComponent, + Image dragImage, + bool allowDraggingToOtherJuceWindows = false, + const Point* imageOffsetFromMouse = nullptr, + const MouseInputSource* inputSourceCausingDrag = nullptr) + { + startDragging (sourceDescription, + sourceComponent, + ScaledImage (dragImage), + allowDraggingToOtherJuceWindows, + imageOffsetFromMouse, + inputSourceCausingDrag); + } + /** Returns true if something is currently being dragged. */ bool isDragAndDropActive() const; @@ -130,13 +146,19 @@ class JUCE_API DragAndDropContainer @see setDragImageForIndex */ - void setCurrentDragImage (const Image& newImage); + void setCurrentDragImage (const ScaledImage& newImage); + + [[deprecated ("This overload does not allow the image's scale to be specified. Use the other overload of setCurrentDragImage instead.")]] + void setCurrentDragImage (const Image& newImage) { setCurrentDragImage (ScaledImage (newImage)); } /** Same as the setCurrentDragImage() method but takes a touch index parameter. @see setCurrentDragImage - */ - void setDragImageForIndex (int index, const Image& newImage); + */ + void setDragImageForIndex (int index, const ScaledImage& newImage); + + [[deprecated ("This overload does not allow the image's scale to be specified. Use the other overload of setDragImageForIndex instead.")]] + void setDragImageForIndex (int index, const Image& newImage) { setDragImageForIndex (index, ScaledImage (newImage)); } /** Utility to find the DragAndDropContainer for a given Component. @@ -238,13 +260,6 @@ class JUCE_API DragAndDropContainer const MouseInputSource* getMouseInputSourceForDrag (Component* sourceComponent, const MouseInputSource* inputSourceCausingDrag); bool isAlreadyDragging (Component* sourceComponent) const noexcept; - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This is just here to cause a compile error in old code that hasn't been changed to use the new - // version of this method. - virtual int dragOperationStarted() { return 0; } - virtual int dragOperationEnded() { return 0; } - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DragAndDropContainer) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.cpp index 5892bbafd..de476a849 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.cpp @@ -26,115 +26,65 @@ namespace juce { -struct CustomMouseCursorInfo -{ - CustomMouseCursorInfo (const Image& im, Point hs, float scale = 1.0f) noexcept - : image (im), hotspot (hs), scaleFactor (scale) - {} - - void* create() const; - - Image image; - const Point hotspot; - const float scaleFactor; - - JUCE_DECLARE_NON_COPYABLE (CustomMouseCursorInfo) -}; - class MouseCursor::SharedCursorHandle { public: explicit SharedCursorHandle (const MouseCursor::StandardCursorType type) - : handle (createStandardMouseCursor (type)), + : handle (type), standardType (type), - isStandard (true) + standard (true) { } - SharedCursorHandle (const Image& image, Point hotSpot, float scaleFactor) - : info (new CustomMouseCursorInfo (image, hotSpot, scaleFactor)), - handle (info->create()), + SharedCursorHandle (const ScaledImage& image, Point hotSpot) + : info { image, hotSpot }, + handle (info), standardType (MouseCursor::NormalCursor), - isStandard (false) + standard (false) { // your hotspot needs to be within the bounds of the image! - jassert (image.getBounds().contains (hotSpot)); + jassert (image.getImage().getBounds().contains (hotSpot)); } - ~SharedCursorHandle() + static std::shared_ptr createStandard (const MouseCursor::StandardCursorType type) { - deleteMouseCursor (handle, isStandard); - } + if (! isPositiveAndBelow (type, MouseCursor::NumStandardCursorTypes)) + return nullptr; - static SharedCursorHandle* createStandard (const MouseCursor::StandardCursorType type) - { - jassert (isPositiveAndBelow (type, MouseCursor::NumStandardCursorTypes)); + static SpinLock mutex; + static std::array, MouseCursor::NumStandardCursorTypes> cursors; - const SpinLock::ScopedLockType sl (lock); - auto& c = getSharedCursor (type); + const SpinLock::ScopedLockType sl (mutex); - if (c == nullptr) - c = new SharedCursorHandle (type); - else - c->retain(); + auto& weak = cursors[type]; - return c; - } + if (auto strong = weak.lock()) + return strong; - bool isStandardType (MouseCursor::StandardCursorType type) const noexcept - { - return type == standardType && isStandard; + auto strong = std::make_shared (type); + weak = strong; + return strong; } - SharedCursorHandle* retain() noexcept - { - ++refCount; - return this; - } - - void release() + bool isStandardType (MouseCursor::StandardCursorType type) const noexcept { - if (--refCount == 0) - { - if (isStandard) - { - const SpinLock::ScopedLockType sl (lock); - getSharedCursor (standardType) = nullptr; - } - - delete this; - } + return type == standardType && standard; } - void* getHandle() const noexcept { return handle; } - void setHandle (void* newHandle) { handle = newHandle; } - + PlatformSpecificHandle* getHandle() noexcept { return &handle; } MouseCursor::StandardCursorType getType() const noexcept { return standardType; } - CustomMouseCursorInfo* getCustomInfo() const noexcept { return info.get(); } private: - std::unique_ptr info; - void* handle; - Atomic refCount { 1 }; + CustomMouseCursorInfo info; + PlatformSpecificHandle handle; const MouseCursor::StandardCursorType standardType; - const bool isStandard; - static SpinLock lock; - - static SharedCursorHandle*& getSharedCursor (const MouseCursor::StandardCursorType type) - { - static SharedCursorHandle* cursors[MouseCursor::NumStandardCursorTypes] = {}; - return cursors[type]; - } + const bool standard; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedCursorHandle) }; -SpinLock MouseCursor::SharedCursorHandle::lock; - //============================================================================== -MouseCursor::MouseCursor() noexcept -{ -} +MouseCursor::MouseCursor() noexcept = default; MouseCursor::MouseCursor (const StandardCursorType type) : cursorHandle (type != MouseCursor::NormalCursor ? SharedCursorHandle::createStandard (type) : nullptr) @@ -142,49 +92,29 @@ MouseCursor::MouseCursor (const StandardCursorType type) } MouseCursor::MouseCursor (const Image& image, int hotSpotX, int hotSpotY) - : MouseCursor (image, hotSpotX, hotSpotY, 1.0f) + : MouseCursor (ScaledImage (image), { hotSpotX, hotSpotY }) { } MouseCursor::MouseCursor (const Image& image, int hotSpotX, int hotSpotY, float scaleFactor) - : cursorHandle (new SharedCursorHandle (image, { hotSpotX, hotSpotY }, scaleFactor)) + : MouseCursor (ScaledImage (image, scaleFactor), { hotSpotX, hotSpotY }) { } -MouseCursor::MouseCursor (const MouseCursor& other) - : cursorHandle (other.cursorHandle == nullptr ? nullptr : other.cursorHandle->retain()) +MouseCursor::MouseCursor (const ScaledImage& image, Point hotSpot) + : cursorHandle (std::make_shared (image, hotSpot)) { } -MouseCursor::~MouseCursor() -{ - if (cursorHandle != nullptr) - cursorHandle->release(); -} +MouseCursor::MouseCursor (const MouseCursor&) = default; -MouseCursor& MouseCursor::operator= (const MouseCursor& other) -{ - if (other.cursorHandle != nullptr) - other.cursorHandle->retain(); +MouseCursor::~MouseCursor() = default; - if (cursorHandle != nullptr) - cursorHandle->release(); +MouseCursor& MouseCursor::operator= (const MouseCursor&) = default; - cursorHandle = other.cursorHandle; - return *this; -} +MouseCursor::MouseCursor (MouseCursor&&) noexcept = default; -MouseCursor::MouseCursor (MouseCursor&& other) noexcept - : cursorHandle (other.cursorHandle) -{ - other.cursorHandle = nullptr; -} - -MouseCursor& MouseCursor::operator= (MouseCursor&& other) noexcept -{ - std::swap (cursorHandle, other.cursorHandle); - return *this; -} +MouseCursor& MouseCursor::operator= (MouseCursor&&) noexcept = default; bool MouseCursor::operator== (const MouseCursor& other) const noexcept { @@ -200,11 +130,6 @@ bool MouseCursor::operator== (StandardCursorType type) const noexcept bool MouseCursor::operator!= (const MouseCursor& other) const noexcept { return ! operator== (other); } bool MouseCursor::operator!= (StandardCursorType type) const noexcept { return ! operator== (type); } -void* MouseCursor::getHandle() const noexcept -{ - return cursorHandle != nullptr ? cursorHandle->getHandle() : nullptr; -} - void MouseCursor::showWaitCursor() { Desktop::getInstance().getMainMouseSource().showMouseCursor (MouseCursor::WaitCursor); @@ -215,4 +140,14 @@ void MouseCursor::hideWaitCursor() Desktop::getInstance().getMainMouseSource().revealCursor(); } +MouseCursor::PlatformSpecificHandle* MouseCursor::getHandle() const noexcept +{ + return cursorHandle != nullptr ? cursorHandle->getHandle() : nullptr; +} + +void MouseCursor::showInWindow (ComponentPeer* peer) const +{ + PlatformSpecificHandle::showInWindow (getHandle(), peer); +} + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.h b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.h index 79187a1e7..9cba044de 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.h @@ -103,6 +103,16 @@ class JUCE_API MouseCursor final */ MouseCursor (const Image& image, int hotSpotX, int hotSpotY, float scaleFactor); + /** Creates a custom cursor from an image. + + @param image the image to use for the cursor - if this is bigger than the + system can manage, it might get scaled down first, and might + also have to be turned to black-and-white if it can't do colour + cursors. + @param hotSpot the position of the cursor's hotspot within the image + */ + MouseCursor (const ScaledImage& image, Point hotSpot); + //============================================================================== /** Creates a copy of another cursor object. */ MouseCursor (const MouseCursor&); @@ -166,15 +176,13 @@ class JUCE_API MouseCursor final private: //============================================================================== class SharedCursorHandle; - friend class SharedCursorHandle; - SharedCursorHandle* cursorHandle = nullptr; + std::shared_ptr cursorHandle; + + class PlatformSpecificHandle; friend class MouseInputSourceInternal; void showInWindow (ComponentPeer*) const; - void* getHandle() const noexcept; - - static void* createStandardMouseCursor (MouseCursor::StandardCursorType); - static void deleteMouseCursor (void* cursorHandle, bool isStandard); + PlatformSpecificHandle* getHandle() const noexcept; JUCE_LEAK_DETECTOR (MouseCursor) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp index ae674dd90..8bf0ff895 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp @@ -78,8 +78,8 @@ class MouseInputSourceInternal : private AsyncUpdater auto& comp = peer->getComponent(); // (the contains() call is needed to test for overlapping desktop windows) - if (comp.containsInternal (relativePos)) - return comp.getComponentAtInternal (relativePos); + if (comp.contains (relativePos)) + return comp.getComponentAt (relativePos); } return nullptr; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.h b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.h index e73906692..b92acd675 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInputSource.h @@ -241,13 +241,15 @@ class JUCE_API MouseInputSource final */ static const Point offscreenMousePos; - #if ! DOXYGEN - // This method has been deprecated and replaced with the isLongPressOrDrag() and hasMovedSignificantlySincePressed() - // methods. If you want the same behaviour you should use isLongPressOrDrag() which accounts for the amount of time - // that the input source has been held down for, but if you only want to know whether it has been moved use - // hasMovedSignificantlySincePressed() instead. - JUCE_DEPRECATED (bool hasMouseMovedSignificantlySincePressed() const noexcept); + //============================================================================== + #ifndef DOXYGEN + [[deprecated ("This method has been replaced with the isLongPressOrDrag and hasMovedSignificantlySincePressed " + "methods. If you want the same behaviour you should use isLongPressOrDrag which accounts for the " + "amount of time that the input source has been held down for, but if you only want to know whether " + "it has been moved use hasMovedSignificantlySincePressed instead.")]] + bool hasMouseMovedSignificantlySincePressed() const noexcept; #endif + private: //============================================================================== friend class ComponentPeer; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm index 4b0587151..f567dcd8c 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm @@ -23,13 +23,26 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). ============================================================================== */ +static void juceFreeAccessibilityPlatformSpecificData (UIAccessibilityElement* element) +{ + if (auto* container = juce::getIvar (element, "container")) + { + object_setInstanceVariable (element, "container", nullptr); + object_setInstanceVariable (container, "handler", nullptr); + + [container release]; + } +} + namespace juce { -#if (defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0) +#if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0 #define JUCE_IOS_CONTAINER_API_AVAILABLE 1 #endif +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") + constexpr auto juceUIAccessibilityContainerTypeNone = #if JUCE_IOS_CONTAINER_API_AVAILABLE UIAccessibilityContainerTypeNone; @@ -51,6 +64,8 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 2; #endif +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + #define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1 //============================================================================== @@ -103,10 +118,14 @@ explicit AccessibilityNativeImpl (AccessibilityHandler& handler) AccessibilityContainer() : ObjCClass ("JUCEUIAccessibilityElementContainer_") { - addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement, "c@:"); - addMethod (@selector (accessibilityFrame), getAccessibilityFrame, @encode (CGRect), "@:"); - addMethod (@selector (accessibilityElements), getAccessibilityElements, "@@:"); - addMethod (@selector (accessibilityContainerType), getAccessibilityContainerType, "i@:"); + addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement); + addMethod (@selector (accessibilityFrame), getAccessibilityFrame); + addMethod (@selector (accessibilityElements), getAccessibilityElements); + + #if JUCE_IOS_CONTAINER_API_AVAILABLE + if (@available (iOS 11.0, *)) + addMethod (@selector (accessibilityContainerType), getAccessibilityContainerType); + #endif addIvar ("handler"); @@ -165,55 +184,68 @@ static NSInteger getAccessibilityContainerType (id self, SEL) class AccessibilityElement : public AccessibleObjCClass { public: + enum class Type { defaultElement, textElement }; + static Holder create (AccessibilityHandler& handler) { - static AccessibilityElement cls; - Holder element ([cls.createInstance() initWithAccessibilityContainer: (id) handler.getComponent().getWindowHandle()]); + static AccessibilityElement cls { Type::defaultElement }; + static AccessibilityElement textCls { Type::textElement }; + + id instance = (hasEditableText (handler) ? textCls : cls).createInstance(); + + Holder element ([instance initWithAccessibilityContainer: (id) handler.getComponent().getWindowHandle()]); object_setInstanceVariable (element.get(), "handler", &handler); return element; } - private: - AccessibilityElement() + AccessibilityElement (Type elementType) { - addMethod (@selector (dealloc), dealloc, "v@:"); - - addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement, "c@:"); - addMethod (@selector (accessibilityContainer), getAccessibilityContainer, "@@:"); - addMethod (@selector (accessibilityFrame), getAccessibilityFrame, @encode (CGRect), "@:"); - addMethod (@selector (accessibilityTraits), getAccessibilityTraits, "i@:"); - addMethod (@selector (accessibilityLabel), getAccessibilityTitle, "@@:"); - addMethod (@selector (accessibilityHint), getAccessibilityHelp, "@@:"); - addMethod (@selector (accessibilityValue), getAccessibilityValue, "@@:"); - addMethod (@selector (setAccessibilityValue:), setAccessibilityValue, "v@:@"); - - addMethod (@selector (accessibilityElementDidBecomeFocused), onFocusGain, "v@:"); - addMethod (@selector (accessibilityElementDidLoseFocus), onFocusLoss, "v@:"); - addMethod (@selector (accessibilityElementIsFocused), isFocused, "c@:"); - addMethod (@selector (accessibilityViewIsModal), getIsAccessibilityModal, "c@:"); - - addMethod (@selector (accessibilityActivate), accessibilityPerformActivate, "c@:"); - addMethod (@selector (accessibilityIncrement), accessibilityPerformIncrement, "c@:"); - addMethod (@selector (accessibilityDecrement), accessibilityPerformDecrement, "c@:"); - - addMethod (@selector (accessibilityLineNumberForPoint:), getAccessibilityLineNumberForPoint, "i@:", @encode (CGPoint)); - addMethod (@selector (accessibilityContentForLineNumber:), getAccessibilityContentForLineNumber, "@@:i"); - addMethod (@selector (accessibilityFrameForLineNumber:), getAccessibilityFrameForLineNumber, @encode (CGRect), "@:i"); - addMethod (@selector (accessibilityPageContent), getAccessibilityPageContent, "@@:"); - - addMethod (@selector (accessibilityDataTableCellElementForRow:column:), getAccessibilityDataTableCellElementForRowColumn, "@@:ii"); - addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount, "i@:"); - addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount, "i@:"); - addMethod (@selector (accessibilityRowRange), getAccessibilityRowIndexRange, @encode (NSRange), "@:"); - addMethod (@selector (accessibilityColumnRange), getAccessibilityColumnIndexRange, @encode (NSRange), "@:"); - - addProtocol (@protocol (UIAccessibilityReadingContent)); + addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement); + addMethod (@selector (accessibilityContainer), getAccessibilityContainer); + addMethod (@selector (accessibilityFrame), getAccessibilityFrame); + addMethod (@selector (accessibilityTraits), getAccessibilityTraits); + addMethod (@selector (accessibilityLabel), getAccessibilityTitle); + addMethod (@selector (accessibilityHint), getAccessibilityHelp); + addMethod (@selector (accessibilityValue), getAccessibilityValue); + addMethod (@selector (setAccessibilityValue:), setAccessibilityValue); + + addMethod (@selector (accessibilityElementDidBecomeFocused), onFocusGain); + addMethod (@selector (accessibilityElementDidLoseFocus), onFocusLoss); + addMethod (@selector (accessibilityElementIsFocused), isFocused); + addMethod (@selector (accessibilityViewIsModal), getIsAccessibilityModal); + + addMethod (@selector (accessibilityActivate), accessibilityPerformActivate); + addMethod (@selector (accessibilityIncrement), accessibilityPerformIncrement); + addMethod (@selector (accessibilityDecrement), accessibilityPerformDecrement); + addMethod (@selector (accessibilityPerformEscape), accessibilityPerformEscape); + + #if JUCE_IOS_CONTAINER_API_AVAILABLE + if (@available (iOS 11.0, *)) + { + addMethod (@selector (accessibilityDataTableCellElementForRow:column:), getAccessibilityDataTableCellElementForRowColumn); + addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount); + addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount); + addMethod (@selector (accessibilityRowRange), getAccessibilityRowIndexRange); + addMethod (@selector (accessibilityColumnRange), getAccessibilityColumnIndexRange); + } + #endif + + if (elementType == Type::textElement) + { + addMethod (@selector (accessibilityLineNumberForPoint:), getAccessibilityLineNumberForPoint); + addMethod (@selector (accessibilityContentForLineNumber:), getAccessibilityContentForLineNumber); + addMethod (@selector (accessibilityFrameForLineNumber:), getAccessibilityFrameForLineNumber); + addMethod (@selector (accessibilityPageContent), getAccessibilityPageContent); + + addProtocol (@protocol (UIAccessibilityReadingContent)); + } addIvar ("container"); registerClass(); } + private: //============================================================================== static UIAccessibilityElement* getContainer (id self) { @@ -221,17 +253,6 @@ static Holder create (AccessibilityHandler& handler) } //============================================================================== - static void dealloc (id self, SEL) - { - if (UIAccessibilityElement* container = getContainer (self)) - { - [container release]; - object_setInstanceVariable (self, "container", nullptr); - } - - sendSuperclassMessage (self, @selector (dealloc)); - } - static id getAccessibilityContainer (id self, SEL) { if (auto* handler = getHandler (self)) @@ -245,14 +266,16 @@ static id getAccessibilityContainer (id self, SEL) return container; static AccessibilityContainer cls; + id windowHandle = (id) handler->getComponent().getWindowHandle(); UIAccessibilityElement* container = [cls.createInstance() initWithAccessibilityContainer: windowHandle]; - object_setInstanceVariable (container, "handler", handler); + [container retain]; + object_setInstanceVariable (container, "handler", handler); object_setInstanceVariable (self, "container", container); - return (id) getContainer (self); + return container; } if (auto* parent = handler->getParent()) @@ -332,6 +355,19 @@ static UIAccessibilityTraits getAccessibilityTraits (id self, SEL) return traits | sendSuperclassMessage (self, @selector (accessibilityTraits)); } + static NSString* getAccessibilityValue (id self, SEL) + { + if (auto* handler = getHandler (self)) + { + if (handler->getCurrentState().isCheckable()) + return handler->getCurrentState().isChecked() ? @"1" : @"0"; + + return (NSString*) getAccessibilityValueFromInterfaces (*handler); + } + + return nil; + } + static void onFocusGain (id self, SEL) { if (auto* handler = getHandler (self)) @@ -377,6 +413,35 @@ static BOOL accessibilityPerformActivate (id self, SEL) return NO; } + static BOOL accessibilityPerformEscape (id self, SEL) + { + if (auto* handler = getHandler (self)) + { + if (auto* modal = Component::getCurrentlyModalComponent()) + { + if (auto* modalHandler = modal->getAccessibilityHandler()) + { + if (modalHandler == handler || modalHandler->isParentOf (handler)) + { + modal->exitModalState (0); + return YES; + } + } + } + } + + return NO; + } + + static id getAccessibilityDataTableCellElementForRowColumn (id self, SEL, NSUInteger row, NSUInteger column) + { + if (auto* tableInterface = getTableInterface (self)) + if (auto* cellHandler = tableInterface->getCellHandler ((int) row, (int) column)) + return (id) cellHandler->getNativeImplementation(); + + return nil; + } + static NSInteger getAccessibilityLineNumberForPoint (id self, SEL, CGPoint point) { if (auto* handler = getHandler (self)) @@ -433,15 +498,6 @@ static CGRect getAccessibilityFrameForLineNumber (id self, SEL, NSInteger lineNu return nil; } - static id getAccessibilityDataTableCellElementForRowColumn (id self, SEL, NSUInteger row, NSUInteger column) - { - if (auto* tableInterface = getTableInterface (self)) - if (auto* cellHandler = tableInterface->getCellHandler ((int) row, (int) column)) - return (id) cellHandler->getNativeImplementation(); - - return nil; - } - //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityElement) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm index 89581ce60..8e2d33a39 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm @@ -23,22 +23,16 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). ============================================================================== */ +static void juceFreeAccessibilityPlatformSpecificData (NSAccessibilityElement*) {} + namespace juce { -#if (! defined MAC_OS_X_VERSION_10_13) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 +#if ! defined (MAC_OS_X_VERSION_10_13) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 using NSAccessibilityRole = NSString*; using NSAccessibilityNotificationName = NSString*; #endif -#if (! defined MAC_OS_X_VERSION_10_9) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 - const NSAccessibilityNotificationName NSAccessibilityLayoutChangedNotificationJuce = @"AXLayoutChanged"; -#else - const NSAccessibilityNotificationName NSAccessibilityLayoutChangedNotificationJuce = NSAccessibilityLayoutChangedNotification; -#endif - -#if JUCE_OBJC_HAS_AVAILABLE_FEATURE || (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) - #define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") @@ -63,9 +57,7 @@ explicit AccessibilityNativeImpl (AccessibilityHandler& handler) public: static Holder create (AccessibilityHandler& handler) { - #if JUCE_OBJC_HAS_AVAILABLE_FEATURE if (@available (macOS 10.10, *)) - #endif { static AccessibilityElement cls; Holder element ([cls.createInstance() init]); @@ -79,70 +71,70 @@ static Holder create (AccessibilityHandler& handler) private: AccessibilityElement() { - addMethod (@selector (accessibilityNotifiesWhenDestroyed), getAccessibilityNotifiesWhenDestroyed, "c@:"); - addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement, "c@:"); - addMethod (@selector (isAccessibilityEnabled), getIsAccessibilityEnabled, "c@:"); - addMethod (@selector (accessibilityWindow), getAccessibilityWindow, "@@:"); - addMethod (@selector (accessibilityTopLevelUIElement), getAccessibilityWindow, "@@:"); - addMethod (@selector (accessibilityFocusedUIElement), getAccessibilityFocusedUIElement, "@@:"); - addMethod (@selector (accessibilityHitTest:), accessibilityHitTest, "@@:", @encode (NSPoint)); - addMethod (@selector (accessibilityParent), getAccessibilityParent, "@@:"); - addMethod (@selector (accessibilityChildren), getAccessibilityChildren, "@@:"); - addMethod (@selector (isAccessibilityFocused), getIsAccessibilityFocused, "c@:"); - addMethod (@selector (setAccessibilityFocused:), setAccessibilityFocused, "v@:c"); - addMethod (@selector (isAccessibilityModal), getIsAccessibilityModal, "c@:"); - addMethod (@selector (accessibilityFrame), getAccessibilityFrame, @encode (NSRect), "@:"); - addMethod (@selector (accessibilityRole), getAccessibilityRole, "@@:"); - addMethod (@selector (accessibilitySubrole), getAccessibilitySubrole, "@@:"); - addMethod (@selector (accessibilityTitle), getAccessibilityTitle, "@@:"); - addMethod (@selector (accessibilityLabel), getAccessibilityLabel, "@@:"); - addMethod (@selector (accessibilityHelp), getAccessibilityHelp, "@@:"); - addMethod (@selector (accessibilityValue), getAccessibilityValue, "@@:"); - addMethod (@selector (setAccessibilityValue:), setAccessibilityValue, "v@:@"); - addMethod (@selector (accessibilitySelectedChildren), getAccessibilitySelectedChildren, "@@:"); - addMethod (@selector (setAccessibilitySelectedChildren:), setAccessibilitySelectedChildren, "v@:@"); - addMethod (@selector (accessibilityOrientation), getAccessibilityOrientation, "i@:@"); - - addMethod (@selector (accessibilityInsertionPointLineNumber), getAccessibilityInsertionPointLineNumber, "i@:"); - addMethod (@selector (accessibilityVisibleCharacterRange), getAccessibilityVisibleCharacterRange, @encode (NSRange), "@:"); - addMethod (@selector (accessibilityNumberOfCharacters), getAccessibilityNumberOfCharacters, "i@:"); - addMethod (@selector (accessibilitySelectedText), getAccessibilitySelectedText, "@@:"); - addMethod (@selector (accessibilitySelectedTextRange), getAccessibilitySelectedTextRange, @encode (NSRange), "@:"); - addMethod (@selector (accessibilityAttributedStringForRange:), getAccessibilityAttributedStringForRange, "@@:", @encode (NSRange)); - addMethod (@selector (accessibilityRangeForLine:), getAccessibilityRangeForLine, @encode (NSRange), "@:i"); - addMethod (@selector (accessibilityStringForRange:), getAccessibilityStringForRange, "@@:", @encode (NSRange)); - addMethod (@selector (accessibilityRangeForPosition:), getAccessibilityRangeForPosition, @encode (NSRange), "@:", @encode (NSPoint)); - addMethod (@selector (accessibilityRangeForIndex:), getAccessibilityRangeForIndex, @encode (NSRange), "@:i"); - addMethod (@selector (accessibilityFrameForRange:), getAccessibilityFrameForRange, @encode (NSRect), "@:", @encode (NSRange)); - addMethod (@selector (accessibilityLineForIndex:), getAccessibilityLineForIndex, "i@:i"); - addMethod (@selector (setAccessibilitySelectedTextRange:), setAccessibilitySelectedTextRange, "v@:", @encode (NSRange)); - - addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount, "i@:"); - addMethod (@selector (accessibilityRows), getAccessibilityRows, "@@:"); - addMethod (@selector (accessibilitySelectedRows), getAccessibilitySelectedRows, "@@:"); - addMethod (@selector (setAccessibilitySelectedRows:), setAccessibilitySelectedRows, "v@:@"); - addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount, "i@:"); - addMethod (@selector (accessibilityColumns), getAccessibilityColumns, "@@:"); - addMethod (@selector (accessibilitySelectedColumns), getAccessibilitySelectedColumns, "@@:"); - addMethod (@selector (setAccessibilitySelectedColumns:), setAccessibilitySelectedColumns, "v@:@"); - - addMethod (@selector (accessibilityRowIndexRange), getAccessibilityRowIndexRange, @encode (NSRange), "@:"); - addMethod (@selector (accessibilityColumnIndexRange), getAccessibilityColumnIndexRange, @encode (NSRange), "@:"); - addMethod (@selector (accessibilityIndex), getAccessibilityIndex, "i@:"); - addMethod (@selector (accessibilityDisclosureLevel), getAccessibilityDisclosureLevel, "i@:"); - addMethod (@selector (isAccessibilityExpanded), getIsAccessibilityExpanded, "c@:"); - - addMethod (@selector (accessibilityPerformIncrement), accessibilityPerformIncrement, "c@:"); - addMethod (@selector (accessibilityPerformDecrement), accessibilityPerformDecrement, "c@:"); - addMethod (@selector (accessibilityPerformDelete), accessibilityPerformDelete, "c@:"); - addMethod (@selector (accessibilityPerformPress), accessibilityPerformPress, "c@:"); - addMethod (@selector (accessibilityPerformShowMenu), accessibilityPerformShowMenu, "c@:"); - addMethod (@selector (accessibilityPerformRaise), accessibilityPerformRaise, "c@:"); - - addMethod (@selector (isAccessibilitySelectorAllowed:), getIsAccessibilitySelectorAllowed, "c@:@"); + addMethod (@selector (accessibilityNotifiesWhenDestroyed), getAccessibilityNotifiesWhenDestroyed); + addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement); + addMethod (@selector (isAccessibilityEnabled), getIsAccessibilityEnabled); + addMethod (@selector (accessibilityWindow), getAccessibilityWindow); + addMethod (@selector (accessibilityTopLevelUIElement), getAccessibilityWindow); + addMethod (@selector (accessibilityFocusedUIElement), getAccessibilityFocusedUIElement); + addMethod (@selector (accessibilityHitTest:), accessibilityHitTest); + addMethod (@selector (accessibilityParent), getAccessibilityParent); + addMethod (@selector (accessibilityChildren), getAccessibilityChildren); + addMethod (@selector (isAccessibilityFocused), getIsAccessibilityFocused); + addMethod (@selector (setAccessibilityFocused:), setAccessibilityFocused); + addMethod (@selector (isAccessibilityModal), getIsAccessibilityModal); + addMethod (@selector (accessibilityFrame), getAccessibilityFrame); + addMethod (@selector (accessibilityRole), getAccessibilityRole); + addMethod (@selector (accessibilitySubrole), getAccessibilitySubrole); + addMethod (@selector (accessibilityTitle), getAccessibilityTitle); + addMethod (@selector (accessibilityLabel), getAccessibilityLabel); + addMethod (@selector (accessibilityHelp), getAccessibilityHelp); + addMethod (@selector (accessibilityValue), getAccessibilityValue); + addMethod (@selector (setAccessibilityValue:), setAccessibilityValue); + addMethod (@selector (accessibilitySelectedChildren), getAccessibilitySelectedChildren); + addMethod (@selector (setAccessibilitySelectedChildren:), setAccessibilitySelectedChildren); + addMethod (@selector (accessibilityOrientation), getAccessibilityOrientation); + + addMethod (@selector (accessibilityInsertionPointLineNumber), getAccessibilityInsertionPointLineNumber); + addMethod (@selector (accessibilityVisibleCharacterRange), getAccessibilityVisibleCharacterRange); + addMethod (@selector (accessibilityNumberOfCharacters), getAccessibilityNumberOfCharacters); + addMethod (@selector (accessibilitySelectedText), getAccessibilitySelectedText); + addMethod (@selector (accessibilitySelectedTextRange), getAccessibilitySelectedTextRange); + addMethod (@selector (accessibilityAttributedStringForRange:), getAccessibilityAttributedStringForRange); + addMethod (@selector (accessibilityRangeForLine:), getAccessibilityRangeForLine); + addMethod (@selector (accessibilityStringForRange:), getAccessibilityStringForRange); + addMethod (@selector (accessibilityRangeForPosition:), getAccessibilityRangeForPosition); + addMethod (@selector (accessibilityRangeForIndex:), getAccessibilityRangeForIndex); + addMethod (@selector (accessibilityFrameForRange:), getAccessibilityFrameForRange); + addMethod (@selector (accessibilityLineForIndex:), getAccessibilityLineForIndex); + addMethod (@selector (setAccessibilitySelectedTextRange:), setAccessibilitySelectedTextRange); + + addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount); + addMethod (@selector (accessibilityRows), getAccessibilityRows); + addMethod (@selector (accessibilitySelectedRows), getAccessibilitySelectedRows); + addMethod (@selector (setAccessibilitySelectedRows:), setAccessibilitySelectedRows); + addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount); + addMethod (@selector (accessibilityColumns), getAccessibilityColumns); + addMethod (@selector (accessibilitySelectedColumns), getAccessibilitySelectedColumns); + addMethod (@selector (setAccessibilitySelectedColumns:), setAccessibilitySelectedColumns); + + addMethod (@selector (accessibilityRowIndexRange), getAccessibilityRowIndexRange); + addMethod (@selector (accessibilityColumnIndexRange), getAccessibilityColumnIndexRange); + addMethod (@selector (accessibilityIndex), getAccessibilityIndex); + addMethod (@selector (accessibilityDisclosureLevel), getAccessibilityDisclosureLevel); + addMethod (@selector (isAccessibilityExpanded), getIsAccessibilityExpanded); + + addMethod (@selector (accessibilityPerformIncrement), accessibilityPerformIncrement); + addMethod (@selector (accessibilityPerformDecrement), accessibilityPerformDecrement); + addMethod (@selector (accessibilityPerformDelete), accessibilityPerformDelete); + addMethod (@selector (accessibilityPerformPress), accessibilityPerformPress); + addMethod (@selector (accessibilityPerformShowMenu), accessibilityPerformShowMenu); + addMethod (@selector (accessibilityPerformRaise), accessibilityPerformRaise); + + addMethod (@selector (isAccessibilitySelectorAllowed:), getIsAccessibilitySelectorAllowed); #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - addMethod (@selector (accessibilityChildrenInNavigationOrder), getAccessibilityChildren, "@@:"); + addMethod (@selector (accessibilityChildrenInNavigationOrder), getAccessibilityChildren); #endif registerClass(); @@ -281,6 +273,19 @@ static id getAccessibilityParent (id self, SEL) return nil; } + static id getAccessibilityValue (id self, SEL) + { + if (auto* handler = getHandler (self)) + { + if (handler->getCurrentState().isCheckable()) + return handler->getCurrentState().isChecked() ? @(1) : @(0); + + return getAccessibilityValueFromInterfaces (*handler); + } + + return nil; + } + static NSArray* getAccessibilitySelectedChildren (id self, SEL) { return getSelectedChildren ([self accessibilityChildren]); @@ -876,6 +881,15 @@ static void sendHandlerNotification (const AccessibilityHandler& handler, } } +static NSAccessibilityNotificationName layoutChangedNotification() +{ + if (@available (macOS 10.9, *)) + return NSAccessibilityLayoutChangedNotification; + + static NSString* layoutChangedString = @"AXLayoutChanged"; + return layoutChangedString; +} + void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType) { auto notification = [eventType] @@ -884,7 +898,7 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte { case InternalAccessibilityEvent::elementCreated: return NSAccessibilityCreatedNotification; case InternalAccessibilityEvent::elementDestroyed: return NSAccessibilityUIElementDestroyedNotification; - case InternalAccessibilityEvent::elementMovedOrResized: return NSAccessibilityLayoutChangedNotificationJuce; + case InternalAccessibilityEvent::elementMovedOrResized: return layoutChangedNotification(); case InternalAccessibilityEvent::focusChanged: return NSAccessibilityFocusedUIElementChangedNotification; case InternalAccessibilityEvent::windowOpened: return NSAccessibilityWindowCreatedNotification; case InternalAccessibilityEvent::windowClosed: break; @@ -908,7 +922,7 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte case AccessibilityEvent::textChanged: case AccessibilityEvent::valueChanged: return NSAccessibilityValueChangedNotification; case AccessibilityEvent::titleChanged: return NSAccessibilityTitleChangedNotification; - case AccessibilityEvent::structureChanged: return NSAccessibilityLayoutChangedNotificationJuce; + case AccessibilityEvent::structureChanged: return layoutChangedNotification(); } return NSAccessibilityNotificationName{}; @@ -922,9 +936,7 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte if (! areAnyAccessibilityClientsActive()) return; - #if JUCE_OBJC_HAS_AVAILABLE_FEATURE if (@available (macOS 10.10, *)) - #endif { auto nsPriority = [priority] { @@ -948,6 +960,4 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte JUCE_END_IGNORE_WARNINGS_GCC_LIKE -#endif - } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm index a0bec48fb..c3050709e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm @@ -27,21 +27,23 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). { //============================================================================== -template -class AccessibleObjCClass : public ObjCClass +struct AccessibleObjCClassDeleter { -private: - struct Deleter + template + void operator() (ElementType* element) const { - void operator() (Base* element) const - { - object_setInstanceVariable (element, "handler", nullptr); - [element release]; - } - }; + juceFreeAccessibilityPlatformSpecificData (element); + object_setInstanceVariable (element, "handler", nullptr); + [element release]; + } +}; + +template +class AccessibleObjCClass : public ObjCClass +{ public: - using Holder = std::unique_ptr; + using Holder = std::unique_ptr; protected: AccessibleObjCClass() : ObjCClass ("JUCEAccessibilityElement_") @@ -76,6 +78,17 @@ static bool hasEditableText (AccessibilityHandler& handler) noexcept && ! handler.getTextInterface()->isReadOnly(); } + static id getAccessibilityValueFromInterfaces (const AccessibilityHandler& handler) + { + if (auto* textInterface = handler.getTextInterface()) + return juceStringToNS (textInterface->getText ({ 0, textInterface->getTotalNumCharacters() })); + + if (auto* valueInterface = handler.getValueInterface()) + return juceStringToNS (valueInterface->getCurrentValueAsString()); + + return nil; + } + //============================================================================== static BOOL getIsAccessibilityElement (id self, SEL) { @@ -86,30 +99,6 @@ static BOOL getIsAccessibilityElement (id self, SEL) return NO; } - static id getAccessibilityValue (id self, SEL) - { - if (auto* handler = getHandler (self)) - { - if (auto* textInterface = handler->getTextInterface()) - return juceStringToNS (textInterface->getText ({ 0, textInterface->getTotalNumCharacters() })); - - if (handler->getCurrentState().isCheckable()) - { - return handler->getCurrentState().isChecked() - #if JUCE_IOS - ? @"1" : @"0"; - #else - ? @(1) : @(0); - #endif - } - - if (auto* valueInterface = handler->getValueInterface()) - return juceStringToNS (valueInterface->getCurrentValueAsString()); - } - - return nil; - } - static void setAccessibilityValue (id self, SEL, NSString* value) { if (auto* handler = getHandler (self)) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp index ff8dc5d8b..7432e3766 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp @@ -63,14 +63,14 @@ class AccessibilityHandler::AccessibilityNativeImpl ~AccessibilityNativeImpl() { + ComSmartPtr provider; + accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress())); + accessibilityElement->invalidateElement(); --providerCount; if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) { - ComSmartPtr provider; - accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress())); - uiaWrapper->disconnectProvider (provider); if (providerCount == 0) @@ -134,7 +134,7 @@ void sendAccessibilityAutomationEvent (const AccessibilityHandler& handler, EVEN { jassert (event != EVENTID{}); - getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* uiaWrapper, ComSmartPtr& provider) + getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* uiaWrapper, ComSmartPtr& provider) { uiaWrapper->raiseAutomationEvent (provider, event); }); @@ -164,6 +164,14 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte return; } + if (eventType == InternalAccessibilityEvent::windowOpened + || eventType == InternalAccessibilityEvent::windowClosed) + { + if (auto* peer = handler.getComponent().getPeer()) + if ((peer->getStyleFlags() & ComponentPeer::windowHasTitleBar) == 0) + return; + } + auto event = [eventType]() -> EVENTID { switch (eventType) @@ -191,6 +199,20 @@ void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventTyp VariantHelpers::setString (getTitle(), &newValue); sendAccessibilityPropertyChangedEvent (*this, UIA_NamePropertyId, newValue); + return; + } + + if (eventType == AccessibilityEvent::valueChanged) + { + if (auto* valueInterface = getValueInterface()) + { + VARIANT newValue; + VariantHelpers::setString (valueInterface->getCurrentValueAsString(), &newValue); + + sendAccessibilityPropertyChangedEvent (*this, UIA_ValueValuePropertyId, newValue); + } + + return; } auto event = [eventType]() -> EVENTID diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp index 62204e113..13a7e1a1b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp @@ -264,10 +264,9 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyI { VariantHelpers::clear (pRetVal); - const auto fragmentRoot = isFragmentRoot(); - - const auto role = accessibilityHandler.getRole(); - const auto state = accessibilityHandler.getCurrentState(); + const auto role = accessibilityHandler.getRole(); + const auto state = accessibilityHandler.getCurrentState(); + const auto ignored = accessibilityHandler.isIgnored(); switch (propertyId) { @@ -287,7 +286,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyI VariantHelpers::setString (accessibilityHandler.getHelp(), pRetVal); break; case UIA_IsContentElementPropertyId: - VariantHelpers::setBool (! accessibilityHandler.isIgnored() && accessibilityHandler.isVisibleWithinParent(), + VariantHelpers::setBool (! ignored && accessibilityHandler.isVisibleWithinParent(), pRetVal); break; case UIA_IsControlElementPropertyId: @@ -320,13 +319,15 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyI pRetVal); break; case UIA_NamePropertyId: - VariantHelpers::setString (getElementName(), pRetVal); + if (! ignored) + VariantHelpers::setString (getElementName(), pRetVal); + break; case UIA_ProcessIdPropertyId: VariantHelpers::setInt ((int) GetCurrentProcessId(), pRetVal); break; case UIA_NativeWindowHandlePropertyId: - if (fragmentRoot) + if (isFragmentRoot()) VariantHelpers::setInt ((int) (pointer_sized_int) accessibilityHandler.getComponent().getWindowHandle(), pRetVal); break; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h index 28191d774..e9852a050 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h @@ -70,7 +70,7 @@ class AccessibilityNativeHandle : public ComBaseClassHelper rtid { UiaAppendRuntimeId, ++idCounter }; + std::array rtid { UiaAppendRuntimeId, ++idCounter }; bool valid = true; //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h index 4a3a29087..2f7991178 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h @@ -124,8 +124,8 @@ class WindowsUIAWrapper : public DeletedAtShutdown } //============================================================================== - template - static FuncType getUiaFunction (HMODULE module, StringRef funcName) + template + static FuncType getUiaFunction (HMODULE module, LPCSTR funcName) { return (FuncType) GetProcAddress (module, funcName); } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ScopedDPIAwarenessDisabler.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ScopedDPIAwarenessDisabler.h new file mode 100644 index 000000000..8c5d60313 --- /dev/null +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ScopedDPIAwarenessDisabler.h @@ -0,0 +1,54 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +//============================================================================== +/** + A Windows-specific class that temporarily sets the DPI awareness context of + the current thread to be DPI unaware and resets it to the previous context + when it goes out of scope. + + If you create one of these before creating a top-level window, the window + will be DPI unaware and bitmap stretched by the OS on a display with >100% + scaling. + + You shouldn't use this unless you really know what you are doing and + are dealing with native HWNDs. + + @tags{GUI} +*/ +class JUCE_API ScopedDPIAwarenessDisabler +{ +public: + ScopedDPIAwarenessDisabler(); + ~ScopedDPIAwarenessDisabler(); + +private: + void* previousContext = nullptr; +}; + +} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_android_Windowing.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_android_Windowing.cpp index cc9969671..5709cb2be 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_android_Windowing.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_android_Windowing.cpp @@ -1236,6 +1236,86 @@ bool Desktop::canUseSemiTransparentWindows() noexcept return true; } +class Desktop::NativeDarkModeChangeDetectorImpl : public ActivityLifecycleCallbacks +{ +public: + NativeDarkModeChangeDetectorImpl() + { + LocalRef appContext (getAppContext()); + + if (appContext != nullptr) + { + auto* env = getEnv(); + + myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks")); + env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get()); + } + } + + ~NativeDarkModeChangeDetectorImpl() override + { + LocalRef appContext (getAppContext()); + + if (appContext != nullptr && myself != nullptr) + { + auto* env = getEnv(); + + env->CallVoidMethod (appContext.get(), + AndroidApplication.unregisterActivityLifecycleCallbacks, + myself.get()); + clear(); + myself.clear(); + } + } + + bool isDarkModeEnabled() const noexcept { return darkModeEnabled; } + + void onActivityStarted (jobject /*activity*/) override + { + const auto isEnabled = getDarkModeSetting(); + + if (darkModeEnabled != isEnabled) + { + darkModeEnabled = isEnabled; + Desktop::getInstance().darkModeChanged(); + } + } + +private: + static bool getDarkModeSetting() + { + auto* env = getEnv(); + + const LocalRef resources (env->CallObjectMethod (getAppContext().get(), AndroidContext.getResources)); + const LocalRef configuration (env->CallObjectMethod (resources, AndroidResources.getConfiguration)); + + const auto uiMode = env->GetIntField (configuration, AndroidConfiguration.uiMode); + + return ((uiMode & UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES); + } + + static constexpr int UI_MODE_NIGHT_MASK = 0x00000030, + UI_MODE_NIGHT_NO = 0x00000010, + UI_MODE_NIGHT_UNDEFINED = 0x00000000, + UI_MODE_NIGHT_YES = 0x00000020; + + GlobalRef myself; + bool darkModeEnabled = getDarkModeSetting(); + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) +}; + +std::unique_ptr Desktop::createNativeDarkModeChangeDetectorImpl() +{ + return std::make_unique(); +} + +bool Desktop::isDarkModeActive() const +{ + return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled(); +} + double Desktop::getDefaultMasterScale() { return 1.0; @@ -1793,12 +1873,14 @@ Image juce_createIconForFile (const File& /*file*/) } //============================================================================== -void* CustomMouseCursorInfo::create() const { return nullptr; } -void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType) { return nullptr; } -void MouseCursor::deleteMouseCursor (void* /*cursorHandle*/, bool /*isStandard*/) {} +class MouseCursor::PlatformSpecificHandle +{ +public: + PlatformSpecificHandle (const MouseCursor::StandardCursorType) {} + PlatformSpecificHandle (const CustomMouseCursorInfo&) {} -//============================================================================== -void MouseCursor::showInWindow (ComponentPeer*) const {} + static void showInWindow (PlatformSpecificHandle*, ComponentPeer*) {} +}; //============================================================================== bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& /*files*/, bool /*canMove*/, diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_ContentSharer.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_ContentSharer.cpp index d72466b32..1971201a1 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_ContentSharer.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_ContentSharer.cpp @@ -26,6 +26,10 @@ namespace juce { +#if ! defined (__IPHONE_10_0) || __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_10_0 + using UIActivityType = NSString*; +#endif + class ContentSharer::ContentSharerNativeImpl : public ContentSharer::Pimpl, private Component { @@ -172,7 +176,7 @@ class ContentSharer::ContentSharerNativeImpl : public ContentSharer::Pimpl, { PopoverDelegateClass() : ObjCClass> ("PopoverDelegateClass_") { - addMethod (@selector (popoverPresentationController:willRepositionPopoverToRect:inView:), willRepositionPopover, "v@:@@@"); + addMethod (@selector (popoverPresentationController:willRepositionPopoverToRect:inView:), willRepositionPopover); registerClass(); } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_FileChooser.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_FileChooser.mm index dcb956659..c3dea9a44 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_FileChooser.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_FileChooser.mm @@ -26,7 +26,7 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). namespace juce { -#if ! (defined (__IPHONE_16_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_16_0) +#if ! (defined (__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0) JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") #define JUCE_DEPRECATION_IGNORED 1 #endif @@ -87,6 +87,8 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). { controller.reset ([controllerClassInstance initWithDocumentTypes: utTypeArray inMode: UIDocumentPickerModeOpen]); + if (@available (iOS 11.0, *)) + [controller.get() setAllowsMultipleSelection: (flags & FileBrowserComponent::canSelectMultipleItems) != 0]; } FileChooserControllerClass::setOwner (controller.get(), this); @@ -224,37 +226,47 @@ static String getFilename (const File& path, const String& fallbackExtension) } //============================================================================== - void didPickDocumentAtURL (NSURL* url) + void didPickDocumentsAtURLs (NSArray* urls) { cancelPendingUpdate(); - bool isWriting = controller.get().documentPickerMode == UIDocumentPickerModeExportToService - | controller.get().documentPickerMode == UIDocumentPickerModeMoveToService; - - NSUInteger accessOptions = isWriting ? 0 : NSFileCoordinatorReadingWithoutChanges; + const auto isWriting = controller.get().documentPickerMode == UIDocumentPickerModeExportToService + || controller.get().documentPickerMode == UIDocumentPickerModeMoveToService; + const auto accessOptions = isWriting ? 0 : NSFileCoordinatorReadingWithoutChanges; - auto* fileAccessIntent = isWriting - ? [NSFileAccessIntent writingIntentWithURL: url options: accessOptions] - : [NSFileAccessIntent readingIntentWithURL: url options: accessOptions]; + auto* fileCoordinator = [[[NSFileCoordinator alloc] initWithFilePresenter: nil] autorelease]; + auto* intents = [[[NSMutableArray alloc] init] autorelease]; - NSArray* intents = @[fileAccessIntent]; - - auto fileCoordinator = [[[NSFileCoordinator alloc] initWithFilePresenter: nil] autorelease]; + for (NSURL* url in urls) + { + auto* fileAccessIntent = isWriting + ? [NSFileAccessIntent writingIntentWithURL: url options: accessOptions] + : [NSFileAccessIntent readingIntentWithURL: url options: accessOptions]; + [intents addObject: fileAccessIntent]; + } [fileCoordinator coordinateAccessWithIntents: intents queue: [NSOperationQueue mainQueue] byAccessor: ^(NSError* err) { - Array chooserResults; + if (err != nil) + { + auto desc = [err localizedDescription]; + ignoreUnused (desc); + jassertfalse; + return; + } + + Array result; - if (err == nil) + for (NSURL* url in urls) { [url startAccessingSecurityScopedResource]; NSError* error = nil; - NSData* bookmark = [url bookmarkDataWithOptions: 0 - includingResourceValuesForKeys: nil - relativeToURL: nil - error: &error]; + auto* bookmark = [url bookmarkDataWithOptions: 0 + includingResourceValuesForKeys: nil + relativeToURL: nil + error: &error]; [bookmark retain]; @@ -273,19 +285,18 @@ void didPickDocumentAtURL (NSURL* url) jassertfalse; } - chooserResults.add (juceUrl); - } - else - { - auto desc = [err localizedDescription]; - ignoreUnused (desc); - jassertfalse; + result.add (std::move (juceUrl)); } - owner.finished (chooserResults); + owner.finished (std::move (result)); }]; } + void didPickDocumentAtURL (NSURL* url) + { + didPickDocumentsAtURLs (@[url]); + } + void pickerWasCancelled() { cancelPendingUpdate(); @@ -301,8 +312,9 @@ void pickerWasCancelled() { addIvar ("owner"); - addMethod (@selector (documentPicker:didPickDocumentAtURL:), didPickDocumentAtURL, "v@:@@"); - addMethod (@selector (documentPickerWasCancelled:), documentPickerWasCancelled, "v@:@"); + addMethod (@selector (documentPicker:didPickDocumentAtURL:), didPickDocumentAtURL); + addMethod (@selector (documentPicker:didPickDocumentsAtURLs:), didPickDocumentsAtURLs); + addMethod (@selector (documentPickerWasCancelled:), documentPickerWasCancelled); addProtocol (@protocol (UIDocumentPickerDelegate)); @@ -319,6 +331,12 @@ static void didPickDocumentAtURL (id self, SEL, UIDocumentPickerViewController*, picker->didPickDocumentAtURL (url); } + static void didPickDocumentsAtURLs (id self, SEL, UIDocumentPickerViewController*, NSArray* urls) + { + if (auto* picker = getOwner (self)) + picker->didPickDocumentsAtURLs (urls); + } + static void documentPickerWasCancelled (id self, SEL, UIDocumentPickerViewController*) { if (auto* picker = getOwner (self)) @@ -331,7 +349,7 @@ static void documentPickerWasCancelled (id self, SEL, UIDocumentPickerViewContro FileChooserControllerClass() : ObjCClass ("FileChooserController_") { addIvar ("owner"); - addMethod (@selector (viewDidDisappear:), viewDidDisappear, "v@:@c"); + addMethod (@selector (viewDidDisappear:), viewDidDisappear); registerClass(); } @@ -382,6 +400,4 @@ static void viewDidDisappear (id self, SEL, BOOL animated) JUCE_END_IGNORE_WARNINGS_GCC_LIKE #endif -#undef JUCE_DEPRECATION_IGNORED - } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm index 3f080588b..301ace17c 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm @@ -23,7 +23,7 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). ============================================================================== */ -#if defined (__IPHONE_13_0) +#if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 #define JUCE_HAS_IOS_POINTER_SUPPORT 1 #else #define JUCE_HAS_IOS_POINTER_SUPPORT 0 @@ -38,15 +38,18 @@ static UIInterfaceOrientation getWindowOrientation() { UIApplication* sharedApplication = [UIApplication sharedApplication]; - #if (defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_13_0) - for (UIScene* scene in [sharedApplication connectedScenes]) - if ([scene isKindOfClass: [UIWindowScene class]]) - return [(UIWindowScene*) scene interfaceOrientation]; + #if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 + if (@available (iOS 13.0, *)) + { + for (UIScene* scene in [sharedApplication connectedScenes]) + if ([scene isKindOfClass: [UIWindowScene class]]) + return [(UIWindowScene*) scene interfaceOrientation]; + } + #endif - return UIInterfaceOrientationPortrait; - #else + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") return [sharedApplication statusBarOrientation]; - #endif + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } namespace Orientations @@ -137,6 +140,8 @@ - (BOOL) canBecomeFirstResponder; - (BOOL) textView: (UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text; +- (void) traitCollectionDidChange: (UITraitCollection*) previousTraitCollection; + - (BOOL) isAccessibilityElement; - (CGRect) accessibilityFrame; - (NSArray*) accessibilityElements; @@ -270,6 +275,11 @@ static int64 getMouseTime (UIEvent* e) noexcept return getMouseTime ([e timestamp]); } + static NSString* getDarkModeNotificationName() + { + return @"ViewDarkModeChanged"; + } + static MultiTouchMapper currentTouches; private: @@ -296,25 +306,27 @@ void messageCallback() override JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer) }; -static void sendScreenBoundsUpdate (JuceUIViewController* c) +static UIViewComponentPeer* getViewPeer (JuceUIViewController* c) { - JuceUIView* juceView = (JuceUIView*) [c view]; + if (JuceUIView* juceView = (JuceUIView*) [c view]) + return juceView->owner; - if (juceView != nil && juceView->owner != nullptr) - juceView->owner->updateScreenBounds(); + jassertfalse; + return nullptr; } -static bool isKioskModeView (JuceUIViewController* c) +static void sendScreenBoundsUpdate (JuceUIViewController* c) { - JuceUIView* juceView = (JuceUIView*) [c view]; + if (auto* peer = getViewPeer (c)) + peer->updateScreenBounds(); +} - if (juceView == nil || juceView->owner == nullptr) - { - jassertfalse; - return false; - } +static bool isKioskModeView (JuceUIViewController* c) +{ + if (auto* peer = getViewPeer (c)) + return Desktop::getInstance().getKioskModeComponent() == &(peer->getComponent()); - return Desktop::getInstance().getKioskModeComponent() == &(juceView->owner->getComponent()); + return false; } MultiTouchMapper UIViewComponentPeer::currentTouches; @@ -544,6 +556,22 @@ - (BOOL) textView: (UITextView*) textView shouldChangeTextInRange: (NSRange) ran nsStringToJuce (text)); } +- (void) traitCollectionDidChange: (UITraitCollection*) previousTraitCollection +{ + [super traitCollectionDidChange: previousTraitCollection]; + + #if defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0 + if (@available (iOS 12.0, *)) + { + const auto wasDarkModeActive = ([previousTraitCollection userInterfaceStyle] == UIUserInterfaceStyleDark); + + if (wasDarkModeActive != Desktop::getInstance().isDarkModeActive()) + [[NSNotificationCenter defaultCenter] postNotificationName: UIViewComponentPeer::getDarkModeNotificationName() + object: nil]; + } + #endif +} + - (BOOL) isAccessibilityElement { return NO; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_Windowing.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_Windowing.mm index 7960a87aa..4b373e521 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_Windowing.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_Windowing.mm @@ -437,8 +437,11 @@ int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr) class iOSMessageBox { public: - iOSMessageBox (const MessageBoxOptions& opts, std::unique_ptr&& cb) - : callback (std::move (cb)) + iOSMessageBox (const MessageBoxOptions& opts, + std::unique_ptr&& cb, + bool deleteOnCompletion) + : callback (std::move (cb)), + shouldDeleteThis (deleteOnCompletion) { if (currentlyFocusedPeer != nullptr) { @@ -480,10 +483,10 @@ void buttonClicked (int buttonIndex) noexcept result = buttonIndex; if (callback != nullptr) - { callback->modalStateFinished (result); + + if (shouldDeleteThis) delete this; - } } private: @@ -501,6 +504,7 @@ void addButton (UIAlertController* alert, const String& text) int result = -1; std::unique_ptr callback; + const bool shouldDeleteThis; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox) }; @@ -517,13 +521,24 @@ static int showDialog (const MessageBoxOptions& options, { jassert (mapFn != nullptr); - iOSMessageBox messageBox (options, nullptr); + iOSMessageBox messageBox (options, nullptr, false); return mapFn (messageBox.getResult()); } } #endif - new iOSMessageBox (options, AlertWindowMappings::getWrappedCallback (callbackIn, mapFn)); + const auto showBox = [options, callbackIn, mapFn] + { + new iOSMessageBox (options, + AlertWindowMappings::getWrappedCallback (callbackIn, mapFn), + true); + }; + + if (MessageManager::getInstance()->isThisTheMessageThread()) + showBox(); + else + MessageManager::callAsync (showBox); + return 0; } @@ -658,7 +673,7 @@ Image juce_createIconForFile (const File&) String SystemClipboard::getTextFromClipboard() { - return nsStringToJuce ([[UIPasteboard generalPasteboard] valueForPasteboardType: @"public.text"]); + return nsStringToJuce ([[UIPasteboard generalPasteboard] string]); } //============================================================================== @@ -678,6 +693,77 @@ Image juce_createIconForFile (const File&) return true; } +bool Desktop::isDarkModeActive() const +{ + #if defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0 + if (@available (iOS 12.0, *)) + return [[[UIScreen mainScreen] traitCollection] userInterfaceStyle] == UIUserInterfaceStyleDark; + #endif + + return false; +} + +class Desktop::NativeDarkModeChangeDetectorImpl +{ +public: + NativeDarkModeChangeDetectorImpl() + { + static DelegateClass delegateClass; + + delegate = [delegateClass.createInstance() init]; + object_setInstanceVariable (delegate, "owner", this); + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + [[NSNotificationCenter defaultCenter] addObserver: delegate + selector: @selector (darkModeChanged:) + name: UIViewComponentPeer::getDarkModeNotificationName() + object: nil]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } + + ~NativeDarkModeChangeDetectorImpl() + { + object_setInstanceVariable (delegate, "owner", nullptr); + [[NSNotificationCenter defaultCenter] removeObserver: delegate]; + [delegate release]; + } + + void darkModeChanged() + { + Desktop::getInstance().darkModeChanged(); + } + +private: + struct DelegateClass : public ObjCClass + { + DelegateClass() : ObjCClass ("JUCEDelegate_") + { + addIvar ("owner"); + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + addMethod (@selector (darkModeChanged:), darkModeChanged); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + registerClass(); + } + + static void darkModeChanged (id self, SEL, NSNotification*) + { + if (auto* owner = getIvar (self, "owner")) + owner->darkModeChanged(); + } + }; + + id delegate = nil; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) +}; + +std::unique_ptr Desktop::createNativeDarkModeChangeDetectorImpl() +{ + return std::make_unique(); +} + Point MouseInputSource::getCurrentRawMousePosition() { return juce_lastMousePos; @@ -716,19 +802,25 @@ Image juce_createIconForFile (const File&) static BorderSize getSafeAreaInsets (float masterScale) { - #if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0 - UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets; + #if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0 + if (@available (iOS 11.0, *)) + { + UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets; + + auto getInset = [&] (CGFloat original) { return roundToInt (original / masterScale); }; - auto getInset = [&] (CGFloat original) { return roundToInt (original / masterScale); }; + return { getInset (safeInsets.top), getInset (safeInsets.left), + getInset (safeInsets.bottom), getInset (safeInsets.right) }; + } + #endif - return { getInset (safeInsets.top), getInset (safeInsets.left), - getInset (safeInsets.bottom), getInset (safeInsets.right) }; - #else + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") auto statusBarSize = [UIApplication sharedApplication].statusBarFrame.size; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + auto statusBarHeight = jmin (statusBarSize.width, statusBarSize.height); return { roundToInt (statusBarHeight / masterScale), 0, 0, 0 }; - #endif } void Displays::findDisplays (float masterScale) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp index 703af843e..e51c10ce2 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp @@ -39,16 +39,23 @@ static bool exeIsAvailable (String executable) return false; } +static bool isSet (int flags, int toCheck) +{ + return (flags & toCheck) != 0; +} + class FileChooser::Native : public FileChooser::Pimpl, private Timer { public: Native (FileChooser& fileChooser, int flags) : owner (fileChooser), - isDirectory ((flags & FileBrowserComponent::canSelectDirectories) != 0), - isSave ((flags & FileBrowserComponent::saveMode) != 0), - selectMultipleFiles ((flags & FileBrowserComponent::canSelectMultipleItems) != 0), - warnAboutOverwrite ((flags & FileBrowserComponent::warnAboutOverwriting) != 0) + // kdialog/zenity only support opening either files or directories. + // Files should take precedence, if requested. + isDirectory (isSet (flags, FileBrowserComponent::canSelectDirectories) && ! isSet (flags, FileBrowserComponent::canSelectFiles)), + isSave (isSet (flags, FileBrowserComponent::saveMode)), + selectMultipleFiles (isSet (flags, FileBrowserComponent::canSelectMultipleItems)), + warnAboutOverwrite (isSet (flags, FileBrowserComponent::warnAboutOverwriting)) { const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 6c862d0c4..c581ab429 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -31,7 +31,8 @@ static int numAlwaysOnTopPeers = 0; bool juce_areThereAnyAlwaysOnTopWindows() { return numAlwaysOnTopPeers > 0; } //============================================================================== -class LinuxComponentPeer : public ComponentPeer +class LinuxComponentPeer : public ComponentPeer, + private XWindowSystemUtilities::XSettings::Listener { public: LinuxComponentPeer (Component& comp, int windowStyleFlags, ::Window parentToAddTo) @@ -41,7 +42,9 @@ class LinuxComponentPeer : public ComponentPeer // it's dangerous to create a window on a thread other than the message thread. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED - if (! XWindowSystem::getInstance()->isX11Available()) + const auto* instance = XWindowSystem::getInstance(); + + if (! instance->isX11Available()) return; if (isAlwaysOnTop) @@ -49,11 +52,14 @@ class LinuxComponentPeer : public ComponentPeer repainter = std::make_unique (*this); - windowH = XWindowSystem::getInstance()->createWindow (parentToAddTo, this); + windowH = instance->createWindow (parentToAddTo, this); parentWindow = parentToAddTo; setTitle (component.getName()); + if (auto* xSettings = instance->getXSettings()) + xSettings->addListener (this); + getNativeRealtimeModifiers = []() -> ModifierKeys { return XWindowSystem::getInstance()->getNativeRealtimeModifiers(); }; } @@ -62,8 +68,13 @@ class LinuxComponentPeer : public ComponentPeer // it's dangerous to delete a window on a thread other than the message thread. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED + auto* instance = XWindowSystem::getInstance(); + repainter = nullptr; - XWindowSystem::getInstance()->destroyWindow (windowH); + instance->destroyWindow (windowH); + + if (auto* xSettings = instance->getXSettings()) + xSettings->removeListener (this); if (isAlwaysOnTop) --numAlwaysOnTopPeers; @@ -322,18 +333,30 @@ class LinuxComponentPeer : public ComponentPeer void setParentWindow (::Window newParent) { parentWindow = newParent; } //============================================================================== + bool isConstrainedNativeWindow() const + { + return constrainer != nullptr + && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable) + && ! isKioskMode(); + } + void updateWindowBounds() { - jassert (windowH != 0); - if (windowH != 0) + if (windowH == 0) { - auto physicalBounds = XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow); + jassertfalse; + return; + } - updateScaleFactorFromNewBounds (physicalBounds, true); + if (isConstrainedNativeWindow()) + XWindowSystem::getInstance()->updateConstraints (windowH); - bounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds) - : physicalBounds / currentScaleFactor; - } + auto physicalBounds = XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow); + + updateScaleFactorFromNewBounds (physicalBounds, true); + + bounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds) + : physicalBounds / currentScaleFactor; } void updateBorderSize() @@ -448,6 +471,16 @@ class LinuxComponentPeer : public ComponentPeer }; //============================================================================== + void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override + { + static StringArray possibleSettings { XWindowSystem::getWindowScalingFactorSettingName(), + "Gdk/UnscaledDPI", + "Xft/DPI" }; + + if (possibleSettings.contains (settingThatHasChanged.name)) + forceDisplayUpdate(); + } + void updateScaleFactorFromNewBounds (const Rectangle& newBounds, bool isPhysical) { Point translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point()); @@ -517,6 +550,55 @@ bool Desktop::canUseSemiTransparentWindows() noexcept return XWindowSystem::getInstance()->canUseSemiTransparentWindows(); } +class Desktop::NativeDarkModeChangeDetectorImpl : private XWindowSystemUtilities::XSettings::Listener +{ +public: + NativeDarkModeChangeDetectorImpl() + { + const auto* windowSystem = XWindowSystem::getInstance(); + + if (auto* xSettings = windowSystem->getXSettings()) + xSettings->addListener (this); + + darkModeEnabled = windowSystem->isDarkModeActive(); + } + + ~NativeDarkModeChangeDetectorImpl() override + { + if (auto* windowSystem = XWindowSystem::getInstanceWithoutCreating()) + if (auto* xSettings = windowSystem->getXSettings()) + xSettings->removeListener (this); + } + + bool isDarkModeEnabled() const noexcept { return darkModeEnabled; } + +private: + void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override + { + if (settingThatHasChanged.name == XWindowSystem::getThemeNameSettingName()) + { + const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, XWindowSystem::getInstance()->isDarkModeActive()); + + if (darkModeEnabled != wasDarkModeEnabled) + Desktop::getInstance().darkModeChanged(); + } + } + + bool darkModeEnabled = false; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) +}; + +std::unique_ptr Desktop::createNativeDarkModeChangeDetectorImpl() +{ + return std::make_unique(); +} + +bool Desktop::isDarkModeActive() const +{ + return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled(); +} + static bool screenSaverAllowed = true; void Desktop::setScreenSaverEnabled (bool isEnabled) @@ -566,27 +648,48 @@ void MouseInputSource::setRawMousePosition (Point newPosition) } //============================================================================== -void* CustomMouseCursorInfo::create() const +class MouseCursor::PlatformSpecificHandle { - return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image, hotspot); -} +public: + explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type) + : cursorHandle (makeHandle (type)) {} -void MouseCursor::deleteMouseCursor (void* cursorHandle, bool) -{ - if (cursorHandle != nullptr) - XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle); -} + explicit PlatformSpecificHandle (const CustomMouseCursorInfo& info) + : cursorHandle (makeHandle (info)) {} -void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type) -{ - return XWindowSystem::getInstance()->createStandardMouseCursor (type); -} + ~PlatformSpecificHandle() + { + if (cursorHandle != Cursor{}) + XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle); + } -void MouseCursor::showInWindow (ComponentPeer* peer) const -{ - if (peer != nullptr) - XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), getHandle()); -} + static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer) + { + const auto cursor = handle != nullptr ? handle->cursorHandle : Cursor{}; + + if (peer != nullptr) + XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), cursor); + } + +private: + static Cursor makeHandle (const CustomMouseCursorInfo& info) + { + const auto image = info.image.getImage(); + return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image.rescaled ((int) (image.getWidth() / info.image.getScale()), + (int) (image.getHeight() / info.image.getScale())), info.hotspot); + } + + static Cursor makeHandle (MouseCursor::StandardCursorType type) + { + return XWindowSystem::getInstance()->createStandardMouseCursor (type); + } + + Cursor cursorHandle; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE (PlatformSpecificHandle) + JUCE_DECLARE_NON_MOVEABLE (PlatformSpecificHandle) +}; //============================================================================== static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_FileChooser.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_FileChooser.mm index a92ce156e..77fb0b721 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_FileChooser.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_FileChooser.mm @@ -82,8 +82,9 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). filters.trim(); filters.removeEmptyStrings(); - NSString* nsTitle = juceStringToNS (owner.title); + auto* nsTitle = juceStringToNS (owner.title); [panel setTitle: nsTitle]; + [panel setReleasedWhenClosed: YES]; JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") [panel setAllowedFileTypes: createAllowedTypesArray (filters)]; @@ -153,22 +154,14 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). if (nsViewPreview != nil) { [panel setAccessoryView: nil]; - [nsViewPreview release]; - - nsViewPreview = nil; - preview = nullptr; } [panel close]; - [panel release]; } if (delegate != nil) - { [delegate release]; - delegate = nil; - } } void launch() override @@ -179,10 +172,17 @@ void launch() override addToDesktop (0); enterModalState (true); - [panel beginWithCompletionHandler:CreateObjCBlock (this, &Native::finished)]; - if (preview != nullptr) - preview->toFront (true); + MessageManager::callAsync ([ref = SafePointer (this)] + { + if (ref == nullptr) + return; + + [ref->panel beginWithCompletionHandler: CreateObjCBlock (ref.getComponent(), &Native::finished)]; + + if (ref->preview != nullptr) + ref->preview->toFront (true); + }); } } @@ -213,28 +213,39 @@ bool canModalEventBeSentToComponent (const Component* targetComponent) override //============================================================================== typedef NSObject DelegateType; + static URL urlFromNSURL (NSURL* url) + { + const auto scheme = nsStringToJuce ([url scheme]); + + auto pathComponents = StringArray::fromTokens (nsStringToJuce ([url path]), "/", {}); + + for (auto& component : pathComponents) + component = URL::addEscapeChars (component, false); + + return { scheme + "://" + pathComponents.joinIntoString ("/") }; + } + void finished (NSInteger result) { Array chooserResults; exitModalState (0); - if (panel != nil && result == - #if defined (MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 - NSModalResponseOK) - #else - NSFileHandlingPanelOKButton) - #endif + const auto okResult = []() -> NSInteger { - auto addURLResult = [&chooserResults] (NSURL* urlToAdd) - { - auto scheme = nsStringToJuce ([urlToAdd scheme]); - auto pathComponents = StringArray::fromTokens (nsStringToJuce ([urlToAdd path]), "/", {}); + if (@available (macOS 10.9, *)) + return NSModalResponseOK; - for (auto& component : pathComponents) - component = URL::addEscapeChars (component, false); + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + return NSFileHandlingPanelOKButton; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + }(); - chooserResults.add (URL (scheme + "://" + pathComponents.joinIntoString ("/"))); + if (panel != nil && result == okResult) + { + auto addURLResult = [&chooserResults] (NSURL* urlToAdd) + { + chooserResults.add (urlFromNSURL (urlToAdd)); }; if (isSave) @@ -254,17 +265,15 @@ void finished (NSInteger result) owner.finished (chooserResults); } - bool shouldShowFilename (const String& filenameToTest) + BOOL shouldShowURL (const URL& urlToTest) { - const File f (filenameToTest); - auto nsFilename = juceStringToNS (filenameToTest); - for (int i = filters.size(); --i >= 0;) - if (f.getFileName().matchesWildcard (filters[i], true)) - return true; + if (urlToTest.getFileName().matchesWildcard (filters[i], true)) + return YES; + const auto f = urlToTest.getLocalFile(); return f.isDirectory() - && ! [[NSWorkspace sharedWorkspace] isFilePackageAtPath: nsFilename]; + && ! [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (f.getFullPathName())]; } void panelSelectionDidChange (id sender) @@ -323,7 +332,7 @@ void ensurePanelSafe() jassert ([panel preventsApplicationTerminationWhenModal]); } - static BOOL preventsApplicationTerminationWhenModal() { return YES; } + static BOOL preventsApplicationTerminationWhenModal (id, SEL) { return YES; } template struct SafeModalPanel : public ObjCClass @@ -331,8 +340,7 @@ void ensurePanelSafe() explicit SafeModalPanel (const char* name) : ObjCClass (name) { this->addMethod (@selector (preventsApplicationTerminationWhenModal), - preventsApplicationTerminationWhenModal, - "c@:"); + preventsApplicationTerminationWhenModal); this->registerClass(); } @@ -355,8 +363,8 @@ explicit SafeModalPanel (const char* name) : ObjCClass (name) { addIvar ("cppObject"); - addMethod (@selector (panel:shouldShowFilename:), shouldShowFilename, "c@:@@"); - addMethod (@selector (panelSelectionDidChange:), panelSelectionDidChange, "c@"); + addMethod (@selector (panel:shouldEnableURL:), shouldEnableURL); + addMethod (@selector (panelSelectionDidChange:), panelSelectionDidChange); addProtocol (@protocol (NSOpenSavePanelDelegate)); @@ -364,18 +372,14 @@ explicit SafeModalPanel (const char* name) : ObjCClass (name) } private: - static BOOL shouldShowFilename (id self, SEL, id /*sender*/, NSString* filename) + static BOOL shouldEnableURL (id self, SEL, id /*sender*/, NSURL* url) { - auto* _this = getIvar (self, "cppObject"); - - return _this->shouldShowFilename (nsStringToJuce (filename)) ? YES : NO; + return getIvar (self, "cppObject")->shouldShowURL (urlFromNSURL (url)); } static void panelSelectionDidChange (id self, SEL, id sender) { - auto* _this = getIvar (self, "cppObject"); - - _this->panelSelectionDidChange (sender); + getIvar (self, "cppObject")->panelSelectionDidChange (sender); } }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MainMenu.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MainMenu.mm index e46673e1f..27e6d3b1f 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MainMenu.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MainMenu.mm @@ -26,6 +26,10 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). namespace juce { +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") +const auto menuItemInvokedSelector = @selector (menuItemInvoked:); +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + //============================================================================== struct JuceMainMenuBarHolder : private DeletedAtShutdown { @@ -116,7 +120,6 @@ void addTopLevelMenu (NSMenu* parent, const PopupMenu& child, const String& name NSMenu* sub = createMenu (child, name, menuId, topLevelIndex, true); [parent setSubmenu: sub forItem: item]; - [sub setAutoenablesItems: false]; [sub release]; } @@ -130,7 +133,6 @@ void updateTopLevelMenu (NSMenuItem* parentItem, const PopupMenu& menuToCopy, co for (PopupMenu::MenuItemIterator iter (menuToCopy); iter.next();) addMenuItem (iter, menu, menuId, topLevelIndex); - [menu setAutoenablesItems: false]; [menu update]; removeItemRecursive ([parentItem submenu]); @@ -285,11 +287,9 @@ void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, } else { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") auto item = [[NSMenuItem alloc] initWithTitle: text - action: @selector (menuItemInvoked:) + action: menuItemInvokedSelector keyEquivalent: nsEmptyString()]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE [item setTag: topLevelIndex]; [item setEnabled: i.isEnabled]; @@ -338,8 +338,6 @@ void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, { NSMenu* m = [[NSMenu alloc] initWithTitle: juceStringToNS (menuName)]; - [m setAutoenablesItems: false]; - if (addDelegate) [m setDelegate: (id) callback]; @@ -368,11 +366,18 @@ void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, { NSArray* array = nil; - #if (! defined (MAC_OS_X_VERSION_10_8)) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 - [menuNib instantiateNibWithOwner: NSApp topLevelObjects: &array]; - #else - [menuNib instantiateWithOwner: NSApp topLevelObjects: &array]; - #endif + if (@available (macOS 10.11, *)) + { + [menuNib instantiateWithOwner: NSApp + topLevelObjects: &array]; + } + else + { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + [menuNib instantiateNibWithOwner: NSApp + topLevelObjects: &array]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } for (id object in array) { @@ -442,9 +447,35 @@ static void flashMenuBar (NSMenu* menu) NSString* f35String = [NSString stringWithCharacters: &f35Key length: 1]; NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: nsStringLiteral ("x") - action: nil + action: menuItemInvokedSelector keyEquivalent: f35String]; - [item setTarget: nil]; + + // When the f35Event is invoked, the item's enablement is checked and a + // NSBeep is triggered if the item appears to be disabled. + // This ValidatorClass exists solely to return YES from validateMenuItem. + struct ValidatorClass : public ObjCClass + { + ValidatorClass() : ObjCClass ("JUCEMenuValidator_") + { + addMethod (menuItemInvokedSelector, menuItemInvoked); + addMethod (@selector (validateMenuItem:), validateMenuItem); + + #if defined (MAC_OS_X_VERSION_10_14) + addProtocol (@protocol (NSMenuItemValidation)); + #endif + + registerClass(); + } + + private: + static BOOL validateMenuItem (id, SEL, NSMenuItem*) { return YES; } + static void menuItemInvoked (id, SEL, NSMenuItem*) {} + }; + + static ValidatorClass validatorClass; + static auto* instance = validatorClass.createInstance(); + + [item setTarget: instance]; [menu insertItem: item atIndex: [menu numberOfItems]]; [item release]; @@ -520,14 +551,16 @@ static void removeItemRecursive (NSMenu* menu) { addIvar ("owner"); - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - addMethod (@selector (menuItemInvoked:), menuItemInvoked, "v@:@"); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - addMethod (@selector (menuNeedsUpdate:), menuNeedsUpdate, "v@:@"); + addMethod (menuItemInvokedSelector, menuItemInvoked); + addMethod (@selector (menuNeedsUpdate:), menuNeedsUpdate); + addMethod (@selector (validateMenuItem:), validateMenuItem); addProtocol (@protocol (NSMenuDelegate)); + #if defined (MAC_OS_X_VERSION_10_14) + addProtocol (@protocol (NSMenuItemValidation)); + #endif + registerClass(); } @@ -537,15 +570,33 @@ static void setOwner (id self, JuceMainMenuHandler* owner) } private: + static auto* getPopupMenuItem (NSMenuItem* item) + { + return getJuceClassFromNSObject ([item representedObject]); + } + + static auto* getOwner (id self) + { + return getIvar (self, "owner"); + } + static void menuItemInvoked (id self, SEL, NSMenuItem* item) { - if (auto* juceItem = getJuceClassFromNSObject ([item representedObject])) - getIvar (self, "owner")->invoke (*juceItem, static_cast ([item tag])); + if (auto* juceItem = getPopupMenuItem (item)) + getOwner (self)->invoke (*juceItem, static_cast ([item tag])); } static void menuNeedsUpdate (id self, SEL, NSMenu* menu) { - getIvar (self, "owner")->updateTopLevelMenu (menu); + getOwner (self)->updateTopLevelMenu (menu); + } + + static BOOL validateMenuItem (id, SEL, NSMenuItem* item) + { + if (auto* juceItem = getPopupMenuItem (item)) + return juceItem->isEnabled; + + return YES; } }; }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm index 01a56bcf7..76a34f0f4 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm @@ -29,8 +29,34 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). #if JUCE_MAC //============================================================================== -namespace MouseCursorHelpers +class MouseCursor::PlatformSpecificHandle { +public: + PlatformSpecificHandle (const MouseCursor::StandardCursorType type) + : cursorHandle (createCursor (type)) {} + + PlatformSpecificHandle (const CustomMouseCursorInfo& info) + : cursorHandle (createCursor (info)) {} + + ~PlatformSpecificHandle() + { + [cursorHandle release]; + } + + static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer*) + { + auto c = [&] + { + if (handle == nullptr || handle->cursorHandle == nullptr) + return [NSCursor arrowCursor]; + + return handle->cursorHandle; + }(); + + [c set]; + } + +private: static NSCursor* fromNSImage (NSImage* im, NSPoint hotspot) { NSCursor* c = [[NSCursor alloc] initWithImage: im @@ -39,13 +65,13 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). return c; } - static void* fromHIServices (const char* filename) + static NSCursor* fromHIServices (const char* filename) { JUCE_AUTORELEASEPOOL { auto cursorPath = String ("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/" "HIServices.framework/Versions/A/Resources/cursors/") - + filename; + + filename; NSImage* originalImage = [[NSImage alloc] initByReferencingFile: juceStringToNS (cursorPath + "/cursor.pdf")]; NSSize originalSize = [originalImage size]; @@ -59,7 +85,7 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). if (CGImageRef rasterCGImage = [originalImage CGImageForProposedRect: nil context: nil hints: [NSDictionary dictionaryWithObjectsAndKeys: - NSImageHintCTM, scaleTransform, nil]]) + NSImageHintCTM, scaleTransform, nil]]) { NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithCGImage: rasterCGImage]; [imageRep setSize: originalSize]; @@ -83,98 +109,88 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). return fromNSImage (resultImage, NSMakePoint (hotspotX, hotspotY)); } } -} - -void* CustomMouseCursorInfo::create() const -{ - return MouseCursorHelpers::fromNSImage (imageToNSImage (image, scaleFactor), - NSMakePoint (hotspot.x, hotspot.y)); -} - -void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type) -{ - JUCE_AUTORELEASEPOOL + static NSCursor* createCursor (const CustomMouseCursorInfo& info) { - NSCursor* c = nil; + return fromNSImage (imageToNSImage (info.image), + NSMakePoint (info.hotspot.x, info.hotspot.y)); + } - switch (type) + static NSCursor* createCursor (const MouseCursor::StandardCursorType type) + { + JUCE_AUTORELEASEPOOL { - case NormalCursor: - case ParentCursor: c = [NSCursor arrowCursor]; break; - case NoCursor: return CustomMouseCursorInfo (Image (Image::ARGB, 8, 8, true), {}).create(); - case DraggingHandCursor: c = [NSCursor openHandCursor]; break; - case WaitCursor: c = [NSCursor arrowCursor]; break; // avoid this on the mac, let the OS provide the beachball - case IBeamCursor: c = [NSCursor IBeamCursor]; break; - case PointingHandCursor: c = [NSCursor pointingHandCursor]; break; - case LeftEdgeResizeCursor: c = [NSCursor resizeLeftCursor]; break; - case RightEdgeResizeCursor: c = [NSCursor resizeRightCursor]; break; - case CrosshairCursor: c = [NSCursor crosshairCursor]; break; - - case CopyingCursor: + NSCursor* c = nil; + + switch (type) { - c = [NSCursor dragCopyCursor]; - break; - } + case NormalCursor: + case ParentCursor: c = [NSCursor arrowCursor]; break; + case NoCursor: return createCursor ({ ScaledImage (Image (Image::ARGB, 8, 8, true)), {} }); + case DraggingHandCursor: c = [NSCursor openHandCursor]; break; + case WaitCursor: c = [NSCursor arrowCursor]; break; // avoid this on the mac, let the OS provide the beachball + case IBeamCursor: c = [NSCursor IBeamCursor]; break; + case PointingHandCursor: c = [NSCursor pointingHandCursor]; break; + case LeftEdgeResizeCursor: c = [NSCursor resizeLeftCursor]; break; + case RightEdgeResizeCursor: c = [NSCursor resizeRightCursor]; break; + case CrosshairCursor: c = [NSCursor crosshairCursor]; break; + + case CopyingCursor: + { + c = [NSCursor dragCopyCursor]; + break; + } - case UpDownResizeCursor: - case TopEdgeResizeCursor: - case BottomEdgeResizeCursor: - if (void* m = MouseCursorHelpers::fromHIServices ("resizenorthsouth")) - return m; + case UpDownResizeCursor: + case TopEdgeResizeCursor: + case BottomEdgeResizeCursor: + if (NSCursor* m = fromHIServices ("resizenorthsouth")) + return m; - c = [NSCursor resizeUpDownCursor]; - break; + c = [NSCursor resizeUpDownCursor]; + break; - case LeftRightResizeCursor: - if (void* m = MouseCursorHelpers::fromHIServices ("resizeeastwest")) - return m; + case LeftRightResizeCursor: + if (NSCursor* m = fromHIServices ("resizeeastwest")) + return m; - c = [NSCursor resizeLeftRightCursor]; - break; + c = [NSCursor resizeLeftRightCursor]; + break; - case TopLeftCornerResizeCursor: - case BottomRightCornerResizeCursor: - return MouseCursorHelpers::fromHIServices ("resizenorthwestsoutheast"); + case TopLeftCornerResizeCursor: + case BottomRightCornerResizeCursor: + return fromHIServices ("resizenorthwestsoutheast"); - case TopRightCornerResizeCursor: - case BottomLeftCornerResizeCursor: - return MouseCursorHelpers::fromHIServices ("resizenortheastsouthwest"); + case TopRightCornerResizeCursor: + case BottomLeftCornerResizeCursor: + return fromHIServices ("resizenortheastsouthwest"); - case UpDownLeftRightResizeCursor: - return MouseCursorHelpers::fromHIServices ("move"); + case UpDownLeftRightResizeCursor: + return fromHIServices ("move"); - case NumStandardCursorTypes: - default: - jassertfalse; - break; - } + case NumStandardCursorTypes: + default: + jassertfalse; + break; + } - [c retain]; - return c; + [c retain]; + return c; + } } -} - -void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool /*isStandard*/) -{ - [((NSCursor*) cursorHandle) release]; -} - -void MouseCursor::showInWindow (ComponentPeer*) const -{ - auto c = (NSCursor*) getHandle(); - - if (c == nil) - c = [NSCursor arrowCursor]; - [c set]; -} + NSCursor* cursorHandle; +}; #else -void* CustomMouseCursorInfo::create() const { return nullptr; } -void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType) { return nullptr; } -void MouseCursor::deleteMouseCursor (void*, bool) {} -void MouseCursor::showInWindow (ComponentPeer*) const {} +class MouseCursor::PlatformSpecificHandle +{ +public: + PlatformSpecificHandle (const MouseCursor::StandardCursorType) {} + PlatformSpecificHandle (const CustomMouseCursorInfo&) {} + + static void showInWindow (PlatformSpecificHandle*, ComponentPeer*) {} +}; #endif diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 92ca65653..504d5fed3 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -61,6 +61,16 @@ - (float)deviceDeltaY; [view registerForDraggedTypes: getSupportedDragTypes()]; + const auto options = NSTrackingMouseEnteredAndExited + | NSTrackingMouseMoved + | NSTrackingEnabledDuringMouseDrag + | NSTrackingActiveAlways + | NSTrackingInVisibleRect; + [view addTrackingArea: [[NSTrackingArea alloc] initWithRect: r + options: options + owner: view + userInfo: nil]]; + notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver: view @@ -70,12 +80,14 @@ - (float)deviceDeltaY; [view setPostsFrameChangedNotifications: YES]; - #if defined (MAC_OS_X_VERSION_10_8) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8) \ - && USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_DRAW_ASYNC + #if USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_DRAW_ASYNC if (! getComponentAsyncLayerBackedViewDisabled (component)) { - [view setWantsLayer: YES]; - [[view layer] setDrawsAsynchronously: YES]; + if (@available (macOS 10.8, *)) + { + [view setWantsLayer: YES]; + [[view layer] setDrawsAsynchronously: YES]; + } } #endif @@ -96,14 +108,8 @@ - (float)deviceDeltaY; defer: YES]; setOwner (window, this); - #if JUCE_OBJC_HAS_AVAILABLE_FEATURE || (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) - #if JUCE_OBJC_HAS_AVAILABLE_FEATURE if (@available (macOS 10.10, *)) - #endif - { [window setAccessibilityElement: YES]; - } - #endif [window orderOut: nil]; [window setDelegate: (id) window]; @@ -113,9 +119,8 @@ - (float)deviceDeltaY; if (! [window isOpaque]) [window setBackgroundColor: [NSColor clearColor]]; - #if defined (MAC_OS_X_VERSION_10_9) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9) - [view setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameAqua]]; - #endif + if (@available (macOS 10.9, *)) + [view setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameAqua]]; [window setHasShadow: ((windowStyleFlags & windowHasDropShadow) != 0)]; @@ -123,7 +128,6 @@ - (float)deviceDeltaY; setAlwaysOnTop (true); [window setContentView: view]; - [window setAcceptsMouseMovedEvents: YES]; // We'll both retain and also release this on closing because plugin hosts can unexpectedly // close the window for us, and also tend to get cause trouble if setReleasedWhenClosed is NO. @@ -133,14 +137,13 @@ - (float)deviceDeltaY; [window setExcludedFromWindowsMenu: (windowStyleFlags & windowIsTemporary) != 0]; [window setIgnoresMouseEvents: (windowStyleFlags & windowIgnoresMouseClicks) != 0]; - if ((windowStyleFlags & (windowHasMaximiseButton | windowHasTitleBar)) == (windowHasMaximiseButton | windowHasTitleBar)) + if ((windowStyleFlags & windowHasMaximiseButton) == windowHasMaximiseButton) [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; - if ([window respondsToSelector: @selector (setRestorable:)]) - [window setRestorable: NO]; + [window setRestorable: NO]; - #if defined (MAC_OS_X_VERSION_10_13) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13) - if ([window respondsToSelector: @selector (setTabbingMode:)]) + #if defined (MAC_OS_X_VERSION_10_12) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12) + if (@available (macOS 10.12, *)) [window setTabbingMode: NSWindowTabbingModeDisallowed]; #endif @@ -261,10 +264,8 @@ void setRepresentedFile (const File& file) override } } - void setBounds (const Rectangle& newBounds, bool isNowFullScreen) override + void setBounds (const Rectangle& newBounds, bool) override { - fullScreen = isNowFullScreen; - auto r = makeNSRect (newBounds); auto oldViewSize = [view frame].size; @@ -350,39 +351,29 @@ void setFullScreen (bool shouldBeFullScreen) override { if (! isSharedWindow) { - auto r = lastNonFullscreenBounds; - if (isMinimised()) setMinimised (false); - if (fullScreen != shouldBeFullScreen) + if (hasNativeTitleBar()) { - if (shouldBeFullScreen && hasNativeTitleBar()) - { - fullScreen = true; - [window performZoom: nil]; - } - else - { - if (shouldBeFullScreen) - r = component.getParentMonitorArea(); - - // (can't call the component's setBounds method because that'll reset our fullscreen flag) - if (r != component.getBounds() && ! r.isEmpty()) - setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, r), shouldBeFullScreen); - } + if (shouldBeFullScreen != isFullScreen()) + [window toggleFullScreen: nil]; + } + else + { + [window zoom: nil]; } } } bool isFullScreen() const override { - return fullScreen; + return ([window styleMask] & NSWindowStyleMaskFullScreen) != 0; } bool isKioskMode() const override { - return isWindowInKioskMode || ComponentPeer::isKioskMode(); + return isFullScreen() && ComponentPeer::isKioskMode(); } static bool isWindowAtPoint (NSWindow* w, NSPoint screenPoint) @@ -398,7 +389,7 @@ bool contains (Point localPos, bool trueIfInAChildWindow) const override NSRect viewFrame = [view frame]; if (! (isPositiveAndBelow (localPos.getX(), viewFrame.size.width) - && isPositiveAndBelow (localPos.getY(), viewFrame.size.height))) + && isPositiveAndBelow (localPos.getY(), viewFrame.size.height))) return false; if (! SystemStats::isRunningInAppExtensionSandbox()) @@ -432,8 +423,8 @@ bool contains (Point localPos, bool trueIfInAChildWindow) const override NSRect v = [view convertRect: [view frame] toView: nil]; NSRect w = [window frame]; - b.setTop ((int) v.origin.y); - b.setBottom ((int) (w.size.height - (v.origin.y + v.size.height))); + b.setTop ((int) (w.size.height - (v.origin.y + v.size.height))); + b.setBottom ((int) v.origin.y); b.setLeft ((int) v.origin.x); b.setRight ((int) (w.size.width - (v.origin.x + v.size.width))); } @@ -441,22 +432,6 @@ bool contains (Point localPos, bool trueIfInAChildWindow) const override return b; } - void updateFullscreenStatus() - { - if (hasNativeTitleBar()) - { - isWindowInKioskMode = (([window styleMask] & NSWindowStyleMaskFullScreen) != 0); - - auto screen = getFrameSize().subtractedFrom (component.getParentMonitorArea()); - - fullScreen = component.getScreenBounds().expanded (2, 2).contains (screen); - } - else - { - isWindowInKioskMode = false; - } - } - bool hasNativeTitleBar() const { return (getStyleFlags() & windowHasTitleBar) != 0; @@ -562,7 +537,7 @@ void setIcon (const Image& newIcon) override if (! windowRepresentsFile) [window setRepresentedFilename:juceStringToNS (" ")]; // can't just use an empty string for some reason... - [[window standardWindowButton:NSWindowDocumentIconButton] setImage:imageToNSImage (newIcon)]; + [[window standardWindowButton:NSWindowDocumentIconButton] setImage:imageToNSImage (ScaledImage (newIcon))]; } } @@ -636,21 +611,12 @@ void redirectMouseMove (NSEvent* ev) void redirectMouseEnter (NSEvent* ev) { - if (shouldIgnoreMouseEnterExit (ev)) - return; - - Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); - ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons(); - sendMouseEvent (ev); + sendMouseEnterExit (ev); } void redirectMouseExit (NSEvent* ev) { - if (shouldIgnoreMouseEnterExit (ev)) - return; - - ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons(); - sendMouseEvent (ev); + sendMouseEnterExit (ev); } static float checkDeviceDeltaReturnValue (float v) noexcept @@ -858,12 +824,15 @@ void drawRect (NSRect r) if (r.size.width < 1.0f || r.size.height < 1.0f) return; - auto cg = (CGContextRef) [[NSGraphicsContext currentContext] - #if (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) - CGContext]; - #else - graphicsPort]; - #endif + auto cg = [] + { + if (@available (macOS 10.10, *)) + return (CGContextRef) [[NSGraphicsContext currentContext] CGContext]; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + return (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + }(); if (! component.isOpaque()) CGContextClearRect (cg, CGContextGetClipBoundingBox (cg)); @@ -899,7 +868,7 @@ void drawRect (NSRect r) NSRect rect = rects[i]; CGContextSaveGState (cg); CGContextClipToRect (cg, CGRectMake (rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)); - drawRect (cg, rect, displayScale); + drawRectWithContext (cg, rect, displayScale); CGContextRestoreGState (cg); } @@ -909,11 +878,11 @@ void drawRect (NSRect r) } #endif - drawRect (cg, r, displayScale); + drawRectWithContext (cg, r, displayScale); invalidateTransparentWindowShadow(); } - void drawRect (CGContextRef cg, NSRect r, float displayScale) + void drawRectWithContext (CGContextRef cg, NSRect r, float displayScale) { #if USE_COREGRAPHICS_RENDERING if (usingCoreGraphics) @@ -1049,12 +1018,14 @@ void sendModalInputAttemptIfBlocked() { if (isBlockedByModalComponent()) if (auto* modal = Component::getCurrentlyModalComponent()) - modal->inputAttemptWhenModal(); + if (auto* otherPeer = modal->getPeer()) + if ((otherPeer->getStyleFlags() & ComponentPeer::windowIsTemporary) != 0) + modal->inputAttemptWhenModal(); } bool canBecomeKeyWindow() { - return component.isVisible() && (getStyleFlags() & juce::ComponentPeer::windowIgnoresKeyPresses) == 0; + return component.isVisible() && (getStyleFlags() & ComponentPeer::windowIgnoresKeyPresses) == 0; } bool canBecomeMainWindow() @@ -1092,7 +1063,6 @@ bool windowShouldClose() void redirectMovedOrResized() { - updateFullscreenStatus(); handleMovedOrResized(); } @@ -1121,12 +1091,15 @@ void viewMovedToWindow() name: NSWindowWillMiniaturizeNotification object: currentWindow]; - #if JUCE_COREGRAPHICS_DRAW_ASYNC [notificationCenter addObserver: view selector: becomeKeySelector name: NSWindowDidBecomeKeyNotification object: currentWindow]; - #endif + + [notificationCenter addObserver: view + selector: resignKeySelector + name: NSWindowDidResignKeyNotification + object: currentWindow]; } } @@ -1141,6 +1114,12 @@ void becomeKey() component.repaint(); } + void resignKey() + { + viewFocusLoss(); + sendModalInputAttemptIfBlocked(); + } + void liveResizingStart() { if (constrainer == nullptr) @@ -1160,7 +1139,7 @@ void liveResizingEnd() NSRect constrainRect (const NSRect r) { - if (constrainer == nullptr || isKioskMode()) + if (constrainer == nullptr || isKioskMode() || isFullScreen()) return r; const auto scale = getComponent().getDesktopScaleFactor(); @@ -1345,14 +1324,19 @@ static unsigned int getNSWindowStyleMask (const int flags) noexcept static NSArray* getSupportedDragTypes() { - const auto type = - #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13 - NSPasteboardTypeFileURL; - #else - kUTTypeFileURL; - #endif + const auto type = [] + { + #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 + if (@available (macOS 10.13, *)) + return NSPasteboardTypeFileURL; + #endif + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + return (NSString*) kUTTypeFileURL; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + }(); - return [NSArray arrayWithObjects: (NSString*) type, (NSString*) kPasteboardTypeFileURLPromise, NSPasteboardTypeString, nil]; + return [NSArray arrayWithObjects: type, (NSString*) kPasteboardTypeFileURLPromise, NSPasteboardTypeString, nil]; } BOOL sendDragCallback (const int type, id sender) @@ -1484,12 +1468,17 @@ void resetWindowPresentation() [NSApp setPresentationOptions: NSApplicationPresentationDefault]; } + void setHasChangedSinceSaved (bool b) override + { + if (! isSharedWindow) + [window setDocumentEdited: b]; + } + //============================================================================== NSWindow* window = nil; NSView* view = nil; WeakReference safeComponent; - bool isSharedWindow = false, fullScreen = false; - bool isWindowInKioskMode = false; + bool isSharedWindow = false; #if USE_COREGRAPHICS_RENDERING bool usingCoreGraphics = true; #else @@ -1514,15 +1503,19 @@ void resetWindowPresentation() static const SEL asyncMouseDownSelector; static const SEL asyncMouseUpSelector; static const SEL becomeKeySelector; + static const SEL resignKeySelector; private: static NSView* createViewInstance(); static NSWindow* createWindowInstance(); - bool shouldIgnoreMouseEnterExit (NSEvent* ev) const + void sendMouseEnterExit (NSEvent* ev) { - auto* eventTrackingArea = [ev trackingArea]; - return eventTrackingArea != nil && ! [[view trackingAreas] containsObject: eventTrackingArea]; + if (auto* area = [ev trackingArea]) + if (! [[view trackingAreas] containsObject: area]) + return; + + sendMouseEvent (ev); } static void setOwner (id viewOrWindow, NSViewComponentPeer* newOwner) @@ -1676,6 +1669,7 @@ void setFullScreenSizeConstraints (const ComponentBoundsConstrainer& c) const auto minSize = NSMakeSize (static_cast (c.getMinimumWidth()), 0.0f); [window setMinFullScreenContentSize: minSize]; + [window setMaxFullScreenContentSize: NSMakeSize (100000, 100000)]; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewComponentPeer) @@ -1689,6 +1683,7 @@ void setFullScreenSizeConstraints (const ComponentBoundsConstrainer& c) const SEL NSViewComponentPeer::asyncMouseDownSelector = @selector (asyncMouseDown:); const SEL NSViewComponentPeer::asyncMouseUpSelector = @selector (asyncMouseUp:); const SEL NSViewComponentPeer::becomeKeySelector = @selector (becomeKey:); +const SEL NSViewComponentPeer::resignKeySelector = @selector (resignKey:); JUCE_END_IGNORE_WARNINGS_GCC_LIKE //============================================================================== @@ -1720,82 +1715,83 @@ static id getAccessibleChild (id self) { JuceNSViewClass() : NSViewComponentPeerWrapper ("JUCEView_") { - addMethod (@selector (isOpaque), isOpaque, "c@:"); - addMethod (@selector (drawRect:), drawRect, "v@:", @encode (NSRect)); - addMethod (@selector (mouseDown:), mouseDown, "v@:@"); - addMethod (@selector (mouseUp:), mouseUp, "v@:@"); - addMethod (@selector (mouseDragged:), mouseDragged, "v@:@"); - addMethod (@selector (mouseMoved:), mouseMoved, "v@:@"); - addMethod (@selector (mouseEntered:), mouseEntered, "v@:@"); - addMethod (@selector (mouseExited:), mouseExited, "v@:@"); - addMethod (@selector (rightMouseDown:), mouseDown, "v@:@"); - addMethod (@selector (rightMouseDragged:), mouseDragged, "v@:@"); - addMethod (@selector (rightMouseUp:), mouseUp, "v@:@"); - addMethod (@selector (otherMouseDown:), mouseDown, "v@:@"); - addMethod (@selector (otherMouseDragged:), mouseDragged, "v@:@"); - addMethod (@selector (otherMouseUp:), mouseUp, "v@:@"); - addMethod (@selector (scrollWheel:), scrollWheel, "v@:@"); - addMethod (@selector (magnifyWithEvent:), magnify, "v@:@"); - addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "c@:@"); - addMethod (@selector (windowWillMiniaturize:), windowWillMiniaturize, "v@:@"); - addMethod (@selector (windowDidDeminiaturize:), windowDidDeminiaturize, "v@:@"); - addMethod (@selector (wantsDefaultClipping), wantsDefaultClipping, "c@:"); - addMethod (@selector (worksWhenModal), worksWhenModal, "c@:"); - addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow, "v@:"); - addMethod (@selector (viewWillDraw), viewWillDraw, "v@:"); - addMethod (@selector (keyDown:), keyDown, "v@:@"); - addMethod (@selector (keyUp:), keyUp, "v@:@"); - addMethod (@selector (insertText:), insertText, "v@:@"); - addMethod (@selector (doCommandBySelector:), doCommandBySelector, "v@::"); - addMethod (@selector (setMarkedText:selectedRange:), setMarkedText, "v@:@", @encode (NSRange)); - addMethod (@selector (unmarkText), unmarkText, "v@:"); - addMethod (@selector (hasMarkedText), hasMarkedText, "c@:"); - addMethod (@selector (conversationIdentifier), conversationIdentifier, "l@:"); - addMethod (@selector (attributedSubstringFromRange:), attributedSubstringFromRange, "@@:", @encode (NSRange)); - addMethod (@selector (markedRange), markedRange, @encode (NSRange), "@:"); - addMethod (@selector (selectedRange), selectedRange, @encode (NSRange), "@:"); - addMethod (@selector (firstRectForCharacterRange:), firstRectForCharacterRange, @encode (NSRect), "@:", @encode (NSRange)); - addMethod (@selector (characterIndexForPoint:), characterIndexForPoint, "L@:", @encode (NSPoint)); - addMethod (@selector (validAttributesForMarkedText), validAttributesForMarkedText, "@@:"); - addMethod (@selector (flagsChanged:), flagsChanged, "v@:@"); - - addMethod (@selector (becomeFirstResponder), becomeFirstResponder, "c@:"); - addMethod (@selector (resignFirstResponder), resignFirstResponder, "c@:"); - addMethod (@selector (acceptsFirstResponder), acceptsFirstResponder, "c@:"); - - addMethod (@selector (draggingEntered:), draggingEntered, @encode (NSDragOperation), "@:@"); - addMethod (@selector (draggingUpdated:), draggingUpdated, @encode (NSDragOperation), "@:@"); - addMethod (@selector (draggingEnded:), draggingEnded, "v@:@"); - addMethod (@selector (draggingExited:), draggingExited, "v@:@"); - addMethod (@selector (prepareForDragOperation:), prepareForDragOperation, "c@:@"); - addMethod (@selector (performDragOperation:), performDragOperation, "c@:@"); - addMethod (@selector (concludeDragOperation:), concludeDragOperation, "v@:@"); - - addMethod (@selector (paste:), paste, "v@:@"); - addMethod (@selector (copy:), copy, "v@:@"); - addMethod (@selector (cut:), cut, "v@:@"); - addMethod (@selector (selectAll:), selectAll, "v@:@"); - - addMethod (@selector (viewWillMoveToWindow:), willMoveToWindow, "v@:@"); - - addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement, "c@:"); - addMethod (@selector (accessibilityChildren), getAccessibilityChildren, "@@:"); - addMethod (@selector (accessibilityHitTest:), accessibilityHitTest, "@@:", @encode (NSPoint)); - addMethod (@selector (accessibilityFocusedUIElement), getAccessibilityFocusedUIElement, "@@:"); + addMethod (@selector (isOpaque), isOpaque); + addMethod (@selector (drawRect:), drawRect); + addMethod (@selector (mouseDown:), mouseDown); + addMethod (@selector (mouseUp:), mouseUp); + addMethod (@selector (mouseDragged:), mouseDragged); + addMethod (@selector (mouseMoved:), mouseMoved); + addMethod (@selector (mouseEntered:), mouseEntered); + addMethod (@selector (mouseExited:), mouseExited); + addMethod (@selector (rightMouseDown:), mouseDown); + addMethod (@selector (rightMouseDragged:), mouseDragged); + addMethod (@selector (rightMouseUp:), mouseUp); + addMethod (@selector (otherMouseDown:), mouseDown); + addMethod (@selector (otherMouseDragged:), mouseDragged); + addMethod (@selector (otherMouseUp:), mouseUp); + addMethod (@selector (scrollWheel:), scrollWheel); + addMethod (@selector (magnifyWithEvent:), magnify); + addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse); + addMethod (@selector (windowWillMiniaturize:), windowWillMiniaturize); + addMethod (@selector (windowDidDeminiaturize:), windowDidDeminiaturize); + addMethod (@selector (wantsDefaultClipping), wantsDefaultClipping); + addMethod (@selector (worksWhenModal), worksWhenModal); + addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow); + addMethod (@selector (viewWillDraw), viewWillDraw); + addMethod (@selector (keyDown:), keyDown); + addMethod (@selector (keyUp:), keyUp); + addMethod (@selector (insertText:), insertText); + addMethod (@selector (doCommandBySelector:), doCommandBySelector); + addMethod (@selector (setMarkedText:selectedRange:), setMarkedText); + addMethod (@selector (unmarkText), unmarkText); + addMethod (@selector (hasMarkedText), hasMarkedText); + addMethod (@selector (conversationIdentifier), conversationIdentifier); + addMethod (@selector (attributedSubstringFromRange:), attributedSubstringFromRange); + addMethod (@selector (markedRange), markedRange); + addMethod (@selector (selectedRange), selectedRange); + addMethod (@selector (firstRectForCharacterRange:), firstRectForCharacterRange); + addMethod (@selector (characterIndexForPoint:), characterIndexForPoint); + addMethod (@selector (validAttributesForMarkedText), validAttributesForMarkedText); + addMethod (@selector (flagsChanged:), flagsChanged); + + addMethod (@selector (becomeFirstResponder), becomeFirstResponder); + addMethod (@selector (resignFirstResponder), resignFirstResponder); + addMethod (@selector (acceptsFirstResponder), acceptsFirstResponder); + + addMethod (@selector (draggingEntered:), draggingEntered); + addMethod (@selector (draggingUpdated:), draggingUpdated); + addMethod (@selector (draggingEnded:), draggingEnded); + addMethod (@selector (draggingExited:), draggingExited); + addMethod (@selector (prepareForDragOperation:), prepareForDragOperation); + addMethod (@selector (performDragOperation:), performDragOperation); + addMethod (@selector (concludeDragOperation:), concludeDragOperation); + + addMethod (@selector (paste:), paste); + addMethod (@selector (copy:), copy); + addMethod (@selector (cut:), cut); + addMethod (@selector (selectAll:), selectAll); + + addMethod (@selector (viewWillMoveToWindow:), willMoveToWindow); + + addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement); + addMethod (@selector (accessibilityChildren), getAccessibilityChildren); + addMethod (@selector (accessibilityHitTest:), accessibilityHitTest); + addMethod (@selector (accessibilityFocusedUIElement), getAccessibilityFocusedUIElement); // deprecated methods required for backwards compatibility - addMethod (@selector (accessibilityIsIgnored), getAccessibilityIsIgnored, "c@:"); - addMethod (@selector (accessibilityAttributeValue:), getAccessibilityAttributeValue, "@@:@"); + addMethod (@selector (accessibilityIsIgnored), getAccessibilityIsIgnored); + addMethod (@selector (accessibilityAttributeValue:), getAccessibilityAttributeValue); - addMethod (@selector (isFlipped), isFlipped, "c@:"); + addMethod (@selector (isFlipped), isFlipped); - addMethod (NSViewComponentPeer::dismissModalsSelector, dismissModals, "v@:"); - addMethod (NSViewComponentPeer::asyncMouseDownSelector, asyncMouseDown, "v@:@"); - addMethod (NSViewComponentPeer::asyncMouseUpSelector, asyncMouseUp, "v@:@"); - addMethod (NSViewComponentPeer::frameChangedSelector, frameChanged, "v@:@"); - addMethod (NSViewComponentPeer::becomeKeySelector, becomeKey, "v@:@"); + addMethod (NSViewComponentPeer::dismissModalsSelector, dismissModals); + addMethod (NSViewComponentPeer::asyncMouseDownSelector, asyncMouseDown); + addMethod (NSViewComponentPeer::asyncMouseUpSelector, asyncMouseUp); + addMethod (NSViewComponentPeer::frameChangedSelector, frameChanged); + addMethod (NSViewComponentPeer::becomeKeySelector, becomeKey); + addMethod (NSViewComponentPeer::resignKeySelector, resignKey); - addMethod (@selector (performKeyEquivalent:), performKeyEquivalent, "c@:@"); + addMethod (@selector (performKeyEquivalent:), performKeyEquivalent); addProtocol (@protocol (NSTextInput)); @@ -1837,29 +1833,30 @@ static void mouseUp (id self, SEL s, NSEvent* ev) } } - static void asyncMouseDown (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseDown (ev); } - static void asyncMouseUp (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseUp (ev); } - static void mouseDragged (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseDrag (ev); } - static void mouseMoved (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseMove (ev); } - static void mouseEntered (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseEnter (ev); } - static void mouseExited (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseExit (ev); } - static void scrollWheel (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseWheel (ev); } - static void magnify (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMagnify (ev); } - static void copy (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectCopy (s); } - static void paste (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectPaste (s); } - static void cut (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectCut (s); } - static void selectAll (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectSelectAll (s); } - static void willMoveToWindow (id self, SEL, NSWindow* w) { if (auto* p = getOwner (self)) p->redirectWillMoveToWindow (w); } + static void asyncMouseDown (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseDown, ev); } + static void asyncMouseUp (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseUp, ev); } + static void mouseDragged (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseDrag, ev); } + static void mouseMoved (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseMove, ev); } + static void mouseEntered (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseEnter, ev); } + static void mouseExited (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseExit, ev); } + static void scrollWheel (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseWheel, ev); } + static void magnify (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMagnify, ev); } + static void copy (id self, SEL, NSObject* s) { callOnOwner (self, &NSViewComponentPeer::redirectCopy, s); } + static void paste (id self, SEL, NSObject* s) { callOnOwner (self, &NSViewComponentPeer::redirectPaste, s); } + static void cut (id self, SEL, NSObject* s) { callOnOwner (self, &NSViewComponentPeer::redirectCut, s); } + static void selectAll (id self, SEL, NSObject* s) { callOnOwner (self, &NSViewComponentPeer::redirectSelectAll, s); } + static void willMoveToWindow (id self, SEL, NSWindow* w) { callOnOwner (self, &NSViewComponentPeer::redirectWillMoveToWindow, w); } static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; } static BOOL wantsDefaultClipping (id, SEL) { return YES; } // (this is the default, but may want to customise it in future) static BOOL worksWhenModal (id self, SEL) { if (auto* p = getOwner (self)) return p->worksWhenModal(); return NO; } - static void drawRect (id self, SEL, NSRect r) { if (auto* p = getOwner (self)) p->drawRect (r); } - static void frameChanged (id self, SEL, NSNotification*) { if (auto* p = getOwner (self)) p->redirectMovedOrResized(); } - static void viewDidMoveToWindow (id self, SEL) { if (auto* p = getOwner (self)) p->viewMovedToWindow(); } - static void dismissModals (id self, SEL) { if (auto* p = getOwner (self)) p->dismissModals(); } - static void becomeKey (id self, SEL) { if (auto* p = getOwner (self)) p->becomeKey(); } + static void drawRect (id self, SEL, NSRect r) { callOnOwner (self, &NSViewComponentPeer::drawRect, r); } + static void frameChanged (id self, SEL, NSNotification*) { callOnOwner (self, &NSViewComponentPeer::redirectMovedOrResized); } + static void viewDidMoveToWindow (id self, SEL) { callOnOwner (self, &NSViewComponentPeer::viewMovedToWindow); } + static void dismissModals (id self, SEL) { callOnOwner (self, &NSViewComponentPeer::dismissModals); } + static void becomeKey (id self, SEL) { callOnOwner (self, &NSViewComponentPeer::becomeKey); } + static void resignKey (id self, SEL) { callOnOwner (self, &NSViewComponentPeer::resignKey); } static BOOL isFlipped (id, SEL) { return true; } @@ -1868,7 +1865,7 @@ static void viewWillDraw (id self, SEL) // Without setting contentsFormat macOS Big Sur will always set the invalid area // to be the entire frame. #if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (NSFoundationVersionNumber > (double) NSFoundationVersionNumber10_11_Max) + if (@available (macOS 10.12, *)) { CALayer* layer = ((NSView*) self).layer; layer.contentsFormat = kCAContentsFormatRGBA8Uint; @@ -2060,23 +2057,18 @@ static NSRect firstRectForCharacterRange (id self, SEL, NSRange) //============================================================================== static void flagsChanged (id self, SEL, NSEvent* ev) { - if (auto* owner = getOwner (self)) - owner->redirectModKeyChange (ev); + callOnOwner (self, &NSViewComponentPeer::redirectModKeyChange, ev); } static BOOL becomeFirstResponder (id self, SEL) { - if (auto* owner = getOwner (self)) - owner->viewFocusGain(); - + callOnOwner (self, &NSViewComponentPeer::viewFocusGain); return YES; } static BOOL resignFirstResponder (id self, SEL) { - if (auto* owner = getOwner (self)) - owner->viewFocusLoss(); - + callOnOwner (self, &NSViewComponentPeer::viewFocusLoss); return YES; } @@ -2108,8 +2100,7 @@ static void draggingEnded (id self, SEL s, id sender) static void draggingExited (id self, SEL, id sender) { - if (auto* owner = getOwner (self)) - owner->sendDragCallback (1, sender); + callOnOwner (self, &NSViewComponentPeer::sendDragCallback, 1, sender); } static BOOL prepareForDragOperation (id, SEL, id) @@ -2186,6 +2177,13 @@ static BOOL performKeyEquivalent (id self, SEL s, NSEvent* event) return sendSuperclassMessage (self, s, event); } + + template + static void callOnOwner (id self, Func&& func, Args&&... args) + { + if (auto* owner = getOwner (self)) + (owner->*func) (std::forward (args)...); + } }; //============================================================================== @@ -2193,30 +2191,30 @@ static BOOL performKeyEquivalent (id self, SEL s, NSEvent* event) { JuceNSWindowClass() : NSViewComponentPeerWrapper ("JUCEWindow_") { - addMethod (@selector (canBecomeKeyWindow), canBecomeKeyWindow, "c@:"); - addMethod (@selector (canBecomeMainWindow), canBecomeMainWindow, "c@:"); - addMethod (@selector (becomeKeyWindow), becomeKeyWindow, "v@:"); - addMethod (@selector (resignKeyWindow), resignKeyWindow, "v@:"); - addMethod (@selector (windowShouldClose:), windowShouldClose, "c@:@"); - addMethod (@selector (constrainFrameRect:toScreen:), constrainFrameRect, @encode (NSRect), "@:", @encode (NSRect), "@"); - addMethod (@selector (windowWillResize:toSize:), windowWillResize, @encode (NSSize), "@:@", @encode (NSSize)); - addMethod (@selector (windowDidExitFullScreen:), windowDidExitFullScreen, "v@:@"); - addMethod (@selector (windowWillEnterFullScreen:), windowWillEnterFullScreen, "v@:@"); - addMethod (@selector (zoom:), zoom, "v@:@"); - addMethod (@selector (windowWillStartLiveResize:), windowWillStartLiveResize, "v@:@"); - addMethod (@selector (windowDidEndLiveResize:), windowDidEndLiveResize, "v@:@"); - addMethod (@selector (window:shouldPopUpDocumentPathMenu:), shouldPopUpPathMenu, "c@:@", @encode (NSMenu*)); - addMethod (@selector (isFlipped), isFlipped, "c@:"); - - addMethod (@selector (accessibilityTitle), getAccessibilityTitle, "@@:"); - addMethod (@selector (accessibilityLabel), getAccessibilityLabel, "@@:"); - addMethod (@selector (accessibilityTopLevelUIElement), getAccessibilityWindow, "@@:"); - addMethod (@selector (accessibilityWindow), getAccessibilityWindow, "@@:"); - addMethod (@selector (accessibilityRole), getAccessibilityRole, "@@:"); - addMethod (@selector (accessibilitySubrole), getAccessibilitySubrole, "@@:"); - - addMethod (@selector (window:shouldDragDocumentWithEvent:from:withPasteboard:), - shouldAllowIconDrag, "c@:@", @encode (NSEvent*), @encode (NSPoint), @encode (NSPasteboard*)); + addMethod (@selector (canBecomeKeyWindow), canBecomeKeyWindow); + addMethod (@selector (canBecomeMainWindow), canBecomeMainWindow); + addMethod (@selector (becomeKeyWindow), becomeKeyWindow); + addMethod (@selector (resignKeyWindow), resignKeyWindow); + addMethod (@selector (windowShouldClose:), windowShouldClose); + addMethod (@selector (constrainFrameRect:toScreen:), constrainFrameRect); + addMethod (@selector (windowWillResize:toSize:), windowWillResize); + addMethod (@selector (windowDidExitFullScreen:), windowDidExitFullScreen); + addMethod (@selector (windowWillEnterFullScreen:), windowWillEnterFullScreen); + addMethod (@selector (windowWillStartLiveResize:), windowWillStartLiveResize); + addMethod (@selector (windowDidEndLiveResize:), windowDidEndLiveResize); + addMethod (@selector (window:shouldPopUpDocumentPathMenu:), shouldPopUpPathMenu); + addMethod (@selector (isFlipped), isFlipped); + addMethod (@selector (windowWillUseStandardFrame:defaultFrame:), windowWillUseStandardFrame); + addMethod (@selector (windowShouldZoom:toFrame:), windowShouldZoomToFrame); + + addMethod (@selector (accessibilityTitle), getAccessibilityTitle); + addMethod (@selector (accessibilityLabel), getAccessibilityLabel); + addMethod (@selector (accessibilityTopLevelUIElement), getAccessibilityWindow); + addMethod (@selector (accessibilityWindow), getAccessibilityWindow); + addMethod (@selector (accessibilityRole), getAccessibilityRole); + addMethod (@selector (accessibilitySubrole), getAccessibilitySubrole); + + addMethod (@selector (window:shouldDragDocumentWithEvent:from:withPasteboard:), shouldAllowIconDrag); addProtocol (@protocol (NSWindowDelegate)); @@ -2227,6 +2225,26 @@ static BOOL performKeyEquivalent (id self, SEL s, NSEvent* event) //============================================================================== static BOOL isFlipped (id, SEL) { return true; } + static NSRect windowWillUseStandardFrame (id self, SEL, NSWindow*, NSRect) + { + if (auto* owner = getOwner (self)) + { + if (auto* constrainer = owner->getConstrainer()) + { + return flippedScreenRect (makeNSRect (owner->getFrameSize().addedTo (owner->getComponent().getScreenBounds() + .withWidth (constrainer->getMaximumWidth()) + .withHeight (constrainer->getMaximumHeight())))); + } + } + + return makeNSRect (Rectangle (10000, 10000)); + } + + static BOOL windowShouldZoomToFrame (id, SEL, NSWindow* window, NSRect frame) + { + return convertToRectFloat ([window frame]).withZeroOrigin() != convertToRectFloat (frame).withZeroOrigin(); + } + static BOOL canBecomeKeyWindow (id self, SEL) { auto* owner = getOwner (self); @@ -2323,19 +2341,6 @@ static void windowWillEnterFullScreen (id self, SEL, NSNotification*) [owner->window setStyleMask: NSWindowStyleMaskBorderless]; } - static void zoom (id self, SEL, id sender) - { - if (auto* owner = getOwner (self)) - { - { - const ScopedValueSetter svs (owner->isZooming, true); - sendSuperclassMessage (self, @selector (zoom:), sender); - } - - owner->redirectMovedOrResized(); - } - } - static void windowWillStartLiveResize (id self, SEL, NSNotification*) { if (auto* owner = getOwner (self)) @@ -2450,23 +2455,19 @@ static NSAccessibilityRole getAccessibilitySubrole (id self, SEL) auto* peer = dynamic_cast (kioskComp->getPeer()); jassert (peer != nullptr); // (this should have been checked by the caller) - if (peer->hasNativeTitleBar() - && [peer->window respondsToSelector: @selector (toggleFullScreen:)]) + if (peer->hasNativeTitleBar()) { if (shouldBeEnabled && ! allowMenusAndBars) [NSApp setPresentationOptions: NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar]; else if (! shouldBeEnabled) [NSApp setPresentationOptions: NSApplicationPresentationDefault]; - [peer->window performSelector: @selector (toggleFullScreen:) withObject: nil]; + [peer->window toggleFullScreen: nil]; } else { if (shouldBeEnabled) { - if (peer->hasNativeTitleBar()) - [peer->window setStyleMask: NSWindowStyleMaskBorderless]; - [NSApp setPresentationOptions: (allowMenusAndBars ? (NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar) : (NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar))]; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm index 4223a345e..7f0d56e3a 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm @@ -82,7 +82,7 @@ NSInteger getRawResult() const [alert setInformativeText: juceStringToNS (options.getMessage())]; [alert setAlertStyle: options.getIconType() == MessageBoxIconType::WarningIcon ? NSAlertStyleCritical - : NSAlertStyleInformational]; + : NSAlertStyleInformational]; const auto button1Text = options.getButtonText (0); @@ -239,11 +239,11 @@ static NSRect getDragRect (NSView* view, NSEvent* event) addIvar ("text"); addIvar ("operation"); - addMethod (@selector (dealloc), dealloc, "v@:"); - addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType, "v@:@@@"); + addMethod (@selector (dealloc), dealloc); + addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType); - addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext, "c@:@@"); - addMethod (@selector (draggingSession:endedAtPoint:operation:), draggingSessionEnded, "v@:@@@"); + addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext); + addMethod (@selector (draggingSession:endedAtPoint:operation:), draggingSessionEnded); addProtocol (@protocol (NSPasteboardItemDataProvider)); @@ -430,6 +430,73 @@ static void draggingSessionEnded (id self, SEL, NSDraggingSession*, NSPoint p, N return upright; } +bool Desktop::isDarkModeActive() const +{ + return [[[NSUserDefaults standardUserDefaults] stringForKey: nsStringLiteral ("AppleInterfaceStyle")] + isEqualToString: nsStringLiteral ("Dark")]; +} + +class Desktop::NativeDarkModeChangeDetectorImpl +{ +public: + NativeDarkModeChangeDetectorImpl() + { + static DelegateClass delegateClass; + + delegate = [delegateClass.createInstance() init]; + object_setInstanceVariable (delegate, "owner", this); + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate + selector: @selector (darkModeChanged:) + name: @"AppleInterfaceThemeChangedNotification" + object: nil]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } + + ~NativeDarkModeChangeDetectorImpl() + { + object_setInstanceVariable (delegate, "owner", nullptr); + [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate]; + [delegate release]; + } + + void darkModeChanged() + { + Desktop::getInstance().darkModeChanged(); + } + +private: + struct DelegateClass : public ObjCClass + { + DelegateClass() : ObjCClass ("JUCEDelegate_") + { + addIvar ("owner"); + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + addMethod (@selector (darkModeChanged:), darkModeChanged); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + registerClass(); + } + + static void darkModeChanged (id self, SEL, NSNotification*) + { + if (auto* owner = getIvar (self, "owner")) + owner->darkModeChanged(); + } + }; + + id delegate = nil; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) +}; + +std::unique_ptr Desktop::createNativeDarkModeChangeDetectorImpl() +{ + return std::make_unique(); +} + //============================================================================== class ScreenSaverDefeater : public Timer { @@ -577,13 +644,17 @@ static void selectImageForDrawing (const Image& image) { [NSGraphicsContext saveGraphicsState]; - #if (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) - [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithCGContext: juce_getImageContext (image) - flipped: false]]; - #else + if (@available (macOS 10.10, *)) + { + [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithCGContext: juce_getImageContext (image) + flipped: false]]; + return; + } + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (image) flipped: false]]; - #endif + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } static void releaseImageAfterDrawing() @@ -634,7 +705,6 @@ static Image createNSWindowSnapshot (NSWindow* nsWindow) } } -Image createSnapshotOfNativeWindow (void*); Image createSnapshotOfNativeWindow (void* nativeWindowHandle) { if (id windowOrView = (id) nativeWindowHandle) @@ -676,10 +746,4 @@ Image createSnapshotOfNativeWindow (void* nativeWindowHandle) ignoreUnused (err); } -bool Desktop::isOSXDarkModeActive() -{ - return [[[NSUserDefaults standardUserDefaults] stringForKey: nsStringLiteral ("AppleInterfaceStyle")] - isEqualToString: nsStringLiteral ("Dark")]; -} - } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index a8c09328b..999e2f54f 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -27,10 +27,6 @@ #include #endif -#if JUCE_MODULE_AVAILABLE_juce_gui_extra - #include -#endif - namespace juce { @@ -357,6 +353,7 @@ using SetProcessDPIAwarenessFunc = HRESULT (WINAPI*) using SetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (DPI_AWARENESS_CONTEXT); using GetDPIForWindowFunc = UINT (WINAPI*) (HWND); using GetDPIForMonitorFunc = HRESULT (WINAPI*) (HMONITOR, Monitor_DPI_Type, UINT*, UINT*); +using GetSystemMetricsForDpiFunc = int (WINAPI*) (int, UINT); using GetProcessDPIAwarenessFunc = HRESULT (WINAPI*) (HANDLE, DPI_Awareness*); using GetWindowDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (HWND); using GetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (); @@ -377,6 +374,32 @@ static EnableNonClientDPIScalingFunc enableNonClientDPIScaling static bool hasCheckedForDPIAwareness = false; +static void loadDPIAwarenessFunctions() +{ + setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware"); + + constexpr auto shcore = "SHCore.dll"; + LoadLibraryA (shcore); + const auto shcoreModule = GetModuleHandleA (shcore); + + if (shcoreModule == nullptr) + return; + + getDPIForMonitor = (GetDPIForMonitorFunc) GetProcAddress (shcoreModule, "GetDpiForMonitor"); + setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness"); + + #if JUCE_WIN_PER_MONITOR_DPI_AWARE + getDPIForWindow = (GetDPIForWindowFunc) getUser32Function ("GetDpiForWindow"); + getProcessDPIAwareness = (GetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "GetProcessDpiAwareness"); + getWindowDPIAwarenessContext = (GetWindowDPIAwarenessContextFunc) getUser32Function ("GetWindowDpiAwarenessContext"); + setThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext"); + getThreadDPIAwarenessContext = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext"); + getAwarenessFromDPIAwarenessContext = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext"); + setProcessDPIAwarenessContext = (SetProcessDPIAwarenessContextFunc) getUser32Function ("SetProcessDpiAwarenessContext"); + enableNonClientDPIScaling = (EnableNonClientDPIScalingFunc) getUser32Function ("EnableNonClientDpiScaling"); + #endif +} + static void setDPIAwareness() { if (hasCheckedForDPIAwareness) @@ -387,45 +410,19 @@ static void setDPIAwareness() if (! JUCEApplicationBase::isStandaloneApp()) return; - const auto shcore = "SHCore.dll"; - LoadLibraryA (shcore); - const auto shcoreModule = GetModuleHandleA (shcore); - - if (shcoreModule != nullptr) - { - getDPIForMonitor = (GetDPIForMonitorFunc) GetProcAddress (shcoreModule, "GetDpiForMonitor"); - - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - getDPIForWindow = (GetDPIForWindowFunc) getUser32Function ("GetDpiForWindow"); - getProcessDPIAwareness = (GetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "GetProcessDpiAwareness"); - getWindowDPIAwarenessContext = (GetWindowDPIAwarenessContextFunc) getUser32Function ("GetWindowDpiAwarenessContext"); - setThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext"); - getThreadDPIAwarenessContext = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext"); - getAwarenessFromDPIAwarenessContext = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext"); - setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness"); - setProcessDPIAwarenessContext = (SetProcessDPIAwarenessContextFunc) getUser32Function ("SetProcessDpiAwarenessContext"); - - if (setProcessDPIAwarenessContext != nullptr - && setProcessDPIAwarenessContext (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) - return; - - enableNonClientDPIScaling = (EnableNonClientDPIScalingFunc) getUser32Function ("EnableNonClientDpiScaling"); - - if (setProcessDPIAwareness != nullptr && enableNonClientDPIScaling != nullptr - && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_Per_Monitor_Aware))) - return; - #endif + loadDPIAwarenessFunctions(); - if (setProcessDPIAwareness == nullptr) - setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness"); + if (setProcessDPIAwarenessContext != nullptr + && setProcessDPIAwarenessContext (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + return; - if (setProcessDPIAwareness != nullptr && getDPIForMonitor != nullptr - && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_System_Aware))) - return; - } + if (setProcessDPIAwareness != nullptr && enableNonClientDPIScaling != nullptr + && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_Per_Monitor_Aware))) + return; - // fallback for pre Windows 8.1 - equivalent to Process_System_DPI_Aware - setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware"); + if (setProcessDPIAwareness != nullptr && getDPIForMonitor != nullptr + && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_System_Aware))) + return; if (setProcessDPIAware != nullptr) setProcessDPIAware(); @@ -440,6 +437,9 @@ static bool isPerMonitorDPIAwareProcess() { setDPIAwareness(); + if (! JUCEApplication::isStandaloneApp()) + return false; + if (getProcessDPIAwareness == nullptr) return false; @@ -572,38 +572,34 @@ ScopedThreadDPIAwarenessSetter::ScopedThreadDPIAwarenessSetter (void* nativeWind pimpl = std::make_unique ((HWND) nativeWindow); } -ScopedThreadDPIAwarenessSetter::~ScopedThreadDPIAwarenessSetter() +ScopedThreadDPIAwarenessSetter::~ScopedThreadDPIAwarenessSetter() = default; + +ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { -} + if (! isPerMonitorDPIAwareThread()) + return; -#if JUCE_MODULE_AVAILABLE_juce_gui_extra - ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() - { - if (! isPerMonitorDPIAwareThread()) - return; + if (setThreadDPIAwarenessContext != nullptr) + { + previousContext = setThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE); - if (setThreadDPIAwarenessContext != nullptr) - { - previousContext = setThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE); + #if JUCE_DEBUG + ++numActiveScopedDpiAwarenessDisablers; + #endif + } +} - #if JUCE_DEBUG - ++numActiveScopedDpiAwarenessDisablers; - #endif - } - } +ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() +{ + if (previousContext != nullptr) + { + setThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext); - ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() - { - if (previousContext != nullptr) - { - setThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext); - - #if JUCE_DEBUG - --numActiveScopedDpiAwarenessDisablers; - #endif - } - } -#endif + #if JUCE_DEBUG + --numActiveScopedDpiAwarenessDisablers; + #endif + } +} //============================================================================== using SettingChangeCallbackFunc = void (*)(void); @@ -619,7 +615,7 @@ static POINT POINTFromPoint (Point p) noexcept { return { p.x, p.y //============================================================================== static const Displays::Display* getCurrentDisplayFromScaleFactor (HWND hwnd); -template +template static Rectangle convertPhysicalScreenRectangleToLogical (Rectangle r, HWND h) noexcept { if (isPerMonitorDPIAwareWindow (h)) @@ -628,7 +624,7 @@ static Rectangle convertPhysicalScreenRectangleToLogical (Rectangle +template static Rectangle convertLogicalScreenRectangleToPhysical (Rectangle r, HWND h) noexcept { if (isPerMonitorDPIAwareWindow (h)) @@ -720,6 +716,10 @@ static void setWindowZOrder (HWND hwnd, HWND insertAfter) } //============================================================================== +#if ! JUCE_MINGW +extern RTL_OSVERSIONINFOW getWindowsVersionInfo(); +#endif + double Desktop::getDefaultMasterScale() { if (! JUCEApplicationBase::isStandaloneApp() || isPerMonitorDPIAwareProcess()) @@ -728,7 +728,97 @@ double Desktop::getDefaultMasterScale() return getGlobalDPI() / USER_DEFAULT_SCREEN_DPI; } -bool Desktop::canUseSemiTransparentWindows() noexcept { return true; } +bool Desktop::canUseSemiTransparentWindows() noexcept +{ + return true; +} + +class Desktop::NativeDarkModeChangeDetectorImpl +{ +public: + NativeDarkModeChangeDetectorImpl() + { + #if ! JUCE_MINGW + const auto winVer = getWindowsVersionInfo(); + + if (winVer.dwMajorVersion >= 10 && winVer.dwBuildNumber >= 17763) + { + const auto uxtheme = "uxtheme.dll"; + LoadLibraryA (uxtheme); + const auto uxthemeModule = GetModuleHandleA (uxtheme); + + if (uxthemeModule != nullptr) + { + shouldAppsUseDarkMode = (ShouldAppsUseDarkModeFunc) GetProcAddress (uxthemeModule, MAKEINTRESOURCEA (132)); + + if (shouldAppsUseDarkMode != nullptr) + darkModeEnabled = shouldAppsUseDarkMode() && ! isHighContrast(); + } + } + #endif + } + + bool isDarkModeEnabled() const noexcept { return darkModeEnabled; } + +private: + static bool isHighContrast() + { + HIGHCONTRASTW highContrast {}; + + if (SystemParametersInfoW (SPI_GETHIGHCONTRAST, sizeof (highContrast), &highContrast, false)) + return highContrast.dwFlags & HCF_HIGHCONTRASTON; + + return false; + } + + static LRESULT CALLBACK callWndProc (int nCode, WPARAM wParam, LPARAM lParam) + { + auto* params = reinterpret_cast (lParam); + + if (nCode >= 0 + && params != nullptr + && params->message == WM_SETTINGCHANGE + && params->lParam != 0 + && CompareStringOrdinal (reinterpret_cast (params->lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) + { + Desktop::getInstance().nativeDarkModeChangeDetectorImpl->colourSetChanged(); + } + + return CallNextHookEx ({}, nCode, wParam, lParam); + } + + void colourSetChanged() + { + if (shouldAppsUseDarkMode != nullptr) + { + const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, shouldAppsUseDarkMode() && ! isHighContrast()); + + if (darkModeEnabled != wasDarkModeEnabled) + Desktop::getInstance().darkModeChanged(); + } + } + + using ShouldAppsUseDarkModeFunc = bool (WINAPI*)(); + ShouldAppsUseDarkModeFunc shouldAppsUseDarkMode = nullptr; + + bool darkModeEnabled = false; + HHOOK hook { SetWindowsHookEx (WH_CALLWNDPROC, + callWndProc, + (HINSTANCE) juce::Process::getCurrentModuleInstanceHandle(), + GetCurrentThreadId()) }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) +}; + +std::unique_ptr Desktop::createNativeDarkModeChangeDetectorImpl() +{ + return std::make_unique(); +} + +bool Desktop::isDarkModeActive() const +{ + return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled(); +} Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { @@ -969,7 +1059,6 @@ class WindowsBitmapImage : public ImagePixelData }; //============================================================================== -Image createSnapshotOfNativeWindow (void*); Image createSnapshotOfNativeWindow (void* nativeWindowHandle) { auto hwnd = (HWND) nativeWindowHandle; @@ -1341,7 +1430,7 @@ struct UWPUIViewSettings //============================================================================== struct ComBaseModule { - ComBaseModule() {} + ComBaseModule() = default; ComBaseModule (LPCWSTR libraryName) : h (::LoadLibrary (libraryName)) {} ComBaseModule (ComBaseModule&& o) : h (o.h) { o.h = nullptr; } ~ComBaseModule() { release(); } @@ -1582,7 +1671,7 @@ class HWNDComponentPeer : public ComponentPeer, const ScopedValueSetter scope (shouldIgnoreModalDismiss, true); if (shouldBeMinimised != isMinimised()) - ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_SHOWNORMAL); + ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_RESTORE); } bool isMinimised() const override @@ -2321,7 +2410,7 @@ class HWNDComponentPeer : public ComponentPeer, static void* toFrontCallback1 (void* h) { - SetForegroundWindow ((HWND) h); + BringWindowToTop ((HWND) h); return nullptr; } @@ -3304,7 +3393,7 @@ class HWNDComponentPeer : public ComponentPeer, LRESULT handlePositionChanging (WINDOWPOS& wp) { - if (isConstrainedNativeWindow()) + if (isConstrainedNativeWindow() && ! isFullScreen()) { if ((wp.flags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE) && (wp.x > -32000 && wp.y > -32000) @@ -3359,24 +3448,24 @@ class HWNDComponentPeer : public ComponentPeer, handleMovedOrResized(); - return ! dontRepaint; // to allow non-accelerated openGL windows to draw themselves correctly.. + return ! dontRepaint; // to allow non-accelerated openGL windows to draw themselves correctly. } //============================================================================== - struct ChildWindowCallbackData - { - std::map windowRectsMap; - float scaleRatio; - }; - LRESULT handleDPIChanging (int newDPI, RECT newRect) { + // Sometimes, windows that should not be automatically scaled (secondary windows in plugins) + // are sent WM_DPICHANGED. The size suggested by the OS is incorrect for our unscaled + // window, so we should ignore it. + if (! isPerMonitorDPIAwareWindow (hwnd)) + return 0; + const auto newScale = (double) newDPI / USER_DEFAULT_SCREEN_DPI; if (approximatelyEqual (scaleFactor, newScale)) return 0; - const auto oldScale = std::exchange (scaleFactor, newScale); + scaleFactor = newScale; { const ScopedValueSetter setter (numInDpiChange, numInDpiChange + 1); @@ -3392,58 +3481,11 @@ class HWNDComponentPeer : public ComponentPeer, updateShadower(); InvalidateRect (hwnd, nullptr, FALSE); - ChildWindowCallbackData callbackData; - callbackData.scaleRatio = (float) (scaleFactor / oldScale); - - EnumChildWindows (hwnd, getChildWindowRectCallback, (LPARAM) &callbackData); scaleFactorListeners.call ([this] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (scaleFactor); }); - EnumChildWindows (hwnd, scaleChildWindowCallback, (LPARAM) &callbackData); return 0; } - static BOOL CALLBACK getChildWindowRectCallback (HWND hwnd, LPARAM data) - { - auto& callbackData = *(reinterpret_cast (data)); - - callbackData.windowRectsMap[hwnd] = getWindowClientRect (hwnd); - return TRUE; - } - - static BOOL CALLBACK scaleChildWindowCallback (HWND hwnd, LPARAM data) - { - auto& callbackData = *(reinterpret_cast (data)); - - auto originalBounds = rectangleFromRECT (callbackData.windowRectsMap[hwnd]); - auto scaledBounds = (originalBounds.toFloat() * callbackData.scaleRatio).toNearestInt(); - auto currentBounds = rectangleFromRECT (getWindowClientRect (hwnd)); - - if (scaledBounds != currentBounds) - { - SetWindowPos (hwnd, - nullptr, - scaledBounds.getX(), - scaledBounds.getY(), - scaledBounds.getWidth(), - scaledBounds.getHeight(), - SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER); - } - - if (auto* peer = getOwnerOfWindow (hwnd)) - peer->handleChildDPIChanging(); - - return TRUE; - } - - void handleChildDPIChanging() - { - scaleFactor = getScaleFactorForWindow (parentToAddTo); - scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (scaleFactor); }); - - updateShadower(); - InvalidateRect (hwnd, nullptr, FALSE); - } - //============================================================================== void handleAppActivation (const WPARAM wParam) { @@ -3771,6 +3813,12 @@ class HWNDComponentPeer : public ComponentPeer, } handleFocusLoss(); + + if (auto* modal = Component::getCurrentlyModalComponent()) + if (auto* peer = modal->getPeer()) + if ((peer->getStyleFlags() & ComponentPeer::windowIsTemporary) != 0) + sendInputAttemptWhenModalMessage(); + break; case WM_ACTIVATEAPP: @@ -4541,7 +4589,7 @@ class WindowsTaskDialog : public WindowsMessageBoxBase int getResult() override { - TASKDIALOGCONFIG config = { 0 }; + TASKDIALOGCONFIG config{}; config.cbSize = sizeof (config); config.pszWindowTitle = title.toWideCharPointer(); @@ -5087,128 +5135,205 @@ Image juce_createIconForFile (const File& file) } //============================================================================== -void* CustomMouseCursorInfo::create() const +class MouseCursor::PlatformSpecificHandle { - const int maxW = GetSystemMetrics (SM_CXCURSOR); - const int maxH = GetSystemMetrics (SM_CYCURSOR); +public: + explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type) + : impl (makeHandle (type)) {} - Image im (image); - int hotspotX = hotspot.x; - int hotspotY = hotspot.y; + explicit PlatformSpecificHandle (const CustomMouseCursorInfo& info) + : impl (makeHandle (info)) {} - if (im.getWidth() > maxW || im.getHeight() > maxH) + static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer) { - im = im.rescaled (maxW, maxH); + SetCursor ([&] + { + if (handle != nullptr && handle->impl != nullptr && peer != nullptr) + return handle->impl->getCursor (*peer); - hotspotX = (hotspotX * maxW) / juce::jmax (1, image.getWidth()); - hotspotY = (hotspotY * maxH) / juce::jmax (1, image.getHeight()); + return LoadCursor (nullptr, IDC_ARROW); + }()); } - return IconConverters::createHICONFromImage (im, FALSE, hotspotX, hotspotY); -} +private: + struct Impl + { + virtual ~Impl() = default; + virtual HCURSOR getCursor (ComponentPeer&) = 0; + }; -void MouseCursor::deleteMouseCursor (void* cursorHandle, bool isStandard) -{ - if (cursorHandle != nullptr && ! isStandard) - DestroyCursor ((HCURSOR) cursorHandle); -} + class BuiltinImpl : public Impl + { + public: + explicit BuiltinImpl (HCURSOR cursorIn) + : cursor (cursorIn) {} -enum -{ - hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor) -}; + HCURSOR getCursor (ComponentPeer&) override { return cursor; } -void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type) -{ - LPCTSTR cursorName = IDC_ARROW; + private: + HCURSOR cursor; + }; - switch (type) + class ImageImpl : public Impl { - case NormalCursor: - case ParentCursor: break; - case NoCursor: return (void*) hiddenMouseCursorHandle; - case WaitCursor: cursorName = IDC_WAIT; break; - case IBeamCursor: cursorName = IDC_IBEAM; break; - case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break; - case CrosshairCursor: cursorName = IDC_CROSS; break; + public: + explicit ImageImpl (const CustomMouseCursorInfo& infoIn) : info (infoIn) {} - case LeftRightResizeCursor: - case LeftEdgeResizeCursor: - case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break; + ~ImageImpl() override + { + for (auto& pair : cursorsBySize) + DestroyCursor (pair.second); + } + + HCURSOR getCursor (ComponentPeer& peer) override + { + JUCE_ASSERT_MESSAGE_THREAD; + + static auto getCursorSize = getCursorSizeForPeerFunction(); - case UpDownResizeCursor: - case TopEdgeResizeCursor: - case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break; + const auto size = getCursorSize (peer); + const auto iter = cursorsBySize.find (size); - case TopLeftCornerResizeCursor: - case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break; + if (iter != cursorsBySize.end()) + return iter->second; - case TopRightCornerResizeCursor: - case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break; + const auto img = info.image.getImage(); + const auto imgW = jmax (1, img.getWidth()); + const auto imgH = jmax (1, img.getHeight()); - case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break; + const auto scale = (float) size / (float) unityCursorSize; + const auto scaleToUse = scale * jmin (1.0f, jmin ((float) unityCursorSize / (float) imgW, + (float) unityCursorSize / (float) imgH)) / info.image.getScale(); + const auto rescaled = img.rescaled (roundToInt (scaleToUse * (float) imgW), + roundToInt (scaleToUse * (float) imgH)); + const auto hx = jlimit (0, rescaled.getWidth(), roundToInt (scaleToUse * (float) info.hotspot.x)); + const auto hy = jlimit (0, rescaled.getHeight(), roundToInt (scaleToUse * (float) info.hotspot.y)); - case DraggingHandCursor: + return cursorsBySize.emplace (size, IconConverters::createHICONFromImage (rescaled, false, hx, hy)).first->second; + } + + private: + const CustomMouseCursorInfo info; + std::map cursorsBySize; + }; + + static auto getCursorSizeForPeerFunction() -> int (*) (ComponentPeer&) + { + static const auto getDpiForMonitor = []() -> GetDPIForMonitorFunc + { + constexpr auto library = "SHCore.dll"; + LoadLibraryA (library); + + if (auto* handle = GetModuleHandleA (library)) + return (GetDPIForMonitorFunc) GetProcAddress (handle, "GetDpiForMonitor"); + + return nullptr; + }(); + + static const auto getSystemMetricsForDpi = []() -> GetSystemMetricsForDpiFunc + { + constexpr auto library = "User32.dll"; + LoadLibraryA (library); + + if (auto* handle = GetModuleHandleA (library)) + return (GetSystemMetricsForDpiFunc) GetProcAddress (handle, "GetSystemMetricsForDpi"); + + return nullptr; + }(); + + if (getDpiForMonitor == nullptr || getSystemMetricsForDpi == nullptr) + return [] (ComponentPeer&) { return unityCursorSize; }; + + return [] (ComponentPeer& p) { - static void* dragHandCursor = nullptr; + const ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { p.getNativeHandle() }; + + UINT dpiX = 0, dpiY = 0; - if (dragHandCursor == nullptr) + if (auto* monitor = MonitorFromWindow ((HWND) p.getNativeHandle(), MONITOR_DEFAULTTONULL)) + if (SUCCEEDED (getDpiForMonitor (monitor, MDT_Default, &dpiX, &dpiY))) + return getSystemMetricsForDpi (SM_CXCURSOR, dpiX); + + return unityCursorSize; + }; + } + + static constexpr auto unityCursorSize = 32; + + static std::unique_ptr makeHandle (const CustomMouseCursorInfo& info) + { + return std::make_unique (info); + } + + static std::unique_ptr makeHandle (const MouseCursor::StandardCursorType type) + { + LPCTSTR cursorName = IDC_ARROW; + + switch (type) + { + case NormalCursor: + case ParentCursor: break; + case NoCursor: return std::make_unique (nullptr); + case WaitCursor: cursorName = IDC_WAIT; break; + case IBeamCursor: cursorName = IDC_IBEAM; break; + case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break; + case CrosshairCursor: cursorName = IDC_CROSS; break; + + case LeftRightResizeCursor: + case LeftEdgeResizeCursor: + case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break; + + case UpDownResizeCursor: + case TopEdgeResizeCursor: + case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break; + + case TopLeftCornerResizeCursor: + case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break; + + case TopRightCornerResizeCursor: + case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break; + + case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break; + + case DraggingHandCursor: { - static const unsigned char dragHandData[] = + static const unsigned char dragHandData[] { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138, 98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; - dragHandCursor = CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), { 8, 7 }).create(); + return makeHandle ({ ScaledImage (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData))), { 8, 7 } }); } - return dragHandCursor; - } - - case CopyingCursor: - { - static void* copyCursor = nullptr; - - if (copyCursor == nullptr) + case CopyingCursor: { - static unsigned char copyCursorData[] = { - 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0, - 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111,78,133,218,215,137,31,82,154,100,200,86,91,202,142, - 12,108,212,87,235,174, 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112,252,114,147,74,83, - 5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 - }; - const int copyCursorSize = 119; + static const unsigned char copyCursorData[] + { 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0, + 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111,78,133,218,215,137,31,82,154,100,200,86,91,202,142, + 12,108,212,87,235,174, 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112,252,114,147,74,83, + 5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 }; - copyCursor = CustomMouseCursorInfo (ImageFileFormat::loadFrom (copyCursorData, copyCursorSize), { 1, 3 }).create(); + return makeHandle ({ ScaledImage (ImageFileFormat::loadFrom (copyCursorData, sizeof (copyCursorData))), { 1, 3 } }); } - return copyCursor; + case NumStandardCursorTypes: JUCE_FALLTHROUGH + default: + jassertfalse; break; } - case NumStandardCursorTypes: JUCE_FALLTHROUGH - default: - jassertfalse; break; - } + return std::make_unique ([&] + { + if (auto* c = LoadCursor (nullptr, cursorName)) + return c; - if (auto cursorH = LoadCursor (nullptr, cursorName)) - return cursorH; + return LoadCursor (nullptr, IDC_ARROW); + }()); + } - return LoadCursor (nullptr, IDC_ARROW); -} + std::unique_ptr impl; +}; //============================================================================== -void MouseCursor::showInWindow (ComponentPeer*) const -{ - auto c = (HCURSOR) getHandle(); - - if (c == nullptr) - c = LoadCursor (nullptr, IDC_ARROW); - else if (c == (HCURSOR) hiddenMouseCursorHandle) - c = nullptr; - - SetCursor (c); -} - JUCE_END_IGNORE_WARNINGS_GCC_LIKE } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_DragAndDrop.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_DragAndDrop.cpp index 38f9d9534..f0c7d70c4 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_DragAndDrop.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_DragAndDrop.cpp @@ -26,8 +26,8 @@ namespace juce { -extern void* createDraggingHandCursor(); -extern ComponentPeer* getPeerFor (::Window); +static Cursor createDraggingHandCursor(); +ComponentPeer* getPeerFor (::Window); //============================================================================== class X11DragState @@ -222,7 +222,14 @@ class X11DragState if ((clientMsg.data.l[1] & 1) != 0) { XWindowSystemUtilities::ScopedXLock xLock; - XWindowSystemUtilities::GetXProperty prop (dragAndDropSourceWindow, atoms.XdndTypeList, 0, 0x8000000L, false, XA_ATOM); + + XWindowSystemUtilities::GetXProperty prop (getDisplay(), + dragAndDropSourceWindow, + atoms.XdndTypeList, + 0, + 0x8000000L, + false, + XA_ATOM); if (prop.success && prop.actualType == XA_ATOM && prop.actualFormat == 32 && prop.numItems != 0) { @@ -266,6 +273,8 @@ class X11DragState { if (auto* peer = getPeerFor (windowH)) peer->handleDragExit (dragInfo); + + resetDragAndDrop(); } void handleDragAndDropSelection (const XEvent& evt) @@ -281,8 +290,13 @@ class X11DragState for (;;) { - XWindowSystemUtilities::GetXProperty prop (evt.xany.window, evt.xselection.property, - (long) (dropData.getSize() / 4), 65536, false, AnyPropertyType); + XWindowSystemUtilities::GetXProperty prop (getDisplay(), + evt.xany.window, + evt.xselection.property, + (long) (dropData.getSize() / 4), + 65536, + false, + AnyPropertyType); if (! prop.success) break; @@ -327,6 +341,8 @@ class X11DragState if (completionCallback != nullptr) completionCallback(); + + dragging = false; } bool externalDragInit (::Window window, bool text, const String& str, std::function&& cb) @@ -518,7 +534,13 @@ class X11DragState int getDnDVersionForWindow (::Window target) { - XWindowSystemUtilities::GetXProperty prop (target, getAtoms().XdndAware, 0, 2, false, AnyPropertyType); + XWindowSystemUtilities::GetXProperty prop (getDisplay(), + target, + getAtoms().XdndAware, + 0, + 2, + false, + AnyPropertyType); if (prop.success && prop.data != None && prop.actualFormat == 32 && prop.numItems == 1) return jmin ((int) prop.data[0], (int) XWindowSystemUtilities::Atoms::DndVersion); @@ -548,12 +570,23 @@ class X11DragState ComponentPeer::DragInfo dragInfoCopy (dragInfo); sendDragAndDropFinish(); + resetDragAndDrop(); if (! dragInfoCopy.isEmpty()) if (auto* peer = getPeerFor (windowH)) peer->handleDragDrop (dragInfoCopy); } + void resetDragAndDrop() + { + dragInfo.clear(); + dragInfo.position = Point (-1, -1); + dragAndDropCurrentMimeType = 0; + dragAndDropSourceWindow = 0; + srcMimeTypeAtomList.clear(); + finishAfterDropDataReceived = false; + } + //============================================================================== ::Window windowH = 0, targetWindow = 0, dragAndDropSourceWindow = 0; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.cpp index e3822fc24..d0a4e94a4 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.cpp @@ -127,6 +127,7 @@ bool X11Symbols::loadAllSymbols() makeSymbolBinding (xGetErrorDatabaseText, "XGetErrorDatabaseText"), makeSymbolBinding (xGetErrorText, "XGetErrorText"), makeSymbolBinding (xGetGeometry, "XGetGeometry"), + makeSymbolBinding (xGetImage, "XGetImage"), makeSymbolBinding (xGetInputFocus, "XGetInputFocus"), makeSymbolBinding (xGetModifierMapping, "XGetModifierMapping"), makeSymbolBinding (xGetPointerMapping, "XGetPointerMapping"), @@ -188,6 +189,7 @@ bool X11Symbols::loadAllSymbols() makeSymbolBinding (xUngrabServer, "XUngrabServer"), makeSymbolBinding (xUnlockDisplay, "XUnlockDisplay"), makeSymbolBinding (xUnmapWindow, "XUnmapWindow"), + makeSymbolBinding (xutf8TextListToTextProperty, "Xutf8TextListToTextProperty"), makeSymbolBinding (xWarpPointer, "XWarpPointer"))) return false; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.h index faee04f94..5202b6aa2 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.h @@ -237,6 +237,10 @@ class JUCE_API X11Symbols (::Display*, ::Drawable, ::Window*, int*, int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*), Status) + JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XGetImage, xGetImage, + (::Display*, ::Drawable, int, int, unsigned int, unsigned int, unsigned long, int), + XImage*) + JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XGetInputFocus, xGetInputFocus, (::Display*, ::Window*, int*), void) @@ -449,6 +453,10 @@ class JUCE_API X11Symbols (char**, int, XTextProperty*), Status) + JUCE_GENERATE_FUNCTION_WITH_DEFAULT (Xutf8TextListToTextProperty, xutf8TextListToTextProperty, + (::Display*, char**, int, XICCEncodingStyle, XTextProperty*), + int) + JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XSync, xSync, (::Display*, Bool), void) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp index c0ca75463..48d0a5187 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp @@ -30,7 +30,7 @@ namespace juce #define JUCE_DEBUG_XERRORS 1 #if ! defined (JUCE_DEBUG_XERRORS_SYNCHRONOUSLY) - #define JUCE_DEBUG_XERRORS_SYNCHRONOUSLY 1 + #define JUCE_DEBUG_XERRORS_SYNCHRONOUSLY 0 #endif #endif @@ -163,11 +163,10 @@ bool XWindowSystemUtilities::Atoms::isMimeTypeFile (::Display* display, Atom ato } //============================================================================== -XWindowSystemUtilities::GetXProperty::GetXProperty (Window window, Atom atom, long offset, - long length, bool shouldDelete, Atom requestedType) +XWindowSystemUtilities::GetXProperty::GetXProperty (::Display* display, Window window, Atom atom, + long offset, long length, bool shouldDelete, Atom requestedType) { - success = (X11Symbols::getInstance()->xGetWindowProperty (XWindowSystem::getInstance()->getDisplay(), - window, atom, offset, length, + success = (X11Symbols::getInstance()->xGetWindowProperty (display, window, atom, offset, length, (Bool) shouldDelete, requestedType, &actualType, &actualFormat, &numItems, &bytesLeft, &data) == Success) && data != nullptr; @@ -179,6 +178,148 @@ XWindowSystemUtilities::GetXProperty::~GetXProperty() X11Symbols::getInstance()->xFree (data); } +//============================================================================== +XWindowSystemUtilities::XSettings::XSettings (::Display* d) + : display (d) +{ + settingsAtom = Atoms::getCreating (display, "_XSETTINGS_SETTINGS"); + + settingsWindow = X11Symbols::getInstance()->xGetSelectionOwner (display, + Atoms::getCreating (display, "_XSETTINGS_S0")); + + jassert (settingsWindow != None); + update(); +} + +XWindowSystemUtilities::XSetting XWindowSystemUtilities::XSettings::getSetting (const String& name) const +{ + const auto iter = settings.find (name); + + if (iter != settings.end()) + return iter->second; + + return {}; +} + +void XWindowSystemUtilities::XSettings::update() +{ + const GetXProperty prop { display, + settingsWindow, + settingsAtom, + 0L, + std::numeric_limits::max(), + false, + settingsAtom }; + + if (prop.success + && prop.actualType == settingsAtom + && prop.actualFormat == 8 + && prop.numItems > 0) + { + const auto bytes = (size_t) prop.numItems; + auto* data = prop.data; + size_t byteNum = 0; + + const auto increment = [&] (size_t amount) + { + data += amount; + byteNum += amount; + }; + + struct Header + { + CARD8 byteOrder; + CARD8 padding[3]; + CARD32 serial; + CARD32 nSettings; + }; + + const auto* header = unalignedPointerCast (data); + const auto headerSerial = (int) header->serial; + increment (sizeof (Header)); + + const auto readCARD16 = [&]() -> CARD16 + { + if (byteNum + sizeof (CARD16) > bytes) + return {}; + + const auto value = header->byteOrder == MSBFirst ? ByteOrder::bigEndianShort (data) + : ByteOrder::littleEndianShort (data); + increment (sizeof (CARD16)); + return value; + }; + + const auto readCARD32 = [&]() -> CARD32 + { + if (byteNum + sizeof (CARD32) > bytes) + return {}; + + const auto value = header->byteOrder == MSBFirst ? ByteOrder::bigEndianInt (data) + : ByteOrder::littleEndianInt (data); + increment (sizeof (CARD32)); + return value; + }; + + const auto readString = [&] (size_t nameLen) -> String + { + const auto padded = (nameLen + 3) & (~(size_t) 3); + + if (byteNum + padded > bytes) + return {}; + + auto* ptr = reinterpret_cast (data); + const String result (ptr, nameLen); + increment (padded); + return result; + }; + + CARD16 setting = 0; + + while (byteNum < bytes && setting < header->nSettings) + { + const auto type = *reinterpret_cast (data); + increment (2); + + const auto name = readString (readCARD16()); + const auto serial = (int) readCARD32(); + + enum { XSettingsTypeInteger, XSettingsTypeString, XSettingsTypeColor }; + + const auto parsedSetting = [&]() -> XSetting + { + switch (type) + { + case XSettingsTypeInteger: + return { name, (int) readCARD32() }; + + case XSettingsTypeString: + return { name, readString (readCARD32()) }; + + case XSettingsTypeColor: + // Order is important, these should be kept as separate statements! + const auto r = (uint8) readCARD16(); + const auto g = (uint8) readCARD16(); + const auto b = (uint8) readCARD16(); + const auto a = (uint8) readCARD16(); + return { name, Colour { r, g, b, a } }; + } + + return {}; + }(); + + if (serial > lastUpdateSerial) + { + settings[parsedSetting.name] = parsedSetting; + listeners.call ([&parsedSetting] (Listener& l) { l.settingChanged (parsedSetting); }); + } + + setting += 1; + } + + lastUpdateSerial = headerSerial; + } +} + //============================================================================== ::Window juce_messageWindowHandle; XContext windowHandleXContext; @@ -676,6 +817,16 @@ namespace Visuals class XBitmapImage : public ImagePixelData { public: + explicit XBitmapImage (XImage* image) + : ImagePixelData (image->depth == 24 ? Image::RGB : Image::ARGB, image->width, image->height), + xImage (image), + imageDepth ((unsigned int) xImage->depth) + { + pixelStride = xImage->bits_per_pixel / 8; + lineStride = xImage->bytes_per_line; + imageData = reinterpret_cast (xImage->data); + } + XBitmapImage (Image::PixelFormat format, int w, int h, bool clearImage, unsigned int imageDepth_, Visual* visual) : ImagePixelData (format, w, h), @@ -699,8 +850,8 @@ class XBitmapImage : public ImagePixelData segmentInfo.shmaddr = (char *) -1; segmentInfo.readOnly = False; - xImage = X11Symbols::getInstance()->xShmCreateImage (display, visual, imageDepth, ZPixmap, nullptr, - &segmentInfo, (unsigned int) w, (unsigned int) h); + xImage.reset (X11Symbols::getInstance()->xShmCreateImage (display, visual, imageDepth, ZPixmap, nullptr, + &segmentInfo, (unsigned int) w, (unsigned int) h)); if (xImage != nullptr) { @@ -739,7 +890,7 @@ class XBitmapImage : public ImagePixelData imageDataAllocated.allocate ((size_t) (lineStride * h), format == Image::ARGB && clearImage); imageData = imageDataAllocated; - xImage = (XImage*) ::calloc (1, sizeof (XImage)); + xImage.reset ((XImage*) ::calloc (1, sizeof (XImage))); xImage->width = w; xImage->height = h; @@ -773,7 +924,7 @@ class XBitmapImage : public ImagePixelData xImage->blue_mask = visual->blue_mask; } - if (! X11Symbols::getInstance()->xInitImage (xImage)) + if (! X11Symbols::getInstance()->xInitImage (xImage.get())) jassertfalse; } } @@ -791,7 +942,6 @@ class XBitmapImage : public ImagePixelData X11Symbols::getInstance()->xShmDetach (display, &segmentInfo); X11Symbols::getInstance()->xFlush (display); - X11Symbols::getInstance()->xDestroyImage (xImage); shmdt (segmentInfo.shmaddr); shmctl (segmentInfo.shmid, IPC_RMID, nullptr); @@ -800,7 +950,6 @@ class XBitmapImage : public ImagePixelData #endif { xImage->data = nullptr; - X11Symbols::getInstance()->xDestroyImage (xImage); } } @@ -877,7 +1026,7 @@ class XBitmapImage : public ImagePixelData auto* pixel = (PixelRGB*) p; p += srcData.pixelStride; - X11Symbols::getInstance()->xPutPixel (xImage, x, y, + X11Symbols::getInstance()->xPutPixel (xImage.get(), x, y, (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask) | (((((uint32) pixel->getGreen()) << gShiftL) >> gShiftR) & gMask) | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask)); @@ -888,10 +1037,10 @@ class XBitmapImage : public ImagePixelData // blit results to screen. #if JUCE_USE_XSHM if (isUsingXShm()) - X11Symbols::getInstance()->xShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True); + X11Symbols::getInstance()->xShmPutImage (display, (::Drawable) window, gc, xImage.get(), sx, sy, dx, dy, dw, dh, True); else #endif - X11Symbols::getInstance()->xPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh); + X11Symbols::getInstance()->xPutImage (display, (::Drawable) window, gc, xImage.get(), sx, sy, dx, dy, dw, dh); } #if JUCE_USE_XSHM @@ -900,7 +1049,15 @@ class XBitmapImage : public ImagePixelData private: //============================================================================== - XImage* xImage = nullptr; + struct Deleter + { + void operator() (XImage* img) const noexcept + { + X11Symbols::getInstance()->xDestroyImage (img); + } + }; + + std::unique_ptr xImage; const unsigned int imageDepth; HeapBlock imageDataAllocated; HeapBlock imageData16Bit; @@ -930,158 +1087,6 @@ class XBitmapImage : public ImagePixelData //=============================== X11 - Displays =============================== namespace DisplayHelpers { - template - void parseXSettings (const unsigned char* dataPtr, const size_t bytes, Callback&& callback) - { - struct Header - { - CARD8 byteOrder; - CARD8 padding[3]; - CARD32 serial; - CARD32 nSettings; - }; - - auto* data = dataPtr; - size_t byteNum = 0; - - const auto increment = [&] (size_t amount) - { - data += amount; - byteNum += amount; - }; - - const auto* header = reinterpret_cast (data); - increment (sizeof (Header)); - - const auto readCARD16 = [&]() -> CARD16 - { - if (byteNum + sizeof (CARD16) > bytes) - return {}; - - const auto value = header->byteOrder == MSBFirst ? ByteOrder::bigEndianShort (data) - : ByteOrder::littleEndianShort (data); - increment (sizeof (CARD16)); - return value; - }; - - const auto readCARD32 = [&]() -> CARD32 - { - if (byteNum + sizeof (CARD32) > bytes) - return {}; - - const auto value = header->byteOrder == MSBFirst ? ByteOrder::bigEndianInt (data) - : ByteOrder::littleEndianInt (data); - increment (sizeof (CARD32)); - return value; - }; - - const auto readString = [&] (size_t nameLen) -> std::string - { - const auto padded = (nameLen + 3) & (~(size_t) 3); - - if (byteNum + padded > bytes) - return {}; - - auto* ptr = reinterpret_cast (data); - const std::string result (ptr, ptr + nameLen); - increment (padded); - return result; - }; - - CARD16 setting = 0; - - while (byteNum < bytes && setting < header->nSettings) - { - const auto type = *reinterpret_cast (data); - increment (2); - - const auto name = readString (readCARD16()); - const auto serial = readCARD32(); - ignoreUnused (serial); - - enum { XSettingsTypeInteger, XSettingsTypeString, XSettingsTypeColor }; - - switch (type) - { - case XSettingsTypeInteger: - { - callback (name, (INT32) readCARD32()); - break; - } - - case XSettingsTypeString: - { - callback (name, readString (readCARD32())); - break; - } - - case XSettingsTypeColor: - { - // Order is important, these should be kept as separate statements! - const auto r = readCARD16(); - const auto g = readCARD16(); - const auto b = readCARD16(); - const auto a = readCARD16(); - callback (name, r, g, b, a); - break; - } - } - - setting += 1; - } - } - - double getScalingFactorFromXSettings() - { - if (auto* display = XWindowSystem::getInstance()->getDisplay()) - { - using namespace XWindowSystemUtilities; - - ScopedXLock xLock; - - const auto selectionWindow = X11Symbols::getInstance()->xGetSelectionOwner (display, - Atoms::getCreating (display, "_XSETTINGS_S0")); - - if (selectionWindow != None) - { - const auto xsettingsSettingsAtom = Atoms::getCreating (display, "_XSETTINGS_SETTINGS"); - - const GetXProperty prop { selectionWindow, - xsettingsSettingsAtom, - 0L, - std::numeric_limits::max(), - false, - xsettingsSettingsAtom }; - - if (prop.success - && prop.actualType == xsettingsSettingsAtom - && prop.actualFormat == 8) - { - struct ExtractRelevantSettings - { - void operator() (const std::string& name, INT32 value) - { - if (name == "Gdk/WindowScalingFactor") - scaleFactor = value; - } - - void operator() (const std::string&, const std::string&) {} - void operator() (const std::string&, CARD16, CARD16, CARD16, CARD16) {} - - INT32 scaleFactor = 0; - }; - - ExtractRelevantSettings callback; - parseXSettings (prop.data, prop.numItems, callback); - - return (double) callback.scaleFactor; - } - } - } - - return 0.0; - } - static double getDisplayDPI (::Display* display, int index) { auto widthMM = X11Symbols::getInstance()->xDisplayWidthMM (display, index); @@ -1096,10 +1101,16 @@ namespace DisplayHelpers static double getDisplayScale (const String& name, double dpi) { - const auto scaleFactorFromXSettings = getScalingFactorFromXSettings(); + if (auto* xSettings = XWindowSystem::getInstance()->getXSettings()) + { + auto windowScalingFactorSetting = xSettings->getSetting (XWindowSystem::getWindowScalingFactorSettingName()); - if (scaleFactorFromXSettings > 0.0) - return scaleFactorFromXSettings; + if (windowScalingFactorSetting.isValid() + && windowScalingFactorSetting.integerValue > 0) + { + return (double) windowScalingFactorSetting.integerValue; + } + } if (name.isNotEmpty()) { @@ -1254,7 +1265,7 @@ namespace ClipboardHelpers { if (display != nullptr) { - XWindowSystemUtilities::GetXProperty prop (window, atom, 0L, 100000, false, AnyPropertyType); + XWindowSystemUtilities::GetXProperty prop (display, window, atom, 0L, 100000, false, AnyPropertyType); if (prop.success) { @@ -1606,12 +1617,16 @@ void XWindowSystem::setTitle (::Window windowH, const String& title) const { jassert (windowH != 0); - XTextProperty nameProperty; + XTextProperty nameProperty{}; char* strings[] = { const_cast (title.toRawUTF8()) }; XWindowSystemUtilities::ScopedXLock xLock; - if (X11Symbols::getInstance()->xStringListToTextProperty (strings, 1, &nameProperty)) + if (X11Symbols::getInstance()->xutf8TextListToTextProperty (display, + strings, + numElementsInArray (strings), + XUTF8StringStyle, + &nameProperty) >= 0) { X11Symbols::getInstance()->xSetWMName (display, windowH, &nameProperty); X11Symbols::getInstance()->xSetWMIconName (display, windowH, &nameProperty); @@ -1705,6 +1720,8 @@ void XWindowSystem::setBounds (::Window windowH, Rectangle newBounds, bool } } + updateConstraints (windowH, *peer); + XWindowSystemUtilities::ScopedXLock xLock; if (auto hints = makeXFreePtr (X11Symbols::getInstance()->xAllocSizeHints())) @@ -1714,14 +1731,6 @@ void XWindowSystem::setBounds (::Window windowH, Rectangle newBounds, bool hints->y = newBounds.getY(); hints->width = newBounds.getWidth(); hints->height = newBounds.getHeight(); - - if ((peer->getStyleFlags() & ComponentPeer::windowIsResizable) == 0) - { - hints->min_width = hints->max_width = hints->width; - hints->min_height = hints->max_height = hints->height; - hints->flags |= PMinSize | PMaxSize; - } - X11Symbols::getInstance()->xSetWMNormalHints (display, windowH, hints.get()); } @@ -1735,6 +1744,41 @@ void XWindowSystem::setBounds (::Window windowH, Rectangle newBounds, bool } } +void XWindowSystem::updateConstraints (::Window windowH) const +{ + if (auto* peer = getPeerFor (windowH)) + updateConstraints (windowH, *peer); +} + +void XWindowSystem::updateConstraints (::Window windowH, ComponentPeer& peer) const +{ + XWindowSystemUtilities::ScopedXLock xLock; + + if (auto hints = makeXFreePtr (X11Symbols::getInstance()->xAllocSizeHints())) + { + if ((peer.getStyleFlags() & ComponentPeer::windowIsResizable) == 0) + { + hints->min_width = hints->max_width = peer.getBounds().getWidth(); + hints->min_height = hints->max_height = peer.getBounds().getHeight(); + hints->flags = PMinSize | PMaxSize; + } + else if (auto* c = peer.getConstrainer()) + { + const auto windowBorder = peer.getFrameSize(); + const auto factor = peer.getPlatformScaleFactor(); + const auto leftAndRight = windowBorder.getLeftAndRight(); + const auto topAndBottom = windowBorder.getTopAndBottom(); + hints->min_width = jmax (1, (int) (factor * c->getMinimumWidth()) - leftAndRight); + hints->max_width = jmax (1, (int) (factor * c->getMaximumWidth()) - leftAndRight); + hints->min_height = jmax (1, (int) (factor * c->getMinimumHeight()) - topAndBottom); + hints->max_height = jmax (1, (int) (factor * c->getMaximumHeight()) - topAndBottom); + hints->flags = PMinSize | PMaxSize; + } + + X11Symbols::getInstance()->xSetWMNormalHints (display, windowH, hints.get()); + } +} + bool XWindowSystem::contains (::Window windowH, Point localPos) const { ::Window root, child; @@ -1757,7 +1801,7 @@ BorderSize XWindowSystem::getBorderSize (::Window windowH) const if (hints != None) { - XWindowSystemUtilities::GetXProperty prop (windowH, hints, 0, 4, false, XA_CARDINAL); + XWindowSystemUtilities::GetXProperty prop (display, windowH, hints, 0, 4, false, XA_CARDINAL); if (prop.success && prop.actualFormat == 32) { @@ -1839,7 +1883,7 @@ bool XWindowSystem::isMinimised (::Window windowH) const jassert (windowH != 0); XWindowSystemUtilities::ScopedXLock xLock; - XWindowSystemUtilities::GetXProperty prop (windowH, atoms.state, 0, 64, false, atoms.state); + XWindowSystemUtilities::GetXProperty prop (display, windowH, atoms.state, 0, 64, false, atoms.state); if (prop.success && prop.actualType == atoms.state && prop.actualFormat == 32 && prop.numItems > 0) @@ -1900,10 +1944,13 @@ void XWindowSystem::toBehind (::Window windowH, ::Window otherWindow) const { jassert (windowH != 0 && otherWindow != 0); - Window newStack[] = { otherWindow, windowH }; + const auto topLevelA = findTopLevelWindowOf (windowH); + const auto topLevelB = findTopLevelWindowOf (otherWindow); + + Window newStack[] = { topLevelA, topLevelB }; XWindowSystemUtilities::ScopedXLock xLock; - X11Symbols::getInstance()->xRestackWindows (display, newStack, 2); + X11Symbols::getInstance()->xRestackWindows (display, newStack, numElementsInArray (newStack)); } bool XWindowSystem::isFocused (::Window windowH) const @@ -2000,6 +2047,37 @@ bool XWindowSystem::canUseARGBImages() const return canUseARGB; } +bool XWindowSystem::isDarkModeActive() const +{ + const auto themeName = [this]() -> String + { + if (xSettings != nullptr) + { + const auto themeNameSetting = xSettings->getSetting (getThemeNameSettingName()); + + if (themeNameSetting.isValid() + && themeNameSetting.stringValue.isNotEmpty()) + { + return themeNameSetting.stringValue; + } + } + + ChildProcess gsettings; + + if (File ("/usr/bin/gsettings").existsAsFile() + && gsettings.start ("/usr/bin/gsettings get org.gnome.desktop.interface gtk-theme", ChildProcess::wantStdOut)) + { + if (gsettings.waitForProcessToFinish (200)) + return gsettings.readAllProcessOutput(); + } + + return {}; + }(); + + return (themeName.isNotEmpty() + && (themeName.containsIgnoreCase ("dark") || themeName.containsIgnoreCase ("black"))); +} + Image XWindowSystem::createImage (bool isSemiTransparent, int width, int height, bool argb) const { auto visualAndDepth = displayVisuals->getBestVisualForWindow (isSemiTransparent); @@ -2116,10 +2194,10 @@ void XWindowSystem::setMousePosition (Point pos) const roundToInt (pos.getX()), roundToInt (pos.getY())); } -void* XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point hotspot) const +Cursor XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point hotspot) const { if (display == nullptr) - return nullptr; + return {}; XWindowSystemUtilities::ScopedXLock xLock; @@ -2140,9 +2218,9 @@ void* XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point for (int x = 0; x < (int) imageW; ++x) *dest++ = image.getPixelAt (x, y).getARGB(); - auto* result = (void*) X11Symbols::getInstance()->xcursorImageLoadCursor (display, xcImage.get()); + auto result = X11Symbols::getInstance()->xcursorImageLoadCursor (display, xcImage.get()); - if (result != nullptr) + if (result != Cursor{}) return result; } #endif @@ -2152,7 +2230,7 @@ void* XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point unsigned int cursorW, cursorH; if (! X11Symbols::getInstance()->xQueryBestCursor (display, root, imageW, imageH, &cursorW, &cursorH)) - return nullptr; + return {}; Image im (Image::ARGB, (int) cursorW, (int) cursorH, true); @@ -2202,20 +2280,20 @@ void* XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point black.red = black.green = black.blue = 0; white.red = white.green = white.blue = 0xffff; - return (void*) X11Symbols::getInstance()->xCreatePixmapCursor (display, sourcePixmap.value, maskPixmap.value, &white, &black, - (unsigned int) hotspotX, (unsigned int) hotspotY); + return X11Symbols::getInstance()->xCreatePixmapCursor (display, sourcePixmap.value, maskPixmap.value, &white, &black, + (unsigned int) hotspotX, (unsigned int) hotspotY); } -void XWindowSystem::deleteMouseCursor (void* cursorHandle) const +void XWindowSystem::deleteMouseCursor (Cursor cursorHandle) const { - if (cursorHandle != nullptr && display != nullptr) + if (cursorHandle != Cursor{} && display != nullptr) { XWindowSystemUtilities::ScopedXLock xLock; X11Symbols::getInstance()->xFreeCursor (display, (Cursor) cursorHandle); } } -void* createDraggingHandCursor() +static Cursor createDraggingHandCursor() { constexpr unsigned char dragHandData[] = { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,16,0, @@ -2223,10 +2301,11 @@ void* createDraggingHandCursor() 114,34,236,37,52,77,217, 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; - return CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, (size_t) numElementsInArray (dragHandData)), { 8, 7 }).create(); + auto image = ImageFileFormat::loadFrom (dragHandData, (size_t) numElementsInArray (dragHandData)); + return XWindowSystem::getInstance()->createCustomMouseCursorInfo (std::move (image), { 8, 7 }); } -void* XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType type) const +Cursor XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType type) const { if (display == nullptr) return None; @@ -2237,7 +2316,7 @@ void* XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType { case MouseCursor::NormalCursor: case MouseCursor::ParentCursor: return None; // Use parent cursor - case MouseCursor::NoCursor: return CustomMouseCursorInfo (Image (Image::ARGB, 16, 16, true), {}).create(); + case MouseCursor::NoCursor: return XWindowSystem::createCustomMouseCursorInfo (Image (Image::ARGB, 16, 16, true), {}); case MouseCursor::WaitCursor: shape = XC_watch; break; case MouseCursor::IBeamCursor: shape = XC_xterm; break; @@ -2265,7 +2344,8 @@ void* XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType 252,114,147,74,83,5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 }; - return CustomMouseCursorInfo (ImageFileFormat::loadFrom (copyCursorData, (size_t) numElementsInArray (copyCursorData)), { 1, 3 }).create(); + auto image = ImageFileFormat::loadFrom (copyCursorData, (size_t) numElementsInArray (copyCursorData)); + return createCustomMouseCursorInfo (std::move (image), { 1, 3 }); } case MouseCursor::NumStandardCursorTypes: @@ -2278,10 +2358,10 @@ void* XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType XWindowSystemUtilities::ScopedXLock xLock; - return (void*) X11Symbols::getInstance()->xCreateFontCursor (display, shape); + return X11Symbols::getInstance()->xCreateFontCursor (display, shape); } -void XWindowSystem::showCursor (::Window windowH, void* cursorHandle) const +void XWindowSystem::showCursor (::Window windowH, Cursor cursorHandle) const { jassert (windowH != 0); @@ -2390,7 +2470,7 @@ Array XWindowSystem::findDisplays (float masterScale) const for (int i = 0; i < numMonitors; ++i) { auto rootWindow = X11Symbols::getInstance()->xRootWindow (display, i); - XWindowSystemUtilities::GetXProperty prop (rootWindow, workAreaHints, 0, 4, false, XA_CARDINAL); + XWindowSystemUtilities::GetXProperty prop (display, rootWindow, workAreaHints, 0, 4, false, XA_CARDINAL); if (! hasWorkAreaData (prop)) continue; @@ -2482,7 +2562,8 @@ Array XWindowSystem::findDisplays (float masterScale) const for (int i = 0; i < numMonitors; ++i) { - XWindowSystemUtilities::GetXProperty prop (X11Symbols::getInstance()->xRootWindow (display, i), + XWindowSystemUtilities::GetXProperty prop (display, + X11Symbols::getInstance()->xRootWindow (display, i), workAreaHints, 0, 4, false, XA_CARDINAL); auto workArea = getWorkArea (prop); @@ -2633,6 +2714,28 @@ String XWindowSystem::getTextFromClipboard() const } //============================================================================== +::Window XWindowSystem::findTopLevelWindowOf (::Window w) const +{ + if (w == 0) + return 0; + + Window* windowList = nullptr; + uint32 windowListSize = 0; + Window parent, root; + + XWindowSystemUtilities::ScopedXLock xLock; + const auto result = X11Symbols::getInstance()->xQueryTree (display, w, &root, &parent, &windowList, &windowListSize); + const auto deleter = makeXFreePtr (windowList); + + if (result == 0) + return 0; + + if (parent == root) + return w; + + return findTopLevelWindowOf (parent); +} + bool XWindowSystem::isParentWindowOf (::Window windowH, ::Window possibleChild) const { if (windowH == 0 || possibleChild == 0) @@ -2914,7 +3017,7 @@ long XWindowSystem::getUserTime (::Window windowH) const { jassert (windowH != 0); - XWindowSystemUtilities::GetXProperty prop (windowH, atoms.userTime, 0, 65536, false, XA_CARDINAL); + XWindowSystemUtilities::GetXProperty prop (display, windowH, atoms.userTime, 0, 65536, false, XA_CARDINAL); if (! prop.success) return 0; @@ -2925,6 +3028,15 @@ long XWindowSystem::getUserTime (::Window windowH) const return result; } +void XWindowSystem::initialiseXSettings() +{ + xSettings = std::make_unique (display); + + X11Symbols::getInstance()->xSelectInput (display, + xSettings->getSettingsWindow(), + StructureNotifyMask | PropertyChangeMask); +} + XWindowSystem::DisplayVisuals::DisplayVisuals (::Display* xDisplay) { auto findVisualWithDepthOrNull = [&] (int desiredDepth) -> Visual* @@ -3009,6 +3121,7 @@ bool XWindowSystem::initialiseXDisplay() initialisePointerMap(); updateModifierMappings(); + initialiseXSettings(); #if JUCE_USE_XSHM if (XSHMHelpers::isShmAvailable (display)) @@ -3499,7 +3612,9 @@ void XWindowSystem::dismissBlockingModals (LinuxComponentPeer* peer) const { if (peer->getComponent().isCurrentlyBlockedByAnotherModalComponent()) if (auto* currentModalComp = Component::getCurrentlyModalComponent()) - currentModalComp->inputAttemptWhenModal(); + if (auto* otherPeer = currentModalComp->getPeer()) + if ((otherPeer->getStyleFlags() & ComponentPeer::windowIsTemporary) != 0) + currentModalComp->inputAttemptWhenModal(); } void XWindowSystem::handleConfigureNotifyEvent (LinuxComponentPeer* peer, XConfigureEvent& confEvent) const @@ -3541,12 +3656,12 @@ void XWindowSystem::propertyNotifyEvent (LinuxComponentPeer* peer, const XProper return false; XWindowSystemUtilities::ScopedXLock xLock; - XWindowSystemUtilities::GetXProperty prop (event.window, atoms.windowState, 0, 128, false, XA_ATOM); + XWindowSystemUtilities::GetXProperty prop (display, event.window, atoms.windowState, 0, 128, false, XA_ATOM); if (! (prop.success && prop.actualFormat == 32 && prop.actualType == XA_ATOM)) return false; - const auto data = reinterpret_cast (prop.data); + const auto* data = unalignedPointerCast (prop.data); const auto end = data + prop.numItems; return std::find (data, end, atoms.windowStateHidden) != end; @@ -3554,6 +3669,9 @@ void XWindowSystem::propertyNotifyEvent (LinuxComponentPeer* peer, const XProper if (isStateChangeEvent() || isHidden()) dismissBlockingModals (peer); + + if (event.atom == XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_FRAME_EXTENTS")) + peer->updateBorderSize(); } void XWindowSystem::handleMappingNotify (XMappingEvent& mappingEvent) const @@ -3679,6 +3797,21 @@ void XWindowSystem::windowMessageReceive (XEvent& event) if (! juce_handleXEmbedEvent (nullptr, &event)) #endif { + auto* instance = XWindowSystem::getInstance(); + + if (auto* xSettings = instance->getXSettings()) + { + if (event.xany.window == xSettings->getSettingsWindow()) + { + if (event.xany.type == PropertyNotify) + xSettings->update(); + else if (event.xany.type == DestroyNotify) + instance->initialiseXSettings(); + + return; + } + } + if (auto* peer = dynamic_cast (getPeerFor (event.xany.window))) { XWindowSystem::getInstance()->handleWindowMessage (peer, event); @@ -3688,8 +3821,6 @@ void XWindowSystem::windowMessageReceive (XEvent& event) if (event.type != ConfigureNotify) return; - const auto* instance = XWindowSystem::getInstance(); - for (auto i = ComponentPeer::getNumPeers(); --i >= 0;) instance->dismissBlockingModals (dynamic_cast (ComponentPeer::getPeer (i)), event.xconfigure); @@ -3705,4 +3836,36 @@ void XWindowSystem::windowMessageReceive (XEvent& event) //============================================================================== JUCE_IMPLEMENT_SINGLETON (XWindowSystem) +Image createSnapshotOfNativeWindow (void* window) +{ + ::Window root; + int wx, wy; + unsigned int ww, wh, bw, bitDepth; + + XWindowSystemUtilities::ScopedXLock xLock; + + const auto display = XWindowSystem::getInstance()->getDisplay(); + + if (! X11Symbols::getInstance()->xGetGeometry (display, (::Drawable) window, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth)) + return {}; + + const auto scale = [] + { + if (auto* d = Desktop::getInstance().getDisplays().getPrimaryDisplay()) + return d->scale; + + return 1.0; + }(); + + auto image = Image { new XBitmapImage { X11Symbols::getInstance()->xGetImage (display, + (::Drawable) window, + 0, + 0, + ww, + wh, + AllPlanes, + ZPixmap) } }; + return image.rescaled ((int) ((double) ww / scale), (int) ((double) wh / scale)); +} + } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h index abe97b3b5..1dee48047 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h @@ -49,8 +49,8 @@ namespace XWindowSystemUtilities */ struct GetXProperty { - GetXProperty (::Window windowH, Atom property, long offset, - long length, bool shouldDelete, Atom requestedType); + GetXProperty (::Display* display, ::Window windowH, Atom property, + long offset, long length, bool shouldDelete, Atom requestedType); ~GetXProperty(); bool success = false; @@ -90,6 +90,77 @@ namespace XWindowSystemUtilities XdndTypeList, XdndActionList, XdndActionDescription, XdndActionCopy, XdndActionPrivate, XembedMsgType, XembedInfo, allowedActions[5], allowedMimeTypes[4], utf8String, clipboard, targets; }; + + //============================================================================== + /** Represents a setting according to the XSETTINGS specification. + + @tags{GUI} + */ + struct XSetting + { + enum class Type + { + integer, + string, + colour, + invalid + }; + + XSetting() = default; + + XSetting (const String& n, int v) : name (n), type (Type::integer), integerValue (v) {} + XSetting (const String& n, const String& v) : name (n), type (Type::string), stringValue (v) {} + XSetting (const String& n, const Colour& v) : name (n), type (Type::colour), colourValue (v) {} + + bool isValid() const noexcept { return type != Type::invalid; } + + String name; + Type type = Type::invalid; + + int integerValue = -1; + String stringValue; + Colour colourValue; + }; + + /** Parses and stores the X11 settings for a display according to the XSETTINGS + specification. + + @tags{GUI} + */ + class XSettings + { + public: + explicit XSettings (::Display*); + + //============================================================================== + void update(); + ::Window getSettingsWindow() const noexcept { return settingsWindow; } + + XSetting getSetting (const String& settingName) const; + + //============================================================================== + struct Listener + { + virtual ~Listener() = default; + virtual void settingChanged (const XSetting& settingThatHasChanged) = 0; + }; + + void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); } + void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); } + + private: + ::Display* display = nullptr; + ::Window settingsWindow = None; + Atom settingsAtom; + + int lastUpdateSerial = -1; + + std::unordered_map settings; + ListenerList listeners; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XSettings) + }; } //============================================================================== @@ -106,6 +177,7 @@ class XWindowSystem : public DeletedAtShutdown void setIcon (::Window , const Image&) const; void setVisible (::Window, bool shouldBeVisible) const; void setBounds (::Window, Rectangle, bool fullScreen) const; + void updateConstraints (::Window) const; BorderSize getBorderSize (::Window) const; Rectangle getWindowBounds (::Window, ::Window parentWindow); @@ -126,6 +198,7 @@ class XWindowSystem : public DeletedAtShutdown bool canUseSemiTransparentWindows() const; bool canUseARGBImages() const; + bool isDarkModeActive() const; int getNumPaintsPendingForWindow (::Window); void processPendingPaintsForWindow (::Window); @@ -140,10 +213,10 @@ class XWindowSystem : public DeletedAtShutdown Point getCurrentMousePosition() const; void setMousePosition (Point pos) const; - void* createCustomMouseCursorInfo (const Image&, Point hotspot) const; - void deleteMouseCursor (void* cursorHandle) const; - void* createStandardMouseCursor (MouseCursor::StandardCursorType) const; - void showCursor (::Window, void* cursorHandle) const; + Cursor createCustomMouseCursorInfo (const Image&, Point hotspot) const; + void deleteMouseCursor (Cursor cursorHandle) const; + Cursor createStandardMouseCursor (MouseCursor::StandardCursorType) const; + void showCursor (::Window, Cursor cursorHandle) const; bool isKeyCurrentlyDown (int keyCode) const; ModifierKeys getNativeRealtimeModifiers() const; @@ -160,11 +233,15 @@ class XWindowSystem : public DeletedAtShutdown String getTextFromClipboard() const; String getLocalClipboardContent() const noexcept { return localClipboardContent; } - ::Display* getDisplay() noexcept { return display; } - const XWindowSystemUtilities::Atoms& getAtoms() const noexcept { return atoms; } + ::Display* getDisplay() const noexcept { return display; } + const XWindowSystemUtilities::Atoms& getAtoms() const noexcept { return atoms; } + XWindowSystemUtilities::XSettings* getXSettings() const noexcept { return xSettings.get(); } bool isX11Available() const noexcept { return xIsAvailable; } + static String getWindowScalingFactorSettingName() { return "Gdk/WindowScalingFactor"; } + static String getThemeNameSettingName() { return "Net/ThemeName"; } + //============================================================================== void handleWindowMessage (LinuxComponentPeer*, XEvent&) const; bool isParentWindowOf (::Window, ::Window possibleChild) const; @@ -216,6 +293,8 @@ class XWindowSystem : public DeletedAtShutdown long getUserTime (::Window) const; + void initialiseXSettings(); + //============================================================================== void handleKeyPressEvent (LinuxComponentPeer*, XKeyEvent&) const; void handleKeyReleaseEvent (LinuxComponentPeer*, const XKeyEvent&) const; @@ -238,6 +317,9 @@ class XWindowSystem : public DeletedAtShutdown void dismissBlockingModals (LinuxComponentPeer*) const; void dismissBlockingModals (LinuxComponentPeer*, const XConfigureEvent&) const; + void updateConstraints (::Window, ComponentPeer&) const; + + ::Window findTopLevelWindowOf (::Window) const; static void windowMessageReceive (XEvent&); @@ -247,6 +329,7 @@ class XWindowSystem : public DeletedAtShutdown XWindowSystemUtilities::Atoms atoms; ::Display* display = nullptr; std::unique_ptr displayVisuals; + std::unique_ptr xSettings; #if JUCE_USE_XSHM std::map<::Window, int> shmPaintsPendingMap; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp index 7b1d63e55..e315f0fd2 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp @@ -209,11 +209,9 @@ ChoicePropertyComponent::~ChoicePropertyComponent() void ChoicePropertyComponent::initialiseComboBox (const Value& v) { if (v != Value()) - { comboBox.setSelectedId (v.getValue(), dontSendNotification); - comboBox.getSelectedIdAsValue().referTo (v); - } + comboBox.getSelectedIdAsValue().referTo (v); comboBox.setEditableText (false); addAndMakeVisible (comboBox); } @@ -222,10 +220,12 @@ void ChoicePropertyComponent::refreshChoices() { comboBox.clear(); - for (auto choice : choices) + for (int i = 0; i < choices.size(); ++i) { + const auto& choice = choices[i]; + if (choice.isNotEmpty()) - comboBox.addItem (choice, choices.indexOf (choice) + 1); + comboBox.addItem (choice, i + 1); else comboBox.addSeparator(); } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.cpp index 10811477a..9e880dc9d 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.cpp @@ -50,7 +50,11 @@ void ComboBox::setEditableText (const bool isEditable) label->setEditable (isEditable, isEditable, false); labelEditableState = (isEditable ? labelIsEditable : labelIsNotEditable); - setWantsKeyboardFocus (labelEditableState == labelIsNotEditable); + const auto isLabelEditable = (labelEditableState == labelIsEditable); + + setWantsKeyboardFocus (! isLabelEditable); + label->setAccessible (isLabelEditable); + resized(); } } @@ -380,6 +384,9 @@ void ComboBox::resized() void ComboBox::enablementChanged() { + if (! isEnabled()) + hidePopup(); + repaint(); } @@ -622,6 +629,9 @@ void ComboBox::handleAsyncUpdate() if (onChange != nullptr) onChange(); + + if (auto* handler = getAccessibilityHandler()) + handler->notifyAccessibilityEvent (AccessibilityEvent::valueChanged); } void ComboBox::sendChange (const NotificationType notification) @@ -646,7 +656,8 @@ class ComboBoxAccessibilityHandler : public AccessibilityHandler explicit ComboBoxAccessibilityHandler (ComboBox& comboBoxToWrap) : AccessibilityHandler (comboBoxToWrap, AccessibilityRole::comboBox, - getAccessibilityActions (comboBoxToWrap)), + getAccessibilityActions (comboBoxToWrap), + { std::make_unique (comboBoxToWrap) }), comboBox (comboBoxToWrap) { } @@ -662,6 +673,25 @@ class ComboBoxAccessibilityHandler : public AccessibilityHandler String getHelp() const override { return comboBox.getTooltip(); } private: + class ComboBoxValueInterface : public AccessibilityTextValueInterface + { + public: + explicit ComboBoxValueInterface (ComboBox& comboBoxToWrap) + : comboBox (comboBoxToWrap) + { + } + + bool isReadOnly() const override { return true; } + String getCurrentValueAsString() const override { return comboBox.getText(); } + void setValueAsString (const String&) override {} + + private: + ComboBox& comboBox; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBoxValueInterface) + }; + static AccessibilityActions getAccessibilityActions (ComboBox& comboBox) { return AccessibilityActions().addAction (AccessibilityActionType::press, [&comboBox] { comboBox.showPopup(); }) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.h b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.h index 662c5dc4f..c1baa02e3 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.h @@ -420,11 +420,14 @@ class JUCE_API ComboBox : public Component, /** @internal */ void parentHierarchyChanged() override; + //============================================================================== + #ifndef DOXYGEN // These methods' bool parameters have changed: see their new method signatures. - JUCE_DEPRECATED (void clear (bool)); - JUCE_DEPRECATED (void setSelectedId (int, bool)); - JUCE_DEPRECATED (void setSelectedItemIndex (int, bool)); - JUCE_DEPRECATED (void setText (const String&, bool)); + [[deprecated]] void clear (bool); + [[deprecated]] void setSelectedId (int, bool); + [[deprecated]] void setSelectedItemIndex (int, bool); + [[deprecated]] void setText (const String&, bool); + #endif private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Label.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Label.cpp index 24ee26a9f..841824311 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Label.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Label.cpp @@ -409,16 +409,8 @@ class LabelKeyboardFocusTraverser : public KeyboardFocusTraverser Component* getDefaultComponent (Component* parent) override { - auto getContainer = [&] - { - if (owner.getCurrentTextEditor() != nullptr && parent == &owner) - return owner.findKeyboardFocusContainer(); - - return parent; - }; - - if (auto* container = getContainer()) - KeyboardFocusTraverser::getDefaultComponent (container); + if (auto* container = getKeyboardFocusContainer (parent)) + return KeyboardFocusTraverser::getDefaultComponent (container); return nullptr; } @@ -426,6 +418,14 @@ class LabelKeyboardFocusTraverser : public KeyboardFocusTraverser Component* getNextComponent (Component* c) override { return KeyboardFocusTraverser::getNextComponent (getComp (c)); } Component* getPreviousComponent (Component* c) override { return KeyboardFocusTraverser::getPreviousComponent (getComp (c)); } + std::vector getAllComponents (Component* parent) override + { + if (auto* container = getKeyboardFocusContainer (parent)) + return KeyboardFocusTraverser::getAllComponents (container); + + return {}; + } + private: Component* getComp (Component* current) const { @@ -436,6 +436,14 @@ class LabelKeyboardFocusTraverser : public KeyboardFocusTraverser return current; } + Component* getKeyboardFocusContainer (Component* parent) const + { + if (owner.getCurrentTextEditor() != nullptr && parent == &owner) + return owner.findKeyboardFocusContainer(); + + return parent; + } + Label& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LabelKeyboardFocusTraverser) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp index f7cec581d..34a599195 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp @@ -26,7 +26,7 @@ namespace juce { -template +template static AccessibilityActions getListRowAccessibilityActions (RowComponentType& rowComponent) { auto onFocus = [&rowComponent] @@ -90,14 +90,13 @@ class ListBox::RowComponent : public Component, addAndMakeVisible (customComponent.get()); customComponent->setBounds (getLocalBounds()); - if (customComponent->getAccessibilityHandler() != nullptr) - invalidateAccessibilityHandler(); + setFocusContainerType (FocusContainerType::focusContainer); + } + else + { + setFocusContainerType (FocusContainerType::none); } } - - if (selectionHasChanged) - if (auto* handler = getAccessibilityHandler()) - isSelected ? handler->grabFocus() : handler->giveAwayFocus(); } void performSelection (const MouseEvent& e, bool isMouseUp) @@ -268,9 +267,6 @@ class ListBox::RowComponent : public Component, std::unique_ptr createAccessibilityHandler() override { - if (customComponent != nullptr && customComponent->getAccessibilityHandler() != nullptr) - return nullptr; - return std::make_unique (*this); } @@ -299,15 +295,23 @@ class ListBox::ListViewport : public Viewport, setViewedComponent (content.release()); } - RowComponent* getComponentForRow (const int row) const noexcept + RowComponent* getComponentForRow (int row) const noexcept + { + if (isPositiveAndBelow (row, rows.size())) + return rows[row]; + + return nullptr; + } + + RowComponent* getComponentForRowWrapped (int row) const noexcept { - return rows [row % jmax (1, rows.size())]; + return rows[row % jmax (1, rows.size())]; } - RowComponent* getComponentForRowIfOnscreen (const int row) const noexcept + RowComponent* getComponentForRowIfOnscreen (int row) const noexcept { return (row >= firstIndex && row < firstIndex + rows.size()) - ? getComponentForRow (row) : nullptr; + ? getComponentForRowWrapped (row) : nullptr; } int getRowNumberOfComponent (Component* const rowComponent) const noexcept @@ -381,7 +385,7 @@ class ListBox::ListViewport : public Viewport, { const int row = i + startIndex; - if (auto* rowComp = getComponentForRow (row)) + if (auto* rowComp = getComponentForRowWrapped (row)) { rowComp->setBounds (0, row * rowH, w, rowH); rowComp->update (row, owner.isRowSelected (row)); @@ -1036,7 +1040,7 @@ void ListBox::repaintRow (const int rowNumber) noexcept repaint (getRowPosition (rowNumber, true)); } -Image ListBox::createSnapshotOfRows (const SparseSet& rows, int& imageX, int& imageY) +ScaledImage ListBox::createSnapshotOfRows (const SparseSet& rows, int& imageX, int& imageY) { Rectangle imageArea; auto firstRow = getRowContainingPosition (0, viewport->getY()); @@ -1058,7 +1062,8 @@ Image ListBox::createSnapshotOfRows (const SparseSet& rows, int& imageX, in imageX = imageArea.getX(); imageY = imageArea.getY(); - auto listScale = Component::getApproximateScaleFactorForComponent (this); + const auto additionalScale = 2.0f; + const auto listScale = Component::getApproximateScaleFactorForComponent (this) * additionalScale; Image snapshot (Image::ARGB, roundToInt ((float) imageArea.getWidth() * listScale), roundToInt ((float) imageArea.getHeight() * listScale), @@ -1071,9 +1076,9 @@ Image ListBox::createSnapshotOfRows (const SparseSet& rows, int& imageX, in if (auto* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i)) { Graphics g (snapshot); - g.setOrigin (getLocalPoint (rowComp, Point()) - imageArea.getPosition()); + g.setOrigin ((getLocalPoint (rowComp, Point()) - imageArea.getPosition()) * additionalScale); - auto rowScale = Component::getApproximateScaleFactorForComponent (rowComp); + const auto rowScale = Component::getApproximateScaleFactorForComponent (rowComp) * additionalScale; if (g.reduceClipRegion (rowComp->getLocalBounds() * rowScale)) { @@ -1086,7 +1091,7 @@ Image ListBox::createSnapshotOfRows (const SparseSet& rows, int& imageX, in } } - return snapshot; + return { snapshot, additionalScale }; } void ListBox::startDragAndDrop (const MouseEvent& e, const SparseSet& rowsToDrag, const var& dragDescription, bool allowDraggingToOtherWindows) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.h b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.h index d21174c17..1a1d79e13 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.h @@ -538,7 +538,7 @@ class JUCE_API ListBox : public Component, @see Component::createComponentSnapshot */ - virtual Image createSnapshotOfRows (const SparseSet& rows, int& x, int& y); + virtual ScaledImage createSnapshotOfRows (const SparseSet& rows, int& x, int& y); /** Returns the viewport that this ListBox uses. @@ -572,6 +572,12 @@ class JUCE_API ListBox : public Component, void startDragAndDrop (const MouseEvent&, const SparseSet& rowsToDrag, const var& dragDescription, bool allowDraggingToOtherWindows); + //============================================================================== + #ifndef DOXYGEN + [[deprecated ("This method's bool parameter has changed: see the new method signature.")]] + void setSelectedRows (const SparseSet&, bool); + #endif + private: //============================================================================== JUCE_PUBLIC_IN_DLL_BUILD (class ListViewport) @@ -593,14 +599,6 @@ class JUCE_API ListBox : public Component, void selectRowInternal (int rowNumber, bool dontScrollToShowThisRow, bool deselectOthersFirst, bool isMouseClick); - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This method's bool parameter has changed: see the new method signature. - JUCE_DEPRECATED (void setSelectedRows (const SparseSet&, bool)); - // This method has been replaced by the more flexible method createSnapshotOfRows. - // Please call createSnapshotOfRows (getSelectedRows(), x, y) to get the same behaviour. - JUCE_DEPRECATED (virtual void createSnapshotOfSelectedRows (int&, int&)) {} - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ListBox) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp index dfdd9530c..763dcb371 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -191,9 +191,10 @@ class Slider::Pimpl : public AsyncUpdater, // this needs to be public otherwis lastCurrentValue = newValue; - // (need to do this comparison because the Value will use equalsWithSameType to compare - // the new and old values, so will generate unwanted change events if the type changes) - if (currentValue != newValue) + // Need to do this comparison because the Value will use equalsWithSameType to compare + // the new and old values, so will generate unwanted change events if the type changes. + // Cast to double before comparing, to prevent comparing as another type (e.g. String). + if (static_cast (currentValue.getValue()) != newValue) currentValue = newValue; updateText(); @@ -1363,7 +1364,8 @@ Slider::ScopedDragNotification::ScopedDragNotification (Slider& s) Slider::ScopedDragNotification::~ScopedDragNotification() { - sliderBeingDragged.pimpl->sendDragEnd(); + if (sliderBeingDragged.pimpl != nullptr) + sliderBeingDragged.pimpl->sendDragEnd(); } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.h b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.h index 4fdb0609a..3443b6b51 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.h @@ -992,6 +992,21 @@ class JUCE_API Slider : public Component, /** @internal */ void mouseEnter (const MouseEvent&) override; + //============================================================================== + #ifndef DOXYGEN + // These methods' bool parameters have changed: see the new method signature. + [[deprecated]] void setValue (double, bool); + [[deprecated]] void setValue (double, bool, bool); + [[deprecated]] void setMinValue (double, bool, bool, bool); + [[deprecated]] void setMinValue (double, bool, bool); + [[deprecated]] void setMinValue (double, bool); + [[deprecated]] void setMaxValue (double, bool, bool, bool); + [[deprecated]] void setMaxValue (double, bool, bool); + [[deprecated]] void setMaxValue (double, bool); + [[deprecated]] void setMinAndMaxValues (double, double, bool, bool); + [[deprecated]] void setMinAndMaxValues (double, double, bool); + #endif + private: //============================================================================== JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl) @@ -1000,20 +1015,6 @@ class JUCE_API Slider : public Component, std::unique_ptr createAccessibilityHandler() override; void init (SliderStyle, TextEntryBoxPosition); - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // These methods' bool parameters have changed: see the new method signature. - JUCE_DEPRECATED (void setValue (double, bool)); - JUCE_DEPRECATED (void setValue (double, bool, bool)); - JUCE_DEPRECATED (void setMinValue (double, bool, bool, bool)); - JUCE_DEPRECATED (void setMinValue (double, bool, bool)); - JUCE_DEPRECATED (void setMinValue (double, bool)); - JUCE_DEPRECATED (void setMaxValue (double, bool, bool, bool)); - JUCE_DEPRECATED (void setMaxValue (double, bool, bool)); - JUCE_DEPRECATED (void setMaxValue (double, bool)); - JUCE_DEPRECATED (void setMinAndMaxValues (double, double, bool, bool)); - JUCE_DEPRECATED (void setMinAndMaxValues (double, double, bool)); - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Slider) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp index be794b082..f9e977da0 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp @@ -174,12 +174,13 @@ void TableHeaderComponent::setColumnWidth (const int columnId, const int newWidt { if (auto* ci = getInfoForId (columnId)) { - if (ci->width != newWidth) + const auto newWidthToUse = jlimit (ci->minimumWidth, ci->maximumWidth, newWidth); + + if (ci->width != newWidthToUse) { auto numColumns = getNumColumns (true); - ci->lastDeliberateWidth = ci->width - = jlimit (ci->minimumWidth, ci->maximumWidth, newWidth); + ci->lastDeliberateWidth = ci->width = newWidthToUse; if (stretchToFit) { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.h b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.h index c3c3e04ed..bd49dd0d0 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.h @@ -186,12 +186,6 @@ class JUCE_API TableListBoxModel @see getDragSourceCustomData, DragAndDropContainer::startDragging */ virtual var getDragSourceDescription (const SparseSet& currentlySelectedRows); - -private: - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This method's signature has changed to take a MouseEvent parameter - please update your code! - JUCE_DEPRECATED_WITH_BODY (virtual int backgroundClicked(), { return 0; }) - #endif }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp index 7b02b02fe..33328e535 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp @@ -660,7 +660,7 @@ struct TextEditor::Iterator auto startX = indexToX (range.getStart()); auto endX = indexToX (range.getEnd()); - return Rectangle (startX, lineY, endX - startX, lineHeight * lineSpacing).toNearestInt(); + return Rectangle (startX, lineY, endX - startX, lineHeight * lineSpacing).getSmallestIntegerContainer(); } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.cpp index 26e7a2b48..4ea4d861b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.cpp @@ -74,7 +74,7 @@ class ToolbarItemComponent::ItemDragAndDropOverlayComponent : public Componen if (DragAndDropContainer* const dnd = DragAndDropContainer::findParentDragContainerFor (this)) { - dnd->startDragging (Toolbar::toolbarDragDescriptor, getParentComponent(), Image(), true, nullptr, &e.source); + dnd->startDragging (Toolbar::toolbarDragDescriptor, getParentComponent(), ScaledImage(), true, nullptr, &e.source); if (ToolbarItemComponent* const tc = getToolbarItemComponent()) { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp index ccc67dacd..c29c6d658 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -67,8 +67,16 @@ class TreeView::ItemComponent : public Component } } - void setMouseIsOverButton (bool isOver) { mouseIsOverButton = isOver; } - TreeViewItem& getRepresentedItem() const noexcept { return item; } + void setMouseIsOverButton (bool isOver) + { + mouseIsOverButton = isOver; + repaint(); + } + + TreeViewItem& getRepresentedItem() const noexcept + { + return item; + } private: //============================================================================== @@ -314,44 +322,66 @@ class TreeView::ContentComponent : public Component, void updateComponents() { - std::vector componentsToKeep; + std::set componentsToKeep; for (auto* treeItem : getAllVisibleItems()) { if (auto* itemComp = getComponentForItem (treeItem)) { - componentsToKeep.push_back (itemComp); + componentsToKeep.insert (itemComp); } else { auto newComp = std::make_unique (*treeItem); addAndMakeVisible (*newComp); - newComp->addMouseListener (this, true); - componentsToKeep.push_back (newComp.get()); + newComp->addMouseListener (this, false); + componentsToKeep.insert (newComp.get()); itemComponents.push_back (std::move (newComp)); } } - for (int i = (int) itemComponents.size(); --i >= 0;) + auto removePredicate = [&] (auto& item) { - auto& comp = itemComponents[(size_t) i]; + if (item == nullptr) + return true; - if (std::find (componentsToKeep.cbegin(), componentsToKeep.cend(), comp.get()) - != componentsToKeep.cend()) - { - auto& treeItem = comp->getRepresentedItem(); - comp->setBounds ({ 0, treeItem.y, getWidth(), treeItem.itemHeight }); - } - else - { - itemComponents.erase (itemComponents.begin() + i); - } + return componentsToKeep.find (item.get()) == componentsToKeep.end() + && ! isMouseDraggingInChildComp (*item); + }; + + const auto iter = std::remove_if (itemComponents.begin(), itemComponents.end(), std::move (removePredicate)); + itemComponents.erase (iter, itemComponents.end()); + + for (auto& comp : itemComponents) + { + auto& treeItem = comp->getRepresentedItem(); + comp->setBounds ({ 0, treeItem.y, getWidth(), treeItem.itemHeight }); } } private: + //============================================================================== + struct ScopedDisableViewportScroll + { + explicit ScopedDisableViewportScroll (ItemComponent& c) + : item (&c) + { + item->setViewportIgnoreDragFlag (true); + } + + ~ScopedDisableViewportScroll() + { + if (item != nullptr) + item->setViewportIgnoreDragFlag (false); + } + + SafePointer item; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedDisableViewportScroll) + }; + //============================================================================== std::unique_ptr createAccessibilityHandler() override { @@ -363,6 +393,7 @@ class TreeView::ContentComponent : public Component, updateItemUnderMouse (e); isDragging = false; + scopedScrollDisabler = nullptr; needSelectionOnMouseUp = false; if (! isEnabled()) @@ -445,14 +476,17 @@ class TreeView::ContentComponent : public Component, { pos.setSize (pos.getWidth(), item.itemHeight); + const auto additionalScale = 2.0f; auto dragImage = Component::createComponentSnapshot (pos, true, - Component::getApproximateScaleFactorForComponent (itemComponent)); + Component::getApproximateScaleFactorForComponent (itemComponent) * additionalScale); dragImage.multiplyAllAlphas (0.6f); auto imageOffset = pos.getPosition() - e.getPosition(); - dragContainer->startDragging (dragDescription, &owner, dragImage, true, &imageOffset, &e.source); + dragContainer->startDragging (dragDescription, &owner, { dragImage, additionalScale }, true, &imageOffset, &e.source); + + scopedScrollDisabler = std::make_unique (*itemComponent); } else { @@ -481,37 +515,34 @@ class TreeView::ContentComponent : public Component, void updateItemUnderMouse (const MouseEvent& e) { - ItemComponent* newItem = nullptr; + if (! owner.openCloseButtonsVisible) + return; - if (owner.openCloseButtonsVisible) + auto* newItem = [this, &e]() -> ItemComponent* { if (auto* itemComponent = getItemComponentAt (e.getPosition())) { auto& item = itemComponent->getRepresentedItem(); - auto pos = item.getItemPosition (false); - if (e.x < pos.getX() - && e.x >= pos.getX() - owner.getIndentSize() - && item.mightContainSubItems()) + if (item.mightContainSubItems()) { - newItem = itemComponent; + const auto xPos = item.getItemPosition (false).getX(); + + if (xPos - owner.getIndentSize() <= e.x && e.x < xPos) + return itemComponent; } } - } + + return nullptr; + }(); if (itemUnderMouse != newItem) { - auto updateItem = [] (ItemComponent* itemComp, bool isMouseOverButton) - { - if (itemComp != nullptr) - { - itemComp->setMouseIsOverButton (isMouseOverButton); - itemComp->repaint(); - } - }; + if (itemUnderMouse != nullptr) + itemUnderMouse->setMouseIsOverButton (false); - updateItem (itemUnderMouse, false); - updateItem (newItem, true); + if (newItem != nullptr) + newItem->setMouseIsOverButton (true); itemUnderMouse = newItem; } @@ -625,6 +656,7 @@ class TreeView::ContentComponent : public Component, std::vector> itemComponents; ItemComponent* itemUnderMouse = nullptr; + std::unique_ptr scopedScrollDisabler; bool isDragging = false, needSelectionOnMouseUp = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentComponent) @@ -632,36 +664,20 @@ class TreeView::ContentComponent : public Component, //============================================================================== class TreeView::TreeViewport : public Viewport, - private Timer + private AsyncUpdater { public: - TreeViewport() = default; - - void updateComponents (bool triggerResize) - { - if (auto* tvc = getContentComp()) - { - if (triggerResize) - tvc->resized(); - else - tvc->updateComponents(); - } - - repaint(); - } + explicit TreeViewport (TreeView& treeView) : owner (treeView) {} void visibleAreaChanged (const Rectangle& newVisibleArea) override { const auto hasScrolledSideways = (newVisibleArea.getX() != lastX); + lastX = newVisibleArea.getX(); updateComponents (hasScrolledSideways); - startTimer (50); - } - - ContentComponent* getContentComp() const noexcept - { - return static_cast (getViewedComponent()); + structureChanged = true; + triggerAsyncUpdate(); } bool keyPressed (const KeyPress& key) override @@ -673,22 +689,76 @@ class TreeView::TreeViewport : public Viewport, return Viewport::keyPressed (key); } + ContentComponent* getContentComp() const noexcept + { + return static_cast (getViewedComponent()); + } + + enum class Async { yes, no }; + + void recalculatePositions (Async useAsyncUpdate) + { + needsRecalculating = true; + + if (useAsyncUpdate == Async::yes) + triggerAsyncUpdate(); + else + handleAsyncUpdate(); + } + private: std::unique_ptr createAccessibilityHandler() override { return createIgnoredAccessibilityHandler (*this); } - void timerCallback() override + void handleAsyncUpdate() override { - stopTimer(); - - if (auto* tree = getParentComponent()) - if (auto* handler = tree->getAccessibilityHandler()) + if (structureChanged) + { + if (auto* handler = owner.getAccessibilityHandler()) handler->notifyAccessibilityEvent (AccessibilityEvent::structureChanged); + + structureChanged = false; + } + + if (needsRecalculating) + { + if (auto* root = owner.rootItem) + { + const auto startY = owner.rootItemVisible ? 0 : -root->itemHeight; + + root->updatePositions (startY); + getViewedComponent()->setSize (jmax (getMaximumVisibleWidth(), root->totalWidth + 50), + root->totalHeight + startY); + } + else + { + getViewedComponent()->setSize (0, 0); + } + + updateComponents (false); + + needsRecalculating = false; + } + } + + void updateComponents (bool triggerResize) + { + if (auto* content = getContentComp()) + { + if (triggerResize) + content->resized(); + else + content->updateComponents(); + } + + repaint(); } + TreeView& owner; int lastX = -1; + bool structureChanged = false, needsRecalculating = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeViewport) }; @@ -696,7 +766,7 @@ class TreeView::TreeViewport : public Viewport, //============================================================================== TreeView::TreeView (const String& name) : Component (name) { - viewport = std::make_unique(); + viewport = std::make_unique (*this); addAndMakeVisible (viewport.get()); viewport->setViewedComponent (new ContentComponent (*this)); @@ -737,7 +807,7 @@ void TreeView::setRootItem (TreeViewItem* const newRootItem) rootItem->setOpen (true); } - updateVisibleItems(); + viewport->recalculatePositions (TreeViewport::Async::no); } } @@ -1097,20 +1167,7 @@ bool TreeView::keyPressed (const KeyPress& key) void TreeView::updateVisibleItems() { - if (rootItem != nullptr) - { - rootItem->updatePositions (rootItemVisible ? 0 : -rootItem->itemHeight); - - viewport->getViewedComponent() - ->setSize (jmax (viewport->getMaximumVisibleWidth(), rootItem->totalWidth + 50), - rootItem->totalHeight - (rootItemVisible ? 0 : rootItem->itemHeight)); - } - else - { - viewport->getViewedComponent()->setSize (0, 0); - } - - viewport->updateComponents (false); + viewport->recalculatePositions (TreeViewport::Async::yes); } //============================================================================== @@ -1774,6 +1831,9 @@ void TreeViewItem::setOwnerView (TreeView* const newOwner) noexcept int TreeViewItem::getIndentX() const noexcept { + if (ownerView == nullptr) + return 0; + int x = ownerView->rootItemVisible ? 1 : 0; if (! ownerView->openCloseButtonsVisible) @@ -2053,6 +2113,9 @@ TreeViewItem::OpennessRestorer::~OpennessRestorer() void TreeViewItem::draw (Graphics& g, int width, bool isMouseOverButton) { + if (ownerView == nullptr) + return; + const auto indent = getIndentX(); const auto itemW = (itemWidth < 0 || drawsInRightMargin) ? width - indent : itemWidth; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.h b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.h index 7f7e55e17..851c404b9 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.h @@ -946,11 +946,6 @@ class JUCE_API TreeView : public Component, int indentSize = -1; bool defaultOpenness = false, rootItemVisible = true, multiSelectEnabled = false, openCloseButtonsVisible = true; - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // this method has been deprecated - see the new version.. - virtual int paintOpenCloseButton (Graphics&, int, int, bool) { return 0; } - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeView) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_AlertWindow.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_AlertWindow.cpp index 76b939c00..230968a54 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_AlertWindow.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_AlertWindow.cpp @@ -47,7 +47,10 @@ AlertWindow::AlertWindow (const String& title, { setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows()); - accessibleMessageLabel.setColour (Label::textColourId, Colours::transparentBlack); + accessibleMessageLabel.setColour (Label::textColourId, Colours::transparentBlack); + accessibleMessageLabel.setColour (Label::backgroundColourId, Colours::transparentBlack); + accessibleMessageLabel.setColour (Label::outlineColourId, Colours::transparentBlack); + accessibleMessageLabel.setInterceptsMouseClicks (false, false); addAndMakeVisible (accessibleMessageLabel); if (message.isEmpty()) @@ -649,6 +652,7 @@ namespace AlertWindowMappings return rawToUniquePtr (ModalCallbackFunction::create (std::move (wrappedCallback))); } + } #if JUCE_MODAL_LOOPS_PERMITTED diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_AlertWindow.h b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_AlertWindow.h index 1e0fae1e7..b4e737890 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_AlertWindow.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_AlertWindow.h @@ -414,9 +414,7 @@ class JUCE_API AlertWindow : public TopLevelWindow #endif //============================================================================== - #if JUCE_MODAL_LOOPS_PERMITTED - // This has been deprecated, use the NativeMessageBox methods instead for more options. - + #if JUCE_MODAL_LOOPS_PERMITTED && ! defined (DOXYGEN) /** Shows an operating-system native dialog box. @param title the title to use at the top @@ -425,9 +423,10 @@ class JUCE_API AlertWindow : public TopLevelWindow it'll show a box with just an ok button @returns true if the ok button was pressed, false if they pressed cancel. */ - JUCE_DEPRECATED (static bool JUCE_CALLTYPE showNativeDialogBox (const String& title, - const String& bodyText, - bool isOkCancel)); + [[deprecated ("Use the NativeMessageBox methods instead for more options")]] + static bool JUCE_CALLTYPE showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel); #endif @@ -499,7 +498,7 @@ class JUCE_API AlertWindow : public TopLevelWindow /** @internal */ int getDesktopWindowStyleFlags() const override; /** @internal */ - float getDesktopScaleFactor() const override { return desktopScale; } + float getDesktopScaleFactor() const override { return desktopScale * Desktop::getInstance().getGlobalScaleFactor(); } private: //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index b5e5c3d7e..d0359907f 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -265,7 +265,7 @@ void CallOutBox::timerCallback() //============================================================================== std::unique_ptr CallOutBox::createAccessibilityHandler() { - return std::make_unique (*this, AccessibilityRole::window); + return std::make_unique (*this, AccessibilityRole::dialogWindow); } } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp index a18f4d211..00a8988b6 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp @@ -322,7 +322,9 @@ void ComponentPeer::handleMovedOrResized() component.sendVisibilityChangeMessage(); } - if (! isFullScreen()) + const auto windowInSpecialState = isFullScreen() || isKioskMode() || nowMinimised; + + if (! windowInSpecialState) lastNonFullscreenBounds = component.getBounds(); } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h index 59c779366..ffe8f2ccb 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h @@ -413,6 +413,11 @@ class JUCE_API ComponentPeer */ virtual double getPlatformScaleFactor() const noexcept { return 1.0; } + /** On platforms that support it, this will update the window's titlebar in some + way to indicate that the window's document needs saving. + */ + virtual void setHasChangedSinceSaved (bool) {} + protected: //============================================================================== static void forceDisplayUpdate(); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_DialogWindow.h b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_DialogWindow.h index 77ba75aea..dd50f1830 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_DialogWindow.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_DialogWindow.h @@ -262,7 +262,7 @@ class JUCE_API DialogWindow : public DocumentWindow /** @internal */ bool keyPressed (const KeyPress&) override; /** @internal */ - float getDesktopScaleFactor() const override { return desktopScale; } + float getDesktopScaleFactor() const override { return desktopScale * Desktop::getInstance().getGlobalScaleFactor(); } private: std::unique_ptr createAccessibilityHandler() override; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ResizableWindow.h b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ResizableWindow.h index 1427c7ac1..f5fef142c 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ResizableWindow.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ResizableWindow.h @@ -313,10 +313,13 @@ class JUCE_API ResizableWindow : public TopLevelWindow }; //============================================================================== - // Deprecated: use setContentOwned() and setContentNonOwned() instead. - JUCE_DEPRECATED (void setContentComponent (Component* newContentComponent, - bool deleteOldOne = true, - bool resizeToFit = false)); + #ifndef DOXYGEN + [[deprecated ("use setContentOwned and setContentNonOwned instead.")]] + void setContentComponent (Component* newContentComponent, + bool deleteOldOne = true, + bool resizeToFit = false); + #endif + using TopLevelWindow::addToDesktop; //============================================================================== @@ -381,6 +384,11 @@ class JUCE_API ResizableWindow : public TopLevelWindow std::unique_ptr resizableCorner; std::unique_ptr resizableBorder; + //============================================================================== + // The parameters for these methods have changed - please update your code! + void getBorderThickness (int& left, int& top, int& right, int& bottom); + void getContentComponentBorder (int& left, int& top, int& right, int& bottom); + private: //============================================================================== Component::SafePointer contentComponent, splashScreen; @@ -399,12 +407,6 @@ class JUCE_API ResizableWindow : public TopLevelWindow void setContent (Component*, bool takeOwnership, bool resizeToFit); void updatePeerConstrainer(); - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // The parameters for these methods have changed - please update your code! - JUCE_DEPRECATED (void getBorderThickness (int& left, int& top, int& right, int& bottom)); - JUCE_DEPRECATED (void getContentComponentBorder (int& left, int& top, int& right, int& bottom)); - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableWindow) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp index a0c25481f..03f7abdff 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp @@ -37,13 +37,19 @@ TooltipWindow::TooltipWindow (Component* parentComp, int delayMs) if (parentComp != nullptr) parentComp->addChildComponent (this); - if (Desktop::getInstance().getMainMouseSource().canHover()) + auto& desktop = Desktop::getInstance(); + + if (desktop.getMainMouseSource().canHover()) + { + desktop.addGlobalMouseListener (this); startTimer (123); + } } TooltipWindow::~TooltipWindow() { hideTip(); + Desktop::getInstance().removeGlobalMouseListener (this); } void TooltipWindow::setMillisecondsBeforeTipAppears (const int newTimeMs) noexcept @@ -56,9 +62,22 @@ void TooltipWindow::paint (Graphics& g) getLookAndFeel().drawTooltip (g, tipShowing, getWidth(), getHeight()); } -void TooltipWindow::mouseEnter (const MouseEvent&) +void TooltipWindow::mouseEnter (const MouseEvent& e) { - hideTip(); + if (e.eventComponent == this) + hideTip(); +} + +void TooltipWindow::mouseDown (const MouseEvent&) +{ + if (isVisible()) + dismissalMouseEventOccured = true; +} + +void TooltipWindow::mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) +{ + if (isVisible()) + dismissalMouseEventOccured = true; } void TooltipWindow::updatePosition (const String& tip, Point pos, Rectangle parentArea) @@ -75,6 +94,11 @@ void TooltipWindow::displayTip (Point screenPos, const String& tip) { jassert (tip.isNotEmpty()); + displayTipInternal (screenPos, tip, ShownManually::yes); +} + +void TooltipWindow::displayTipInternal (Point screenPos, const String& tip, ShownManually shownManually) +{ if (! reentrant) { ScopedValueSetter setter (reentrant, true, false); @@ -120,6 +144,8 @@ void TooltipWindow::displayTip (Point screenPos, const String& tip) #endif toFront (false); + manuallyShownTip = shownManually == ShownManually::yes ? tip : String(); + dismissalMouseEventOccured = false; } } @@ -138,12 +164,17 @@ String TooltipWindow::getTipFor (Component& c) void TooltipWindow::hideTip() { - if (! reentrant) + if (isVisible() && ! reentrant) { - tipShowing.clear(); + tipShowing = {}; + manuallyShownTip = {}; + dismissalMouseEventOccured = false; + removeFromDesktop(); setVisible (false); + lastHideTime = Time::getApproximateMillisecondCounter(); + #if JUCE_DEBUG activeTooltipWindows.removeAllInstancesOf (this); #endif @@ -165,60 +196,59 @@ std::unique_ptr TooltipWindow::createAccessibilityHandler( void TooltipWindow::timerCallback() { - auto& desktop = Desktop::getInstance(); - auto mouseSource = desktop.getMainMouseSource(); - auto now = Time::getApproximateMillisecondCounter(); - + const auto mouseSource = Desktop::getInstance().getMainMouseSource(); auto* newComp = mouseSource.isTouch() ? nullptr : mouseSource.getComponentUnderMouse(); - if (newComp == nullptr || getParentComponent() == nullptr || newComp->getPeer() == getPeer()) + if (manuallyShownTip.isNotEmpty()) { - auto newTip = newComp != nullptr ? getTipFor (*newComp) : String(); - bool tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse); - lastComponentUnderMouse = newComp; - lastTipUnderMouse = newTip; + if (dismissalMouseEventOccured || newComp == nullptr) + hideTip(); - auto clickCount = desktop.getMouseButtonClickCounter(); - auto wheelCount = desktop.getMouseWheelMoveCounter(); - bool mouseWasClicked = (clickCount > mouseClicks || wheelCount > mouseWheelMoves); - mouseClicks = clickCount; - mouseWheelMoves = wheelCount; + return; + } - auto mousePos = mouseSource.getScreenPosition(); - bool mouseMovedQuickly = mousePos.getDistanceFrom (lastMousePos) > 12; + if (newComp == nullptr || getParentComponent() == nullptr || newComp->getPeer() == getPeer()) + { + const auto newTip = newComp != nullptr ? getTipFor (*newComp) : String(); + + const auto mousePos = mouseSource.getScreenPosition(); + const auto mouseMovedQuickly = (mousePos.getDistanceFrom (lastMousePos) > 12); lastMousePos = mousePos; - if (tipChanged || mouseWasClicked || mouseMovedQuickly) + const auto tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse); + const auto now = Time::getApproximateMillisecondCounter(); + + if (tipChanged || dismissalMouseEventOccured || mouseMovedQuickly) lastCompChangeTime = now; + const auto showTip = [this, &mouseSource, &mousePos, &newTip] + { + if (mouseSource.getLastMouseDownPosition() != lastMousePos) + displayTipInternal (mousePos.roundToInt(), newTip, ShownManually::no); + }; + if (isVisible() || now < lastHideTime + 500) { // if a tip is currently visible (or has just disappeared), update to a new one // immediately if needed.. - if (newComp == nullptr || mouseWasClicked || newTip.isEmpty()) - { - if (isVisible()) - { - lastHideTime = now; - hideTip(); - } - } + if (newComp == nullptr || dismissalMouseEventOccured || newTip.isEmpty()) + hideTip(); else if (tipChanged) - { - displayTip (mousePos.roundToInt(), newTip); - } + showTip(); } else { - // if there isn't currently a tip, but one is needed, only let it - // appear after a timeout.. + // if there isn't currently a tip, but one is needed, only let it appear after a timeout if (newTip.isNotEmpty() - && newTip != tipShowing - && now > lastCompChangeTime + (uint32) millisecondsBeforeTipAppears) + && newTip != tipShowing + && now > lastCompChangeTime + (uint32) millisecondsBeforeTipAppears) { - displayTip (mousePos.roundToInt(), newTip); + showTip(); } } + + lastComponentUnderMouse = newComp; + lastTipUnderMouse = newTip; } } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.h b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.h index 782b3a09f..dae2fa4de 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.h @@ -84,7 +84,13 @@ class JUCE_API TooltipWindow : public Component, */ void setMillisecondsBeforeTipAppears (int newTimeMs = 700) noexcept; - /** Can be called to manually force a tip to be shown at a particular location. */ + /** Can be called to manually force a tip to be shown at a particular location. + + The tip will be shown until hideTip() is called, or a dismissal mouse event + occurs. + + @see hideTip + */ void displayTip (Point screenPosition, const String& text); /** Can be called to manually hide the tip if it's showing. */ @@ -121,11 +127,6 @@ class JUCE_API TooltipWindow : public Component, /** returns the bounds for a tooltip at the given screen coordinate, constrained within the given desktop area. */ virtual Rectangle getTooltipBounds (const String& tipText, Point screenPos, Rectangle parentArea) = 0; virtual void drawTooltip (Graphics&, const String& text, int width, int height) = 0; - - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This method has been replaced by getTooltipBounds() - virtual int getTooltipSize (const String&, int&, int&) { return 0; } - #endif }; //============================================================================== @@ -136,15 +137,19 @@ class JUCE_API TooltipWindow : public Component, //============================================================================== Point lastMousePos; Component* lastComponentUnderMouse = nullptr; - String tipShowing, lastTipUnderMouse; + String tipShowing, lastTipUnderMouse, manuallyShownTip; int millisecondsBeforeTipAppears; - int mouseClicks = 0, mouseWheelMoves = 0; unsigned int lastCompChangeTime = 0, lastHideTime = 0; - bool reentrant = false; + bool reentrant = false, dismissalMouseEventOccured = false; + + enum ShownManually { yes, no }; + void displayTipInternal (Point, const String&, ShownManually); std::unique_ptr createAccessibilityHandler() override; void paint (Graphics&) override; void mouseEnter (const MouseEvent&) override; + void mouseDown (const MouseEvent&) override; + void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override; void timerCallback() override; void updatePosition (const String&, Point, Rectangle); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp index 2bd472ce9..ba53da1fb 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp @@ -298,7 +298,9 @@ void TopLevelWindow::centreAroundComponent (Component* c, const int width, const } else { - auto targetCentre = c->localPointToGlobal (c->getLocalBounds().getCentre()) / getDesktopScaleFactor(); + const auto scale = getDesktopScaleFactor() / Desktop::getInstance().getGlobalScaleFactor(); + + auto targetCentre = c->localPointToGlobal (c->getLocalBounds().getCentre()) / scale; auto parentArea = c->getParentMonitorArea(); if (auto* parent = getParentComponent()) From 6ae352ae9287745737a40de3589efa3809341fc1 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sun, 12 Dec 2021 13:11:06 -0800 Subject: [PATCH 10/30] fix spelling font file, remove argentinian from installer --- tools/installer/MIDI2LR.xml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tools/installer/MIDI2LR.xml b/tools/installer/MIDI2LR.xml index c1640abb1..382d10ad1 100644 --- a/tools/installer/MIDI2LR.xml +++ b/tools/installer/MIDI2LR.xml @@ -187,7 +187,7 @@ ../../data/application/fonts/NotoSans-Regular-Plus-Thai.ttf - ../../data/application/fonts/NotoSans-Bold-plus-Thai.ttf + ../../data/application/fonts/NotoSans-Bold-Plus-Thai.ttf ../../data/application/fonts/NotoSansJP-Regular.otf @@ -444,7 +444,7 @@ ${installdir}/MIDI2LR.lrplugin/CustomProfiles.lua - en es_AR pt_BR nl fr de it ja ko pt ru zh_CN es sv th zh_TW + en pt_BR nl fr de it ja ko pt ru zh_CN es sv th zh_TW 1 lzham-ultra 1 @@ -486,11 +486,6 @@ utf-8 data/translations/custom-es.lng - - es_AR - utf-8 - data/translations/custom-es.lng - fr utf-8 @@ -594,11 +589,6 @@ utf-8 data/translations/ReadMe-es.txt - - es_AR - utf-8 - data/translations/ReadMe-es.txt - fr utf-8 From 3c8c4ef9e155dbbcc76cddd6b45332f544d3d47e Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sun, 12 Dec 2021 17:01:46 -0800 Subject: [PATCH 11/30] Revert to using FMT_STRING; fmt lib unable to automatically check Both MSVC and Xcode broken in this regard --- src/application/CommandSet.cpp | 4 ++-- src/application/CommandTableModel.cpp | 14 +++++------ src/application/DebugInfo.cpp | 24 +++++++++---------- src/application/KeyMap.mm | 2 +- src/application/LR_IPC_In.cpp | 18 +++++++------- src/application/LR_IPC_Out.cpp | 18 ++++++++------ src/application/MIDIReceiver.cpp | 10 ++++---- src/application/MIDISender.cpp | 8 +++---- src/application/Main.cpp | 34 +++++++++++++++++---------- src/application/MainComponent.cpp | 10 ++++---- src/application/Misc.cpp | 9 +++---- src/application/Ocpp.mm | 12 +++++----- src/application/ProfileManager.cpp | 7 +++--- src/application/SendKeysMac.cpp | 11 +++++---- src/application/SendKeysWin.cpp | 7 +++--- src/application/SettingsComponent.cpp | 4 ++-- src/application/SettingsManager.cpp | 2 +- src/application/SettingsManager.h | 2 +- src/application/VersionChecker.cpp | 5 ++-- 19 files changed, 109 insertions(+), 92 deletions(-) diff --git a/src/application/CommandSet.cpp b/src/application/CommandSet.cpp index 39487ed86..1c0632b6b 100644 --- a/src/application/CommandSet.cpp +++ b/src/application/CommandSet.cpp @@ -68,7 +68,7 @@ CommandSet::Impl::Impl() #pragma warning(suppress : 26414) /* too large to construct on stack */ const auto iarchive {std::make_unique(infile)}; (*iarchive)(*this); - rsj::Log(fmt::format("MenuTrans.xml archive loaded from {}.", p.string())); + rsj::Log(fmt::format(FMT_STRING("MenuTrans.xml archive loaded from {}."), p.string())); } else { rsj::LogAndAlertError( @@ -100,7 +100,7 @@ size_t CommandSet::CommandTextIndex(const std::string& command) const const auto found {cmd_idx_.find(command)}; if (found == cmd_idx_.end()) { if (command != "Unmapped"s) { /*Old version of Unassigned*/ - rsj::Log(fmt::format("Command not found in CommandTextIndex: {}.", command)); + rsj::Log(fmt::format(FMT_STRING("Command not found in CommandTextIndex: {}."), command)); } return 0; } diff --git a/src/application/CommandTableModel.cpp b/src/application/CommandTableModel.cpp index ca401e525..cb2ce9df8 100644 --- a/src/application/CommandTableModel.cpp +++ b/src/application/CommandTableModel.cpp @@ -56,25 +56,25 @@ void CommandTableModel::paintCell(juce::Graphics& g, int row_number, const int c const auto cmd {profile_.GetMessageForNumber(gsl::narrow_cast(row_number))}; cmd.msg_id_type) { case rsj::MessageType::kNoteOn: - format_str = fmt::format("{} | Note : {}", cmd.channel, cmd.control_number); + format_str = fmt::format(FMT_STRING("{} | Note : {}"), cmd.channel, cmd.control_number); break; case rsj::MessageType::kNoteOff: - format_str = fmt::format("{} | Note Off: {}", cmd.channel, cmd.control_number); + format_str = fmt::format(FMT_STRING("{} | Note Off: {}"), cmd.channel, cmd.control_number); break; case rsj::MessageType::kCc: - format_str = fmt::format("{} | CC: {}", cmd.channel, cmd.control_number); + format_str = fmt::format(FMT_STRING("{} | CC: {}"), cmd.channel, cmd.control_number); break; case rsj::MessageType::kPw: - format_str = fmt::format("{} | Pitch Bend", cmd.channel); + format_str = fmt::format(FMT_STRING("{} | Pitch Bend"), cmd.channel); break; case rsj::MessageType::kKeyPressure: - format_str = fmt::format("{} | Key Pressure: {}", cmd.channel, cmd.control_number); + format_str = fmt::format(FMT_STRING("{} | Key Pressure: {}"), cmd.channel, cmd.control_number); break; case rsj::MessageType::kChanPressure: - format_str = fmt::format("{} | Channel Pressure", cmd.channel); + format_str = fmt::format(FMT_STRING("{} | Channel Pressure"), cmd.channel); break; case rsj::MessageType::kPgmChange: - format_str = fmt::format("{} | Program Change", cmd.channel); + format_str = fmt::format(FMT_STRING("{} | Program Change"), cmd.channel); break; case rsj::MessageType::kSystem: break; diff --git a/src/application/DebugInfo.cpp b/src/application/DebugInfo.cpp index 6a5c9b705..c547f9042 100644 --- a/src/application/DebugInfo.cpp +++ b/src/application/DebugInfo.cpp @@ -134,11 +134,11 @@ namespace { if (const auto f = kKeyboardNames.find(klid); f != kKeyboardNames.end()) { return f->second; } - return fmt::format("KLID not in keyboard_names: 0x{}.", klid_ascii.data()); + return fmt::format(FMT_STRING("KLID not in keyboard_names: 0x{}."), klid_ascii.data()); } catch (...) { auto msg { - fmt::format("Exception when finding KLID name. KLID: 0x{}.", klid_ascii.data())}; + fmt::format(FMT_STRING("Exception when finding KLID name. KLID: 0x{}."), klid_ascii.data())}; rsj::Log(msg); return msg; } @@ -158,35 +158,35 @@ namespace { DebugInfo::DebugInfo(std::string_view profile_directory) noexcept { try { - LogAndSave(fmt::format("Application: System language {}.", + LogAndSave(fmt::format(FMT_STRING("Application: System language {}."), juce::SystemStats::getDisplayLanguage().toStdString())); LogAndSave( - fmt::format("Application: CPU {}.", juce::SystemStats::getCpuModel().toStdString())); + fmt::format(FMT_STRING("Application: CPU {}."), juce::SystemStats::getCpuModel().toStdString())); // ReSharper disable CppUnreachableCode if constexpr (kNdebug) { LogAndSave( - fmt::format("Application: Application version {}.", ProjectInfo::versionString)); + fmt::format(FMT_STRING("Application: Application version {}."), ProjectInfo::versionString)); } else { LogAndSave( - fmt::format("Application: Application version {}-debug.", ProjectInfo::versionString)); + fmt::format(FMT_STRING("Application: Application version {}-debug."), ProjectInfo::versionString)); } // ReSharper restore CppUnreachableCode - LogAndSave(fmt::format("Application: Application path {}.", + LogAndSave(fmt::format(FMT_STRING("Application: Application path {}."), juce::File::getSpecialLocation(juce::File::currentApplicationFile) .getFullPathName() .toStdString())); - LogAndSave(fmt::format("Application: Profile directory {}.", profile_directory)); - LogAndSave(fmt::format("Application: Log file directory {}.", + LogAndSave(fmt::format(FMT_STRING("Application: Profile directory {}."), profile_directory)); + LogAndSave(fmt::format(FMT_STRING("Application: Log file directory {}."), ww898::utf::conv(rsj::AppLogFilePath(MIDI2LR_UC_LITERAL(""))))); - LogAndSave(fmt::format("Application: Settings file directory {}.", + LogAndSave(fmt::format(FMT_STRING("Application: Settings file directory {}."), ww898::utf::conv(rsj::AppDataFilePath(MIDI2LR_UC_LITERAL(""))))); #ifdef _WIN32 /* MacOS defers keyboard layout information until first keystroke sent */ - LogAndSave(fmt::format("Application: Keyboard type {}.", GetKeyboardLayout())); + LogAndSave(fmt::format(FMT_STRING("Application: Keyboard type {}."), GetKeyboardLayout())); #endif LogAndSave( - fmt::format("Juce version {}.", juce::SystemStats::getJUCEVersion().toStdString())); + fmt::format(FMT_STRING("Juce version {}."), juce::SystemStats::getJUCEVersion().toStdString())); } catch (...) { try { diff --git a/src/application/KeyMap.mm b/src/application/KeyMap.mm index 1c6ff265b..a72383850 100644 --- a/src/application/KeyMap.mm +++ b/src/application/KeyMap.mm @@ -165,6 +165,6 @@ bool FillInSucceeded() rsj::Log("20ms sleep for message queue waiting for FillInMessageLoop to run."); } rsj::Log(fmt::format( - "Making KeyMap. Keyboard type {}. KeyMap is {}.", GetKeyboardLayout(), FillInSucceeded())); + FMT_STRING("Making KeyMap. Keyboard type {}. KeyMap is {}."), GetKeyboardLayout(), FillInSucceeded())); return InternalKeyMap(); } diff --git a/src/application/LR_IPC_In.cpp b/src/application/LR_IPC_In.cpp index eb10f6b66..bc4ea78b5 100644 --- a/src/application/LR_IPC_In.cpp +++ b/src/application/LR_IPC_In.cpp @@ -75,11 +75,11 @@ void LrIpcIn::Stop() * shutdown() before closing the socket. */ socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); if (ec) { - rsj::Log(fmt::format("LR_IPC_In socket shutdown error {}.", ec.message())); + rsj::Log(fmt::format(FMT_STRING("LR_IPC_In socket shutdown error {}."), ec.message())); ec.clear(); } socket_.close(ec); - if (ec) { rsj::Log(fmt::format("LR_IPC_In socket close error {}.", ec.message())); } + if (ec) { rsj::Log(fmt::format(FMT_STRING("LR_IPC_In socket close error {}."), ec.message())); } } #ifdef __cpp_lib_semaphore read_running_.acquire(); @@ -89,7 +89,7 @@ void LrIpcIn::Stop() #endif /* clear input queue after port closed */ if (const auto m {line_.clear_count_emplace(kTerminate)}) { - rsj::Log(fmt::format("{} left in queue in LrIpcIn destructor.", m)); + rsj::Log(fmt::format(FMT_STRING("{} left in queue in LrIpcIn destructor."), m)); } } catch (const std::exception& e) { @@ -116,11 +116,11 @@ void LrIpcIn::Connect() Read(); } else { - rsj::Log(fmt::format("LR_IPC_In did not connect. {}.", error.message())); + rsj::Log(fmt::format(FMT_STRING("LR_IPC_In did not connect. {}."), error.message())); asio::error_code ec2; socket_.close(ec2); if (ec2) { - rsj::Log(fmt::format("LR_IPC_In socket close error {}.", ec2.message())); + rsj::Log(fmt::format(FMT_STRING("LR_IPC_In socket close error {}."), ec2.message())); } } }); @@ -156,14 +156,14 @@ void LrIpcIn::ProcessLine() return; } if (value_view.empty()) { - rsj::Log(fmt::format("No value attached to message. Message from plugin was \"{}\".", + rsj::Log(fmt::format(FMT_STRING("No value attached to message. Message from plugin was \"{}\"."), rsj::ReplaceInvisibleChars(line_copy))); } else if (command == "SwitchProfile") { profile_manager_.SwitchToProfile(std::string(value_view)); } else if (command == "Log") { - rsj::Log(fmt::format("Plugin: {}.", value_view)); + rsj::Log(fmt::format(FMT_STRING("Plugin: {}."), value_view)); } else if (command == "SendKey") { const auto modifiers {std::stoi(std::string(value_view))}; @@ -178,7 +178,7 @@ void LrIpcIn::ProcessLine() } } rsj::LogAndAlertError( - fmt::format("SendKey couldn't identify keystroke. Message from plugin was \"{}\".", + fmt::format(FMT_STRING("SendKey couldn't identify keystroke. Message from plugin was \"{}\"."), rsj::ReplaceInvisibleChars(line_copy))); } else { /* send associated messages to MIDI OUT devices */ @@ -223,7 +223,7 @@ void LrIpcIn::Read() Read(); } else { - rsj::Log(fmt::format("LR_IPC_In Read error: {}.", error.message())); + rsj::Log(fmt::format(FMT_STRING("LR_IPC_In Read error: {}."), error.message())); #ifdef __cpp_lib_semaphore read_running_.release(); #else diff --git a/src/application/LR_IPC_Out.cpp b/src/application/LR_IPC_Out.cpp index 832123395..aa95f14ec 100644 --- a/src/application/LR_IPC_Out.cpp +++ b/src/application/LR_IPC_Out.cpp @@ -84,7 +84,7 @@ void LrIpcOut::Stop() thread_should_exit_.store(true, std::memory_order_release); /* clear output queue before port closed */ if (const auto m {command_.clear_count_emplace(kTerminate)}) { - rsj::Log(fmt::format("{} left in queue in LrIpcOut destructor.", m)); + rsj::Log(fmt::format(FMT_STRING("{} left in queue in LrIpcOut destructor."), m)); } { std::scoped_lock lk(callback_mtx_); @@ -103,11 +103,13 @@ void LrIpcOut::Stop() * shutdown() before closing the socket. */ socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); if (ec) { - rsj::Log(fmt::format("LR_IPC_Out socket shutdown error {}.", ec.message())); + rsj::Log(fmt::format(FMT_STRING("LR_IPC_Out socket shutdown error {}."), ec.message())); ec.clear(); } socket_.close(ec); - if (ec) { rsj::Log(fmt::format("LR_IPC_Out socket close error {}.", ec.message())); } + if (ec) { + rsj::Log(fmt::format(FMT_STRING("LR_IPC_Out socket close error {}."), ec.message())); + } } } @@ -129,11 +131,13 @@ void LrIpcOut::Connect() SendOut(); } else { - rsj::Log(fmt::format("LR_IPC_Out did not connect. {}.", error.message())); + rsj::Log( + fmt::format(FMT_STRING("LR_IPC_Out did not connect. {}."), error.message())); asio::error_code ec2; socket_.close(ec2); if (ec2) { - rsj::Log(fmt::format("LR_IPC_Out socket close error {}.", ec2.message())); + rsj::Log( + fmt::format(FMT_STRING("LR_IPC_Out socket close error {}."), ec2.message())); } } }); @@ -192,7 +196,7 @@ void LrIpcOut::MidiCmdCallback(const rsj::MidiMessage& mm) const auto wrap { std::find(wrap_.begin(), wrap_.end(), command_to_send) != wrap_.end()}; const auto computed_value {controls_model_.ControllerToPlugin(mm, wrap)}; - SendCommand(fmt::format("{} {}\n", command_to_send, computed_value)); + SendCommand(fmt::format(FMT_STRING("{} {}\n"), command_to_send, computed_value)); } } } @@ -235,7 +239,7 @@ void LrIpcOut::SendOut() } cv_.notify_one(); #endif - rsj::Log(fmt::format("LR_IPC_Out Write: {}.", error.message())); + rsj::Log(fmt::format(FMT_STRING("LR_IPC_Out Write: {}."), error.message())); } }); } diff --git a/src/application/MIDIReceiver.cpp b/src/application/MIDIReceiver.cpp index 1b1125fdc..027405bcd 100644 --- a/src/application/MIDIReceiver.cpp +++ b/src/application/MIDIReceiver.cpp @@ -56,10 +56,10 @@ void MidiReceiver::Stop() { for (const auto& dev : input_devices_) { dev->stop(); - rsj::Log(fmt::format("Stopped input device {}.", dev->getName().toStdString())); + rsj::Log(fmt::format(FMT_STRING("Stopped input device {}."), dev->getName().toStdString())); } if (const auto remaining {messages_.clear_count_push({kTerminate, nullptr})}) { - rsj::Log(fmt::format("{} left in queue in MidiReceiver StopRunning.", remaining)); + rsj::Log(fmt::format(FMT_STRING("{} left in queue in MidiReceiver StopRunning."), remaining)); } } @@ -68,7 +68,7 @@ void MidiReceiver::RescanDevices() try { for (const auto& dev : input_devices_) { dev->stop(); - rsj::Log(fmt::format("Stopped input device {}.", dev->getName().toStdString())); + rsj::Log(fmt::format(FMT_STRING("Stopped input device {}."), dev->getName().toStdString())); } input_devices_.clear(); rsj::Log("Cleared input devices."); @@ -89,12 +89,12 @@ void MidiReceiver::TryToOpen() if (devices_.EnabledOrNew(open_device->getDeviceInfo(), "input")) { open_device->start(); rsj::Log( - fmt::format("Opened input device {}.", open_device->getName().toStdString())); + fmt::format(FMT_STRING("Opened input device {}."), open_device->getName().toStdString())); input_devices_.push_back(std::move(open_device)); } else { rsj::Log( - fmt::format("Ignored input device {}.", open_device->getName().toStdString())); + fmt::format(FMT_STRING("Ignored input device {}."), open_device->getName().toStdString())); } } } diff --git a/src/application/MIDISender.cpp b/src/application/MIDISender.cpp index 0aac7fa59..ea139f0dd 100644 --- a/src/application/MIDISender.cpp +++ b/src/application/MIDISender.cpp @@ -114,20 +114,20 @@ void MidiSender::InitDevices() if constexpr (MSWindows) { if (devname != "Microsoft GS Wavetable Synth" && devices_.EnabledOrNew(open_device->getDeviceInfo(), "output")) { - rsj::Log(fmt::format("Opened output device {}.", devname)); + rsj::Log(fmt::format(FMT_STRING("Opened output device {}."), devname)); output_devices_.push_back(std::move(open_device)); } else { - rsj::Log(fmt::format("Ignored output device {}.", devname)); + rsj::Log(fmt::format(FMT_STRING("Ignored output device {}."), devname)); } } else { if (devices_.EnabledOrNew(open_device->getDeviceInfo(), "output")) { - rsj::Log(fmt::format("Opened output device {}.", devname)); + rsj::Log(fmt::format(FMT_STRING("Opened output device {}."), devname)); output_devices_.push_back(std::move(open_device)); } else { - rsj::Log(fmt::format("Ignored output device {}.", devname)); + rsj::Log(fmt::format(FMT_STRING("Ignored output device {}."), devname)); } } } diff --git a/src/application/Main.cpp b/src/application/Main.cpp index 9469b410f..e93c0ca6f 100644 --- a/src/application/Main.cpp +++ b/src/application/Main.cpp @@ -51,7 +51,8 @@ #else #include #endif -/*weird xcode error for semaphore in this file only with cpp20: release has been explicitly marked unavailable here*/ +/*weird xcode error for semaphore in this file only with cpp20: release has been explicitly marked + * unavailable here*/ #ifndef _WIN32 #include #undef __cpp_lib_semaphore @@ -144,7 +145,7 @@ namespace { std::rethrow_exception(exc); } catch (const std::exception& e) { - rsj::Log(fmt::format("Terminate called, exception {}.", e.what())); + rsj::Log(fmt::format(FMT_STRING("Terminate called, exception {}."), e.what())); } catch (...) { rsj::Log("Terminate called, unknown exception type."); @@ -198,7 +199,8 @@ class MIDI2LRApplication final : public juce::JUCEApplication { MIDI2LR_FAST_FLOATS; if constexpr (kNdebug) { io_context_.run(); } else { - rsj::Log(fmt::format("io_thread0_ ran {} handlers.", io_context_.run())); + rsj::Log( + fmt::format(FMT_STRING("io_thread0_ ran {} handlers."), io_context_.run())); } }); io_thread1_ = std::async(std::launch::async, [this] { @@ -206,7 +208,8 @@ class MIDI2LRApplication final : public juce::JUCEApplication { MIDI2LR_FAST_FLOATS; if constexpr (kNdebug) { io_context_.run(); } else { - rsj::Log(fmt::format("io_thread1_ ran {} handlers.", io_context_.run())); + rsj::Log( + fmt::format(FMT_STRING("io_thread1_ ran {} handlers."), io_context_.run())); } }); CCoptions::LinkToControlsModel(&controls_model_); @@ -329,15 +332,16 @@ class MIDI2LRApplication final : public juce::JUCEApplication { + " {}, {} line {}. Total uncaught {}."}; rsj::LogAndAlertError(fmt::format(msgt, e->what(), source_filename.toStdString(), line_number, std::uncaught_exceptions()), - fmt::format("Unhandled exception {}, {} line {}. Total uncaught {}.", e->what(), - source_filename.toStdString(), line_number, std::uncaught_exceptions())); + fmt::format(FMT_STRING("Unhandled exception {}, {} line {}. Total uncaught {}."), + e->what(), source_filename.toStdString(), line_number, + std::uncaught_exceptions())); } else { const auto msgt {juce::translate("unhandled exception").toStdString() + " {} line {}. Total uncaught {}."}; rsj::LogAndAlertError(fmt::format(msgt, source_filename.toStdString(), line_number, std::uncaught_exceptions()), - fmt::format("Unhandled exception {} line {}. Total uncaught {}.", + fmt::format(FMT_STRING("Unhandled exception {} line {}. Total uncaught {}."), source_filename.toStdString(), line_number, std::uncaught_exceptions())); } } @@ -356,8 +360,8 @@ class MIDI2LRApplication final : public juce::JUCEApplication { const auto file_name {rsj::AppDataFilePath(kDefaultsFile)}; const auto profile_file {juce::File(file_name.data())}; profile_.ToXmlFile(profile_file); - rsj::Log(fmt::format( - "Default profile saved to {}.", profile_file.getFullPathName().toStdString())); + rsj::Log(fmt::format(FMT_STRING("Default profile saved to {}."), + profile_file.getFullPathName().toStdString())); } catch (const std::exception& e) { MIDI2LR_E_RESPONSE; @@ -373,7 +377,8 @@ class MIDI2LRApplication final : public juce::JUCEApplication { #pragma warning(suppress : 26414) /* too large to construct on stack */ const auto oarchive {std::make_unique(outfile)}; (*oarchive)(controls_model_); - rsj::Log(fmt::format("ControlsModel archive in Main saved to {}.", p.string())); + rsj::Log( + fmt::format(FMT_STRING("ControlsModel archive in Main saved to {}."), p.string())); } else { rsj::LogAndAlertError( @@ -394,7 +399,8 @@ class MIDI2LRApplication final : public juce::JUCEApplication { #pragma warning(suppress : 26414) /* too large to construct on stack */ const auto iarchive {std::make_unique(in_file)}; (*iarchive)(controls_model_); - rsj::Log(fmt::format("ControlsModel archive in Main loaded from {}.", px.string())); + rsj::Log(fmt::format( + FMT_STRING("ControlsModel archive in Main loaded from {}."), px.string())); } } catch (const std::exception& e) { @@ -446,7 +452,8 @@ class MIDI2LRApplication final : public juce::JUCEApplication { juce::Typeface::createSystemTypefaceFor(font_data.getData(), font_data.getSize())); } else { - rsj::Log(fmt::format("Unable to load primary font file {}.", font1_name.toStdString())); + rsj::Log(fmt::format( + FMT_STRING("Unable to load primary font file {}."), font1_name.toStdString())); } if (font2_name.isNotEmpty()) { font_data.reset(); @@ -456,7 +463,8 @@ class MIDI2LRApplication final : public juce::JUCEApplication { juce::Typeface::createSystemTypefaceFor(font_data.getData(), font_data.getSize()); } else { - rsj::Log(fmt::format("Unable to load bold font file {}.", font2_name.toStdString())); + rsj::Log(fmt::format( + FMT_STRING("Unable to load bold font file {}."), font2_name.toStdString())); } } } diff --git a/src/application/MainComponent.cpp b/src/application/MainComponent.cpp index 45e33d8df..30fe38f4e 100644 --- a/src/application/MainComponent.cpp +++ b/src/application/MainComponent.cpp @@ -128,8 +128,8 @@ void MainContentComponent::Init() if (chooser.browseForFileToOpen()) { if (const auto parsed {juce::parseXML(chooser.getResult())}) { const auto new_profile {chooser.getResult()}; - lr_ipc_out_.SendCommand(fmt::format( - "ChangedToFullPath {}\n", new_profile.getFullPathName().toStdString())); + lr_ipc_out_.SendCommand(fmt::format(FMT_STRING("ChangedToFullPath {}\n"), + new_profile.getFullPathName().toStdString())); profile_name_label_.setText( new_profile.getFileName(), juce::NotificationType::dontSendNotification); profile_.FromXml(parsed.get()); @@ -141,7 +141,7 @@ void MainContentComponent::Init() } } else { - rsj::Log(fmt::format("Unable to load profile {}.", + rsj::Log(fmt::format(FMT_STRING("Unable to load profile {}."), chooser.getResult().getFullPathName().toStdString())); } } @@ -294,8 +294,8 @@ void MainContentComponent::MidiCmdCallback(const rsj::MidiMessage& mm) /* Display the MIDI parameters and add/highlight row in table corresponding to the message msg * is 1-based for channel, which display expects */ const rsj::MidiMessageId msg {mm}; - last_command_ = fmt::format( - "{}: {}{} [{}]", msg.channel, mm.message_type_byte, msg.control_number, mm.value); + last_command_ = fmt::format(FMT_STRING("{}: {}{} [{}]"), msg.channel, mm.message_type_byte, + msg.control_number, mm.value); profile_.AddRowUnmapped(msg); row_to_select_ = gsl::narrow_cast(profile_.GetRowForMessage(msg)); triggerAsyncUpdate(); diff --git a/src/application/Misc.cpp b/src/application/Misc.cpp index 02ad435c8..3734bc030 100644 --- a/src/application/Misc.cpp +++ b/src/application/Misc.cpp @@ -47,7 +47,8 @@ void rsj::LabelThread(gsl::czstring<> threadname) { auto result {pthread_setname_np(threadname)}; if (result) { - rsj::Log(fmt::format("Label thread {} failed with {} error.", threadname, result)); + rsj::Log( + fmt::format(FMT_STRING("Label thread {} failed with {} error."), threadname, result)); } } #endif @@ -308,7 +309,7 @@ void rsj::ExceptionResponse( try { const auto alert_text { fmt::format(juce::translate("Exception ").toStdString() + "{} {}.", e.what(), fu)}; - const auto error_text {fmt::format("Exception {} {}.", e.what(), fu)}; + const auto error_text {fmt::format(FMT_STRING("Exception {} {}."), e.what(), fu)}; rsj::LogAndAlertError(alert_text, error_text); } catch (...) { //-V565 @@ -323,7 +324,7 @@ void rsj::ExceptionResponse( try { const auto alert_text {fmt::format( juce::translate("Exception ").toStdString() + "{} {}::{}.", e.what(), id, fu)}; - const auto error_text {fmt::format("Exception {} {}::{}.", e.what(), id, fu)}; + const auto error_text {fmt::format(FMT_STRING("Exception {} {}::{}."), e.what(), id, fu)}; rsj::LogAndAlertError(alert_text, error_text); } catch (...) { //-V565 //-V5002 @@ -340,7 +341,7 @@ std::wstring rsj::AppDataFilePath(std::wstring_view file_name) { wil::unique_cotaskmem_string pathptr {nullptr}; if (SUCCEEDED_LOG(SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pathptr))) { - return fmt::format(L"{}\\MIDI2LR\\{}", pathptr.get(), file_name); + return fmt::format(FMT_STRING(L"{}\\MIDI2LR\\{}"), pathptr.get(), file_name); } return std::wstring(file_name); } diff --git a/src/application/Ocpp.mm b/src/application/Ocpp.mm index 8d82bcb8a..5fa833b33 100644 --- a/src/application/Ocpp.mm +++ b/src/application/Ocpp.mm @@ -52,14 +52,14 @@ AEDisposeDesc(&addressDesc); switch (status) { case errAEEventWouldRequireUserConsent: - rsj::Log(fmt::format("Automation permission pending for {}.", bundleIdentifierCString)); + rsj::Log(fmt::format(FMT_STRING("Automation permission pending for {}."), bundleIdentifierCString)); break; case noErr: - rsj::Log(fmt::format("Automation permission granted for {}.", bundleIdentifierCString)); + rsj::Log(fmt::format(FMT_STRING("Automation permission granted for {}."), bundleIdentifierCString)); break; case errAEEventNotPermitted: { - rsj::Log(fmt::format("Automation permission denied for {}.", bundleIdentifierCString)); + rsj::Log(fmt::format(FMT_STRING("Automation permission denied for {}."), bundleIdentifierCString)); const auto title {juce::translate( "MIDI2LR needs your authorization to send keystrokes to Lightroom")}; const auto message {juce::translate( @@ -70,16 +70,16 @@ break; } case procNotFound: - rsj::Log(fmt::format("Application not found. Automation permission unknown for {}.", + rsj::Log(fmt::format(FMT_STRING("Application not found. Automation permission unknown for {}."), bundleIdentifierCString)); break; default: - rsj::Log(fmt::format("Unexpected return value when checking automation permission for {}.", + rsj::Log(fmt::format(FMT_STRING("Unexpected return value when checking automation permission for {}."), bundleIdentifierCString)); break; } } else { - rsj::Log(fmt::format("AECreateDesc returned error {}.", aeresult)); + rsj::Log(fmt::format(FMT_STRING("AECreateDesc returned error {}."), aeresult)); } } diff --git a/src/application/ProfileManager.cpp b/src/application/ProfileManager.cpp index c129e090f..f7c865772 100644 --- a/src/application/ProfileManager.cpp +++ b/src/application/ProfileManager.cpp @@ -75,10 +75,11 @@ void ProfileManager::SwitchToProfile(const juce::String& profile) if (profile_file.exists()) { if (const auto parsed {juce::parseXML(profile_file)}) { for (const auto& cb : callbacks_) { cb(parsed.get(), profile); } - lr_ipc_out_.SendCommand(fmt::format("ChangedToDirectory {}\n", + lr_ipc_out_.SendCommand(fmt::format(FMT_STRING("ChangedToDirectory {}\n"), juce::File::addTrailingSeparator(profile_location_.getFullPathName()) .toStdString())); - lr_ipc_out_.SendCommand(fmt::format("ChangedToFile {}\n", profile.toStdString())); + lr_ipc_out_.SendCommand( + fmt::format(FMT_STRING("ChangedToFile {}\n"), profile.toStdString())); } } } @@ -160,7 +161,7 @@ void ProfileManager::ConnectionCallback(const bool connected, const bool blocked { try { if (connected && !blocked) { - lr_ipc_out_.SendCommand(fmt::format("ChangedToDirectory {}\n", + lr_ipc_out_.SendCommand(fmt::format(FMT_STRING("ChangedToDirectory {}\n"), juce::File::addTrailingSeparator(profile_location_.getFullPathName()).toStdString())); } } diff --git a/src/application/SendKeysMac.cpp b/src/application/SendKeysMac.cpp index fdb8e9229..b268b436d 100644 --- a/src/application/SendKeysMac.cpp +++ b/src/application/SendKeysMac.cpp @@ -324,8 +324,8 @@ void rsj::SendKeyDownUp(const std::string& key, const rsj::ActiveModifiers& mods flags |= kCGEventFlagMaskShift; } else { - rsj::LogAndAlertError( - fmt::format("Unsupported character was used: \"{}\", no ANSI equivalent.", key)); + rsj::LogAndAlertError(fmt::format( + FMT_STRING("Unsupported character was used: \"{}\", no ANSI equivalent."), key)); return; } } @@ -340,11 +340,12 @@ void rsj::SendKeyDownUp(const std::string& key, const rsj::ActiveModifiers& mods } catch (const std::exception& e) { rsj::LogAndAlertError(fmt::format( - "Exception in key sending function for key: \"{}\". Exception: {}.", key, e.what())); + FMT_STRING("Exception in key sending function for key: \"{}\". Exception: {}."), key, + e.what())); } catch (...) { - rsj::LogAndAlertError( - fmt::format("Non-standard exception in key sending function for key: \"{}\".", key)); + rsj::LogAndAlertError(fmt::format( + FMT_STRING("Non-standard exception in key sending function for key: \"{}\"."), key)); } } #endif \ No newline at end of file diff --git a/src/application/SendKeysWin.cpp b/src/application/SendKeysWin.cpp index 4af329113..f184f628d 100644 --- a/src/application/SendKeysWin.cpp +++ b/src/application/SendKeysWin.cpp @@ -47,7 +47,8 @@ namespace { if (length) { /* check for issues with extra-long window titles and log them */ if (rsj::cmp_greater_equal(length + 1, buffer.size())) { - rsj::Log(fmt::format(L"EnumWindowsProc window text length > {}, truncated text is {}.", + rsj::Log(fmt::format( + FMT_STRING(L"EnumWindowsProc window text length > {}, truncated text is {}."), buffer.size(), buffer.data()) .data()); } /* try to find Lightroom Classic. Use Lightroom as fallback */ @@ -186,8 +187,8 @@ void rsj::SendKeyDownUp(const std::string& key, const rsj::ActiveModifiers& mods "Exception in key sending function for key: \"{}\". Exception: {}.", key, e.what())); } catch (...) { - rsj::LogAndAlertError( - fmt::format("Non-standard exception in key sending function for key: \"{}\".", key)); + rsj::LogAndAlertError(fmt::format( + FMT_STRING("Non-standard exception in key sending function for key: \"{}\"."), key)); } } #pragma warning(pop) diff --git a/src/application/SettingsComponent.cpp b/src/application/SettingsComponent.cpp index fcb46eb63..5c4db1bf6 100644 --- a/src/application/SettingsComponent.cpp +++ b/src/application/SettingsComponent.cpp @@ -82,7 +82,7 @@ void SettingsComponent::Init() if (chooser.browseForDirectory()) { const auto profile_location {chooser.getResult().getFullPathName()}; settings_manager_.SetProfileDirectory(profile_location); - rsj::Log(fmt::format("Profile location set to {}.", profile_location.toStdString())); + rsj::Log(fmt::format(FMT_STRING("Profile location set to {}."), profile_location.toStdString())); profile_location_label_.setText( profile_location, juce::NotificationType::dontSendNotification); } @@ -124,7 +124,7 @@ void SettingsComponent::Init() settings_manager_.SetAutoHideTime( gsl::narrow(std::lrint(autohide_setting_.getValue()))); rsj::Log( - fmt::format("Autohide time set to {} seconds.", settings_manager_.GetAutoHideTime())); + fmt::format(FMT_STRING("Autohide time set to {} seconds."), settings_manager_.GetAutoHideTime())); }; /* turn it on */ activateLayout(); diff --git a/src/application/SettingsManager.cpp b/src/application/SettingsManager.cpp index fba236f62..7e316ac02 100644 --- a/src/application/SettingsManager.cpp +++ b/src/application/SettingsManager.cpp @@ -64,7 +64,7 @@ void SettingsManager::ConnectionCallback(const bool connected, const bool blocke const DebugInfo db {GetProfileDirectory().toStdString()}; lr_ipc_out_.SendCommand("AppInfoClear 1\n"); for (const auto& info : db.GetInfo()) { - lr_ipc_out_.SendCommand(fmt::format("AppInfo {}\n", info)); + lr_ipc_out_.SendCommand(fmt::format(FMT_STRING("AppInfo {}\n"), info)); } lr_ipc_out_.SendCommand("AppInfoDone 1\n"); lr_ipc_out_.SendCommand("GetPluginInfo 1\n"); diff --git a/src/application/SettingsManager.h b/src/application/SettingsManager.h index 56df0d002..f6a9b9abb 100644 --- a/src/application/SettingsManager.h +++ b/src/application/SettingsManager.h @@ -66,7 +66,7 @@ class SettingsManager final { void SetPickupEnabled(bool enabled) { properties_file_->setValue("pickup_enabled", enabled); - lr_ipc_out_.SendCommand(fmt::format("Pickup {}\n", enabled ? '1' : '0')); + lr_ipc_out_.SendCommand(fmt::format(FMT_STRING("Pickup {}\n"), enabled ? '1' : '0')); } void SetProfileDirectory(const juce::String& profile_directory) { diff --git a/src/application/VersionChecker.cpp b/src/application/VersionChecker.cpp index 13d239f97..f5a436ecf 100644 --- a/src/application/VersionChecker.cpp +++ b/src/application/VersionChecker.cpp @@ -35,7 +35,7 @@ namespace { const auto minor {vers >> 16 & 0xFFU}; const auto rev {vers >> 8 & 0xFFU}; const auto build {vers & 0xFFU}; - return fmt::format("{}.{}.{}.{}", major, minor, rev, build); + return fmt::format(FMT_STRING("{}.{}.{}.{}"), major, minor, rev, build); } } // namespace @@ -97,7 +97,8 @@ void VersionChecker::Run() noexcept last_checked = std::min(new_version_, ProjectInfo::versionNumber); settings_manager_.SetLastVersionFound(last_checked); } - rsj::Log(fmt::format("Version available {}, version last checked {}, current version {}.", + rsj::Log(fmt::format( + FMT_STRING("Version available {}, version last checked {}, current version {}."), IntToVersion(new_version_), IntToVersion(last_checked), IntToVersion(ProjectInfo::versionNumber))); if (new_version_ > ProjectInfo::versionNumber && new_version_ != last_checked From d6239ab3b25c7bb14c0f0201a5ff516a664c8000 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Tue, 14 Dec 2021 10:12:52 -0800 Subject: [PATCH 12/30] next/prev preset. Closes #586 rate-limit previous next to avoid Lightroom error add translation for previous/next preset --- data/plugin/TranslatedStrings_de.txt | 2 + data/plugin/TranslatedStrings_en.txt | 2 + data/plugin/TranslatedStrings_es.txt | 2 + data/plugin/TranslatedStrings_fr.txt | 2 + data/plugin/TranslatedStrings_it.txt | 2 + data/plugin/TranslatedStrings_ja.txt | 2 + data/plugin/TranslatedStrings_ko.txt | 2 + data/plugin/TranslatedStrings_nl.txt | 2 + data/plugin/TranslatedStrings_pt.txt | 2 + data/plugin/TranslatedStrings_ru.txt | 2 + data/plugin/TranslatedStrings_sv.txt | 2 + data/plugin/TranslatedStrings_th.txt | 2 + data/plugin/TranslatedStrings_zh_cn.txt | 2 + data/plugin/TranslatedStrings_zh_tw.txt | 2 + src/application/Misc.cpp | 4 +- src/plugin/Client.lua | 163 ++++++++++++------------ src/plugin/ClientUtilities.lua | 23 ---- src/plugin/Database.lua | 3 + src/plugin/PrefReset.lua | 1 + src/plugin/Presets.lua | 95 +++++++++++++- 20 files changed, 208 insertions(+), 109 deletions(-) diff --git a/data/plugin/TranslatedStrings_de.txt b/data/plugin/TranslatedStrings_de.txt index fbe5028a5..ef99bcf62 100644 --- a/data/plugin/TranslatedStrings_de.txt +++ b/data/plugin/TranslatedStrings_de.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=Konfiguration &importieren" "$$$/MIDI2LR/Menu/NextMaskGroup=Nächste Maskengruppe" "$$$/MIDI2LR/Menu/NextMaskTool=Nächstes Maskenwerkzeug" +"$$$/MIDI2LR/Menu/PresetNext=Nächste Voreinstellung" +"$$$/MIDI2LR/Menu/PresetPrevious=Vorherige Voreinstellung" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Vorherige Maskengruppe" "$$$/MIDI2LR/Menu/PreviousMaskTool=Vorheriges Maskenwerkzeug" "$$$/MIDI2LR/Menu/StartApplication=Anwendung starten" diff --git a/data/plugin/TranslatedStrings_en.txt b/data/plugin/TranslatedStrings_en.txt index 9874b8c2e..51c0cd165 100644 --- a/data/plugin/TranslatedStrings_en.txt +++ b/data/plugin/TranslatedStrings_en.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=&Import configuration" "$$$/MIDI2LR/Menu/NextMaskGroup=Next mask group" "$$$/MIDI2LR/Menu/NextMaskTool=Next mask tool" +"$$$/MIDI2LR/Menu/PresetNext=Next preset" +"$$$/MIDI2LR/Menu/PresetPrevious=Previous preset" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Previous mask group" "$$$/MIDI2LR/Menu/PreviousMaskTool=Previous mask tool" "$$$/MIDI2LR/Menu/StartApplication=&Start application" diff --git a/data/plugin/TranslatedStrings_es.txt b/data/plugin/TranslatedStrings_es.txt index 0c84630e0..63a0bc4e1 100644 --- a/data/plugin/TranslatedStrings_es.txt +++ b/data/plugin/TranslatedStrings_es.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=&Importar configuración" "$$$/MIDI2LR/Menu/NextMaskGroup=Siguiente grupo de máscara" "$$$/MIDI2LR/Menu/NextMaskTool=Siguiente herramienta de máscara" +"$$$/MIDI2LR/Menu/PresetNext=Presintonía siguiente" +"$$$/MIDI2LR/Menu/PresetPrevious=Anterior preestablecido" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Предыдущая группа масок" "$$$/MIDI2LR/Menu/PreviousMaskTool=Herramienta de máscara anterior" "$$$/MIDI2LR/Menu/StartApplication=&Iniciar aplicación" diff --git a/data/plugin/TranslatedStrings_fr.txt b/data/plugin/TranslatedStrings_fr.txt index 6b8f81533..cbec63581 100644 --- a/data/plugin/TranslatedStrings_fr.txt +++ b/data/plugin/TranslatedStrings_fr.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=&Importer la configuration" "$$$/MIDI2LR/Menu/NextMaskGroup=Groupe de masques suivant" "$$$/MIDI2LR/Menu/NextMaskTool=Outil de masque suivant" +"$$$/MIDI2LR/Menu/PresetNext=Présélection suivante" +"$$$/MIDI2LR/Menu/PresetPrevious=Présélection précédente" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Groupe de masques précédent" "$$$/MIDI2LR/Menu/PreviousMaskTool=Outil de masque précédent" "$$$/MIDI2LR/Menu/StartApplication=&Démarrer l’application" diff --git a/data/plugin/TranslatedStrings_it.txt b/data/plugin/TranslatedStrings_it.txt index 61b69d194..640c5c706 100644 --- a/data/plugin/TranslatedStrings_it.txt +++ b/data/plugin/TranslatedStrings_it.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=&Importa configurazione" "$$$/MIDI2LR/Menu/NextMaskGroup=Gruppo maschera successivo" "$$$/MIDI2LR/Menu/NextMaskTool=Strumento maschera successiva" +"$$$/MIDI2LR/Menu/PresetNext=Impostazione predefinita successiva" +"$$$/MIDI2LR/Menu/PresetPrevious=Impostazione predefinita precedente" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Gruppo maschera precedente" "$$$/MIDI2LR/Menu/PreviousMaskTool=Strumento maschera precedente" "$$$/MIDI2LR/Menu/StartApplication=&Avvia applicazione" diff --git a/data/plugin/TranslatedStrings_ja.txt b/data/plugin/TranslatedStrings_ja.txt index 6876997b5..312112c87 100644 --- a/data/plugin/TranslatedStrings_ja.txt +++ b/data/plugin/TranslatedStrings_ja.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=構成のインポート(&I)" "$$$/MIDI2LR/Menu/NextMaskGroup=次のマスクグループ" "$$$/MIDI2LR/Menu/NextMaskTool=次のマスクツール" +"$$$/MIDI2LR/Menu/PresetNext=次のプリセット" +"$$$/MIDI2LR/Menu/PresetPrevious=前のプリセット" "$$$/MIDI2LR/Menu/PreviousMaskGroup=前のマスクグループ" "$$$/MIDI2LR/Menu/PreviousMaskTool=以前のマスクツール" "$$$/MIDI2LR/Menu/StartApplication=アプリケーションの起動(&S)" diff --git a/data/plugin/TranslatedStrings_ko.txt b/data/plugin/TranslatedStrings_ko.txt index fe62ec0e4..b33980b02 100644 --- a/data/plugin/TranslatedStrings_ko.txt +++ b/data/plugin/TranslatedStrings_ko.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=구성 가져오기(&I)" "$$$/MIDI2LR/Menu/NextMaskGroup=다음 마스크 그룹" "$$$/MIDI2LR/Menu/NextMaskTool=다음 마스크 도구" +"$$$/MIDI2LR/Menu/PresetNext=다음 기본 설정" +"$$$/MIDI2LR/Menu/PresetPrevious=이전 기본 설정" "$$$/MIDI2LR/Menu/PreviousMaskGroup=이전 마스크 그룹" "$$$/MIDI2LR/Menu/PreviousMaskTool=이전 마스크 도구" "$$$/MIDI2LR/Menu/StartApplication=응용 프로그램 시작(&S)" diff --git a/data/plugin/TranslatedStrings_nl.txt b/data/plugin/TranslatedStrings_nl.txt index 1682ae95a..dafaaa6fd 100644 --- a/data/plugin/TranslatedStrings_nl.txt +++ b/data/plugin/TranslatedStrings_nl.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=Configuratie &importeren" "$$$/MIDI2LR/Menu/NextMaskGroup=Volgende maskergroep" "$$$/MIDI2LR/Menu/NextMaskTool=Volgende maskertool" +"$$$/MIDI2LR/Menu/PresetNext=Volgende voorkeursinstelling" +"$$$/MIDI2LR/Menu/PresetPrevious=Vorige voorkeursinstelling" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Vorige maskergroep" "$$$/MIDI2LR/Menu/PreviousMaskTool=Vorige maskertool" "$$$/MIDI2LR/Menu/StartApplication=Toepassing starten" diff --git a/data/plugin/TranslatedStrings_pt.txt b/data/plugin/TranslatedStrings_pt.txt index 4c28d7056..0bb38f693 100644 --- a/data/plugin/TranslatedStrings_pt.txt +++ b/data/plugin/TranslatedStrings_pt.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=&Importar configuração" "$$$/MIDI2LR/Menu/NextMaskGroup=Próximo grupo de máscara" "$$$/MIDI2LR/Menu/NextMaskTool=Próxima ferramenta de máscara" +"$$$/MIDI2LR/Menu/PresetNext=Próxima predefinição" +"$$$/MIDI2LR/Menu/PresetPrevious=Predefinição anterior" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Grupo de máscara anterior" "$$$/MIDI2LR/Menu/PreviousMaskTool=Ferramenta de máscara anterior" "$$$/MIDI2LR/Menu/StartApplication=&Iniciar Aplicativo" diff --git a/data/plugin/TranslatedStrings_ru.txt b/data/plugin/TranslatedStrings_ru.txt index 70afe8b4c..5a8369b2e 100644 --- a/data/plugin/TranslatedStrings_ru.txt +++ b/data/plugin/TranslatedStrings_ru.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=Импорт конфигурации" "$$$/MIDI2LR/Menu/NextMaskGroup=Следующая группа масок" "$$$/MIDI2LR/Menu/NextMaskTool=Инструмент \"Следующая маска\"" +"$$$/MIDI2LR/Menu/PresetNext=Следующая заданная настройка" +"$$$/MIDI2LR/Menu/PresetPrevious=Предыдущая заданная настройка" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Предыдущая группа масок" "$$$/MIDI2LR/Menu/PreviousMaskTool=Предыдущий инструмент маски" "$$$/MIDI2LR/Menu/StartApplication=Запустить приложение" diff --git a/data/plugin/TranslatedStrings_sv.txt b/data/plugin/TranslatedStrings_sv.txt index cb4804611..b04196b40 100644 --- a/data/plugin/TranslatedStrings_sv.txt +++ b/data/plugin/TranslatedStrings_sv.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=&Importera konfigurering" "$$$/MIDI2LR/Menu/NextMaskGroup=Nästa maskgrupp" "$$$/MIDI2LR/Menu/NextMaskTool=Nästa maskverktyg" +"$$$/MIDI2LR/Menu/PresetNext=Nästa förinställning" +"$$$/MIDI2LR/Menu/PresetPrevious=Föregående förinställning" "$$$/MIDI2LR/Menu/PreviousMaskGroup=Föregående maskgrupp" "$$$/MIDI2LR/Menu/PreviousMaskTool=Tidigare maskverktyg" "$$$/MIDI2LR/Menu/StartApplication=Starta tillämpningsprogram" diff --git a/data/plugin/TranslatedStrings_th.txt b/data/plugin/TranslatedStrings_th.txt index 3645ae9f8..7aed97db9 100644 --- a/data/plugin/TranslatedStrings_th.txt +++ b/data/plugin/TranslatedStrings_th.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=นำเข้าการตั้งค่าคอนฟิก" "$$$/MIDI2LR/Menu/NextMaskGroup=กลุ่มหน้ากากต่อไป" "$$$/MIDI2LR/Menu/NextMaskTool=เครื่องมือมาสก์ถัดไป" +"$$$/MIDI2LR/Menu/PresetNext=สถานีที่ตั้งไว้ถัดไป" +"$$$/MIDI2LR/Menu/PresetPrevious=สถานีที่ตั้งไว้ก่อนหน้า" "$$$/MIDI2LR/Menu/PreviousMaskGroup=กลุ่มหน้ากากก่อนหน้า" "$$$/MIDI2LR/Menu/PreviousMaskTool=เครื่องมือหน้ากากก่อนหน้า" "$$$/MIDI2LR/Menu/StartApplication=เริ่มแอพพลิเคชั่น" diff --git a/data/plugin/TranslatedStrings_zh_cn.txt b/data/plugin/TranslatedStrings_zh_cn.txt index 44b1f4525..c69ba2f53 100644 --- a/data/plugin/TranslatedStrings_zh_cn.txt +++ b/data/plugin/TranslatedStrings_zh_cn.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=导入配置(&I)" "$$$/MIDI2LR/Menu/NextMaskGroup=下一个面具组" "$$$/MIDI2LR/Menu/NextMaskTool=下一个遮罩工具" +"$$$/MIDI2LR/Menu/PresetNext=下一个预置" +"$$$/MIDI2LR/Menu/PresetPrevious=上一个预置" "$$$/MIDI2LR/Menu/PreviousMaskGroup=上一个面具组" "$$$/MIDI2LR/Menu/PreviousMaskTool=上一个遮罩工具" "$$$/MIDI2LR/Menu/StartApplication=启动应用程序(&S)" diff --git a/data/plugin/TranslatedStrings_zh_tw.txt b/data/plugin/TranslatedStrings_zh_tw.txt index d55bb360d..e5c7041a3 100644 --- a/data/plugin/TranslatedStrings_zh_tw.txt +++ b/data/plugin/TranslatedStrings_zh_tw.txt @@ -22,6 +22,8 @@ "$$$/MIDI2LR/Menu/ImportConfiguration=匯入設定(&I)" "$$$/MIDI2LR/Menu/NextMaskGroup=下一個面具組" "$$$/MIDI2LR/Menu/NextMaskTool=下一個遮罩工具" +"$$$/MIDI2LR/Menu/PresetNext=下一個預設設定" +"$$$/MIDI2LR/Menu/PresetPrevious=上一個預設設定" "$$$/MIDI2LR/Menu/PreviousMaskGroup=上一個面具組" "$$$/MIDI2LR/Menu/PreviousMaskTool=上一個遮罩工具" "$$$/MIDI2LR/Menu/StartApplication=啟動應用程式(&S)" diff --git a/src/application/Misc.cpp b/src/application/Misc.cpp index 3734bc030..f15eb7c59 100644 --- a/src/application/Misc.cpp +++ b/src/application/Misc.cpp @@ -56,8 +56,8 @@ void rsj::LabelThread(gsl::czstring<> threadname) /**************String Routines************************************************/ /*****************************************************************************/ namespace { - ::std::array ascii_map {"\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a", - "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0E", "\\x0F", "\\x10", "\\x11", "\\x12", + constexpr ::std::array ascii_map {"\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", + "\\a", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0E", "\\x0F", "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17", "\\x18", "\\x19", "\\x1A", "\\x1B", "\\x1C", "\\x1D", "\\x1E", "\\x1F", " ", "!", "\\\""}; } // namespace diff --git a/src/plugin/Client.lua b/src/plugin/Client.lua index bbea2de4f..52478eee2 100644 --- a/src/plugin/Client.lua +++ b/src/plugin/Client.lua @@ -78,6 +78,7 @@ LrTasks.startAsyncTask( local Limits = require 'Limits' local LocalPresets = require 'LocalPresets' local Mask = require 'Mask' + local Presets = require 'Presets' local Profiles = require 'Profiles' local Virtual = require 'Virtual' local LrApplication = import 'LrApplication' @@ -467,86 +468,88 @@ LrTasks.startAsyncTask( PostCropVignetteStyleColorPriority = CU.wrapFOM(LrDevelopController.setValue,'PostCropVignetteStyle',2), PostCropVignetteStyleHighlightPriority = CU.wrapFOM(LrDevelopController.setValue,'PostCropVignetteStyle',1), PostCropVignetteStylePaintOverlay = CU.wrapFOM(LrDevelopController.setValue,'PostCropVignetteStyle',3), - Preset_1 = CU.fApplyPreset(1), - Preset_10 = CU.fApplyPreset(10), - Preset_11 = CU.fApplyPreset(11), - Preset_12 = CU.fApplyPreset(12), - Preset_13 = CU.fApplyPreset(13), - Preset_14 = CU.fApplyPreset(14), - Preset_15 = CU.fApplyPreset(15), - Preset_16 = CU.fApplyPreset(16), - Preset_17 = CU.fApplyPreset(17), - Preset_18 = CU.fApplyPreset(18), - Preset_19 = CU.fApplyPreset(19), - Preset_2 = CU.fApplyPreset(2), - Preset_20 = CU.fApplyPreset(20), - Preset_21 = CU.fApplyPreset(21), - Preset_22 = CU.fApplyPreset(22), - Preset_23 = CU.fApplyPreset(23), - Preset_24 = CU.fApplyPreset(24), - Preset_25 = CU.fApplyPreset(25), - Preset_26 = CU.fApplyPreset(26), - Preset_27 = CU.fApplyPreset(27), - Preset_28 = CU.fApplyPreset(28), - Preset_29 = CU.fApplyPreset(29), - Preset_3 = CU.fApplyPreset(3), - Preset_30 = CU.fApplyPreset(30), - Preset_31 = CU.fApplyPreset(31), - Preset_32 = CU.fApplyPreset(32), - Preset_33 = CU.fApplyPreset(33), - Preset_34 = CU.fApplyPreset(34), - Preset_35 = CU.fApplyPreset(35), - Preset_36 = CU.fApplyPreset(36), - Preset_37 = CU.fApplyPreset(37), - Preset_38 = CU.fApplyPreset(38), - Preset_39 = CU.fApplyPreset(39), - Preset_4 = CU.fApplyPreset(4), - Preset_40 = CU.fApplyPreset(40), - Preset_41 = CU.fApplyPreset(41), - Preset_42 = CU.fApplyPreset(42), - Preset_43 = CU.fApplyPreset(43), - Preset_44 = CU.fApplyPreset(44), - Preset_45 = CU.fApplyPreset(45), - Preset_46 = CU.fApplyPreset(46), - Preset_47 = CU.fApplyPreset(47), - Preset_48 = CU.fApplyPreset(48), - Preset_49 = CU.fApplyPreset(49), - Preset_5 = CU.fApplyPreset(5), - Preset_50 = CU.fApplyPreset(50), - Preset_51 = CU.fApplyPreset(51), - Preset_52 = CU.fApplyPreset(52), - Preset_53 = CU.fApplyPreset(53), - Preset_54 = CU.fApplyPreset(54), - Preset_55 = CU.fApplyPreset(55), - Preset_56 = CU.fApplyPreset(56), - Preset_57 = CU.fApplyPreset(57), - Preset_58 = CU.fApplyPreset(58), - Preset_59 = CU.fApplyPreset(59), - Preset_6 = CU.fApplyPreset(6), - Preset_60 = CU.fApplyPreset(60), - Preset_61 = CU.fApplyPreset(61), - Preset_62 = CU.fApplyPreset(62), - Preset_63 = CU.fApplyPreset(63), - Preset_64 = CU.fApplyPreset(64), - Preset_65 = CU.fApplyPreset(65), - Preset_66 = CU.fApplyPreset(66), - Preset_67 = CU.fApplyPreset(67), - Preset_68 = CU.fApplyPreset(68), - Preset_69 = CU.fApplyPreset(69), - Preset_7 = CU.fApplyPreset(7), - Preset_70 = CU.fApplyPreset(70), - Preset_71 = CU.fApplyPreset(71), - Preset_72 = CU.fApplyPreset(72), - Preset_73 = CU.fApplyPreset(73), - Preset_74 = CU.fApplyPreset(74), - Preset_75 = CU.fApplyPreset(75), - Preset_76 = CU.fApplyPreset(76), - Preset_77 = CU.fApplyPreset(77), - Preset_78 = CU.fApplyPreset(78), - Preset_79 = CU.fApplyPreset(79), - Preset_8 = CU.fApplyPreset(8), - Preset_80 = CU.fApplyPreset(80), - Preset_9 = CU.fApplyPreset(9), + PresetNext = Presets.NextPreset, + PresetPrevious = Presets.PreviousPreset, + Preset_1 = Presets.fApplyPreset(1), + Preset_10 = Presets.fApplyPreset(10), + Preset_11 = Presets.fApplyPreset(11), + Preset_12 = Presets.fApplyPreset(12), + Preset_13 = Presets.fApplyPreset(13), + Preset_14 = Presets.fApplyPreset(14), + Preset_15 = Presets.fApplyPreset(15), + Preset_16 = Presets.fApplyPreset(16), + Preset_17 = Presets.fApplyPreset(17), + Preset_18 = Presets.fApplyPreset(18), + Preset_19 = Presets.fApplyPreset(19), + Preset_2 = Presets.fApplyPreset(2), + Preset_20 = Presets.fApplyPreset(20), + Preset_21 = Presets.fApplyPreset(21), + Preset_22 = Presets.fApplyPreset(22), + Preset_23 = Presets.fApplyPreset(23), + Preset_24 = Presets.fApplyPreset(24), + Preset_25 = Presets.fApplyPreset(25), + Preset_26 = Presets.fApplyPreset(26), + Preset_27 = Presets.fApplyPreset(27), + Preset_28 = Presets.fApplyPreset(28), + Preset_29 = Presets.fApplyPreset(29), + Preset_3 = Presets.fApplyPreset(3), + Preset_30 = Presets.fApplyPreset(30), + Preset_31 = Presets.fApplyPreset(31), + Preset_32 = Presets.fApplyPreset(32), + Preset_33 = Presets.fApplyPreset(33), + Preset_34 = Presets.fApplyPreset(34), + Preset_35 = Presets.fApplyPreset(35), + Preset_36 = Presets.fApplyPreset(36), + Preset_37 = Presets.fApplyPreset(37), + Preset_38 = Presets.fApplyPreset(38), + Preset_39 = Presets.fApplyPreset(39), + Preset_4 = Presets.fApplyPreset(4), + Preset_40 = Presets.fApplyPreset(40), + Preset_41 = Presets.fApplyPreset(41), + Preset_42 = Presets.fApplyPreset(42), + Preset_43 = Presets.fApplyPreset(43), + Preset_44 = Presets.fApplyPreset(44), + Preset_45 = Presets.fApplyPreset(45), + Preset_46 = Presets.fApplyPreset(46), + Preset_47 = Presets.fApplyPreset(47), + Preset_48 = Presets.fApplyPreset(48), + Preset_49 = Presets.fApplyPreset(49), + Preset_5 = Presets.fApplyPreset(5), + Preset_50 = Presets.fApplyPreset(50), + Preset_51 = Presets.fApplyPreset(51), + Preset_52 = Presets.fApplyPreset(52), + Preset_53 = Presets.fApplyPreset(53), + Preset_54 = Presets.fApplyPreset(54), + Preset_55 = Presets.fApplyPreset(55), + Preset_56 = Presets.fApplyPreset(56), + Preset_57 = Presets.fApplyPreset(57), + Preset_58 = Presets.fApplyPreset(58), + Preset_59 = Presets.fApplyPreset(59), + Preset_6 = Presets.fApplyPreset(6), + Preset_60 = Presets.fApplyPreset(60), + Preset_61 = Presets.fApplyPreset(61), + Preset_62 = Presets.fApplyPreset(62), + Preset_63 = Presets.fApplyPreset(63), + Preset_64 = Presets.fApplyPreset(64), + Preset_65 = Presets.fApplyPreset(65), + Preset_66 = Presets.fApplyPreset(66), + Preset_67 = Presets.fApplyPreset(67), + Preset_68 = Presets.fApplyPreset(68), + Preset_69 = Presets.fApplyPreset(69), + Preset_7 = Presets.fApplyPreset(7), + Preset_70 = Presets.fApplyPreset(70), + Preset_71 = Presets.fApplyPreset(71), + Preset_72 = Presets.fApplyPreset(72), + Preset_73 = Presets.fApplyPreset(73), + Preset_74 = Presets.fApplyPreset(74), + Preset_75 = Presets.fApplyPreset(75), + Preset_76 = Presets.fApplyPreset(76), + Preset_77 = Presets.fApplyPreset(77), + Preset_78 = Presets.fApplyPreset(78), + Preset_79 = Presets.fApplyPreset(79), + Preset_8 = Presets.fApplyPreset(8), + Preset_80 = Presets.fApplyPreset(80), + Preset_9 = Presets.fApplyPreset(9), Prev = LrSelection.previousPhoto, QuickDevBlacksLarge = CU.quickDevAdjust('Blacks',20,'QuickDevBlacksLarge'), QuickDevBlacksLargeDec = CU.quickDevAdjust('Blacks',-20,'QuickDevBlacksLargeDec'), diff --git a/src/plugin/ClientUtilities.lua b/src/plugin/ClientUtilities.lua index 37e8a9756..23bee56d3 100644 --- a/src/plugin/ClientUtilities.lua +++ b/src/plugin/ClientUtilities.lua @@ -340,28 +340,6 @@ local function RemoveFilters() end end -local function fApplyPreset(presetnumber) - return function() - local presetUuid = ProgramPreferences.Presets[presetnumber] - if presetUuid == nil or LrApplication.activeCatalog():getTargetPhoto() == nil then return end - local preset = LrApplication.developPresetByUuid(presetUuid) - LrTasks.startAsyncTask ( function () - LrApplication.activeCatalog():withWriteAccessDo( - 'Apply preset '..preset:getName(), - function() - if ProgramPreferences.ClientShowBezelOnChange then - LrDialogs.showBezel(preset:getName()) - end - LrApplication.activeCatalog():getTargetPhoto():applyDevelopPreset(preset) - end, - { timeout = 4, - callback = function() LrDialogs.showError(LOC("$$$/AgCustomMetadataRegistry/UpdateCatalog/Error=The catalog could not be updated with additional module metadata.")..'PastePreset.') end, - asynchronous = true } - ) - end ) - end -end - local function fChangePanel(panelname) return function() execFOM(LrDevelopController.revealPanel,panelname) @@ -884,7 +862,6 @@ return { cg_reset_current = cg_reset_current, execFOM = execFOM, fApplyFilter = fApplyFilter, - fApplyPreset = fApplyPreset, fChangeModule = fChangeModule, fChangePanel = fChangePanel, fSimulateKeys = fSimulateKeys, diff --git a/src/plugin/Database.lua b/src/plugin/Database.lua index caa72d060..f480082fc 100644 --- a/src/plugin/Database.lua +++ b/src/plugin/Database.lua @@ -698,6 +698,9 @@ local DataBase = { -- --develop: develop presets -- + {Command='PresetPrevious',Type='button',Translation=LOC('$$$/MIDI2LR/Menu/PresetPrevious=Previous preset'),Group=developPresets,Explanation='Apply previous preset (in numerical order) to active photo only.'}, + {Command='PresetNext',Type='button',Translation=LOC('$$$/MIDI2LR/Menu/PresetNext=Next preset'),Group=developPresets,Explanation='Apply next preset (in numerical order) to active photo only.'}, + {Command='PresetPreviousNext',Type='repeat',Translation=LOC('$$$/MIDI2LR/Menu/PresetPrevious=Previous preset')..'—'..LOC('$$$/MIDI2LR/Menu/PresetNext=Next preset'),Group=developPresets,Explanation='Turning knob clockwise applies next preset, counterclockwise, previous.'..repeatexp,Repeats={'PresetNext','PresetPrevious'}}, {Command='Preset_1',Type='button',Translation=developPreset..' 1',Group=developPresets,Explanation='Apply preset 1 to active photo only.'}, {Command='Preset_2',Type='button',Translation=developPreset..' 2',Group=developPresets,Explanation='Apply preset 2 to active photo only.'}, {Command='Preset_3',Type='button',Translation=developPreset..' 3',Group=developPresets,Explanation='Apply preset 3 to active photo only.'}, diff --git a/src/plugin/PrefReset.lua b/src/plugin/PrefReset.lua index e7f0db5b1..5f4250a0c 100644 --- a/src/plugin/PrefReset.lua +++ b/src/plugin/PrefReset.lua @@ -36,5 +36,6 @@ end Init.UseDefaultsAll() Preferences.Save() prefs[1] = nil +prefs = nil prefs = prefs diff --git a/src/plugin/Presets.lua b/src/plugin/Presets.lua index 0e2f8ebf7..35a6d50ae 100644 --- a/src/plugin/Presets.lua +++ b/src/plugin/Presets.lua @@ -3,7 +3,7 @@ Presets.lua Manages develop presets for plugin - + This file is part of MIDI2LR. Copyright 2015 by Rory Jaffe. MIDI2LR is free software: you can redistribute it and/or modify it under the @@ -15,13 +15,17 @@ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with -MIDI2LR. If not, see . +MIDI2LR. If not, see . ------------------------------------------------------------------------------]] local LrApplication = import 'LrApplication' +local LrTasks = import 'LrTasks' +local LrDialogs = import 'LrDialogs' local LrView = import 'LrView' local number_of_presets = 80 +local currentpreset = 1 +local lastchange = 0 local function StartDialog(obstable,f) --populate table with presets @@ -73,7 +77,7 @@ local function StartDialog(obstable,f) for i = 1,group_cols do local j = math.floor((i*group_rows-1)/button_rows)+1 --to determine which list of selected presets to include local label = (i-1)*group_rows+1 ..'-'..i*group_rows --must have space after 1 before .. - tabs[i] = f:tab_view_item {title = label, + tabs[i] = f:tab_view_item {title = label, identifier = 'tabview-'..label, f:row{ f:column(grouppresets[i]), @@ -95,7 +99,90 @@ local function EndDialog(obstable, status) end end +local function ApplyPreset(presetnumber) + local presetUuid = ProgramPreferences.Presets[presetnumber] + if presetUuid == nil or LrApplication.activeCatalog():getTargetPhoto() == nil then return end + currentpreset = presetnumber -- for next/prev preset + local preset = LrApplication.developPresetByUuid(presetUuid) + LrTasks.startAsyncTask ( function () + LrApplication.activeCatalog():withWriteAccessDo( + 'Apply preset '..preset:getName(), + function() + if ProgramPreferences.ClientShowBezelOnChange then + LrDialogs.showBezel(preset:getName()) + end + LrApplication.activeCatalog():getTargetPhoto():applyDevelopPreset(preset) + end, + { timeout = 4, + callback = function() LrDialogs.showError(LOC("$$$/AgCustomMetadataRegistry/UpdateCatalog/Error=The catalog could not be updated with additional module metadata.")..'PastePreset.') end, + asynchronous = true } + ) + end ) +end + +local function fApplyPreset(presetnumber) + return function() + local presetUuid = ProgramPreferences.Presets[presetnumber] + if presetUuid == nil or LrApplication.activeCatalog():getTargetPhoto() == nil then return end + currentpreset = presetnumber -- for next/prev preset + local preset = LrApplication.developPresetByUuid(presetUuid) + LrTasks.startAsyncTask ( function () + LrApplication.activeCatalog():withWriteAccessDo( + 'Apply preset '..preset:getName(), + function() + if ProgramPreferences.ClientShowBezelOnChange then + LrDialogs.showBezel(preset:getName()) + end + LrApplication.activeCatalog():getTargetPhoto():applyDevelopPreset(preset) + end, + { timeout = 4, + callback = function() LrDialogs.showError(LOC("$$$/AgCustomMetadataRegistry/UpdateCatalog/Error=The catalog could not be updated with additional module metadata.")..'PastePreset.') end, + asynchronous = true } + ) + end ) + end +end + +local function NextPreset() + local testpreset + if lastchange + 0.5 > os.clock() then + return + end + lastchange = os.clock() + for i = 1, number_of_presets do + testpreset = currentpreset + i + if testpreset > number_of_presets then + testpreset = testpreset - number_of_presets + end + if ProgramPreferences.Presets[testpreset] then + ApplyPreset(testpreset) + break + end + end +end + +local function PreviousPreset() + local testpreset + if lastchange + 0.5 > os.clock() then + return + end + lastchange = os.clock() + for i = 1, number_of_presets do + testpreset = currentpreset - i + if testpreset < 1 then + testpreset = testpreset + number_of_presets + end + if ProgramPreferences.Presets[testpreset] then + ApplyPreset(testpreset) + break + end + end +end + return { - StartDialog = StartDialog, EndDialog = EndDialog, + NextPreset = NextPreset, + PreviousPreset = PreviousPreset, + StartDialog = StartDialog, + fApplyPreset = fApplyPreset, } \ No newline at end of file From 402023c47e181166c3d7f712892ae1426018912b Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Tue, 14 Dec 2021 09:54:19 -0800 Subject: [PATCH 13/30] Fix crop:move horizontally when rotated. Closes #585 --- src/plugin/ClientUtilities.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin/ClientUtilities.lua b/src/plugin/ClientUtilities.lua index 23bee56d3..55378eb43 100644 --- a/src/plugin/ClientUtilities.lua +++ b/src/plugin/ClientUtilities.lua @@ -799,12 +799,12 @@ local function RatioCrop(param, value, UpdateParam) UpdateParam("CropLeft",new_left,true) elseif param == "CropMoveVertical" then local new_top = (1 - (prior_c_bottom - prior_c_top)) * tonumber(value) - local new_bottom = new_top + prior_c_bottom - prior_c_top + local new_bottom = new_top - prior_c_top + prior_c_bottom UpdateParam("CropBottom",new_bottom,true) UpdateParam("CropTop", new_top) elseif param == "CropMoveHorizontal" then local new_left = (1 - (prior_c_right - prior_c_left)) * tonumber(value) - local new_right = new_left + prior_c_bottom - prior_c_top + local new_right = new_left - prior_c_left + prior_c_right UpdateParam("CropLeft",new_left,true) UpdateParam("CropRight", new_right) end From 176039501caf951a7551b69345d61f8e8c1d9ab7 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Tue, 14 Dec 2021 10:00:18 -0800 Subject: [PATCH 14/30] fast math for Visual C++ version only where needed --- src/application/ControlsModel.cpp | 5 +++++ src/application/TextButtonAligned.cpp | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/application/ControlsModel.cpp b/src/application/ControlsModel.cpp index 7928c8f3a..8211243a2 100644 --- a/src/application/ControlsModel.cpp +++ b/src/application/ControlsModel.cpp @@ -257,9 +257,14 @@ int ChannelModel::PluginToController( /* TODO(C26451): int subtraction: can it overflow? */ const auto clow {cc_low_.at(controlnumber)}; const auto chigh {cc_high_.at(controlnumber)}; +#ifdef _WIN32 + const auto newv {std::clamp( + _cvt_dtoi_fast(value * static_cast(chigh - clow)) + clow, clow, chigh)}; +#else const auto newv {std::clamp( gsl::narrow(std::lrint(value * static_cast(chigh - clow))) + clow, clow, chigh)}; +#endif #ifdef __cpp_lib_atomic_ref std::atomic_ref(current_v_.at(controlnumber)).store(newv, std::memory_order_release); #else diff --git a/src/application/TextButtonAligned.cpp b/src/application/TextButtonAligned.cpp index 13f324882..029ae49b0 100644 --- a/src/application/TextButtonAligned.cpp +++ b/src/application/TextButtonAligned.cpp @@ -51,8 +51,11 @@ void TextButtonAligned::DrawButtonText(juce::Graphics& g, juce::TextButton& butt const auto y_indent {std::min(4, button.proportionOfHeight(0.3F))}; //-V112 const auto corner_size {std::min(button.getHeight(), button.getWidth()) / 2}; - +#ifdef _WIN32 + const auto font_height {_cvt_ftoi_fast(font.getHeight() * 0.6F)}; +#else const auto font_height {gsl::narrow_cast(std::lrint(font.getHeight() * 0.6F))}; +#endif const auto left_indent { std::min(font_height, 2 + corner_size / (button.isConnectedOnLeft() ? 4 : 2))}; //-V112 const auto right_indent { From 601bd6346473691238ea82eff6dae9a7badc63ee Mon Sep 17 00:00:00 2001 From: rsjaffe Date: Wed, 15 Dec 2021 15:38:36 -0800 Subject: [PATCH 15/30] Update CONTRIBUTING.md --- docs/CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index c53307411..e4044af65 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -4,7 +4,7 @@ Looking to contribute something to MIDI2LR? **Here's how you can help.** ## Reporting issues -On Github, we only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems that we can fix within the MIDI2LR code. Lightroom bugs and questions/concerns about the MIDI2LR program may be discussed in our [MIDI2LR Group Page](https://groups.google.com/forum/#!forum/midi2lr). Please read the following guidelines before opening any issue. +On Github issues page, we only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems that we can fix within the MIDI2LR code. Lightroom bugs and questions/concerns about the MIDI2LR program may be discussed in our [MIDI2LR Discussion Page](https://github.com/rsjaffe/MIDI2LR/discussions). Please read the following guidelines before opening any issue. 1. **Search for existing issues.** We get duplicate issues, and you'd help us out a lot by first checking if someone else has reported the same issue. Moreover, the issue may have already been resolved with a fix available. Also check out the [*known issues* section](https://github.com/rsjaffe/MIDI2LR/wiki#faqknown-issues) of the wiki. 2. **Create an isolated and reproducible test case.** Be sure the problem exists in MIDI2LR's code with a description of the steps to replicate the bug that should be included in each bug report. @@ -30,7 +30,7 @@ On Github, we only accept issues that are bug reports or feature requests. Bugs - all_lower: Parameters, local variables, global variables, class and struct public fields, union members, namespaces. - all_lower_: Protected and private class and struct fields. - kUpperCamelCase: Enum members, global constants, other constants. - - ALL_UPPER: Macros. + - ALL_UPPER: Macros. New macro names should start with *MIDI2LR_*. ## Coding standards: Lua - Emulate the formatting in current files. Avoid adding code to the message loops in Client.lua unless absolutely necessary, as those changes can slow down Lightroom. From c6bb15236d3208a66f0b0fe01582f2e1f66763f1 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Wed, 15 Dec 2021 15:40:05 -0800 Subject: [PATCH 16/30] Fast math in selected places; update BUILD.txt --- docs/BUILD.txt | 3 ++- src/application/ControlsModel.cpp | 2 +- src/application/TextButtonAligned.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/BUILD.txt b/docs/BUILD.txt index 2c47ddf37..a741c5cfa 100644 --- a/docs/BUILD.txt +++ b/docs/BUILD.txt @@ -48,5 +48,6 @@ Assembling the plugin Add the exe file (windows) or app directory (OSX) to a new directory named MIDI2LR.lrplugin. Then add to MIDI2LR.lrplugin all the files in the project's -src/plugin/ directory and all the files in the data/application/fonts directory. +src/plugin/ directory, all the files in the data/application/fonts/ directory, and all the *.txt files in the data/plugin/ directory. + That plugin can then be installed in Lightroom as usual. diff --git a/src/application/ControlsModel.cpp b/src/application/ControlsModel.cpp index 8211243a2..560ca9581 100644 --- a/src/application/ControlsModel.cpp +++ b/src/application/ControlsModel.cpp @@ -262,7 +262,7 @@ int ChannelModel::PluginToController( _cvt_dtoi_fast(value * static_cast(chigh - clow)) + clow, clow, chigh)}; #else const auto newv {std::clamp( - gsl::narrow(std::lrint(value * static_cast(chigh - clow))) + clow, + gsl::narrow_cast(value * static_cast(chigh - clow)) + clow, clow, chigh)}; #endif #ifdef __cpp_lib_atomic_ref diff --git a/src/application/TextButtonAligned.cpp b/src/application/TextButtonAligned.cpp index 029ae49b0..f2dd24c23 100644 --- a/src/application/TextButtonAligned.cpp +++ b/src/application/TextButtonAligned.cpp @@ -54,7 +54,7 @@ void TextButtonAligned::DrawButtonText(juce::Graphics& g, juce::TextButton& butt #ifdef _WIN32 const auto font_height {_cvt_ftoi_fast(font.getHeight() * 0.6F)}; #else - const auto font_height {gsl::narrow_cast(std::lrint(font.getHeight() * 0.6F))}; + const auto font_height {gsl::narrow_cast(font.getHeight() * 0.6F)}; #endif const auto left_indent { std::min(font_height, 2 + corner_size / (button.isConnectedOnLeft() ? 4 : 2))}; //-V112 From dd247bc4a78390b07690a93acf7f7af8f4752961 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Thu, 16 Dec 2021 09:53:05 -0800 Subject: [PATCH 17/30] turns out that fast math truncates, not rounds formatting --- src/application/CommandSet.cpp | 3 ++- src/application/ControlsModel.cpp | 9 +++++---- src/application/MIDIReceiver.cpp | 11 ++++++----- src/application/Misc.cpp | 2 +- src/application/SettingsComponent.cpp | 7 ++++--- src/application/TextButtonAligned.cpp | 4 ++-- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/application/CommandSet.cpp b/src/application/CommandSet.cpp index 1c0632b6b..2abef4c71 100644 --- a/src/application/CommandSet.cpp +++ b/src/application/CommandSet.cpp @@ -100,7 +100,8 @@ size_t CommandSet::CommandTextIndex(const std::string& command) const const auto found {cmd_idx_.find(command)}; if (found == cmd_idx_.end()) { if (command != "Unmapped"s) { /*Old version of Unassigned*/ - rsj::Log(fmt::format(FMT_STRING("Command not found in CommandTextIndex: {}."), command)); + rsj::Log( + fmt::format(FMT_STRING("Command not found in CommandTextIndex: {}."), command)); } return 0; } diff --git a/src/application/ControlsModel.cpp b/src/application/ControlsModel.cpp index 560ca9581..5fb256504 100644 --- a/src/application/ControlsModel.cpp +++ b/src/application/ControlsModel.cpp @@ -258,12 +258,13 @@ int ChannelModel::PluginToController( const auto clow {cc_low_.at(controlnumber)}; const auto chigh {cc_high_.at(controlnumber)}; #ifdef _WIN32 - const auto newv {std::clamp( - _cvt_dtoi_fast(value * static_cast(chigh - clow)) + clow, clow, chigh)}; + const auto newv { + std::clamp(_cvt_dtoi_fast(value * static_cast(chigh - clow) + 0.5) + clow, + clow, chigh)}; #else const auto newv {std::clamp( - gsl::narrow_cast(value * static_cast(chigh - clow)) + clow, - clow, chigh)}; + gsl::narrow_cast(value * static_cast(chigh - clow) + 0.5) + clow, clow, + chigh)}; #endif #ifdef __cpp_lib_atomic_ref std::atomic_ref(current_v_.at(controlnumber)).store(newv, std::memory_order_release); diff --git a/src/application/MIDIReceiver.cpp b/src/application/MIDIReceiver.cpp index 027405bcd..855c60e49 100644 --- a/src/application/MIDIReceiver.cpp +++ b/src/application/MIDIReceiver.cpp @@ -68,7 +68,8 @@ void MidiReceiver::RescanDevices() try { for (const auto& dev : input_devices_) { dev->stop(); - rsj::Log(fmt::format(FMT_STRING("Stopped input device {}."), dev->getName().toStdString())); + rsj::Log( + fmt::format(FMT_STRING("Stopped input device {}."), dev->getName().toStdString())); } input_devices_.clear(); rsj::Log("Cleared input devices."); @@ -88,13 +89,13 @@ void MidiReceiver::TryToOpen() if (auto open_device {juce::MidiInput::openDevice(device.identifier, this)}) { if (devices_.EnabledOrNew(open_device->getDeviceInfo(), "input")) { open_device->start(); - rsj::Log( - fmt::format(FMT_STRING("Opened input device {}."), open_device->getName().toStdString())); + rsj::Log(fmt::format( + FMT_STRING("Opened input device {}."), open_device->getName().toStdString())); input_devices_.push_back(std::move(open_device)); } else { - rsj::Log( - fmt::format(FMT_STRING("Ignored input device {}."), open_device->getName().toStdString())); + rsj::Log(fmt::format( + FMT_STRING("Ignored input device {}."), open_device->getName().toStdString())); } } } diff --git a/src/application/Misc.cpp b/src/application/Misc.cpp index f15eb7c59..ea0227e9f 100644 --- a/src/application/Misc.cpp +++ b/src/application/Misc.cpp @@ -56,7 +56,7 @@ void rsj::LabelThread(gsl::czstring<> threadname) /**************String Routines************************************************/ /*****************************************************************************/ namespace { - constexpr ::std::array ascii_map {"\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", + constexpr ::std::array ascii_map {"\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0E", "\\x0F", "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17", "\\x18", "\\x19", "\\x1A", "\\x1B", "\\x1C", "\\x1D", "\\x1E", "\\x1F", " ", "!", "\\\""}; diff --git a/src/application/SettingsComponent.cpp b/src/application/SettingsComponent.cpp index 5c4db1bf6..e361ef436 100644 --- a/src/application/SettingsComponent.cpp +++ b/src/application/SettingsComponent.cpp @@ -82,7 +82,8 @@ void SettingsComponent::Init() if (chooser.browseForDirectory()) { const auto profile_location {chooser.getResult().getFullPathName()}; settings_manager_.SetProfileDirectory(profile_location); - rsj::Log(fmt::format(FMT_STRING("Profile location set to {}."), profile_location.toStdString())); + rsj::Log(fmt::format( + FMT_STRING("Profile location set to {}."), profile_location.toStdString())); profile_location_label_.setText( profile_location, juce::NotificationType::dontSendNotification); } @@ -123,8 +124,8 @@ void SettingsComponent::Init() autohide_setting_.onValueChange = [this] { settings_manager_.SetAutoHideTime( gsl::narrow(std::lrint(autohide_setting_.getValue()))); - rsj::Log( - fmt::format(FMT_STRING("Autohide time set to {} seconds."), settings_manager_.GetAutoHideTime())); + rsj::Log(fmt::format( + FMT_STRING("Autohide time set to {} seconds."), settings_manager_.GetAutoHideTime())); }; /* turn it on */ activateLayout(); diff --git a/src/application/TextButtonAligned.cpp b/src/application/TextButtonAligned.cpp index f2dd24c23..50ccb3a9e 100644 --- a/src/application/TextButtonAligned.cpp +++ b/src/application/TextButtonAligned.cpp @@ -52,9 +52,9 @@ void TextButtonAligned::DrawButtonText(juce::Graphics& g, juce::TextButton& butt const auto y_indent {std::min(4, button.proportionOfHeight(0.3F))}; //-V112 const auto corner_size {std::min(button.getHeight(), button.getWidth()) / 2}; #ifdef _WIN32 - const auto font_height {_cvt_ftoi_fast(font.getHeight() * 0.6F)}; + const auto font_height {_cvt_ftoi_fast(font.getHeight() * 0.6F + 0.5F)}; #else - const auto font_height {gsl::narrow_cast(font.getHeight() * 0.6F)}; + const auto font_height {gsl::narrow_cast(font.getHeight() * 0.6F + 0.5F)}; #endif const auto left_indent { std::min(font_height, 2 + corner_size / (button.isConnectedOnLeft() ? 4 : 2))}; //-V112 From aaf9c87411616bc7351dbfe2f77bb235a569d167 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Mon, 20 Dec 2021 11:00:53 -0800 Subject: [PATCH 18/30] Update Juce to 6.1.4 --- build/MacOS/MIDI2LR.xcodeproj/project.pbxproj | 6 +- build/Windows/MIDI2LR_App.vcxproj | 4 +- .../juce_audio_basics/juce_audio_basics.h | 2 +- .../juce_audio_devices/juce_audio_devices.h | 2 +- .../native/juce_android_OpenSL.cpp | 2 +- .../native/juce_win32_ASIO.cpp | 2 +- .../modules/juce_core/juce_core.h | 2 +- .../juce_core/native/juce_posix_SharedCode.h | 11 +- .../juce_core/system/juce_StandardHeader.h | 2 +- .../juce_core/text/juce_CharPointer_UTF8.h | 2 +- .../juce_data_structures.cpp | 5 +- .../juce_data_structures.h | 4 +- .../juce_ValueTreePropertyWithDefault.h | 334 ++++++++++++++++++ ...uce_ValueTreePropertyWithDefault_test.cpp} | 48 ++- .../values/juce_ValueWithDefault.h | 244 ------------- .../modules/juce_events/juce_events.h | 2 +- .../modules/juce_graphics/juce_graphics.h | 2 +- .../native/juce_mac_CoreGraphicsContext.mm | 58 ++- .../native/juce_mac_CoreGraphicsHelpers.h | 2 +- .../modules/juce_gui_basics/juce_gui_basics.h | 2 +- .../lookandfeel/juce_LookAndFeel_V2.cpp | 5 + .../native/juce_mac_NSViewComponentPeer.mm | 7 +- .../native/juce_win32_Windowing.cpp | 4 +- .../juce_ChoicePropertyComponent.cpp | 59 ++-- .../properties/juce_ChoicePropertyComponent.h | 21 +- .../juce_MultiChoicePropertyComponent.cpp | 56 ++- .../juce_MultiChoicePropertyComponent.h | 17 +- .../properties/juce_TextPropertyComponent.cpp | 42 +-- .../properties/juce_TextPropertyComponent.h | 16 +- .../juce_gui_basics/widgets/juce_Slider.cpp | 1 - 30 files changed, 508 insertions(+), 456 deletions(-) create mode 100644 external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h rename external/JuceLibraryCode/modules/juce_data_structures/values/{juce_ValueWithDefault.cpp => juce_ValueTreePropertyWithDefault_test.cpp} (56%) delete mode 100644 external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.h diff --git a/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj b/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj index 707644948..f8b1616ef 100644 --- a/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj +++ b/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj @@ -358,7 +358,7 @@ DFD30DF9237597CD5AF365AE = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1310; + LastUpgradeCheck = 1320; ORGANIZATIONNAME = "Rory Jaffe"; TargetAttributes = { FD946FDE29418E71B61DACDC = { @@ -489,7 +489,7 @@ "NDEBUG=1", "JUCE_DISPLAY_SPLASH_SCREEN=0", "JUCE_USE_DARK_SPLASH_SCREEN=1", - "JUCE_PROJUCER_VERSION=0x60103", + "JUCE_PROJUCER_VERSION=0x60104", "JUCE_MODULE_AVAILABLE_juce_audio_basics=1", "JUCE_MODULE_AVAILABLE_juce_audio_devices=1", "JUCE_MODULE_AVAILABLE_juce_core=1", @@ -620,7 +620,7 @@ "DEBUG=1", "JUCE_DISPLAY_SPLASH_SCREEN=0", "JUCE_USE_DARK_SPLASH_SCREEN=1", - "JUCE_PROJUCER_VERSION=0x60103", + "JUCE_PROJUCER_VERSION=0x60104", "JUCE_MODULE_AVAILABLE_juce_audio_basics=1", "JUCE_MODULE_AVAILABLE_juce_audio_devices=1", "JUCE_MODULE_AVAILABLE_juce_core=1", diff --git a/build/Windows/MIDI2LR_App.vcxproj b/build/Windows/MIDI2LR_App.vcxproj index d520144ad..648b0c807 100644 --- a/build/Windows/MIDI2LR_App.vcxproj +++ b/build/Windows/MIDI2LR_App.vcxproj @@ -64,7 +64,7 @@ Disabled ProgramDatabase ..\..\external\JuceLibraryCode;..\..\external\JuceLibraryCode\modules;../../external/;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60103;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60104;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) MultiThreadedDebugDLL true NotUsing @@ -105,7 +105,7 @@ Full ..\..\external\JuceLibraryCode;..\..\external\JuceLibraryCode\modules;../../external/;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60103;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60104;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) MultiThreaded true NotUsing diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h b/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h index 814f7f297..c82b48868 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h @@ -32,7 +32,7 @@ ID: juce_audio_basics vendor: juce - version: 6.1.3 + version: 6.1.4 name: JUCE audio and MIDI data classes description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h b/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h index 881578eaf..3416aa16d 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h @@ -32,7 +32,7 @@ ID: juce_audio_devices vendor: juce - version: 6.1.3 + version: 6.1.4 name: JUCE audio and MIDI I/O device classes description: Classes to play and record from audio and MIDI I/O devices website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index c95cdb5b0..904b13c96 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -255,7 +255,7 @@ struct BufferHelpers } AudioData::deinterleaveSamples (AudioData::InterleavedSource { srcInterleaved, numChannels }, - AudioData::nonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + AudioData::NonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, audioBuffer.getNumSamples()); } diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index 2a96b4c7e..4abef064b 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -919,7 +919,7 @@ class ASIOAudioIODevice : public AudioIODevice, { granularity = jmax (16, (int) granularity); - for (int i = jmax ((int) (minSize + 15) & ~15, (int) granularity); i < jmin (6400, (int) maxSize); i += granularity) + for (int i = jmax ((int) (minSize + 15) & ~15, (int) granularity); i <= jmin (6400, (int) maxSize); i += granularity) bufferSizes.addIfNotAlreadyThere (granularity * (i / granularity)); } else if (granularity < 0) diff --git a/external/JuceLibraryCode/modules/juce_core/juce_core.h b/external/JuceLibraryCode/modules/juce_core/juce_core.h index 9cf87dca4..de3b331b0 100644 --- a/external/JuceLibraryCode/modules/juce_core/juce_core.h +++ b/external/JuceLibraryCode/modules/juce_core/juce_core.h @@ -32,7 +32,7 @@ ID: juce_core vendor: juce - version: 6.1.3 + version: 6.1.4 name: JUCE core classes description: The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h b/external/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h index ca63258be..aead2eda4 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h @@ -960,7 +960,12 @@ void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) bool Thread::setThreadPriority (void* handle, int priority) { constexpr auto maxInputPriority = 10; - constexpr auto lowestRealtimePriority = 8; + + #if JUCE_LINUX || JUCE_BSD + constexpr auto lowestRrPriority = 8; + #else + constexpr auto lowestRrPriority = 0; + #endif struct sched_param param; int policy; @@ -971,7 +976,7 @@ bool Thread::setThreadPriority (void* handle, int priority) if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0) return false; - policy = priority < lowestRealtimePriority ? SCHED_OTHER : SCHED_RR; + policy = priority < lowestRrPriority ? SCHED_OTHER : SCHED_RR; const auto minPriority = sched_get_priority_min (policy); const auto maxPriority = sched_get_priority_max (policy); @@ -981,7 +986,7 @@ bool Thread::setThreadPriority (void* handle, int priority) if (policy == SCHED_OTHER) return 0; - return jmap (priority, lowestRealtimePriority, maxInputPriority, minPriority, maxPriority); + return jmap (priority, lowestRrPriority, maxInputPriority, minPriority, maxPriority); }(); return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0; diff --git a/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h b/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h index d0e57cbff..b44aa8ac7 100644 --- a/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h +++ b/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h @@ -29,7 +29,7 @@ */ #define JUCE_MAJOR_VERSION 6 #define JUCE_MINOR_VERSION 1 -#define JUCE_BUILDNUMBER 3 +#define JUCE_BUILDNUMBER 4 /** Current JUCE version number. diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h b/external/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h index df8d74ed3..b83926f06 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h @@ -449,7 +449,7 @@ class CharPointer_UTF8 final } /** Returns true if the first character of this string is whitespace. */ - bool isWhitespace() const noexcept { const CharType c = *data; return c == ' ' || (c <= 13 && c >= 9); } + bool isWhitespace() const noexcept { return CharacterFunctions::isWhitespace ((juce_wchar) *(*this)); } /** Returns true if the first character of this string is a digit. */ bool isDigit() const noexcept { const CharType c = *data; return c >= '0' && c <= '9'; } /** Returns true if the first character of this string is a letter. */ diff --git a/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp b/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp index 78a3da615..cceefd2a8 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp +++ b/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp @@ -38,7 +38,10 @@ #include "values/juce_ValueTree.cpp" #include "values/juce_ValueTreeSynchroniser.cpp" #include "values/juce_CachedValue.cpp" -#include "values/juce_ValueWithDefault.cpp" #include "undomanager/juce_UndoManager.cpp" #include "app_properties/juce_ApplicationProperties.cpp" #include "app_properties/juce_PropertiesFile.cpp" + +#if JUCE_UNIT_TESTS + #include "values/juce_ValueTreePropertyWithDefault_test.cpp" +#endif diff --git a/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h b/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h index 469f13571..499e0c17a 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h +++ b/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h @@ -35,7 +35,7 @@ ID: juce_data_structures vendor: juce - version: 6.1.3 + version: 6.1.4 name: JUCE data model helper classes description: Classes for undo/redo management, and smart data structures. website: http://www.juce.com/juce @@ -61,6 +61,6 @@ #include "values/juce_ValueTree.h" #include "values/juce_ValueTreeSynchroniser.h" #include "values/juce_CachedValue.h" -#include "values/juce_ValueWithDefault.h" +#include "values/juce_ValueTreePropertyWithDefault.h" #include "app_properties/juce_PropertiesFile.h" #include "app_properties/juce_ApplicationProperties.h" diff --git a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h new file mode 100644 index 000000000..dc380ca34 --- /dev/null +++ b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h @@ -0,0 +1,334 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +//============================================================================== +/** + This class acts as a wrapper around a property inside a ValueTree. + + If the property inside the ValueTree is missing it will return a default value, + which can be specified in the constructor or by calling setDefault(). + + @tags{DataStructures} +*/ +class JUCE_API ValueTreePropertyWithDefault : private Value::Listener +{ +public: + //============================================================================== + /** Creates an uninitialised ValueTreePropertyWithDefault object. + + Initialise it using one of the referTo() methods. + */ + ValueTreePropertyWithDefault() = default; + + /** Creates a ValueTreePropertyWithDefault object for the specified property. + + The default value will be an empty var. + */ + ValueTreePropertyWithDefault (ValueTree& tree, + const Identifier& propertyID, + UndoManager* um) + { + referTo (tree, propertyID, um); + } + + /** Creates an ValueTreePropertyWithDefault object for the specified property. + + The default value will be defaultToUse. + */ + ValueTreePropertyWithDefault (ValueTree& tree, + const Identifier& propertyID, + UndoManager* um, + var defaultToUse) + { + referTo (tree, propertyID, um, defaultToUse); + } + + /** Creates a ValueTreePropertyWithDefault object for the specified property. + + The default value will be defaultToUse. + + Use this constructor if the underlying var object being controlled is an array and + it will handle the conversion to/from a delimited String that can be written to + XML format. + */ + ValueTreePropertyWithDefault (ValueTree& tree, + const Identifier& propertyID, + UndoManager* um, + var defaultToUse, + StringRef arrayDelimiter) + { + referTo (tree, propertyID, um, defaultToUse, arrayDelimiter); + } + + /** Creates a ValueTreePropertyWithDefault object from another ValueTreePropertyWithDefault object. */ + ValueTreePropertyWithDefault (const ValueTreePropertyWithDefault& other) + { + referToWithDefault (other.targetTree, + other.targetProperty, + other.undoManager, + other.defaultValue, + other.delimiter); + } + + /** Destructor. */ + ~ValueTreePropertyWithDefault() override + { + defaultValue.removeListener (this); + } + + //============================================================================== + /** Returns the current value of the property. + + If the property does not exist this returns the default value. + */ + var get() const noexcept + { + if (isUsingDefault()) + return defaultValue; + + if (delimiter.isNotEmpty()) + return delimitedStringToVarArray (targetTree[targetProperty].toString(), delimiter); + + return targetTree[targetProperty]; + } + + /** Returns the current property as a Value object. */ + Value getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); } + + /** Returns the current default value. */ + var getDefault() const { return defaultValue; } + + /** Sets the default value to a new var. */ + void setDefault (const var& newDefault) { defaultValue = newDefault; } + + /** Returns true if the property does not exist in the referenced ValueTree. */ + bool isUsingDefault() const { return ! targetTree.hasProperty (targetProperty); } + + /** Removes the property from the referenced ValueTree. */ + void resetToDefault() noexcept { targetTree.removeProperty (targetProperty, nullptr); } + + /** You can assign a lambda to this callback and it will called when the default + value is changed. + + @see setDefault + */ + std::function onDefaultChange; + + //============================================================================== + /** Sets the property and returns the new ValueTreePropertyWithDefault. + + This will modify the property in the referenced ValueTree. + */ + ValueTreePropertyWithDefault& operator= (const var& newValue) + { + setValue (newValue, undoManager); + return *this; + } + + /** Sets the property. + + This will modify the property in the referenced ValueTree. + */ + void setValue (const var& newValue, UndoManager* undoManagerToUse) + { + if (auto* array = newValue.getArray()) + targetTree.setProperty (targetProperty, varArrayToDelimitedString (*array, delimiter), undoManagerToUse); + else + targetTree.setProperty (targetProperty, newValue, undoManagerToUse); + } + + //============================================================================== + /** Makes the ValueTreePropertyWithDefault refer to the specified property inside + the given ValueTree. + + The default value will be an empty var. + */ + void referTo (ValueTree tree, + const Identifier& property, + UndoManager* um) + { + referToWithDefault (tree, + property, + um, + Value (new SynchronousValueSource (var())), + {}); + } + + /** Makes the ValueTreePropertyWithDefault refer to the specified property inside + the given ValueTree. + + The default value will be defaultVal. + */ + void referTo (ValueTree tree, + const Identifier& property, + UndoManager* um, + var defaultVal) + { + referToWithDefault (tree, + property, + um, + Value (new SynchronousValueSource (defaultVal)), + {}); + } + + /** Makes the ValueTreePropertyWithDefault refer to the specified property inside + the given ValueTree. + + The default value will be defaultVal. + */ + void referTo (ValueTree tree, + const Identifier& property, + UndoManager* um, + var defaultVal, + StringRef arrayDelimiter) + { + referToWithDefault (tree, + property, + um, + Value (new SynchronousValueSource (defaultVal)), + arrayDelimiter); + } + + //============================================================================== + /** Returns a reference to the ValueTree containing the referenced property. */ + ValueTree& getValueTree() noexcept { return targetTree; } + + /** Returns the property ID of the referenced property. */ + Identifier& getPropertyID() noexcept { return targetProperty; } + + /** Returns the UndoManager that is being used. */ + UndoManager* getUndoManager() noexcept { return undoManager; } + + //============================================================================== + ValueTreePropertyWithDefault& operator= (const ValueTreePropertyWithDefault& other) + { + referToWithDefault (other.targetTree, + other.targetProperty, + other.undoManager, + other.defaultValue, + other.delimiter); + + return *this; + } + +private: + //============================================================================== + class SynchronousValueSource : public Value::ValueSource + { + public: + explicit SynchronousValueSource (const var& initialValue) + : value (initialValue) + { + } + + var getValue() const override + { + return value; + } + + void setValue (const var& newValue) override + { + if (! newValue.equalsWithSameType (value)) + { + value = newValue; + sendChangeMessage (true); + } + } + + private: + var value; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SynchronousValueSource) + }; + + //============================================================================== + static String varArrayToDelimitedString (const Array& input, StringRef delim) + { + // if you are trying to control a var that is an array then you need to + // set a delimiter string that will be used when writing to XML! + jassert (delim.isNotEmpty()); + + StringArray elements; + + for (auto& v : input) + elements.add (v.toString()); + + return elements.joinIntoString (delim); + } + + static Array delimitedStringToVarArray (StringRef input, StringRef delim) + { + Array arr; + + for (auto t : StringArray::fromTokens (input, delim, {})) + arr.add (t); + + return arr; + } + + void valueChanged (Value&) override + { + if (onDefaultChange != nullptr) + onDefaultChange(); + } + + void referToWithDefault (ValueTree v, + const Identifier& i, + UndoManager* um, + const Value& defaultVal, + StringRef del) + { + targetTree = v; + targetProperty = i; + undoManager = um; + defaultValue.referTo (defaultVal); + delimiter = del; + + defaultValue.addListener (this); + } + + //============================================================================== + ValueTree targetTree; + Identifier targetProperty; + UndoManager* undoManager = nullptr; + Value defaultValue; + String delimiter; + + //============================================================================== + JUCE_LEAK_DETECTOR (ValueTreePropertyWithDefault) +}; + +//============================================================================== +#ifndef DOXYGEN +using ValueWithDefault [[deprecated ("This class has been renamed to better describe what is does. " + "This declaration is here for backwards compatibility and new " + "code should use the new class name.")]] + = ValueTreePropertyWithDefault; +#endif + +} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault_test.cpp similarity index 56% rename from external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp rename to external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault_test.cpp index 70ecc51ff..997a5b0a1 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp +++ b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault_test.cpp @@ -26,31 +26,29 @@ namespace juce { -#if JUCE_UNIT_TESTS - -class ValueWithDefaultTests : public UnitTest +class ValueTreePropertyWithDefaultTests : public UnitTest { public: - ValueWithDefaultTests() - : UnitTest ("ValueWithDefault", UnitTestCategories::values) + ValueTreePropertyWithDefaultTests() + : UnitTest ("ValueTreePropertyWithDefault", UnitTestCategories::values) {} void runTest() override { beginTest ("default constructor"); { - ValueWithDefault vwd; - expect (vwd.isUsingDefault()); - expect (vwd.get() == var()); + ValueTreePropertyWithDefault value; + expect (value.isUsingDefault()); + expect (value.get() == var()); } beginTest ("missing property"); { ValueTree t ("root"); - ValueWithDefault vwd (t, "testKey", nullptr, "default"); + ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default"); - expect (vwd.isUsingDefault()); - expectEquals (vwd.get().toString(), String ("default")); + expect (value.isUsingDefault()); + expectEquals (value.get().toString(), String ("default")); } beginTest ("non-empty property"); @@ -58,21 +56,21 @@ class ValueWithDefaultTests : public UnitTest ValueTree t ("root"); t.setProperty ("testKey", "non-default", nullptr); - ValueWithDefault vwd (t, "testKey", nullptr, "default"); + ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default"); - expect (! vwd.isUsingDefault()); - expectEquals (vwd.get().toString(), String ("non-default")); + expect (! value.isUsingDefault()); + expectEquals (value.get().toString(), String ("non-default")); } beginTest ("set default"); { ValueTree t ("root"); - ValueWithDefault vwd (t, "testkey", nullptr); - vwd.setDefault ("default"); + ValueTreePropertyWithDefault value (t, "testkey", nullptr); + value.setDefault ("default"); - expect (vwd.isUsingDefault()); - expectEquals (vwd.get().toString(), String ("default")); + expect (value.isUsingDefault()); + expectEquals (value.get().toString(), String ("default")); } beginTest ("set value"); @@ -80,22 +78,20 @@ class ValueWithDefaultTests : public UnitTest ValueTree t ("root"); t.setProperty ("testkey", "testvalue", nullptr); - ValueWithDefault vwd (t, "testkey", nullptr, "default"); - vwd = "newvalue"; + ValueTreePropertyWithDefault value (t, "testkey", nullptr, "default"); + value = "newvalue"; - expect (! vwd.isUsingDefault()); + expect (! value.isUsingDefault()); expectEquals (t["testkey"].toString(), String ("newvalue")); - vwd.resetToDefault(); + value.resetToDefault(); - expect (vwd.isUsingDefault()); + expect (value.isUsingDefault()); expect (t["testkey"] == var()); } } }; -static ValueWithDefaultTests valueWithDefaultTests; - -#endif +static ValueTreePropertyWithDefaultTests valueTreePropertyWithDefaultTests; } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.h b/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.h deleted file mode 100644 index 42c90583f..000000000 --- a/external/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2020 - Raw Material Software Limited - - JUCE is an open source library subject to commercial or open-source - licensing. - - By using JUCE, you agree to the terms of both the JUCE 6 End-User License - Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). - - End User License Agreement: www.juce.com/juce-6-licence - Privacy Policy: www.juce.com/juce-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - This class acts as a wrapper around a property inside a ValueTree. - - If the property inside the ValueTree is missing the ValueWithDefault will automatically - return a default value, which can be specified when initialising the ValueWithDefault. - - @tags{DataStructures} -*/ -class ValueWithDefault -{ -public: - //============================================================================== - /** Creates an unitialised ValueWithDefault. Initialise it using one of the referTo() methods. */ - ValueWithDefault() = default; - - /** Creates an ValueWithDefault object. The default value will be an empty var. */ - ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um) - : targetTree (tree), - targetProperty (propertyID), - undoManager (um), - defaultValue() - { - } - - /** Creates an ValueWithDefault object. The default value will be defaultToUse. */ - ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um, - const var& defaultToUse) - : targetTree (tree), - targetProperty (propertyID), - undoManager (um), - defaultValue (defaultToUse) - { - } - - /** Creates an ValueWithDefault object. The default value will be defaultToUse. - - Use this constructor if the underlying var object being controlled is an array and - it will handle the conversion to/from a delimited String that can be written to - XML format. - */ - ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um, - const var& defaultToUse, StringRef arrayDelimiter) - : targetTree (tree), - targetProperty (propertyID), - undoManager (um), - defaultValue (defaultToUse), - delimiter (arrayDelimiter) - { - } - - /** Creates a ValueWithDefault object from another ValueWithDefault object. */ - ValueWithDefault (const ValueWithDefault& other) - : targetTree (other.targetTree), - targetProperty (other.targetProperty), - undoManager (other.undoManager), - defaultValue (other.defaultValue), - delimiter (other.delimiter) - { - } - - //============================================================================== - /** Returns the current value of the property. If the property does not exist this - returns the default value. - */ - var get() const noexcept - { - if (isUsingDefault()) - return defaultValue; - - if (delimiter.isNotEmpty()) - return delimitedStringToVarArray (targetTree[targetProperty].toString()); - - return targetTree[targetProperty]; - } - - /** Returns the current property as a Value object. */ - Value getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); } - - /** Returns the current default value. */ - var getDefault() const { return defaultValue; } - - /** Sets the default value to a new var. */ - void setDefault (const var& newDefault) - { - if (defaultValue != newDefault) - { - defaultValue = newDefault; - - if (onDefaultChange != nullptr) - onDefaultChange(); - } - } - - /** Returns true if the property does not exist in the referenced ValueTree. */ - bool isUsingDefault() const - { - return ! targetTree.hasProperty (targetProperty); - } - - /** Removes the property from the referenced ValueTree. */ - void resetToDefault() noexcept - { - targetTree.removeProperty (targetProperty, nullptr); - } - - /** You can assign a lambda to this callback object to have it called when the default value is changed. */ - std::function onDefaultChange; - - //============================================================================== - /** Sets the property and returns the new ValueWithDefault. This will modify the property in the referenced ValueTree. */ - ValueWithDefault& operator= (const var& newValue) - { - setValue (newValue, undoManager); - return *this; - } - - /** Sets the property. This will actually modify the property in the referenced ValueTree. */ - void setValue (const var& newValue, UndoManager* undoManagerToUse) - { - if (auto* array = newValue.getArray()) - targetTree.setProperty (targetProperty, varArrayToDelimitedString (*array), undoManagerToUse); - else - targetTree.setProperty (targetProperty, newValue, undoManagerToUse); - } - - //============================================================================== - /** Makes the ValueWithDefault refer to the specified property inside the given ValueTree. */ - void referTo (ValueTree& tree, const Identifier& property, UndoManager* um) - { - referToWithDefault (tree, property, um, var(), {}); - } - - /** Makes the ValueWithDefault refer to the specified property inside the given ValueTree, - and specifies a default value to use. - */ - void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const var& defaultVal) - { - referToWithDefault (tree, property, um, defaultVal, {}); - } - - void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, - const var& defaultVal, StringRef arrayDelimiter) - { - referToWithDefault (tree, property, um, defaultVal, arrayDelimiter); - } - - //============================================================================== - /** Returns a reference to the ValueTree containing the referenced property. */ - ValueTree& getValueTree() noexcept { return targetTree; } - - /** Returns the property ID of the referenced property. */ - Identifier& getPropertyID() noexcept { return targetProperty; } - - /** Returns the UndoManager that is being used. */ - UndoManager* getUndoManager() noexcept { return undoManager; } - - //============================================================================== - ValueWithDefault& operator= (const ValueWithDefault& other) - { - referToWithDefault (other.targetTree, other.targetProperty, other.undoManager, - other.defaultValue, other.delimiter); - - return *this; - } - -private: - //============================================================================== - ValueTree targetTree; - Identifier targetProperty; - UndoManager* undoManager = nullptr; - var defaultValue; - - String delimiter; - - //============================================================================== - void referToWithDefault (const ValueTree& v, const Identifier& i, UndoManager* um, - const var& defaultVal, StringRef del) - { - targetTree = v; - targetProperty = i; - undoManager = um; - defaultValue = defaultVal; - delimiter = del; - } - - //============================================================================== - String varArrayToDelimitedString (const Array& input) const noexcept - { - // if you are trying to control a var that is an array then you need to - // set a delimiter string that will be used when writing to XML! - jassert (delimiter.isNotEmpty()); - - StringArray elements; - - for (auto& v : input) - elements.add (v.toString()); - - return elements.joinIntoString (delimiter); - } - - Array delimitedStringToVarArray (StringRef input) const noexcept - { - Array arr; - - for (auto t : StringArray::fromTokens (input, delimiter, {})) - arr.add (t); - - return arr; - } - - //============================================================================== - JUCE_DECLARE_WEAK_REFERENCEABLE (ValueWithDefault) -}; - -} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_events/juce_events.h b/external/JuceLibraryCode/modules/juce_events/juce_events.h index 0adcb314f..1f396a303 100644 --- a/external/JuceLibraryCode/modules/juce_events/juce_events.h +++ b/external/JuceLibraryCode/modules/juce_events/juce_events.h @@ -32,7 +32,7 @@ ID: juce_events vendor: juce - version: 6.1.3 + version: 6.1.4 name: JUCE message and event handling classes description: Classes for running an application's main event loop and sending/receiving messages, timers, etc. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h index 50152712f..f5de90ebf 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h +++ b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h @@ -35,7 +35,7 @@ ID: juce_graphics vendor: juce - version: 6.1.3 + version: 6.1.4 name: JUCE graphics classes description: Classes for 2D vector graphics, image loading/saving, font handling, etc. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index e40229ff3..e126d5145 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -48,12 +48,12 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). numComponents += (size_t) lineStride; #endif - imageDataHolder->data.allocate (numComponents, clearImage); + imageData->data.allocate (numComponents, clearImage); auto colourSpace = detail::ColorSpacePtr { CGColorSpaceCreateWithName ((format == Image::SingleChannel) ? kCGColorSpaceGenericGrayGamma2_2 : kCGColorSpaceSRGB) }; - context = detail::ContextPtr { CGBitmapContextCreate (imageDataHolder->data, (size_t) width, (size_t) height, 8, (size_t) lineStride, + context = detail::ContextPtr { CGBitmapContextCreate (imageData->data, (size_t) width, (size_t) height, 8, (size_t) lineStride, colourSpace.get(), getCGImageFlags (format)) }; } @@ -71,7 +71,7 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { - bitmap.data = imageDataHolder->data + x * pixelStride + y * lineStride; + bitmap.data = imageData->data + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; @@ -86,7 +86,7 @@ void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::Bitma ImagePixelData::Ptr clone() override { auto im = new CoreGraphicsPixelData (pixelFormat, width, height, false); - memcpy (im->imageDataHolder->data, imageDataHolder->data, (size_t) (lineStride * height)); + memcpy (im->imageData->data, imageData->data, (size_t) (lineStride * height)); return *im; } @@ -98,12 +98,9 @@ static CGImageRef getCachedImageRef (const Image& juceImage, CGColorSpaceRef col auto cgim = dynamic_cast (juceImage.getPixelData()); if (cgim != nullptr && cgim->cachedImageRef != nullptr) - { - CGImageRetain (cgim->cachedImageRef.get()); - return cgim->cachedImageRef.get(); - } + return CGImageRetain (cgim->cachedImageRef.get()); - CGImageRef ref = createImage (juceImage, colourSpace, false); + CGImageRef ref = createImage (juceImage, colourSpace); if (cgim != nullptr) cgim->cachedImageRef.reset (CGImageRetain (ref)); @@ -111,33 +108,25 @@ static CGImageRef getCachedImageRef (const Image& juceImage, CGColorSpaceRef col return ref; } - static CGImageRef createImage (const Image& juceImage, CGColorSpaceRef colourSpace, bool mustOutliveSource) + static CGImageRef createImage (const Image& juceImage, CGColorSpaceRef colourSpace) { const Image::BitmapData srcData (juceImage, Image::BitmapData::readOnly); detail::DataProviderPtr provider; - if (mustOutliveSource) + if (auto* cgim = dynamic_cast (juceImage.getPixelData())) + { + provider = detail::DataProviderPtr { CGDataProviderCreateWithData (new ImageDataContainer::Ptr (cgim->imageData), + srcData.data, + (size_t) srcData.lineStride * (size_t) srcData.height, + [] (void * __nullable info, const void*, size_t) { delete (ImageDataContainer::Ptr*) info; }) }; + } + else { CFUniquePtr data (CFDataCreate (nullptr, (const UInt8*) srcData.data, (CFIndex) ((size_t) srcData.lineStride * (size_t) srcData.height))); provider = detail::DataProviderPtr { CGDataProviderCreateWithCFData (data.get()) }; } - else - { - auto* imageDataContainer = [] (const Image& img) -> HeapBlockContainer::Ptr* - { - if (auto* cgim = dynamic_cast (img.getPixelData())) - return new HeapBlockContainer::Ptr (cgim->imageDataHolder); - - return nullptr; - } (juceImage); - - provider = detail::DataProviderPtr { CGDataProviderCreateWithData (imageDataContainer, - srcData.data, - (size_t) srcData.lineStride * (size_t) srcData.height, - [] (void * __nullable info, const void*, size_t) { delete (HeapBlockContainer::Ptr*) info; }) }; - } CGImageRef imageRef = CGImageCreate ((size_t) srcData.width, (size_t) srcData.height, @@ -154,13 +143,15 @@ static CGImageRef createImage (const Image& juceImage, CGColorSpaceRef colourSpa detail::ContextPtr context; detail::ImagePtr cachedImageRef; - struct HeapBlockContainer : public ReferenceCountedObject + struct ImageDataContainer : public ReferenceCountedObject { - using Ptr = ReferenceCountedObjectPtr; + ImageDataContainer() = default; + + using Ptr = ReferenceCountedObjectPtr; HeapBlock data; }; - HeapBlockContainer::Ptr imageDataHolder = new HeapBlockContainer(); + ImageDataContainer::Ptr imageData = new ImageDataContainer(); int pixelStride, lineStride; private: @@ -329,7 +320,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) if (sourceImage.getFormat() != Image::SingleChannel) singleChannelImage = sourceImage.convertedToFormat (Image::SingleChannel); - auto image = detail::ImagePtr { CoreGraphicsPixelData::createImage (singleChannelImage, greyColourSpace.get(), true) }; + auto image = detail::ImagePtr { CoreGraphicsPixelData::createImage (singleChannelImage, greyColourSpace.get()) }; flip(); auto t = AffineTransform::verticalFlip (sourceImage.getHeight()).followedBy (transform); @@ -899,10 +890,9 @@ Image juce_createImageFromCIImage (CIImage* im, int w, int h) return Image (*cgImage); } -CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef colourSpace, - const bool mustOutliveSource) +CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef colourSpace) { - return CoreGraphicsPixelData::createImage (juceImage, colourSpace, mustOutliveSource); + return CoreGraphicsPixelData::createImage (juceImage, colourSpace); } CGContextRef juce_getImageContext (const Image& image) @@ -941,7 +931,7 @@ Image juce_createImageFromUIImage (UIImage* img) [im setSize: requiredSize]; detail::ColorSpacePtr colourSpace { CGColorSpaceCreateWithName (kCGColorSpaceSRGB) }; - detail::ImagePtr imageRef { juce_createCoreGraphicsImage (image, colourSpace.get(), true) }; + detail::ImagePtr imageRef { juce_createCoreGraphicsImage (image, colourSpace.get()) }; NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithCGImage: imageRef.get()]; [imageRep setSize: requiredSize]; diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h index 61b1056d8..007fe31d2 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h @@ -94,7 +94,7 @@ namespace #endif } -CGImageRef juce_createCoreGraphicsImage (const Image&, CGColorSpaceRef, bool mustOutliveSource); +CGImageRef juce_createCoreGraphicsImage (const Image&, CGColorSpaceRef); CGContextRef juce_getImageContext (const Image&); #if JUCE_IOS diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h index 7e0e2e551..a92fc233b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h @@ -35,7 +35,7 @@ ID: juce_gui_basics vendor: juce - version: 6.1.3 + version: 6.1.4 name: JUCE GUI core classes description: Basic user-interface components and related classes. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 887db66c2..800a79f0d 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -1598,6 +1598,11 @@ class LookAndFeel_V2::SliderLabelComp : public Label SliderLabelComp() : Label ({}, {}) {} void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override {} + + std::unique_ptr createAccessibilityHandler() override + { + return createIgnoredAccessibilityHandler (*this); + } }; Label* LookAndFeel_V2::createSliderTextBox (Slider& slider) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 504d5fed3..e8c9991f7 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -535,9 +535,10 @@ void setIcon (const Image& newIcon) override { // need to set a dummy represented file here to show the file icon (which we then set to the new icon) if (! windowRepresentsFile) - [window setRepresentedFilename:juceStringToNS (" ")]; // can't just use an empty string for some reason... + [window setRepresentedFilename: juceStringToNS (" ")]; // can't just use an empty string for some reason... - [[window standardWindowButton:NSWindowDocumentIconButton] setImage:imageToNSImage (ScaledImage (newIcon))]; + auto img = NSUniquePtr { imageToNSImage (ScaledImage (newIcon)) }; + [[window standardWindowButton: NSWindowDocumentIconButton] setImage: img.get()]; } } @@ -925,7 +926,7 @@ Image temp (component.isOpaque() ? Image::RGB : Image::ARGB, } detail::ColorSpacePtr colourSpace { CGColorSpaceCreateWithName (kCGColorSpaceSRGB) }; - CGImageRef image = juce_createCoreGraphicsImage (temp, colourSpace.get(), false); + CGImageRef image = juce_createCoreGraphicsImage (temp, colourSpace.get()); CGContextConcatCTM (cg, CGAffineTransformMake (1, 0, 0, -1, r.origin.x, r.origin.y + clipH)); CGContextDrawImage (cg, CGRectMake (0.0f, 0.0f, clipW, clipH), image); CGImageRelease (image); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 999e2f54f..b5757476a 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -4033,7 +4033,9 @@ class HWNDComponentPeer : public ComponentPeer, return false; if (auto* current = Component::getCurrentlyModalComponent()) - current->inputAttemptWhenModal(); + if (auto* owner = getOwnerOfWindow ((HWND) current->getWindowHandle())) + if (! owner->shouldIgnoreModalDismiss) + current->inputAttemptWhenModal(); return true; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp index e315f0fd2..63a5bb284 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp @@ -27,11 +27,11 @@ namespace juce { //============================================================================== -class ChoicePropertyComponent::RemapperValueSource : public Value::ValueSource, - private Value::Listener +class ChoiceRemapperValueSource : public Value::ValueSource, + private Value::Listener { public: - RemapperValueSource (const Value& source, const Array& map) + ChoiceRemapperValueSource (const Value& source, const Array& map) : sourceValue (source), mappings (map) { @@ -64,17 +64,17 @@ class ChoicePropertyComponent::RemapperValueSource : public Value::ValueSourc void valueChanged (Value&) override { sendChangeMessage (true); } //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSource) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChoiceRemapperValueSource) }; //============================================================================== -class ChoicePropertyComponent::RemapperValueSourceWithDefault : public Value::ValueSource, - private Value::Listener +class ChoiceRemapperValueSourceWithDefault : public Value::ValueSource, + private Value::Listener { public: - RemapperValueSourceWithDefault (ValueWithDefault* vwd, const Array& map) - : valueWithDefault (vwd), - sourceValue (valueWithDefault->getPropertyAsValue()), + ChoiceRemapperValueSourceWithDefault (const ValueTreePropertyWithDefault& v, const Array& map) + : value (v), + sourceValue (value.getPropertyAsValue()), mappings (map) { sourceValue.addListener (this); @@ -82,7 +82,7 @@ class ChoicePropertyComponent::RemapperValueSourceWithDefault : public Value: var getValue() const override { - if (valueWithDefault != nullptr && ! valueWithDefault->isUsingDefault()) + if (! value.isUsingDefault()) { const auto target = sourceValue.getValue(); const auto equalsWithSameType = [&target] (const var& map) { return map.equalsWithSameType (target); }; @@ -101,33 +101,30 @@ class ChoicePropertyComponent::RemapperValueSourceWithDefault : public Value: void setValue (const var& newValue) override { - if (valueWithDefault == nullptr) - return; - auto newValueInt = static_cast (newValue); if (newValueInt == -1) { - valueWithDefault->resetToDefault(); + value.resetToDefault(); } else { auto remappedVal = mappings [newValueInt - 1]; if (! remappedVal.equalsWithSameType (sourceValue)) - *valueWithDefault = remappedVal; + value = remappedVal; } } private: void valueChanged (Value&) override { sendChangeMessage (true); } - WeakReference valueWithDefault; + ValueTreePropertyWithDefault value; Value sourceValue; Array mappings; //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSourceWithDefault) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChoiceRemapperValueSourceWithDefault) }; //============================================================================== @@ -155,23 +152,23 @@ ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl, : ChoicePropertyComponent (name, choiceList, correspondingValues) { refreshChoices(); - initialiseComboBox (Value (new RemapperValueSource (valueToControl, correspondingValues))); + initialiseComboBox (Value (new ChoiceRemapperValueSource (valueToControl, correspondingValues))); } -ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToControl, +ChoicePropertyComponent::ChoicePropertyComponent (const ValueTreePropertyWithDefault& valueToControl, const String& name, const StringArray& choiceList, const Array& correspondingValues) : ChoicePropertyComponent (name, choiceList, correspondingValues) { - valueWithDefault = &valueToControl; + value = valueToControl; - auto getDefaultString = [this, correspondingValues] { return choices [correspondingValues.indexOf (valueWithDefault->getDefault())]; }; + auto getDefaultString = [this, correspondingValues] { return choices [correspondingValues.indexOf (value.getDefault())]; }; refreshChoices (getDefaultString()); - initialiseComboBox (Value (new RemapperValueSourceWithDefault (valueWithDefault, correspondingValues))); + initialiseComboBox (Value (new ChoiceRemapperValueSourceWithDefault (value, correspondingValues))); - valueWithDefault->onDefaultChange = [this, getDefaultString] + value.onDefaultChange = [this, getDefaultString] { auto selectedId = comboBox.getSelectedId(); refreshChoices (getDefaultString()); @@ -179,19 +176,19 @@ ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToContr }; } -ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToControl, +ChoicePropertyComponent::ChoicePropertyComponent (const ValueTreePropertyWithDefault& valueToControl, const String& name) : PropertyComponent (name), choices ({ "Enabled", "Disabled" }) { - valueWithDefault = &valueToControl; + value = valueToControl; - auto getDefaultString = [this] { return valueWithDefault->getDefault() ? "Enabled" : "Disabled"; }; + auto getDefaultString = [this] { return value.getDefault() ? "Enabled" : "Disabled"; }; refreshChoices (getDefaultString()); - initialiseComboBox (Value (new RemapperValueSourceWithDefault (valueWithDefault, { true, false }))); + initialiseComboBox (Value (new ChoiceRemapperValueSourceWithDefault (value, { true, false }))); - valueWithDefault->onDefaultChange = [this, getDefaultString] + value.onDefaultChange = [this, getDefaultString] { auto selectedId = comboBox.getSelectedId(); refreshChoices (getDefaultString()); @@ -199,12 +196,6 @@ ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToContr }; } -ChoicePropertyComponent::~ChoicePropertyComponent() -{ - if (valueWithDefault != nullptr) - valueWithDefault->onDefaultChange = nullptr; -} - //============================================================================== void ChoicePropertyComponent::initialiseComboBox (const Value& v) { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h index e42183686..28b501e3e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h @@ -79,34 +79,30 @@ class JUCE_API ChoicePropertyComponent : public PropertyComponent const StringArray& choices, const Array& correspondingValues); - /** Creates the component using a ValueWithDefault object. This will add an item to the ComboBox for the + /** Creates the component using a ValueTreePropertyWithDefault object. This will add an item to the ComboBox for the default value with an ID of -1. - @param valueToControl the ValueWithDefault object that contains the Value object that the combo box will read and control. + @param valueToControl the ValueTreePropertyWithDefault object that contains the Value object that the combo box will read and control. @param propertyName the name of the property @param choices the list of possible values that the drop-down list will contain @param correspondingValues a list of values corresponding to each item in the 'choices' StringArray. These are the values that will be read and written to the valueToControl value. This array must contain the same number of items as the choices array - */ - ChoicePropertyComponent (ValueWithDefault& valueToControl, + ChoicePropertyComponent (const ValueTreePropertyWithDefault& valueToControl, const String& propertyName, const StringArray& choices, const Array& correspondingValues); - /** Creates the component using a ValueWithDefault object, adding an item to the ComboBox for the + /** Creates the component using a ValueTreePropertyWithDefault object, adding an item to the ComboBox for the default value with an ID of -1 as well as adding separate "Enabled" and "Disabled" options. This is useful for simple on/off choices that also need a default value. */ - ChoicePropertyComponent (ValueWithDefault& valueToControl, + ChoicePropertyComponent (const ValueTreePropertyWithDefault& valueToControl, const String& propertyName); - /** Destructor. */ - ~ChoicePropertyComponent() override; - //============================================================================== /** Called when the user selects an item from the combo box. @@ -138,10 +134,6 @@ class JUCE_API ChoicePropertyComponent : public PropertyComponent StringArray choices; private: - //============================================================================== - class RemapperValueSource; - class RemapperValueSourceWithDefault; - //============================================================================== void initialiseComboBox (const Value&); void refreshChoices(); @@ -150,11 +142,10 @@ class JUCE_API ChoicePropertyComponent : public PropertyComponent void changeIndex(); //============================================================================== + ValueTreePropertyWithDefault value; ComboBox comboBox; bool isCustomClass = false; - WeakReference valueWithDefault; - //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChoicePropertyComponent) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.cpp index 64f23a555..b55058113 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.cpp @@ -111,10 +111,11 @@ class MultiChoicePropertyComponent::MultiChoiceRemapperSourceWithDefault : pu private Value::Listener { public: - MultiChoiceRemapperSourceWithDefault (ValueWithDefault* vwd, var v, int c, ToggleButton* b) - : valueWithDefault (vwd), + MultiChoiceRemapperSourceWithDefault (const ValueTreePropertyWithDefault& val, + var v, int c, ToggleButton* b) + : value (val), varToControl (v), - sourceValue (valueWithDefault->getPropertyAsValue()), + sourceValue (value.getPropertyAsValue()), maxChoices (c), buttonToControl (b) { @@ -123,16 +124,13 @@ class MultiChoicePropertyComponent::MultiChoiceRemapperSourceWithDefault : pu var getValue() const override { - if (valueWithDefault == nullptr) - return {}; - - auto v = valueWithDefault->get(); + auto v = value.get(); if (auto* arr = v.getArray()) { if (arr->contains (varToControl)) { - updateButtonTickColour (buttonToControl, valueWithDefault->isUsingDefault()); + updateButtonTickColour (buttonToControl, value.isUsingDefault()); return true; } } @@ -142,14 +140,11 @@ class MultiChoicePropertyComponent::MultiChoiceRemapperSourceWithDefault : pu void setValue (const var& newValue) override { - if (valueWithDefault == nullptr) - return; - - auto v = valueWithDefault->get(); + auto v = value.get(); OptionalScopedPointer> arrayToControl; - if (valueWithDefault->isUsingDefault()) + if (value.isUsingDefault()) arrayToControl.set (new Array(), true); // use an empty array so the default values are overwritten else arrayToControl.set (v.getArray(), false); @@ -160,7 +155,7 @@ class MultiChoicePropertyComponent::MultiChoiceRemapperSourceWithDefault : pu bool newState = newValue; - if (valueWithDefault->isUsingDefault()) + if (value.isUsingDefault()) { if (auto* defaultArray = v.getArray()) { @@ -182,10 +177,10 @@ class MultiChoicePropertyComponent::MultiChoiceRemapperSourceWithDefault : pu StringComparator c; temp.sort (c); - *valueWithDefault = temp; + value = temp; if (temp.size() == 0) - valueWithDefault->resetToDefault(); + value.resetToDefault(); } } @@ -194,7 +189,7 @@ class MultiChoicePropertyComponent::MultiChoiceRemapperSourceWithDefault : pu void valueChanged (Value&) override { sendChangeMessage (true); } //============================================================================== - WeakReference valueWithDefault; + ValueTreePropertyWithDefault value; var varToControl; Value sourceValue; @@ -215,7 +210,7 @@ int MultiChoicePropertyComponent::getTotalButtonsHeight (int numButtons) MultiChoicePropertyComponent::MultiChoicePropertyComponent (const String& propertyName, const StringArray& choices, const Array& correspondingValues) -: PropertyComponent (propertyName, jmin (getTotalButtonsHeight (choices.size()), collapsedHeight)) + : PropertyComponent (propertyName, jmin (getTotalButtonsHeight (choices.size()), collapsedHeight)) { // The array of corresponding values must contain one value for each of the items in // the choices array! @@ -261,31 +256,25 @@ MultiChoicePropertyComponent::MultiChoicePropertyComponent (const Value& valueTo maxChoices))); } -MultiChoicePropertyComponent::MultiChoicePropertyComponent (ValueWithDefault& valueToControl, +MultiChoicePropertyComponent::MultiChoicePropertyComponent (const ValueTreePropertyWithDefault& valueToControl, const String& propertyName, const StringArray& choices, const Array& correspondingValues, int maxChoices) : MultiChoicePropertyComponent (propertyName, choices, correspondingValues) { - valueWithDefault = &valueToControl; + value = valueToControl; // The value to control must be an array! - jassert (valueWithDefault->get().isArray()); + jassert (value.get().isArray()); for (int i = 0; i < choiceButtons.size(); ++i) - choiceButtons[i]->getToggleStateValue().referTo (Value (new MultiChoiceRemapperSourceWithDefault (valueWithDefault, + choiceButtons[i]->getToggleStateValue().referTo (Value (new MultiChoiceRemapperSourceWithDefault (value, correspondingValues[i], maxChoices, choiceButtons[i]))); - valueWithDefault->onDefaultChange = [this] { repaint(); }; -} - -MultiChoicePropertyComponent::~MultiChoicePropertyComponent() -{ - if (valueWithDefault != nullptr) - valueWithDefault->onDefaultChange = nullptr; + value.onDefaultChange = [this] { repaint(); }; } void MultiChoicePropertyComponent::paint (Graphics& g) @@ -361,13 +350,10 @@ void MultiChoicePropertyComponent::lookAndFeelChanged() auto iconColour = findColour (TextEditor::backgroundColourId).contrasting(); expandButton.setColours (iconColour, iconColour.darker(), iconColour.darker()); - if (valueWithDefault != nullptr) - { - auto usingDefault = valueWithDefault->isUsingDefault(); + const auto usingDefault = value.isUsingDefault(); - for (auto* button : choiceButtons) - updateButtonTickColour (button, usingDefault); - } + for (auto* button : choiceButtons) + updateButtonTickColour (button, usingDefault); } } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.h b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.h index e03ac148f..fa5182941 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.h @@ -63,9 +63,9 @@ class MultiChoicePropertyComponent : public PropertyComponent const Array& correspondingValues, int maxChoices = -1); - /** Creates the component using a ValueWithDefault object. This will select the default options. + /** Creates the component using a ValueTreePropertyWithDefault object. This will select the default options. - @param valueToControl the ValueWithDefault object that contains the Value object that the ToggleButtons will read and control. + @param valueToControl the ValueTreePropertyWithDefault object that contains the Value object that the ToggleButtons will read and control. @param propertyName the name of the property @param choices the list of possible values that will be represented @param correspondingValues a list of values corresponding to each item in the 'choices' StringArray. @@ -75,14 +75,12 @@ class MultiChoicePropertyComponent : public PropertyComponent @param maxChoices the maximum number of values which can be selected at once. The default of -1 will not limit the number that can be selected */ - MultiChoicePropertyComponent (ValueWithDefault& valueToControl, + MultiChoicePropertyComponent (const ValueTreePropertyWithDefault& valueToControl, const String& propertyName, const StringArray& choices, const Array& correspondingValues, int maxChoices = -1); - ~MultiChoicePropertyComponent() override; - //============================================================================== /** Returns true if the list of options is expanded. */ bool isExpanded() const noexcept { return expanded; } @@ -100,7 +98,11 @@ class MultiChoicePropertyComponent : public PropertyComponent */ void setExpanded (bool expanded) noexcept; - /** You can assign a lambda to this callback object to have it called when the MultiChoicePropertyComponent height changes. */ + /** You can assign a lambda to this callback object to have it called when the + height of this component changes in response to being expanded/collapsed. + + @see setExpanded + */ std::function onHeightChange; //============================================================================== @@ -121,8 +123,6 @@ class MultiChoicePropertyComponent : public PropertyComponent void lookAndFeelChanged() override; //============================================================================== - WeakReference valueWithDefault; - static constexpr int collapsedHeight = 125; static constexpr int buttonHeight = 25; static constexpr int expandAreaHeight = 20; @@ -130,6 +130,7 @@ class MultiChoicePropertyComponent : public PropertyComponent int maxHeight = 0, numHidden = 0; bool expandable = false, expanded = false; + ValueTreePropertyWithDefault value; OwnedArray choiceButtons; ShapeButton expandButton { "Expand", Colours::transparentBlack, Colours::transparentBlack, Colours::transparentBlack }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_TextPropertyComponent.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_TextPropertyComponent.cpp index a2a60694e..b2769cac6 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_TextPropertyComponent.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_TextPropertyComponent.cpp @@ -120,38 +120,38 @@ class TextPropertyComponent::LabelComp : public Label, }; //============================================================================== -class TextPropertyComponent::RemapperValueSourceWithDefault : public Value::ValueSource +class TextRemapperValueSourceWithDefault : public Value::ValueSource { public: - RemapperValueSourceWithDefault (ValueWithDefault* vwd) - : valueWithDefault (vwd) + TextRemapperValueSourceWithDefault (const ValueTreePropertyWithDefault& v) + : value (v) { } var getValue() const override { - if (valueWithDefault == nullptr || valueWithDefault->isUsingDefault()) + if (value.isUsingDefault()) return {}; - return valueWithDefault->get(); + return value.get(); } void setValue (const var& newValue) override { - if (valueWithDefault == nullptr) + if (newValue.toString().isEmpty()) + { + value.resetToDefault(); return; + } - if (newValue.toString().isEmpty()) - valueWithDefault->resetToDefault(); - else - *valueWithDefault = newValue; + value = newValue; } private: - WeakReference valueWithDefault; + ValueTreePropertyWithDefault value; //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSourceWithDefault) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextRemapperValueSourceWithDefault) }; //============================================================================== @@ -172,27 +172,23 @@ TextPropertyComponent::TextPropertyComponent (const Value& valueToControl, const textEditor->getTextValue().referTo (valueToControl); } -TextPropertyComponent::TextPropertyComponent (ValueWithDefault& valueToControl, const String& name, +TextPropertyComponent::TextPropertyComponent (const ValueTreePropertyWithDefault& valueToControl, const String& name, int maxNumChars, bool multiLine, bool isEditable) : TextPropertyComponent (name, maxNumChars, multiLine, isEditable) { - valueWithDefault = &valueToControl; + value = valueToControl; - textEditor->getTextValue().referTo (Value (new RemapperValueSourceWithDefault (valueWithDefault))); - textEditor->setTextToDisplayWhenEmpty (valueWithDefault->getDefault(), 0.5f); + textEditor->getTextValue().referTo (Value (new TextRemapperValueSourceWithDefault (value))); + textEditor->setTextToDisplayWhenEmpty (value.getDefault(), 0.5f); - valueWithDefault->onDefaultChange = [this] + value.onDefaultChange = [this] { - textEditor->setTextToDisplayWhenEmpty (valueWithDefault->getDefault(), 0.5f); + textEditor->setTextToDisplayWhenEmpty (value.getDefault(), 0.5f); repaint(); }; } -TextPropertyComponent::~TextPropertyComponent() -{ - if (valueWithDefault != nullptr) - valueWithDefault->onDefaultChange = nullptr; -} +TextPropertyComponent::~TextPropertyComponent() {} void TextPropertyComponent::setText (const String& newText) { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_TextPropertyComponent.h b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_TextPropertyComponent.h index 437209eb7..2dbfc06ef 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_TextPropertyComponent.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/properties/juce_TextPropertyComponent.h @@ -73,7 +73,7 @@ class JUCE_API TextPropertyComponent : public PropertyComponent /** Creates a text property component with a default value. - @param valueToControl The ValueWithDefault that is controlled by the TextPropertyComponent. + @param valueToControl The ValueTreePropertyWithDefault that is controlled by the TextPropertyComponent. @param propertyName The name of the property @param maxNumChars If not zero, then this specifies the maximum allowable length of the string. If zero, then the string will have no length limit. @@ -82,13 +82,12 @@ class JUCE_API TextPropertyComponent : public PropertyComponent @see TextEditor, setEditable */ - TextPropertyComponent (ValueWithDefault& valueToControl, + TextPropertyComponent (const ValueTreePropertyWithDefault& valueToControl, const String& propertyName, int maxNumChars, bool isMultiLine, bool isEditable = true); - /** Destructor. */ ~TextPropertyComponent() override; //============================================================================== @@ -167,22 +166,19 @@ class JUCE_API TextPropertyComponent : public PropertyComponent virtual void textWasEdited(); private: - class RemapperValueSourceWithDefault; - class LabelComp; - friend class LabelComp; - //============================================================================== void callListeners(); void createEditor (int maxNumChars, bool isEditable); //============================================================================== - bool isMultiLine; + class LabelComp; + const bool isMultiLine; + + ValueTreePropertyWithDefault value; std::unique_ptr textEditor; ListenerList listenerList; - WeakReference valueWithDefault; - //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextPropertyComponent) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp index 763dcb371..9905a1d88 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -567,7 +567,6 @@ class Slider::Pimpl : public AsyncUpdater, // this needs to be public otherwis owner.addAndMakeVisible (valueBox.get()); valueBox->setWantsKeyboardFocus (false); - valueBox->setAccessible (false); valueBox->setText (previousTextBoxContent, dontSendNotification); valueBox->setTooltip (owner.getTooltip()); updateTextBoxEnablement(); From c9e92def497f01cbeca4c5aabd74d85b4dee3102 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Tue, 21 Dec 2021 13:28:28 -0800 Subject: [PATCH 19/30] static analyzer formatting --- src/application/CommandTableModel.cpp | 9 ++++-- src/application/ControlsModel.h | 2 +- src/application/DebugInfo.cpp | 23 ++++++++------- src/application/LR_IPC_In.cpp | 19 +++++++----- src/application/Main.cpp | 2 +- src/application/MidiUtilities.h | 42 +++++++++++++-------------- src/application/SendKeys.cpp | 4 +-- src/application/SendKeys.h | 4 +-- src/application/SettingsComponent.cpp | 4 +-- 9 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/application/CommandTableModel.cpp b/src/application/CommandTableModel.cpp index cb2ce9df8..4e90db771 100644 --- a/src/application/CommandTableModel.cpp +++ b/src/application/CommandTableModel.cpp @@ -56,10 +56,12 @@ void CommandTableModel::paintCell(juce::Graphics& g, int row_number, const int c const auto cmd {profile_.GetMessageForNumber(gsl::narrow_cast(row_number))}; cmd.msg_id_type) { case rsj::MessageType::kNoteOn: - format_str = fmt::format(FMT_STRING("{} | Note : {}"), cmd.channel, cmd.control_number); + format_str = + fmt::format(FMT_STRING("{} | Note : {}"), cmd.channel, cmd.control_number); break; case rsj::MessageType::kNoteOff: - format_str = fmt::format(FMT_STRING("{} | Note Off: {}"), cmd.channel, cmd.control_number); + format_str = + fmt::format(FMT_STRING("{} | Note Off: {}"), cmd.channel, cmd.control_number); break; case rsj::MessageType::kCc: format_str = fmt::format(FMT_STRING("{} | CC: {}"), cmd.channel, cmd.control_number); @@ -68,7 +70,8 @@ void CommandTableModel::paintCell(juce::Graphics& g, int row_number, const int c format_str = fmt::format(FMT_STRING("{} | Pitch Bend"), cmd.channel); break; case rsj::MessageType::kKeyPressure: - format_str = fmt::format(FMT_STRING("{} | Key Pressure: {}"), cmd.channel, cmd.control_number); + format_str = fmt::format( + FMT_STRING("{} | Key Pressure: {}"), cmd.channel, cmd.control_number); break; case rsj::MessageType::kChanPressure: format_str = fmt::format(FMT_STRING("{} | Channel Pressure"), cmd.channel); diff --git a/src/application/ControlsModel.h b/src/application/ControlsModel.h index 0ce94d48e..ef48b0466 100644 --- a/src/application/ControlsModel.h +++ b/src/application/ControlsModel.h @@ -244,7 +244,7 @@ class ControlsModel { return all_controls_.at(channel).GetCcMax(controlnumber); } - int GetCcMin(int channel, int controlnumber) + int GetCcMin(int channel, int controlnumber) const { return all_controls_.at(channel).GetCcMin(controlnumber); } diff --git a/src/application/DebugInfo.cpp b/src/application/DebugInfo.cpp index c547f9042..e0c036c95 100644 --- a/src/application/DebugInfo.cpp +++ b/src/application/DebugInfo.cpp @@ -134,11 +134,12 @@ namespace { if (const auto f = kKeyboardNames.find(klid); f != kKeyboardNames.end()) { return f->second; } - return fmt::format(FMT_STRING("KLID not in keyboard_names: 0x{}."), klid_ascii.data()); + return fmt::format( + FMT_STRING("KLID not in keyboard_names: 0x{}."), klid_ascii.data()); } catch (...) { - auto msg { - fmt::format(FMT_STRING("Exception when finding KLID name. KLID: 0x{}."), klid_ascii.data())}; + auto msg {fmt::format( + FMT_STRING("Exception when finding KLID name. KLID: 0x{}."), klid_ascii.data())}; rsj::Log(msg); return msg; } @@ -160,16 +161,16 @@ DebugInfo::DebugInfo(std::string_view profile_directory) noexcept try { LogAndSave(fmt::format(FMT_STRING("Application: System language {}."), juce::SystemStats::getDisplayLanguage().toStdString())); - LogAndSave( - fmt::format(FMT_STRING("Application: CPU {}."), juce::SystemStats::getCpuModel().toStdString())); + LogAndSave(fmt::format( + FMT_STRING("Application: CPU {}."), juce::SystemStats::getCpuModel().toStdString())); // ReSharper disable CppUnreachableCode if constexpr (kNdebug) { - LogAndSave( - fmt::format(FMT_STRING("Application: Application version {}."), ProjectInfo::versionString)); + LogAndSave(fmt::format( + FMT_STRING("Application: Application version {}."), ProjectInfo::versionString)); } else { - LogAndSave( - fmt::format(FMT_STRING("Application: Application version {}-debug."), ProjectInfo::versionString)); + LogAndSave(fmt::format( + FMT_STRING("Application: Application version {}-debug."), ProjectInfo::versionString)); } // ReSharper restore CppUnreachableCode LogAndSave(fmt::format(FMT_STRING("Application: Application path {}."), @@ -185,8 +186,8 @@ DebugInfo::DebugInfo(std::string_view profile_directory) noexcept /* MacOS defers keyboard layout information until first keystroke sent */ LogAndSave(fmt::format(FMT_STRING("Application: Keyboard type {}."), GetKeyboardLayout())); #endif - LogAndSave( - fmt::format(FMT_STRING("Juce version {}."), juce::SystemStats::getJUCEVersion().toStdString())); + LogAndSave(fmt::format( + FMT_STRING("Juce version {}."), juce::SystemStats::getJUCEVersion().toStdString())); } catch (...) { try { diff --git a/src/application/LR_IPC_In.cpp b/src/application/LR_IPC_In.cpp index bc4ea78b5..2c2408211 100644 --- a/src/application/LR_IPC_In.cpp +++ b/src/application/LR_IPC_In.cpp @@ -79,7 +79,9 @@ void LrIpcIn::Stop() ec.clear(); } socket_.close(ec); - if (ec) { rsj::Log(fmt::format(FMT_STRING("LR_IPC_In socket close error {}."), ec.message())); } + if (ec) { + rsj::Log(fmt::format(FMT_STRING("LR_IPC_In socket close error {}."), ec.message())); + } } #ifdef __cpp_lib_semaphore read_running_.acquire(); @@ -116,11 +118,13 @@ void LrIpcIn::Connect() Read(); } else { - rsj::Log(fmt::format(FMT_STRING("LR_IPC_In did not connect. {}."), error.message())); + rsj::Log( + fmt::format(FMT_STRING("LR_IPC_In did not connect. {}."), error.message())); asio::error_code ec2; socket_.close(ec2); if (ec2) { - rsj::Log(fmt::format(FMT_STRING("LR_IPC_In socket close error {}."), ec2.message())); + rsj::Log( + fmt::format(FMT_STRING("LR_IPC_In socket close error {}."), ec2.message())); } } }); @@ -156,7 +160,8 @@ void LrIpcIn::ProcessLine() return; } if (value_view.empty()) { - rsj::Log(fmt::format(FMT_STRING("No value attached to message. Message from plugin was \"{}\"."), + rsj::Log(fmt::format( + FMT_STRING("No value attached to message. Message from plugin was \"{}\"."), rsj::ReplaceInvisibleChars(line_copy))); } else if (command == "SwitchProfile") { @@ -177,9 +182,9 @@ void LrIpcIn::ProcessLine() continue; /* skip log and alert error */ } } - rsj::LogAndAlertError( - fmt::format(FMT_STRING("SendKey couldn't identify keystroke. Message from plugin was \"{}\"."), - rsj::ReplaceInvisibleChars(line_copy))); + rsj::LogAndAlertError(fmt::format( + FMT_STRING("SendKey couldn't identify keystroke. Message from plugin was \"{}\"."), + rsj::ReplaceInvisibleChars(line_copy))); } else { /* send associated messages to MIDI OUT devices */ const auto original_value {std::stod(std::string(value_view))}; diff --git a/src/application/Main.cpp b/src/application/Main.cpp index e93c0ca6f..e0584c2c5 100644 --- a/src/application/Main.cpp +++ b/src/application/Main.cpp @@ -130,7 +130,7 @@ namespace { private: std::unique_ptr logger_ {juce::FileLogger::createDefaultAppLogger( - "MIDI2LR", "MIDI2LR.log", "", 32 * 1024)}; //-V112 + "MIDI2LR", "MIDI2LR.log", "", 32LL * 1024LL)}; //-V112 }; [[noreturn]] void OnTerminate() noexcept diff --git a/src/application/MidiUtilities.h b/src/application/MidiUtilities.h index 73696251f..280b3c1bb 100644 --- a/src/application/MidiUtilities.h +++ b/src/application/MidiUtilities.h @@ -92,30 +92,28 @@ namespace rsj { } } // namespace rsj -namespace fmt { - template struct formatter { - template constexpr auto parse(ParseContext& ctx) - { /* parsing copied from fmt's chrono.h */ - auto it {ctx.begin()}; - if (!it) { return ctx.end(); } - if (it != ctx.end() && *it == ':') { std::advance(it, 1); } - auto end {std::find(it, ctx.end(), '}')}; - tm_format_.reserve(detail::to_unsigned(end - it + 1)); - tm_format_.append(it, end); - tm_format_.push_back('\0'); - return end; - } +template struct fmt::formatter { + template constexpr auto parse(ParseContext& ctx) + { /* parsing copied from fmt's chrono.h */ + auto it {ctx.begin()}; + if (!it) { return ctx.end(); } + if (it != ctx.end() && *it == ':') { std::advance(it, 1); } + auto end {std::find(it, ctx.end(), '}')}; + tm_format_.reserve(detail::to_unsigned(end - it + 1)); + tm_format_.append(it, end); + tm_format_.push_back('\0'); + return end; + } - template auto format(const rsj::MessageType& p, FormatContext& ctx) - { - if (tm_format_[0] == 'n') { return format_to(ctx.out(), "{}", rsj::MessageTypeToName(p)); } - return format_to(ctx.out(), "{}", rsj::MessageTypeToLabel(p)); - } + template auto format(const rsj::MessageType& p, FormatContext& ctx) + { + if (tm_format_[0] == 'n') { return format_to(ctx.out(), "{}", rsj::MessageTypeToName(p)); } + return format_to(ctx.out(), "{}", rsj::MessageTypeToLabel(p)); + } - private: - basic_memory_buffer tm_format_; - }; -} // namespace fmt + private: + basic_memory_buffer tm_format_; +}; // namespace fmt /*****************************************************************************/ /*************MidiMessage*****************************************************/ diff --git a/src/application/SendKeys.cpp b/src/application/SendKeys.cpp index b9b113156..92649ae81 100644 --- a/src/application/SendKeys.cpp +++ b/src/application/SendKeys.cpp @@ -16,7 +16,7 @@ #include "SendKeys.h" namespace rsj { - ActiveModifiers ActiveModifiers::FromWindows(const int from) noexcept + constexpr ActiveModifiers ActiveModifiers::FromWindows(const int from) noexcept { /* shift coded as follows: 1: shift, 2: ctrl, 4: alt, 8: hankaku */ ActiveModifiers am {}; @@ -27,7 +27,7 @@ namespace rsj { return am; } - ActiveModifiers ActiveModifiers::FromMidi2LR(const int from) noexcept + constexpr ActiveModifiers ActiveModifiers::FromMidi2LR(const int from) noexcept { ActiveModifiers am {}; am.alt_opt = from & 1; diff --git a/src/application/SendKeys.h b/src/application/SendKeys.h index 7e2485c8a..b7b93807a 100644 --- a/src/application/SendKeys.h +++ b/src/application/SendKeys.h @@ -25,8 +25,8 @@ namespace rsj { bool shift {false}; bool hankaku {false}; - static ActiveModifiers FromWindows(int from) noexcept; - static ActiveModifiers FromMidi2LR(int from) noexcept; + [[nodiscard]] static constexpr ActiveModifiers FromWindows(int from) noexcept; + [[nodiscard]] static constexpr ActiveModifiers FromMidi2LR(int from) noexcept; }; void SendKeyDownUp(const std::string& key, const rsj::ActiveModifiers& mods) noexcept; diff --git a/src/application/SettingsComponent.cpp b/src/application/SettingsComponent.cpp index e361ef436..80b71e71e 100644 --- a/src/application/SettingsComponent.cpp +++ b/src/application/SettingsComponent.cpp @@ -116,8 +116,8 @@ void SettingsComponent::Init() autohide_setting_.setBounds(kSettingsLeft, 245, kSettingsWidth - 2 * kSettingsLeft, 50); autohide_setting_.setRange(0., 10., 1.); - autohide_setting_.setValue(static_cast(settings_manager_.GetAutoHideTime()), - juce::NotificationType::dontSendNotification); + autohide_setting_.setValue( + settings_manager_.GetAutoHideTime(), juce::NotificationType::dontSendNotification); addToLayout(&autohide_setting_, anchorMidLeft, anchorMidRight); addAndMakeVisible(autohide_setting_); From 8ce66f13d53ff3e7f6f7b1e4217eb47b6e08c451 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sat, 25 Dec 2021 15:04:39 -0800 Subject: [PATCH 20/30] add option to save diagnostic log show log file --- src/plugin/Info.lua | 4 ++++ src/plugin/LogSave.lua | 30 ++++++++++++++++++++++++++++++ tools/installer/MIDI2LR.xml | 3 +++ 3 files changed, 37 insertions(+) create mode 100644 src/plugin/LogSave.lua diff --git a/src/plugin/Info.lua b/src/plugin/Info.lua index c2f3e1787..ec87fb2a8 100644 --- a/src/plugin/Info.lua +++ b/src/plugin/Info.lua @@ -90,6 +90,10 @@ return { title = LOC("$$$/AgWPG/Dialogs/About/Title=About"), file = "About.lua", }, + { + title = LOC("$$$/AgPreferences/Sync/GenerateFullDiagnosticLog=Generate diagnostic report"), + file = "LogSave.lua", + }, }, VERSION = { major=5, minor=2, revision=0, build=0} } diff --git a/src/plugin/LogSave.lua b/src/plugin/LogSave.lua new file mode 100644 index 000000000..10ed6d71f --- /dev/null +++ b/src/plugin/LogSave.lua @@ -0,0 +1,30 @@ +--[[ + +LogSave.lua +Saves diagnostic log to a file + +This file is part of MIDI2LR. Copyright 2015 by Rory Jaffe. + +MIDI2LR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later version. + +MIDI2LR is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +MIDI2LR. If not, see . +------------------------------------------------------------------------------]] + +local LrFileUtils = import 'LrFileUtils' +local LrPathUtils = import 'LrPathUtils' +local LrShell = import 'LrShell' +local Ut = require 'Utilities' + +local logfile = LrPathUtils.child(Ut.applogpath(), 'MIDI2LR.log') +if LrFileUtils.exists(logfile) then + LrShell.revealInShell(logfile) +else + LrShell.revealInShell(LrPathUtils.child(Ut.applogpath(), 'MIDI2LRinfo.txt')) +end \ No newline at end of file diff --git a/tools/installer/MIDI2LR.xml b/tools/installer/MIDI2LR.xml index 382d10ad1..d524f3ab6 100644 --- a/tools/installer/MIDI2LR.xml +++ b/tools/installer/MIDI2LR.xml @@ -90,6 +90,9 @@ ../../src/plugin/LocalPresets.lua + + ../../src/plugin/LogSave.lua + ../../src/plugin/Mask.lua From 3af80a7ea26a0adcddfeea2052c4b3ac1c31fe1a Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sat, 25 Dec 2021 14:54:55 -0800 Subject: [PATCH 21/30] update default menutrans --- data/plugin/MenuTrans.xml | 1062 ++++++++++++++++++++++--------------- 1 file changed, 629 insertions(+), 433 deletions(-) diff --git a/data/plugin/MenuTrans.xml b/data/plugin/MenuTrans.xml index 45df8a4e1..e996837ec 100644 --- a/data/plugin/MenuTrans.xml +++ b/data/plugin/MenuTrans.xml @@ -615,221 +615,297 @@ Quick develop - QuickDevExpLarge - Quick develop Increase exposure 1 stop + QuickDevTempLarge + Quick develop Increase Temperature: 20 - QuickDevExpLargeDec - Quick develop Decrease exposure 1 stop + QuickDevTempLargeDec + Quick develop Decrease Temperature: -20 - QuickDevExpSmall - Quick develop Increase exposure 1/3 stop + QuickDevTempSmall + Quick develop Increase Temperature: 5 - QuickDevExpSmallDec - Quick develop Decrease exposure 1/3 stop + QuickDevTempSmalDec + Quick develop Decrease Temperature: -5 - QuickDevContrastLarge - Quick develop Increase contrast 20 + QuickDevTempAdj + Quick develop Temperature - QuickDevContrastLargeDec - Quick develop Decrease contrast -20 + QuickDevTintLarge + Quick develop Increase Tint: 20 - QuickDevContrastSmall - Quick develop Increase contrast 5 + QuickDevTintLargeDec + Quick develop Decrease Tint: -20 - QuickDevContrastSmallDec - Quick develop Decrease contrast -5 + QuickDevTintSmall + Quick develop Increase Tint: 5 - QuickDevHighlightsLarge - Quick develop Increase highlights 20 + QuickDevTintSmallDec + Quick develop Decrease Tint: -5 - QuickDevHighlightsLargeDec - Quick develop Decrease highlights -20 + QuickDevTintAdj + Quick develop Tint - QuickDevHighlightsSmall - Quick develop Increase highlights 5 + QuickDevExpLarge + Quick develop Increase exposure 1 stop - QuickDevHighlightsSmallDec - Quick develop Decrease highlights -5 + QuickDevExpLargeDec + Quick develop Decrease exposure 1 stop - QuickDevShadowsLarge - Quick develop Decrease shadows 20 + QuickDevExpSmall + Quick develop Increase exposure 1/3 stop - QuickDevShadowsLargeDec - Quick develop Increase shadows -20 + QuickDevExpSmallDec + Quick develop Decrease exposure 1/3 stop - QuickDevShadowsSmall - Quick develop Decrease shadows 5 + QuickDevExpAdj + Quick develop Exposure - QuickDevShadowsSmallDec - Quick develop Increase shadows -5 + QuickDevContrastLarge + Quick develop Increase contrast 20 - QuickDevWhitesLarge - Quick develop Increase white clipping 20 + QuickDevContrastLargeDec + Quick develop Decrease contrast -20 - QuickDevWhitesLargeDec - Quick develop Decrease white clipping -20 + QuickDevContrastSmall + Quick develop Increase contrast 5 - QuickDevWhitesSmall - Quick develop Increase white clipping 5 + QuickDevContrastSmallDec + Quick develop Decrease contrast -5 - QuickDevWhitesSmallDec - Quick develop Decrease white clipping -5 + QuickDevContrastAdj + Quick develop Contrast - QuickDevBlacksLarge - Quick develop Decrease black clipping 20 + QuickDevHighlightsLarge + Quick develop Increase highlights 20 - QuickDevBlacksLargeDec - Quick develop Increase black clipping -20 + QuickDevHighlightsLargeDec + Quick develop Decrease highlights -20 - QuickDevBlacksSmall - Quick develop Decrease black clipping 5 + QuickDevHighlightsSmall + Quick develop Increase highlights 5 - QuickDevBlacksSmallDec - Quick develop Increase black clipping -5 + QuickDevHighlightsSmallDec + Quick develop Decrease highlights -5 - QuickDevClarityLarge - Quick develop Increase clarity 20 + QuickDevHighlightsAdj + Quick develop Highlights - QuickDevClarityLargeDec - Quick develop Decrease clarity -20 + QuickDevShadowsLarge + Quick develop Decrease shadows 20 - QuickDevClaritySmall - Quick develop Increase clarity 5 + QuickDevShadowsLargeDec + Quick develop Increase shadows -20 - QuickDevClaritySmallDec - Quick develop Decrease clarity -5 + QuickDevShadowsSmall + Quick develop Decrease shadows 5 - QuickDevVibranceLarge - Quick develop Increase vibrance 20 + QuickDevShadowsSmallDec + Quick develop Increase shadows -5 - QuickDevVibranceLargeDec - Quick develop Decrease vibrance -20 + QuickDevShadowsAdj + Quick develop Shadows - QuickDevVibranceSmall - Quick develop Increase vibrance 5 + QuickDevWhitesLarge + Quick develop Increase white clipping 20 - QuickDevVibranceSmallDec - Quick develop Decrease vibrance -5 + QuickDevWhitesLargeDec + Quick develop Decrease white clipping -20 - QuickDevSatLarge - Quick develop Increase Saturation: 20 + QuickDevWhitesSmall + Quick develop Increase white clipping 5 - QuickDevSatLargeDec - Quick develop Decrease Saturation: -20 + QuickDevWhitesSmallDec + Quick develop Decrease white clipping -5 - QuickDevSatSmall - Quick develop Increase Saturation: 5 + QuickDevWhitesAdj + Quick develop Whites - QuickDevSatSmallDec - Quick develop Decrease Saturation: -5 + QuickDevBlacksLarge + Quick develop Decrease black clipping 20 - QuickDevWBAuto - Quick develop White Balance Auto + QuickDevBlacksLargeDec + Quick develop Increase black clipping -20 - QuickDevWBDaylight - Quick develop White Balance Daylight + QuickDevBlacksSmall + Quick develop Decrease black clipping 5 - QuickDevWBCloudy - Quick develop White Balance Cloudy + QuickDevBlacksSmallDec + Quick develop Increase black clipping -5 - QuickDevWBShade - Quick develop White Balance Shade + QuickDevBlacksAdj + Quick develop Blacks - QuickDevWBTungsten - Quick develop White Balance Tungsten + QuickDevClarityLarge + Quick develop Increase clarity 20 - QuickDevWBFluorescent - Quick develop White Balance Fluorescent + QuickDevClarityLargeDec + Quick develop Decrease clarity -20 - QuickDevWBFlash - Quick develop White Balance Flash + QuickDevClaritySmall + Quick develop Increase clarity 5 - SetTreatmentBW - Quick develop Set treatment B&W + QuickDevClaritySmallDec + Quick develop Decrease clarity -5 - SetTreatmentColor - Quick develop Set treatment Color + QuickDevClarityAdj + Quick develop Clarity - QuickDevCropAspectOriginal - Quick develop Set crop ratio: Original + QuickDevVibranceLarge + Quick develop Increase vibrance 20 - QuickDevCropAspectAsShot - Quick develop Set crop ratio: As shot + QuickDevVibranceLargeDec + Quick develop Decrease vibrance -20 - QuickDevCropAspect1x1 - Quick develop Set crop ratio: 1 x 1 + QuickDevVibranceSmall + Quick develop Increase vibrance 5 - QuickDevCropAspect2x3 - Quick develop Set crop ratio: 2 x 3 + QuickDevVibranceSmallDec + Quick develop Decrease vibrance -5 - QuickDevCropAspect3x4 - Quick develop Set crop ratio: 3 x 4 + QuickDevVibranceAdj + Quick develop Vibrance - QuickDevCropAspect4x5 - Quick develop Set crop ratio: 4 x 5 + QuickDevSatLarge + Quick develop Increase Saturation: 20 - QuickDevCropAspect5x7 - Quick develop Set crop ratio: 5 x 7 + QuickDevSatLargeDec + Quick develop Decrease Saturation: -20 - QuickDevCropAspect85x11 - Quick develop Set crop ratio: 8.5 x 11 + QuickDevSatSmall + Quick develop Increase Saturation: 5 + QuickDevSatSmallDec + Quick develop Decrease Saturation: -5 + + + QuickDevSatAdj + Quick develop Saturation + + + QuickDevWBAuto + Quick develop White Balance Auto + + + QuickDevWBDaylight + Quick develop White Balance Daylight + + + QuickDevWBCloudy + Quick develop White Balance Cloudy + + + QuickDevWBShade + Quick develop White Balance Shade + + + QuickDevWBTungsten + Quick develop White Balance Tungsten + + + QuickDevWBFluorescent + Quick develop White Balance Fluorescent + + + QuickDevWBFlash + Quick develop White Balance Flash + + + SetTreatmentBW + Quick develop Set treatment B&W + + + SetTreatmentColor + Quick develop Set treatment Color + + + QuickDevCropAspectOriginal + Quick develop Set crop ratio: Original + + + QuickDevCropAspectAsShot + Quick develop Set crop ratio: As shot + + + QuickDevCropAspect1x1 + Quick develop Set crop ratio: 1 x 1 + + + QuickDevCropAspect2x3 + Quick develop Set crop ratio: 2 x 3 + + + QuickDevCropAspect3x4 + Quick develop Set crop ratio: 3 x 4 + + + QuickDevCropAspect4x5 + Quick develop Set crop ratio: 4 x 5 + + + QuickDevCropAspect5x7 + Quick develop Set crop ratio: 5 x 7 + + + QuickDevCropAspect85x11 + Quick develop Set crop ratio: 8.5 x 11 + + QuickDevCropAspect9x16 Quick develop Set crop ratio: 9 x 16 - + @@ -1771,93 +1847,89 @@ Lens Profile Vignetting Scale - CropConstrainToWarp - Constrain to Warp - - ResetLensProfileDistortionScale Reset Lens Profile Distortion Scale - - + + ResetLensProfileChromaticAberrationScale Reset chromatic Aberration — Scale - - + + ResetLensProfileVignettingScale Reset Lens Profile Vignetting Scale - - + + DefringePurpleAmount Defringe Purple Amount - - + + DefringePurpleHueLo Defringe Purple Hue - Low - - + + DefringePurpleHueHi Defringe Purple Hue - High - - + + DefringeGreenAmount Defringe Green Amount - - + + DefringeGreenHueLo Defringe Green Hue - Low - - + + DefringeGreenHueHi Defringe Green Hue - High - - + + ResetDefringePurpleAmount Reset Defringe Purple Amount - - + + ResetDefringePurpleHueLo Reset Defringe Purple Hue - Low - - + + ResetDefringePurpleHueHi Reset Defringe Purple Hue - High - - + + ResetDefringeGreenAmount Reset Defringe Green Amount - - + + ResetDefringeGreenHueLo Reset Defringe Green Hue - Low - - + + ResetDefringeGreenHueHi Reset Defringe Green Hue - High - - + + LensManualDistortionAmount Lens Manual Distortion Amount - - + + VignetteAmount Vignette Amount - - + + VignetteMidpoint Vignette Midpoint - - + + ResetLensManualDistortionAmount Reset Lens Manual Distortion Amount - - + + ResetVignetteAmount Reset Vignette Amount - - + + ResetVignetteMidpoint Reset Vignette Midpoint - + @@ -1896,69 +1968,73 @@ Perspective Correction Full - ResetPerspectiveUpright - Reset Upright Settings + CropConstrainToWarp + Constrain to Warp - PerspectiveVertical - Perspective Vertical + ResetPerspectiveUpright + Reset Upright Settings - PerspectiveHorizontal - Perspective Horizontal + PerspectiveVertical + Perspective Vertical - PerspectiveRotate - Perspective Rotate + PerspectiveHorizontal + Perspective Horizontal - PerspectiveScale - Perspective Scale + PerspectiveRotate + Perspective Rotate - PerspectiveAspect - Perspective Aspect + PerspectiveScale + Perspective Scale - PerspectiveX - Perspective X + PerspectiveAspect + Perspective Aspect - PerspectiveY - Perspective Y + PerspectiveX + Perspective X - ResetTransforms - Reset all transforms + PerspectiveY + Perspective Y - ResetPerspectiveVertical - Reset Perspective Vertical + ResetTransforms + Reset all transforms - ResetPerspectiveHorizontal - Reset Perspective Horizontal + ResetPerspectiveVertical + Reset Perspective Vertical - ResetPerspectiveRotate - Reset Perspective Rotate + ResetPerspectiveHorizontal + Reset Perspective Horizontal - ResetPerspectiveScale - Reset Perspective Scale + ResetPerspectiveRotate + Reset Perspective Rotate - ResetPerspectiveAspect - Reset Perspective Aspect + ResetPerspectiveScale + Reset Perspective Scale - ResetPerspectiveX - Reset Perspective X + ResetPerspectiveAspect + Reset Perspective Aspect + ResetPerspectiveX + Reset Perspective X + + ResetPerspectiveY Reset Perspective Y - + @@ -2135,325 +2211,337 @@ Develop Presets - Preset_1 - Develop Preset 1 + PresetNext + Next preset - Preset_2 - Develop Preset 2 + PresetPrevious + Previous preset - Preset_3 - Develop Preset 3 + PresetPreviousNext + Previous preset—Next preset - Preset_4 - Develop Preset 4 + Preset_1 + Develop Preset 1 - Preset_5 - Develop Preset 5 + Preset_2 + Develop Preset 2 - Preset_6 - Develop Preset 6 + Preset_3 + Develop Preset 3 - Preset_7 - Develop Preset 7 + Preset_4 + Develop Preset 4 - Preset_8 - Develop Preset 8 + Preset_5 + Develop Preset 5 - Preset_9 - Develop Preset 9 + Preset_6 + Develop Preset 6 - Preset_10 - Develop Preset 10 + Preset_7 + Develop Preset 7 - Preset_11 - Develop Preset 11 + Preset_8 + Develop Preset 8 - Preset_12 - Develop Preset 12 + Preset_9 + Develop Preset 9 - Preset_13 - Develop Preset 13 + Preset_10 + Develop Preset 10 - Preset_14 - Develop Preset 14 + Preset_11 + Develop Preset 11 - Preset_15 - Develop Preset 15 + Preset_12 + Develop Preset 12 - Preset_16 - Develop Preset 16 + Preset_13 + Develop Preset 13 - Preset_17 - Develop Preset 17 + Preset_14 + Develop Preset 14 - Preset_18 - Develop Preset 18 + Preset_15 + Develop Preset 15 - Preset_19 - Develop Preset 19 + Preset_16 + Develop Preset 16 - Preset_20 - Develop Preset 20 + Preset_17 + Develop Preset 17 - Preset_21 - Develop Preset 21 + Preset_18 + Develop Preset 18 - Preset_22 - Develop Preset 22 + Preset_19 + Develop Preset 19 - Preset_23 - Develop Preset 23 + Preset_20 + Develop Preset 20 - Preset_24 - Develop Preset 24 + Preset_21 + Develop Preset 21 - Preset_25 - Develop Preset 25 + Preset_22 + Develop Preset 22 - Preset_26 - Develop Preset 26 + Preset_23 + Develop Preset 23 - Preset_27 - Develop Preset 27 + Preset_24 + Develop Preset 24 - Preset_28 - Develop Preset 28 + Preset_25 + Develop Preset 25 - Preset_29 - Develop Preset 29 + Preset_26 + Develop Preset 26 - Preset_30 - Develop Preset 30 + Preset_27 + Develop Preset 27 - Preset_31 - Develop Preset 31 + Preset_28 + Develop Preset 28 - Preset_32 - Develop Preset 32 + Preset_29 + Develop Preset 29 - Preset_33 - Develop Preset 33 + Preset_30 + Develop Preset 30 - Preset_34 - Develop Preset 34 + Preset_31 + Develop Preset 31 - Preset_35 - Develop Preset 35 + Preset_32 + Develop Preset 32 - Preset_36 - Develop Preset 36 + Preset_33 + Develop Preset 33 - Preset_37 - Develop Preset 37 + Preset_34 + Develop Preset 34 - Preset_38 - Develop Preset 38 + Preset_35 + Develop Preset 35 - Preset_39 - Develop Preset 39 + Preset_36 + Develop Preset 36 - Preset_40 - Develop Preset 40 + Preset_37 + Develop Preset 37 - Preset_41 - Develop Preset 41 + Preset_38 + Develop Preset 38 - Preset_42 - Develop Preset 42 + Preset_39 + Develop Preset 39 - Preset_43 - Develop Preset 43 + Preset_40 + Develop Preset 40 - Preset_44 - Develop Preset 44 + Preset_41 + Develop Preset 41 - Preset_45 - Develop Preset 45 + Preset_42 + Develop Preset 42 - Preset_46 - Develop Preset 46 + Preset_43 + Develop Preset 43 - Preset_47 - Develop Preset 47 + Preset_44 + Develop Preset 44 - Preset_48 - Develop Preset 48 + Preset_45 + Develop Preset 45 - Preset_49 - Develop Preset 49 + Preset_46 + Develop Preset 46 - Preset_50 - Develop Preset 50 + Preset_47 + Develop Preset 47 - Preset_51 - Develop Preset 51 + Preset_48 + Develop Preset 48 - Preset_52 - Develop Preset 52 + Preset_49 + Develop Preset 49 - Preset_53 - Develop Preset 53 + Preset_50 + Develop Preset 50 - Preset_54 - Develop Preset 54 + Preset_51 + Develop Preset 51 - Preset_55 - Develop Preset 55 + Preset_52 + Develop Preset 52 - Preset_56 - Develop Preset 56 + Preset_53 + Develop Preset 53 - Preset_57 - Develop Preset 57 + Preset_54 + Develop Preset 54 - Preset_58 - Develop Preset 58 + Preset_55 + Develop Preset 55 - Preset_59 - Develop Preset 59 + Preset_56 + Develop Preset 56 - Preset_60 - Develop Preset 60 + Preset_57 + Develop Preset 57 - Preset_61 - Develop Preset 61 + Preset_58 + Develop Preset 58 - Preset_62 - Develop Preset 62 + Preset_59 + Develop Preset 59 - Preset_63 - Develop Preset 63 + Preset_60 + Develop Preset 60 - Preset_64 - Develop Preset 64 + Preset_61 + Develop Preset 61 - Preset_65 - Develop Preset 65 + Preset_62 + Develop Preset 62 - Preset_66 - Develop Preset 66 + Preset_63 + Develop Preset 63 - Preset_67 - Develop Preset 67 + Preset_64 + Develop Preset 64 - Preset_68 - Develop Preset 68 + Preset_65 + Develop Preset 65 - Preset_69 - Develop Preset 69 + Preset_66 + Develop Preset 66 - Preset_70 - Develop Preset 70 + Preset_67 + Develop Preset 67 - Preset_71 - Develop Preset 71 + Preset_68 + Develop Preset 68 + Preset_69 + Develop Preset 69 + + + Preset_70 + Develop Preset 70 + + + Preset_71 + Develop Preset 71 + + Preset_72 Develop Preset 72 - - + + Preset_73 Develop Preset 73 - - + + Preset_74 Develop Preset 74 - - + + Preset_75 Develop Preset 75 - - + + Preset_76 Develop Preset 76 - - + + Preset_77 Develop Preset 77 - - + + Preset_78 Develop Preset 78 - - + + Preset_79 Develop Preset 79 - - + + Preset_80 Develop Preset 80 - + @@ -3829,278 +3917,386 @@ - Key2Key1 + Key28Key27 - Key2 1 + Key28 1 - Key1 1 + Key27 1 - ZoomInOut + Key12Key11 - ZoomInSmallStep 1 + Key12 1 - ZoomOutSmallStep 1 + Key11 1 - Key28Key27 + QuickDevExpAdj - Key28 1 + QuickDevExpSmall 1 - Key27 1 + QuickDevExpSmallDec 1 - ChangeFeatherSize + Key14Key13 - BrushFeatherLarger 1 + Key14 1 - BrushFeatherSmaller 1 + Key13 1 - Key12Key11 + ZoomOutIn - Key12 1 + ZoomOutSmallStep 1 - Key11 1 + ZoomInSmallStep 1 - ChangeBrushSize + Key40Key39 - BrushSizeLarger 1 + Key40 1 - BrushSizeSmaller 1 + Key39 1 - Key24Key23 + SelectRightLeft - Key24 1 + Select1Right 1 - Key23 1 + Select1Left 1 - RedoUndo + QuickDevShadowsAdj - Redo 1 + QuickDevShadowsSmall 1 - Undo 1 + QuickDevShadowsSmallDec 1 - Key26Key25 + NextPrev - Key26 1 + Next 1 - Key25 1 + Prev 1 - ChangeCurrentSlider + QuickDevHighlightsAdj - SliderIncrease 1 + QuickDevHighlightsSmall 1 - SliderDecrease 1 + QuickDevHighlightsSmallDec 1 - Key14Key13 + Key18Key17 - Key14 1 + Key18 1 - Key13 1 + Key17 1 - Key32Key31 + QuickDevClarityAdj - Key32 1 + QuickDevClaritySmall 1 - Key31 1 + QuickDevClaritySmallDec 1 - ZoomOutIn + Key22Key21 - ZoomOutSmallStep 1 + Key22 1 - ZoomInSmallStep 1 + Key21 1 - IncreaseDecreaseRating + Key4Key3 - IncreaseRating 1 + Key4 1 - DecreaseRating 1 + Key3 1 - Key40Key39 + Key32Key31 - Key40 1 + Key32 1 - Key39 1 + Key31 1 - Key6Key5 + ZoomInOut - Key6 1 + ZoomInSmallStep 1 - Key5 1 + ZoomOutSmallStep 1 - Key38Key37 + Key20Key19 - Key38 1 + Key20 1 - Key37 1 + Key19 1 - SelectRightLeft + ChangeFeatherSize - Select1Right 1 + BrushFeatherLarger 1 - Select1Left 1 + BrushFeatherSmaller 1 - Key10Key9 + Key24Key23 - Key10 1 + Key24 1 - Key9 1 + Key23 1 - Key34Key33 + QuickDevTintAdj - Key34 1 + QuickDevTintSmall 1 - Key33 1 + QuickDevTintSmallDec 1 - NextPrev + Key26Key25 - Next 1 + Key26 1 - Prev 1 + Key25 1 - Key16Key15 + PresetPreviousNext - Key16 1 + PresetNext 1 - Key15 1 + PresetPrevious 1 - Key36Key35 + RedoUndo - Key36 1 + Redo 1 - Key35 1 + Undo 1 - Key18Key17 + ChangeCurrentSlider - Key18 1 + SliderIncrease 1 - Key17 1 + SliderDecrease 1 - ChangeLastDevelopParameter + QuickDevWhitesAdj - IncrementLastDevelopParameter 1 + QuickDevWhitesSmall 1 - DecrementLastDevelopParameter 1 + QuickDevWhitesSmallDec 1 - Key30Key29 + IncreaseDecreaseRating - Key30 1 + IncreaseRating 1 - Key29 1 + DecreaseRating 1 - Key22Key21 + ChangeLastDevelopParameter - Key22 1 + IncrementLastDevelopParameter 1 - Key21 1 + DecrementLastDevelopParameter 1 - Key8Key7 + Key6Key5 - Key8 1 + Key6 1 - Key7 1 + Key5 1 - Key20Key19 + QuickDevSatAdj - Key20 1 + QuickDevSatSmall 1 - Key19 1 + QuickDevSatSmallDec 1 - Key4Key3 + QuickDevVibranceAdj - Key4 1 + QuickDevVibranceSmall 1 - Key3 1 + QuickDevVibranceSmallDec 1 + + Key10Key9 + + Key10 1 + + Key9 1 + + + + + Key34Key33 + + Key34 1 + + Key33 1 + + + + + QuickDevContrastAdj + + QuickDevContrastSmall 1 + + QuickDevContrastSmallDec 1 + + + + + Key16Key15 + + Key16 1 + + Key15 1 + + + + + Key2Key1 + + Key2 1 + + Key1 1 + + + + + Key38Key37 + + Key38 1 + + Key37 1 + + + + + QuickDevBlacksAdj + + QuickDevBlacksSmall 1 + + QuickDevBlacksSmallDec 1 + + + + + Key30Key29 + + Key30 1 + + Key29 1 + + + + + ChangeBrushSize + + BrushSizeLarger 1 + + BrushSizeSmaller 1 + + + + + Key8Key7 + + Key8 1 + + Key7 1 + + + + + QuickDevTempAdj + + QuickDevTempSmall 1 + + QuickDevTempSmalDec 1 + + + + + Key36Key35 + + Key36 1 + + Key35 1 + + + - ColorGradeGlobalHue + ColorGradeGlobalHue SplitToningHighlightHue ColorGradeMidtoneHue SplitToningShadowHue From b29caaf389ae7a252e637530f8fff631a977b1cd Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sat, 25 Dec 2021 15:20:40 -0800 Subject: [PATCH 22/30] fix appveyor error, revert changes to SendKeys --- src/application/SendKeys.cpp | 4 ++-- src/application/SendKeys.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/application/SendKeys.cpp b/src/application/SendKeys.cpp index 92649ae81..b9b113156 100644 --- a/src/application/SendKeys.cpp +++ b/src/application/SendKeys.cpp @@ -16,7 +16,7 @@ #include "SendKeys.h" namespace rsj { - constexpr ActiveModifiers ActiveModifiers::FromWindows(const int from) noexcept + ActiveModifiers ActiveModifiers::FromWindows(const int from) noexcept { /* shift coded as follows: 1: shift, 2: ctrl, 4: alt, 8: hankaku */ ActiveModifiers am {}; @@ -27,7 +27,7 @@ namespace rsj { return am; } - constexpr ActiveModifiers ActiveModifiers::FromMidi2LR(const int from) noexcept + ActiveModifiers ActiveModifiers::FromMidi2LR(const int from) noexcept { ActiveModifiers am {}; am.alt_opt = from & 1; diff --git a/src/application/SendKeys.h b/src/application/SendKeys.h index b7b93807a..7e2485c8a 100644 --- a/src/application/SendKeys.h +++ b/src/application/SendKeys.h @@ -25,8 +25,8 @@ namespace rsj { bool shift {false}; bool hankaku {false}; - [[nodiscard]] static constexpr ActiveModifiers FromWindows(int from) noexcept; - [[nodiscard]] static constexpr ActiveModifiers FromMidi2LR(int from) noexcept; + static ActiveModifiers FromWindows(int from) noexcept; + static ActiveModifiers FromMidi2LR(int from) noexcept; }; void SendKeyDownUp(const std::string& key, const rsj::ActiveModifiers& mods) noexcept; From 83546d6f2b5917546f2a366775f9df6419354d0d Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Thu, 30 Dec 2021 14:47:23 -0800 Subject: [PATCH 23/30] clean up variable naming --- src/application/SendKeysMac.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/application/SendKeysMac.cpp b/src/application/SendKeysMac.cpp index b268b436d..f3098afec 100644 --- a/src/application/SendKeysMac.cpp +++ b/src/application/SendKeysMac.cpp @@ -266,7 +266,7 @@ namespace { {"numpad multiply", kVK_ANSI_KeypadMultiply}, {"numpad divide", kVK_ANSI_KeypadDivide}, {"numpad decimal", kVK_ANSI_KeypadDecimal}}; - const std::unordered_map kNonASCIIKeyMap {{"-", kVK_ANSI_Minus}, + const std::unordered_map kANSIKeyMap {{"-", kVK_ANSI_Minus}, {",", kVK_ANSI_Comma}, {".", kVK_ANSI_Period}, {"/", kVK_ANSI_Slash}, {";", kVK_ANSI_Semicolon}, {"[", kVK_ANSI_LeftBracket}, {"'", kVK_ANSI_Quote}, {"\\", kVK_ANSI_Backslash}, {"]", kVK_ANSI_RightBracket}, {"`", kVK_ANSI_Grave}, @@ -281,8 +281,8 @@ namespace { {"v", kVK_ANSI_V}, {"w", kVK_ANSI_W}, {"x", kVK_ANSI_X}, {"y", kVK_ANSI_Y}, {"z", kVK_ANSI_Z}}; - const std::unordered_map kNonASCIIKeyMapShifted { - {"_", kVK_ANSI_Minus}, {"<", kVK_ANSI_Comma}, {">", kVK_ANSI_Period}, {"?", kVK_ANSI_Slash}, + const std::unordered_map kANSIKeyMapShifted {{"_", kVK_ANSI_Minus}, + {"<", kVK_ANSI_Comma}, {">", kVK_ANSI_Period}, {"?", kVK_ANSI_Slash}, {":", kVK_ANSI_Semicolon}, {"{", kVK_ANSI_LeftBracket}, {"\"", kVK_ANSI_Quote}, {"|", kVK_ANSI_Backslash}, {"}", kVK_ANSI_RightBracket}, {"~", kVK_ANSI_Grave}, {"+", kVK_ANSI_Equal}, {")", kVK_ANSI_0}, {"!", kVK_ANSI_1}, {"@", kVK_ANSI_2}, @@ -314,12 +314,12 @@ void rsj::SendKeyDownUp(const std::string& key, const rsj::ActiveModifiers& mods if (k_data.option) { flags |= kCGEventFlagMaskAlternate; } } else { - if (const auto mapped_key {kNonASCIIKeyMap.find(rsj::ToLower(key))}; - mapped_key != kNonASCIIKeyMap.end()) { - vk = mapped_key->second; + if (const auto mapped_unshifted_key {kANSIKeyMap.find(rsj::ToLower(key))}; + mapped_unshifted_key != kANSIKeyMap.end()) { + vk = mapped_unshifted_key->second; } - else if (const auto mapped_shifted_key {kNonASCIIKeyMapShifted.find(key)}; - mapped_shifted_key != kNonASCIIKeyMapShifted.end()) { + else if (const auto mapped_shifted_key {kANSIKeyMapShifted.find(key)}; + mapped_shifted_key != kANSIKeyMapShifted.end()) { vk = mapped_shifted_key->second; flags |= kCGEventFlagMaskShift; } From 39dc2283e4a2203014273e9f13b6f2f0c473755d Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Tue, 4 Jan 2022 15:18:35 -0800 Subject: [PATCH 24/30] fix log formatting --- src/application/Misc.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/application/Misc.cpp b/src/application/Misc.cpp index ea0227e9f..f639679df 100644 --- a/src/application/Misc.cpp +++ b/src/application/Misc.cpp @@ -130,8 +130,10 @@ void rsj::Log(const juce::String& info, const std::source_location& location) no { try { if (juce::Logger::getCurrentLogger()) { - juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ":" - + location.file_name() + ":" + location.line() + " " + info); + juce::String localname {location.file_name()}; + localname = localname.substring(localname.lastIndexOfChar('\\') + 1); + juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ' ' + localname + + '(' + juce::String(location.line()) + ") " + info); } } catch (...) { //-V565 //-V5002 @@ -142,8 +144,10 @@ void rsj::Log(gsl::czstring<> info, const std::source_location& location) noexce { try { if (juce::Logger::getCurrentLogger()) { - juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ":" - + location.file_name() + ":" + location.line() + " " + juce::String localname {location.file_name()}; + localname = localname.substring(localname.lastIndexOfChar('\\') + 1); + juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ' ' + localname + + '(' + juce::String(location.line()) + ") " + juce::String::fromUTF8(info)); } } @@ -155,8 +159,10 @@ void rsj::Log(gsl::cwzstring<> info, const std::source_location& location) noexc { try { if (juce::Logger::getCurrentLogger()) { - juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ":" - + location.file_name() + ":" + location.line() + " " + info); + juce::String localname {location.file_name()}; + localname = localname.substring(localname.lastIndexOfChar('\\') + 1); + juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ' ' + localname + + '(' + juce::String(location.line()) + ") " + info); } } catch (...) { //-V565 //-V5002 @@ -225,7 +231,7 @@ void rsj::Log(const juce::String& info) noexcept { try { if (juce::Logger::getCurrentLogger()) { - juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ": " + info); + juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + " " + info); } } catch (...) { //-V565 @@ -237,7 +243,7 @@ void rsj::Log(gsl::czstring<> info) noexcept try { if (juce::Logger::getCurrentLogger()) { juce::Logger::writeToLog( - juce::Time::getCurrentTime().toISO8601(true) + ": " + juce::String::fromUTF8(info)); + juce::Time::getCurrentTime().toISO8601(true) + " " + juce::String::fromUTF8(info)); } } catch (...) { //-V565 @@ -248,7 +254,7 @@ void rsj::Log(gsl::cwzstring<> info) noexcept { try { if (juce::Logger::getCurrentLogger()) { - juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ": " + info); + juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + " " + info); } } catch (...) { //-V565 From 2fb97fcaef99c6d8c8398d8a0f3b5af7e078f1df Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Tue, 11 Jan 2022 15:18:09 -0800 Subject: [PATCH 25/30] address LR crash on start/profile change --- src/application/ProfileManager.cpp | 2 - src/plugin/Database.lua | 56 ++++++++++--------- src/plugin/Profiles.lua | 86 +++++++++++++++++------------- 3 files changed, 79 insertions(+), 65 deletions(-) diff --git a/src/application/ProfileManager.cpp b/src/application/ProfileManager.cpp index f7c865772..b1d2ad7e9 100644 --- a/src/application/ProfileManager.cpp +++ b/src/application/ProfileManager.cpp @@ -45,8 +45,6 @@ void ProfileManager::SetProfileDirectory(const juce::File& directory) file_array.sort(); profiles_.clear(); for (const auto& file : file_array) { profiles_.push_back(file.getFileName()); } - if (!profiles_.empty()) { SwitchToProfile(profiles_.front()); } - current_profile_index_ = 0; } catch (const std::exception& e) { MIDI2LR_E_RESPONSE; diff --git a/src/plugin/Database.lua b/src/plugin/Database.lua index f480082fc..963112bcb 100644 --- a/src/plugin/Database.lua +++ b/src/plugin/Database.lua @@ -55,6 +55,10 @@ for wraps, would enter wrap = true for values that wrap around (e.g., color whee for repeats, would enter repeats={cw_key,ccw_key} for what gets sent when turn right/left Command Type Experimental Translation Group (in app) Explanation Panel (in LR Devel) also optional are PV1 PV2 PV3 PV4 PV5. These change the bezel display for different process versions +Optional: AltParameter : define for those parameters in which + LrApplication.activeCatalog():getTargetPhoto():getDevelopSettings() returns several relevant + names (e.g., Clarity Clarity2012). also define as 'Direct' for those that should be retrieved + by LrDevelopController.getValue (e.g., local adjustment parameters) -----------------------------------------------------------------------------]] --local function LOC(str) return str end--for debugging @@ -399,15 +403,15 @@ local DataBase = { {Command='AutoTone',Type='button',Translation=LOC('$$$/AgDevelopShortcuts/Auto_Tone=Auto Tone'),Group=basicTone,Explanation='Auto Tone.',Panel='adjustPanel'}, {Command='Temperature',Type='parameter',Translation=LOC('$$$/AgCameraRawNamedSettings/CameraRawSettingMapping/Temperature=Temperature'),Group=basicTone,Explanation='Fine-tunes the white balance using the Kelvin color temperature scale. Move the slider to the left to make the photo appear cooler, and right to warm the photo colors.',Panel='adjustPanel'}, {Command='Tint',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Tint=Tint'),Group=basicTone,Explanation='Fine-tunes the white balance to compensate for a green or magenta tint. Move the slider to the left (negative values) to add green to the photo; move it to the right (positive values) to add magenta.',Panel='adjustPanel'}, - {Command='Exposure',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Exposure=Exposure'),Group=basicTone,Explanation='Sets the overall image brightness. Adjust the slider until the photo looks good and the image is the desired brightness.',Panel='adjustPanel'}, - {Command='Contrast',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Contrast=Contrast'),Group=basicTone,Explanation='Increases or decreases image contrast, mainly affecting midtones. When you increase contrast, the middle-to-dark image areas become darker, and the middle-to-light image areas become lighter. The image tones are inversely affected as you decrease contrast.',Panel='adjustPanel'}, - {Command='Highlights',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Highlights=Highlights')..' ('..LOC('$$$/AgCameraRawUI/HighlightRecovery=Highlight Recovery')..' in '..P1and2..')',PV1=LOC('$$$/AgCameraRawUI/HighlightRecovery=Highlight Recovery'),PV3=LOC('$$$/AgCameraRawUI/Highlights=Highlights'),Group=basicTone,Explanation='Adjusts bright image areas. Drag to the left to darken highlights and recover \226\128\156blown out\226\128\157 highlight details. Drag to the right to brighten highlights while minimizing clipping.

In '..P1and2..', controls recovery and reduces the tones of extreme highlights and attempts to recover highlight detail lost because of camera overexposure. Lightroom can recover detail in raw image files if one or two channels are clipped.',Panel='adjustPanel'}, + {Command='Exposure',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Exposure=Exposure'),Group=basicTone,Explanation='Sets the overall image brightness. Adjust the slider until the photo looks good and the image is the desired brightness.',Panel='adjustPanel',AltParameter='Exposure2012'}, + {Command='Contrast',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Contrast=Contrast'),Group=basicTone,Explanation='Increases or decreases image contrast, mainly affecting midtones. When you increase contrast, the middle-to-dark image areas become darker, and the middle-to-light image areas become lighter. The image tones are inversely affected as you decrease contrast.',Panel='adjustPanel',AltParameter='Contrast2012'}, + {Command='Highlights',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Highlights=Highlights')..' ('..LOC('$$$/AgCameraRawUI/HighlightRecovery=Highlight Recovery')..' in '..P1and2..')',PV1=LOC('$$$/AgCameraRawUI/HighlightRecovery=Highlight Recovery'),PV3=LOC('$$$/AgCameraRawUI/Highlights=Highlights'),Group=basicTone,Explanation='Adjusts bright image areas. Drag to the left to darken highlights and recover \226\128\156blown out\226\128\157 highlight details. Drag to the right to brighten highlights while minimizing clipping.

In '..P1and2..', controls recovery and reduces the tones of extreme highlights and attempts to recover highlight detail lost because of camera overexposure. Lightroom can recover detail in raw image files if one or two channels are clipped.',Panel='adjustPanel',AltParameter='Highlights2012'}, {Command='Brightness',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Brightness=Brightness'),Group=basicTone,Explanation='Adjusts image brightness, mainly affecting midtones. Adjust Brightness after setting Exposure, Recovery, and Blacks sliders. Large brightness adjustments can affect shadow or highlight clipping, so you may want to readjust the Exposure, Recovery, or Blacks slider after adjusting brightness. No effect unless in PV 1 or PV 2)',Panel='adjustPanel'}, - {Command='Shadows',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Shadows=Shadows')..' ('..LOC('$$$/AgCameraRawUI/FillLight=Fill Light')..' in '..P1and2..')',PV1=LOC('$$$/AgCameraRawUI/FillLight=Fill Light'),PV3=LOC('$$$/AgCameraRawUI/Shadows=Shadows'),Group=basicTone,Explanation='Adjusts dark image areas. Drag to the left to darken shadows while minimizing clipping. Drag to the right to brighten shadows and recover shadow details.

In '..P1and2..', controls Fill Light, and lightens shadow to reveal more detail while maintaining blacks. Take care not to over apply the setting and reveal image noise.',Panel='adjustPanel'}, - {Command='Whites',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Whites=Whites')..' ('..LOC('$$$/AgCameraRawUI/Brightness=Brightness')..' in '..P1and2..')',PV1=LOC('$$$/AgCameraRawUI/Brightness=Brightness'),PV3=LOC('$$$/AgCameraRawUI/Whites=Whites'),Group=basicTone,Explanation='Adjusts white clipping. Drag to the left to reduce clipping in highlights. Drag to the right to increase highlight clipping.',Panel='adjustPanel'}, - {Command='Blacks',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Blacks=Blacks'),Group=basicTone,Explanation='Specifies which image values map to black. Moving the slider to the right increases the areas that become black, sometimes creating the impression of increased image contrast. The greatest effect is in the shadows, with much less change in the midtones and highlights.',Panel='adjustPanel'}, + {Command='Shadows',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Shadows=Shadows')..' ('..LOC('$$$/AgCameraRawUI/FillLight=Fill Light')..' in '..P1and2..')',PV1=LOC('$$$/AgCameraRawUI/FillLight=Fill Light'),PV3=LOC('$$$/AgCameraRawUI/Shadows=Shadows'),Group=basicTone,Explanation='Adjusts dark image areas. Drag to the left to darken shadows while minimizing clipping. Drag to the right to brighten shadows and recover shadow details.

In '..P1and2..', controls Fill Light, and lightens shadow to reveal more detail while maintaining blacks. Take care not to over apply the setting and reveal image noise.',Panel='adjustPanel',AltParameter='Shadows2012'}, + {Command='Whites',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Whites=Whites')..' ('..LOC('$$$/AgCameraRawUI/Brightness=Brightness')..' in '..P1and2..')',PV1=LOC('$$$/AgCameraRawUI/Brightness=Brightness'),PV3=LOC('$$$/AgCameraRawUI/Whites=Whites'),Group=basicTone,Explanation='Adjusts white clipping. Drag to the left to reduce clipping in highlights. Drag to the right to increase highlight clipping.',Panel='adjustPanel',AltParameter='Whites2012'}, + {Command='Blacks',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Blacks=Blacks'),Group=basicTone,Explanation='Specifies which image values map to black. Moving the slider to the right increases the areas that become black, sometimes creating the impression of increased image contrast. The greatest effect is in the shadows, with much less change in the midtones and highlights.',Panel='adjustPanel',AltParameter='Blacks2012'}, {Command='Texture',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Texture=Texture')..' (PV 3+)',PV3=LOC('$$$/AgCameraRawUI/Texture=Texture'),Group=basicTone,Explanation='Adjust the Texture slider negatively to smooth skin and retain fine pore details to ensure natural-looking skin. Increase the Texture amount to accentuate details such as bark or hair without affecting less detailed areas, like the out of focus areas in a photograph, Process Version 3+. Photos with Process Version 3 or 4 will be auto updated to current process version on changing Texture.',Panel='adjustPanel'}, - {Command='Clarity',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Clarity=Clarity'),Group=basicTone,Explanation='Adds depth to an image by increasing local contrast. When using this setting, it is best to zoom in to 100% or greater.',Panel='adjustPanel'}, + {Command='Clarity',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Clarity=Clarity'),Group=basicTone,Explanation='Adds depth to an image by increasing local contrast. When using this setting, it is best to zoom in to 100% or greater.',Panel='adjustPanel',AltParameter='Clarity2012'}, {Command='Dehaze',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/DehazeAmount=Dehaze Amount')..' (PV 3+)',PV3=LOC('$$$/AgCameraRawUI/DehazeAmount=Dehaze Amount'),Group=basicTone,Explanation='Controls the amount of haze in a photograph. Drag to the right to remove haze; drag to the left to add haze. For PV 3+ only.',Panel='adjustPanel'}, {Command='Vibrance',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Vibrance=Vibrance'),Group=basicTone,Explanation='Adjusts the saturation so that clipping is minimized as colors approach full saturation, changing the saturation of all lower-saturated colors with less effect on the higher-saturated colors. Vibrance also prevents skin tones from becoming over saturated.',Panel='adjustPanel'}, {Command='Saturation',Type='parameter',Translation=LOC('$$$/AgCameraRawUI/Saturation=Saturation'),Group=basicTone,Explanation='Adjusts the saturation of all image colors equally from -100 (monochrome) to +100 (double the saturation).',Panel='adjustPanel'}, @@ -983,24 +987,24 @@ local DataBase = { {Command='ChangeFeatherSize',Type='repeat',Translation=brush..' — '..feather,Group=localizedAdjustments,Explanation='Sends keystroke to Lightroom. The keystroke varies according to the which Language Lightroom is set to. Change adjustment brush feather size. This works with spot, gradient, radial filter and localized adjustment tools. **Caution**: With gradient and radial filter, make sure *brush* is selected when using this command. Turning knob clockwise sends Increase Size signals to Lightroom, counterclockwise Decrease Size.'..repeatexp,Repeats={'BrushFeatherLarger','BrushFeatherSmaller'}}, {Command='BrushFeatherSmaller',Type='button',Translation=brush..' — '..feather..' — '..smaller,Group=localizedAdjustments,Explanation='Sends keystroke to Lightroom. The keystroke varies according to the which Language Lightroom is set to. Reduce adjustment brush feather. This works with spot, gradient, radial filter and localized adjustment tools. **Caution**: With gradient and radial filter, make sure *brush* is selected when using this command.'}, {Command='BrushFeatherLarger',Type='button',Translation=brush..' — '..feather..' — '..larger,Group=localizedAdjustments,Explanation='Sends keystroke to Lightroom. The keystroke varies according to the which Language Lightroom is set to. Increase adjustment brush feather. This works with spot, gradient, radial filter and localized adjustment tools. **Caution**: With gradient and radial filter, make sure *brush* is selected when using this command.'}, - {Command='local_Temperature',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Temperature=Temp.')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Temperature=Temp.'),Group=localizedAdjustments,Explanation='Adjust Temperature for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Tint',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Tint=Tint')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Tint=Tint'),Group=localizedAdjustments,Explanation='Adjust Tint for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Exposure',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Exposure=Exposure')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Exposure=Exposure'),Group=localizedAdjustments,Explanation='Adjust Exposure for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Contrast',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Contrast=Contrast')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Contrast=Contrast'),Group=localizedAdjustments,Explanation='Adjust Contrast for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Highlights',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Highlights=Highlights')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Highlights=Highlights'),Group=localizedAdjustments,Explanation='Adjust Highlights for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Shadows',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Shadows=Shadows')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Shadows=Shadows'),Group=localizedAdjustments,Explanation='Adjust Shadows for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Whites2012',Type='parameter',Experimental=true,Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Whites=Whites')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Whites=Whites'),Group=localizedAdjustments,Explanation='Adjust Whites for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Blacks2012',Type='parameter',Experimental=true,Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Blacks=Blacks')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Blacks=Blacks'),Group=localizedAdjustments,Explanation='Adjust Blacks for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Texture',Type='parameter',Translation=locadj..' '..LOC('$$$/AgCameraRawUI/Texture=Texture'),Group=localizedAdjustments,Explanation='Adjust Texture for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Clarity',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Clarity=Clarity')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Clarity=Clarity'),Group=localizedAdjustments,Explanation='Adjust Clarity for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Dehaze',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Dehaze=Dehaze')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Dehaze=Dehaze'),Group=localizedAdjustments,Explanation='Adjust Dehaze for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Hue',Translation=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Hue=Hue')..' (PV 5+)',PV5=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Hue=Hue'),Group=localizedAdjustments,Explanation='Adjust Hue for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Saturation',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Saturation=Saturation')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Saturation=Saturation'),Group=localizedAdjustments,Explanation='Adjust Saturation for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Sharpness',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Sharpness=Sharpness')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Sharpness=Sharpness'),Group=localizedAdjustments,Explanation='Adjust Sharpness for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_LuminanceNoise',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/LuminanceNoiseReduction=Luminance Noise Reduction')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/LuminanceNoiseReduction=Luminance Noise Reduction'),Group=localizedAdjustments,Explanation='Adjust Luminance Noise for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Moire',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/MoireReduction=Moire')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/MoireReduction=Moire'),Group=localizedAdjustments,Explanation='Adjust Moire for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_Defringe',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Defringe=Defringe')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Defringe=Defringe'),Group=localizedAdjustments,Explanation='Adjust Defringe for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, - {Command='local_ToningLuminance',Type='parameter',Translation=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Luminance=Luminance')..' (PV 2)',PV2=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Luminance=Luminance'),PV3=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Luminance=Luminance')..' (PV 2)',Group=localizedAdjustments,Explanation='Adjust Toning Luminance for the currently active tool: Brush, Radial Filter, or Graduated Filter.'}, + {Command='local_Temperature',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Temperature=Temp.')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Temperature=Temp.'),Group=localizedAdjustments,Explanation='Adjust Temperature for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Tint',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Tint=Tint')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Tint=Tint'),Group=localizedAdjustments,Explanation='Adjust Tint for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Exposure',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Exposure=Exposure')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Exposure=Exposure'),Group=localizedAdjustments,Explanation='Adjust Exposure for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Contrast',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Contrast=Contrast')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Contrast=Contrast'),Group=localizedAdjustments,Explanation='Adjust Contrast for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Highlights',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Highlights=Highlights')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Highlights=Highlights'),Group=localizedAdjustments,Explanation='Adjust Highlights for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Shadows',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Shadows=Shadows')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Shadows=Shadows'),Group=localizedAdjustments,Explanation='Adjust Shadows for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Whites2012',Type='parameter',Experimental=true,Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Whites=Whites')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Whites=Whites'),Group=localizedAdjustments,Explanation='Adjust Whites for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Blacks2012',Type='parameter',Experimental=true,Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Blacks=Blacks')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Blacks=Blacks'),Group=localizedAdjustments,Explanation='Adjust Blacks for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Texture',Type='parameter',Translation=locadj..' '..LOC('$$$/AgCameraRawUI/Texture=Texture'),Group=localizedAdjustments,Explanation='Adjust Texture for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Clarity',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Clarity=Clarity')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Clarity=Clarity'),Group=localizedAdjustments,Explanation='Adjust Clarity for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Dehaze',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Dehaze=Dehaze')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Dehaze=Dehaze'),Group=localizedAdjustments,Explanation='Adjust Dehaze for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Hue',Type='parameter',Translation=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Hue=Hue')..' (PV 5+)',PV5=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Hue=Hue'),Group=localizedAdjustments,Explanation='Adjust Hue for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Saturation',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Saturation=Saturation')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Saturation=Saturation'),Group=localizedAdjustments,Explanation='Adjust Saturation for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Sharpness',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Sharpness=Sharpness')..' ('..P2and3plus..')',PV2=locadj..' '..LOC('$$$/AgDevelop/Localized/Sharpness=Sharpness'),Group=localizedAdjustments,Explanation='Adjust Sharpness for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_LuminanceNoise',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/LuminanceNoiseReduction=Luminance Noise Reduction')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/LuminanceNoiseReduction=Luminance Noise Reduction'),Group=localizedAdjustments,Explanation='Adjust Luminance Noise for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Moire',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/MoireReduction=Moire')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/MoireReduction=Moire'),Group=localizedAdjustments,Explanation='Adjust Moire for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_Defringe',Type='parameter',Translation=locadj..' '..LOC('$$$/AgDevelop/Localized/Defringe=Defringe')..' (PV 3+)',PV3=locadj..' '..LOC('$$$/AgDevelop/Localized/Defringe=Defringe'),Group=localizedAdjustments,Explanation='Adjust Defringe for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, + {Command='local_ToningLuminance',Type='parameter',Translation=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Luminance=Luminance')..' (PV 2)',PV2=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Luminance=Luminance'),PV3=locadj..' '..LOC('$$$/AgCameraRawNamedSettings/SaveNamedDialog/Luminance=Luminance')..' (PV 2)',Group=localizedAdjustments,Explanation='Adjust Toning Luminance for the currently active tool: Brush, Radial Filter, or Graduated Filter.',AltParameter='Direct'}, --local adjustment resets {Command='ResetRedeye',Type='button',Translation=LOC('$$$/AgLibrary/Ops/ResetRedeye=Reset Red-Eye'),Group=localadjresets,Explanation='Delete red eye correction.'}, {Command='ResetSpotRem',Type='button',Translation=LOC('$$$/AgLibrary/Ops/ResetSpotRemoval=Reset Spot Removal'),Group=localadjresets,Explanation='Delete spot removal.'}, @@ -1155,7 +1159,7 @@ for _,v in ipairs(DataBase) do CmdPanel[v.Command] = v.Panel end if v.Type == 'parameter' then - Parameters[v.Command] = true + Parameters[v.Command] = v.AltParameter or v.Command elseif v.Type == 'button' then ValidActions[v.Command] = true end diff --git a/src/plugin/Profiles.lua b/src/plugin/Profiles.lua index ed519ddd8..ee1f7b9a3 100644 --- a/src/plugin/Profiles.lua +++ b/src/plugin/Profiles.lua @@ -22,11 +22,13 @@ local Database = require 'Database' local Init = require 'Init' local Limits = require 'Limits' local ProfileTypes = require 'ProfileTypes' +local LrApplication = import 'LrApplication' local LrApplicationView = import 'LrApplicationView' local LrDevelopController = import 'LrDevelopController' local LrDialogs = import 'LrDialogs' local LrFileUtils = import 'LrFileUtils' local LrStringUtils = import 'LrStringUtils' +local LrTasks = import 'LrTasks' local LrView = import 'LrView' local currentTMP = {Tool = '', Module = '', Panel = '', Profile = ''} @@ -42,46 +44,56 @@ local function doprofilechange(newprofile) loadedprofile = newprofile if Limits.LimitsCanBeSet() then -- refresh MIDI controller since mapping has changed - -- refresh crop values - local val_bottom = LrDevelopController.getValue("CropBottom") - MIDI2LR.SERVER:send(string.format('CropBottomRight %g\n', val_bottom)) - MIDI2LR.SERVER:send(string.format('CropBottomLeft %g\n', val_bottom)) - MIDI2LR.SERVER:send(string.format('CropAll %g\n', val_bottom)) - MIDI2LR.SERVER:send(string.format('CropBottom %g\n', val_bottom)) - local val_top = LrDevelopController.getValue("CropTop") - MIDI2LR.SERVER:send(string.format('CropTopRight %g\n', val_top)) - MIDI2LR.SERVER:send(string.format('CropTopLeft %g\n', val_top)) - MIDI2LR.SERVER:send(string.format('CropTop %g\n', val_top)) - local val_left = LrDevelopController.getValue("CropLeft") - local val_right = LrDevelopController.getValue("CropRight") - MIDI2LR.SERVER:send(string.format('CropLeft %g\n', val_left)) - MIDI2LR.SERVER:send(string.format('CropRight %g\n', val_right)) - local range_v = (1 - (val_bottom - val_top)) - if range_v == 0.0 then - MIDI2LR.SERVER:send('CropMoveVertical 0\n') - else - MIDI2LR.SERVER:send(string.format('CropMoveVertical %g\n', val_top / range_v)) - end - local range_h = (1 - (val_right - val_left)) - if range_h == 0.0 then - MIDI2LR.SERVER:send('CropMoveHorizontal 0\n') - else - MIDI2LR.SERVER:send(string.format('CropMoveHorizontal %g\n', val_left / range_h)) - end - for param in pairs(Database.Parameters) do - local min,max = Limits.GetMinMax(param) --can't include ClientUtilities: circular reference - local lrvalue = LrDevelopController.getValue(param) - if type(min) == 'number' and type(max) == 'number' and type(lrvalue) == 'number' then - local midivalue = (lrvalue-min)/(max-min) - if midivalue >= 1.0 then - MIDI2LR.SERVER:send(string.format('%s 1.0\n', param)) - elseif midivalue <= 0.0 then -- = catches -0.0 and sends it as 0.0 - MIDI2LR.SERVER:send(string.format('%s 0.0\n', param)) + LrTasks.startAsyncTask ( function () + if LrApplication.activeCatalog():getTargetPhoto() == nil then return end + local photoval = LrApplication.activeCatalog():getTargetPhoto():getDevelopSettings() + -- refresh crop values + local val_bottom = photoval.CropBottom + MIDI2LR.SERVER:send(string.format('CropBottomRight %g\n', val_bottom)) + MIDI2LR.SERVER:send(string.format('CropBottomLeft %g\n', val_bottom)) + MIDI2LR.SERVER:send(string.format('CropAll %g\n', val_bottom)) + MIDI2LR.SERVER:send(string.format('CropBottom %g\n', val_bottom)) + local val_top = photoval.CropTop + MIDI2LR.SERVER:send(string.format('CropTopRight %g\n', val_top)) + MIDI2LR.SERVER:send(string.format('CropTopLeft %g\n', val_top)) + MIDI2LR.SERVER:send(string.format('CropTop %g\n', val_top)) + local val_left = photoval.CropLeft + local val_right = photoval.CropRight + MIDI2LR.SERVER:send(string.format('CropLeft %g\n', val_left)) + MIDI2LR.SERVER:send(string.format('CropRight %g\n', val_right)) + local range_v = (1 - (val_bottom - val_top)) + if range_v == 0.0 then + MIDI2LR.SERVER:send('CropMoveVertical 0\n') + else + MIDI2LR.SERVER:send(string.format('CropMoveVertical %g\n', val_top / range_v)) + end + local range_h = (1 - (val_right - val_left)) + if range_h == 0.0 then + MIDI2LR.SERVER:send('CropMoveHorizontal 0\n') else - MIDI2LR.SERVER:send(string.format('%s %g\n', param, midivalue)) + MIDI2LR.SERVER:send(string.format('CropMoveHorizontal %g\n', val_left / range_h)) + end + for param,altparam in pairs(Database.Parameters) do + local min,max = Limits.GetMinMax(param) --can't include ClientUtilities: circular reference + local lrvalue + if altparam == 'Direct' then + lrvalue = LrDevelopController.getValue(param) + else + lrvalue = (photoval[param] or 0) + (photoval[altparam] or 0) + end + if type(min) == 'number' and type(max) == 'number' and type(lrvalue) == 'number' then + local midivalue = (lrvalue-min)/(max-min) + if midivalue >= 1.0 then + MIDI2LR.SERVER:send(string.format('%s 1.0\n', param)) + elseif midivalue <= 0.0 then -- = catches -0.0 and sends it as 0.0 + MIDI2LR.SERVER:send(string.format('%s 0.0\n', param)) + else + MIDI2LR.SERVER:send(string.format('%s %g\n', param, midivalue)) + end + end end end - end + ) end end From 366cfff0d27c26888171fee788d7b1a8965056ea Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Tue, 11 Jan 2022 15:27:38 -0800 Subject: [PATCH 26/30] Version 5.3.0.0 --- MIDI2LR.jucer | 2 +- build/MacOS/Info-App.plist | 4 ++-- build/MacOS/MIDI2LR.xcodeproj/project.pbxproj | 8 ++++---- build/Windows/MIDI2LR_App.vcxproj | 4 ++-- build/Windows/resources.rc | 6 +++--- external/JuceLibraryCode/JuceHeader.h | 4 ++-- src/plugin/Info.lua | 2 +- tools/doxygen/Midi2lr.Doxyfile | 2 +- tools/installer/MIDI2LR.xml | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/MIDI2LR.jucer b/MIDI2LR.jucer index f42d7302e..02ad2f9f2 100644 --- a/MIDI2LR.jucer +++ b/MIDI2LR.jucer @@ -1,6 +1,6 @@ -CFBundleSignature ???? CFBundleShortVersionString - 5.2.0.0 + 5.3.0.0 CFBundleVersion - 5.2.0.0 + 5.3.0.0 NSHumanReadableCopyright Copyright (C) 2015 by Rory Jaffe. NSHighResolutionCapable diff --git a/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj b/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj index f8b1616ef..359159bda 100644 --- a/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj +++ b/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj @@ -516,8 +516,8 @@ "JUCE_STANDALONE_APPLICATION=1", "JUCE_MODAL_LOOPS_PERMITTED=1", "JUCER_XCODE_MAC_46BB2872=1", - "JUCE_APP_VERSION=5.2.0.0", - "JUCE_APP_VERSION_HEX=0x5020000", + "JUCE_APP_VERSION=5.3.0.0", + "JUCE_APP_VERSION_HEX=0x5030000", "JucePlugin_Build_VST=0", "JucePlugin_Build_VST3=0", "JucePlugin_Build_AU=0", @@ -647,8 +647,8 @@ "JUCE_STANDALONE_APPLICATION=1", "JUCE_MODAL_LOOPS_PERMITTED=1", "JUCER_XCODE_MAC_46BB2872=1", - "JUCE_APP_VERSION=5.2.0.0", - "JUCE_APP_VERSION_HEX=0x5020000", + "JUCE_APP_VERSION=5.3.0.0", + "JUCE_APP_VERSION_HEX=0x5030000", "JucePlugin_Build_VST=0", "JucePlugin_Build_VST3=0", "JucePlugin_Build_AU=0", diff --git a/build/Windows/MIDI2LR_App.vcxproj b/build/Windows/MIDI2LR_App.vcxproj index 648b0c807..58ad1dcba 100644 --- a/build/Windows/MIDI2LR_App.vcxproj +++ b/build/Windows/MIDI2LR_App.vcxproj @@ -64,7 +64,7 @@ Disabled ProgramDatabase ..\..\external\JuceLibraryCode;..\..\external\JuceLibraryCode\modules;../../external/;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60104;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60104;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.3.0.0;JUCE_APP_VERSION_HEX=0x5030000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) MultiThreadedDebugDLL true NotUsing @@ -105,7 +105,7 @@ Full ..\..\external\JuceLibraryCode;..\..\external\JuceLibraryCode\modules;../../external/;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60104;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.2.0.0;JUCE_APP_VERSION_HEX=0x5020000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60104;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.3.0.0;JUCE_APP_VERSION_HEX=0x5030000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) MultiThreaded true NotUsing diff --git a/build/Windows/resources.rc b/build/Windows/resources.rc index fc70aa1fb..4fee5a078 100644 --- a/build/Windows/resources.rc +++ b/build/Windows/resources.rc @@ -9,7 +9,7 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 5,2,0,0 +FILEVERSION 5,3,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -18,9 +18,9 @@ BEGIN VALUE "CompanyName", "Rory Jaffe\0" VALUE "LegalCopyright", "Copyright (C) 2015 by Rory Jaffe.\0" VALUE "FileDescription", "MIDI2LR\0" - VALUE "FileVersion", "5.2.0.0\0" + VALUE "FileVersion", "5.3.0.0\0" VALUE "ProductName", "MIDI2LR\0" - VALUE "ProductVersion", "5.2.0.0\0" + VALUE "ProductVersion", "5.3.0.0\0" END END diff --git a/external/JuceLibraryCode/JuceHeader.h b/external/JuceLibraryCode/JuceHeader.h index 5681de0c6..f7ed95b22 100644 --- a/external/JuceLibraryCode/JuceHeader.h +++ b/external/JuceLibraryCode/JuceHeader.h @@ -38,7 +38,7 @@ namespace ProjectInfo { const char* const projectName = "MIDI2LR"; const char* const companyName = "Rory Jaffe"; - const char* const versionString = "5.2.0.0"; - const int versionNumber = 0x5020000; + const char* const versionString = "5.3.0.0"; + const int versionNumber = 0x5030000; } #endif diff --git a/src/plugin/Info.lua b/src/plugin/Info.lua index ec87fb2a8..bfe232cca 100644 --- a/src/plugin/Info.lua +++ b/src/plugin/Info.lua @@ -95,5 +95,5 @@ return { file = "LogSave.lua", }, }, - VERSION = { major=5, minor=2, revision=0, build=0} + VERSION = { major=5, minor=3, revision=0, build=0} } diff --git a/tools/doxygen/Midi2lr.Doxyfile b/tools/doxygen/Midi2lr.Doxyfile index e105d2270..98cc77b66 100644 --- a/tools/doxygen/Midi2lr.Doxyfile +++ b/tools/doxygen/Midi2lr.Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = MIDI2LR # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 5.2.0.0 +PROJECT_NUMBER = 5.3.0.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/tools/installer/MIDI2LR.xml b/tools/installer/MIDI2LR.xml index d524f3ab6..5da112294 100644 --- a/tools/installer/MIDI2LR.xml +++ b/tools/installer/MIDI2LR.xml @@ -1,7 +1,7 @@ MIDI2LR MIDI2LR - 5.2.0.0 + 5.3.0.0 ${product_shortname}-${product_version}-${debuglabel}${platform_name}-installer.${platform_exec_suffix} data/translations/ReadMe.txt ../../LICENSE.txt From 4759c9df94b087a60d331ea37812e2c30c5c59a7 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Tue, 11 Jan 2022 15:28:23 -0800 Subject: [PATCH 27/30] Simplify logging --- src/application/Misc.cpp | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/application/Misc.cpp b/src/application/Misc.cpp index f639679df..b4b5bd02e 100644 --- a/src/application/Misc.cpp +++ b/src/application/Misc.cpp @@ -142,31 +142,12 @@ void rsj::Log(const juce::String& info, const std::source_location& location) no void rsj::Log(gsl::czstring<> info, const std::source_location& location) noexcept { - try { - if (juce::Logger::getCurrentLogger()) { - juce::String localname {location.file_name()}; - localname = localname.substring(localname.lastIndexOfChar('\\') + 1); - juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ' ' + localname - + '(' + juce::String(location.line()) + ") " - + juce::String::fromUTF8(info)); - } - } - catch (...) { //-V565 //-V5002 - } + rsj::Log(juce::String::fromUTF8(info), location); } void rsj::Log(gsl::cwzstring<> info, const std::source_location& location) noexcept { - try { - if (juce::Logger::getCurrentLogger()) { - juce::String localname {location.file_name()}; - localname = localname.substring(localname.lastIndexOfChar('\\') + 1); - juce::Logger::writeToLog(juce::Time::getCurrentTime().toISO8601(true) + ' ' + localname - + '(' + juce::String(location.line()) + ") " + info); - } - } - catch (...) { //-V565 //-V5002 - } + rsj::Log(juce::String(info), location); } void rsj::LogAndAlertError( From fc127121c8d82b58c41ff64a07adf2fed74527fe Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Fri, 21 Jan 2022 11:49:59 -0800 Subject: [PATCH 28/30] bind_front --- src/application/LR_IPC_Out.h | 4 ++++ src/application/MIDIReceiver.h | 4 ++++ src/application/ProfileManager.h | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/application/LR_IPC_Out.h b/src/application/LR_IPC_Out.h index 1c06ea483..83581b563 100644 --- a/src/application/LR_IPC_Out.h +++ b/src/application/LR_IPC_Out.h @@ -58,7 +58,11 @@ class LrIpcOut { { if (object && mf) { std::scoped_lock lk(callback_mtx_); +#ifdef __cpp_lib_bind_front + callbacks_.emplace_back(std::bind_front(mf, object)); +#else callbacks_.emplace_back([=](bool a, bool b) { (object->*mf)(a, b); }); +#endif } } void SendCommand(std::string&& command) diff --git a/src/application/MIDIReceiver.h b/src/application/MIDIReceiver.h index 14ddf2b0a..c4e5f1aad 100644 --- a/src/application/MIDIReceiver.h +++ b/src/application/MIDIReceiver.h @@ -48,7 +48,11 @@ class MidiReceiver final : juce::MidiInputCallback { void AddCallback(_In_ T* const object, _In_ void (T::*const mf)(const rsj::MidiMessage&)) { if (object && mf) { +#ifdef __cpp_lib_bind_front + callbacks_.emplace_back(std::bind_front(mf, object)); +#else callbacks_.emplace_back([=](const rsj::MidiMessage& a) { (object->*mf)(a); }); +#endif } } diff --git a/src/application/ProfileManager.h b/src/application/ProfileManager.h index 61127e10b..d9d9b6546 100644 --- a/src/application/ProfileManager.h +++ b/src/application/ProfileManager.h @@ -49,8 +49,12 @@ class ProfileManager final : juce::AsyncUpdater { _In_ T* const object, _In_ void (T::*const mf)(juce::XmlElement*, const juce::String&)) { if (object && mf) { +#ifdef __cpp_lib_bind_front + callbacks_.emplace_back(std::bind_front(mf,object)); +#else callbacks_.emplace_back( [=](juce::XmlElement* a, const juce::String& b) { (object->*mf)(a, b); }); +#endif } } [[nodiscard]] const juce::String& GetProfileDirectory() const noexcept From 7375315e1e38d0a2175857d9b7ae73726fe5ef43 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sat, 22 Jan 2022 12:15:24 -0800 Subject: [PATCH 29/30] cleanup --- src/plugin/Profiles.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin/Profiles.lua b/src/plugin/Profiles.lua index 4c0e648b1..9bf52ed49 100644 --- a/src/plugin/Profiles.lua +++ b/src/plugin/Profiles.lua @@ -42,10 +42,10 @@ local function doprofilechange(newprofile) LrDialogs.showBezel(filename) end loadedprofile = newprofile - if Limits.LimitsCanBeSet() then + if (LrApplication.activeCatalog():getTargetPhoto() ~= nil) and + (LrApplicationView.getCurrentModuleName() == 'develop') then -- refresh MIDI controller since mapping has changed LrTasks.startAsyncTask ( function () - if LrApplication.activeCatalog():getTargetPhoto() == nil then return end local photoval = LrApplication.activeCatalog():getTargetPhoto():getDevelopSettings() -- refresh crop values local val_bottom = photoval.CropBottom From 0997a8cef1ca9adc41ff2d92f7757154a344cd64 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Fri, 28 Jan 2022 10:58:38 -0800 Subject: [PATCH 30/30] V 5.3.0.1 juce 6.1.5 --- MIDI2LR.jucer | 2 +- build/MacOS/Info-App.plist | 4 +- build/MacOS/MIDI2LR.xcodeproj/project.pbxproj | 12 +- build/Windows/MIDI2LR_App.vcxproj | 4 +- build/Windows/resources.rc | 6 +- external/JuceLibraryCode/JuceHeader.h | 4 +- .../buffers/juce_AudioProcessLoadMeasurer.cpp | 5 + .../buffers/juce_FloatVectorOperations.cpp | 1317 +++++++++++------ .../buffers/juce_FloatVectorOperations.h | 248 ++-- .../juce_audio_basics/juce_audio_basics.h | 2 +- .../midi/ump/juce_UMPSysEx7.h | 2 +- .../mpe/juce_MPEInstrument.cpp | 76 +- .../mpe/juce_MPEInstrument.h | 33 +- .../juce_audio_basics/mpe/juce_MPENote.h | 2 +- .../mpe/juce_MPESynthesiser.cpp | 8 +- .../mpe/juce_MPESynthesiser.h | 5 +- .../mpe/juce_MPESynthesiserBase.cpp | 35 +- .../mpe/juce_MPESynthesiserBase.h | 11 +- .../juce_audio_basics/mpe/juce_MPEUtils.cpp | 87 +- .../juce_audio_basics/mpe/juce_MPEUtils.h | 7 +- .../juce_audio_basics/mpe/juce_MPEValue.cpp | 36 +- .../juce_audio_basics/mpe/juce_MPEValue.h | 6 + .../mpe/juce_MPEZoneLayout.cpp | 24 +- .../mpe/juce_MPEZoneLayout.h | 187 +-- .../audio_io/juce_AudioDeviceManager.cpp | 127 +- .../juce_audio_devices/juce_audio_devices.h | 2 +- .../native/juce_linux_ALSA.cpp | 2 +- .../native/juce_mac_CoreAudio.cpp | 18 +- .../native/juce_win32_ASIO.cpp | 2 +- .../native/juce_win32_WASAPI.cpp | 23 +- .../modules/juce_core/containers/juce_Array.h | 12 +- .../juce_core/files/juce_TemporaryFile.cpp | 2 +- .../modules/juce_core/juce_core.h | 2 +- .../modules/juce_core/maths/juce_Range.h | 3 +- .../native/juce_BasicNativeHeaders.h | 4 +- .../juce_core/native/juce_mac_Network.mm | 3 +- .../juce_core/native/juce_win32_ComSmartPtr.h | 4 +- .../juce_core/native/juce_win32_Files.cpp | 3 +- .../juce_core/native/juce_win32_Threads.cpp | 2 - .../juce_core/system/juce_PlatformDefs.h | 2 +- .../juce_core/system/juce_StandardHeader.h | 3 +- .../juce_core/text/juce_StringPairArray.cpp | 6 +- .../juce_core/text/juce_StringPairArray.h | 6 + .../modules/juce_core/time/juce_Time.cpp | 6 +- .../modules/juce_core/xml/juce_XmlDocument.h | 2 +- .../modules/juce_core/zip/juce_ZipFile.cpp | 90 +- .../modules/juce_core/zip/juce_ZipFile.h | 19 + .../juce_data_structures.h | 2 +- .../modules/juce_events/juce_events.h | 2 +- .../juce_graphics/images/juce_ScaledImage.h | 2 +- .../modules/juce_graphics/juce_graphics.cpp | 8 + .../modules/juce_graphics/juce_graphics.h | 2 +- .../juce_win32_DirectWriteTypeLayout.cpp | 5 +- .../native/juce_win32_DirectWriteTypeface.cpp | 6 +- .../juce_graphics/native/juce_win32_Fonts.cpp | 1 + .../juce_AccessibilityValueInterface.h | 2 +- .../components/juce_Component.h | 18 + .../juce_gui_basics/desktop/juce_Desktop.cpp | 20 + .../juce_gui_basics/desktop/juce_Desktop.h | 3 + .../juce_gui_basics/juce_gui_basics.cpp | 92 +- .../modules/juce_gui_basics/juce_gui_basics.h | 4 +- .../layout/juce_MultiDocumentPanel.h | 2 +- .../lookandfeel/juce_LookAndFeel.h | 58 +- .../lookandfeel/juce_LookAndFeel_V2.cpp | 36 +- .../lookandfeel/juce_LookAndFeel_V2.h | 3 +- .../lookandfeel/juce_LookAndFeel_V4.cpp | 13 +- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 110 +- .../juce_gui_basics/menus/juce_PopupMenu.h | 61 +- .../juce_gui_basics/misc/juce_DropShadower.h | 17 +- .../misc/juce_FocusOutline.cpp | 185 +++ .../juce_gui_basics/misc/juce_FocusOutline.h | 100 ++ .../mouse/juce_MouseCursor.cpp | 2 +- .../juce_android_Accessibility.cpp | 6 +- .../accessibility/juce_ios_Accessibility.mm | 4 +- .../accessibility/juce_mac_Accessibility.mm | 3 +- .../juce_mac_AccessibilitySharedCode.mm | 4 + .../juce_win32_Accessibility.cpp | 18 +- .../juce_win32_AccessibilityElement.cpp | 117 +- .../juce_win32_AccessibilityElement.h | 14 +- .../accessibility/juce_win32_ComInterfaces.h | 371 +++++ .../juce_win32_UIAExpandCollapseProvider.h | 17 +- .../juce_win32_UIAGridItemProvider.h | 7 +- .../juce_win32_UIAGridProvider.h | 7 +- .../juce_win32_UIAInvokeProvider.h | 9 +- .../juce_win32_UIARangeValueProvider.h | 7 +- .../juce_win32_UIASelectionProvider.h | 31 +- .../juce_win32_UIATextProvider.h | 115 +- .../juce_win32_UIAToggleProvider.h | 18 +- .../juce_win32_UIATransformProvider.h | 7 +- .../juce_win32_UIAValueProvider.h | 7 +- .../juce_win32_UIAWindowProvider.h | 29 +- .../juce_win32_WindowsUIAWrapper.h | 2 + .../app/com/rmsl/juce/ComponentPeerView.java | 40 +- .../native/juce_android_Windowing.cpp | 604 ++++---- .../native/juce_common_MimeTypes.cpp | 2 +- .../native/juce_ios_FileChooser.mm | 13 +- .../native/juce_ios_UIViewComponentPeer.mm | 30 +- .../native/juce_mac_NSViewComponentPeer.mm | 221 ++- .../native/juce_win32_FileChooser.cpp | 14 +- .../native/juce_win32_Windowing.cpp | 59 +- .../native/x11/juce_linux_XWindowSystem.cpp | 49 +- .../native/x11/juce_linux_XWindowSystem.h | 4 +- .../juce_gui_basics/widgets/juce_Slider.h | 2 +- .../widgets/juce_TextEditor.cpp | 7 + .../juce_gui_basics/widgets/juce_TreeView.cpp | 3 + .../windows/juce_ComponentPeer.h | 31 + .../windows/juce_TooltipWindow.cpp | 14 +- .../windows/juce_TooltipWindow.h | 2 +- .../windows/juce_TopLevelWindow.cpp | 10 +- 109 files changed, 3481 insertions(+), 1637 deletions(-) create mode 100644 external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_FocusOutline.cpp create mode 100644 external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_FocusOutline.h create mode 100644 external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_ComInterfaces.h diff --git a/MIDI2LR.jucer b/MIDI2LR.jucer index 02ad2f9f2..b2ba8651c 100644 --- a/MIDI2LR.jucer +++ b/MIDI2LR.jucer @@ -1,6 +1,6 @@ -CFBundleSignature ???? CFBundleShortVersionString - 5.3.0.0 + 5.3.0.1 CFBundleVersion - 5.3.0.0 + 5.3.0.1 NSHumanReadableCopyright Copyright (C) 2015 by Rory Jaffe. NSHighResolutionCapable diff --git a/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj b/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj index 359159bda..ae5f1e954 100644 --- a/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj +++ b/build/MacOS/MIDI2LR.xcodeproj/project.pbxproj @@ -489,7 +489,7 @@ "NDEBUG=1", "JUCE_DISPLAY_SPLASH_SCREEN=0", "JUCE_USE_DARK_SPLASH_SCREEN=1", - "JUCE_PROJUCER_VERSION=0x60104", + "JUCE_PROJUCER_VERSION=0x60105", "JUCE_MODULE_AVAILABLE_juce_audio_basics=1", "JUCE_MODULE_AVAILABLE_juce_audio_devices=1", "JUCE_MODULE_AVAILABLE_juce_core=1", @@ -516,8 +516,8 @@ "JUCE_STANDALONE_APPLICATION=1", "JUCE_MODAL_LOOPS_PERMITTED=1", "JUCER_XCODE_MAC_46BB2872=1", - "JUCE_APP_VERSION=5.3.0.0", - "JUCE_APP_VERSION_HEX=0x5030000", + "JUCE_APP_VERSION=5.3.0.1", + "JUCE_APP_VERSION_HEX=0x5030001", "JucePlugin_Build_VST=0", "JucePlugin_Build_VST3=0", "JucePlugin_Build_AU=0", @@ -620,7 +620,7 @@ "DEBUG=1", "JUCE_DISPLAY_SPLASH_SCREEN=0", "JUCE_USE_DARK_SPLASH_SCREEN=1", - "JUCE_PROJUCER_VERSION=0x60104", + "JUCE_PROJUCER_VERSION=0x60105", "JUCE_MODULE_AVAILABLE_juce_audio_basics=1", "JUCE_MODULE_AVAILABLE_juce_audio_devices=1", "JUCE_MODULE_AVAILABLE_juce_core=1", @@ -647,8 +647,8 @@ "JUCE_STANDALONE_APPLICATION=1", "JUCE_MODAL_LOOPS_PERMITTED=1", "JUCER_XCODE_MAC_46BB2872=1", - "JUCE_APP_VERSION=5.3.0.0", - "JUCE_APP_VERSION_HEX=0x5030000", + "JUCE_APP_VERSION=5.3.0.1", + "JUCE_APP_VERSION_HEX=0x5030001", "JucePlugin_Build_VST=0", "JucePlugin_Build_VST3=0", "JucePlugin_Build_AU=0", diff --git a/build/Windows/MIDI2LR_App.vcxproj b/build/Windows/MIDI2LR_App.vcxproj index 58ad1dcba..eaad45807 100644 --- a/build/Windows/MIDI2LR_App.vcxproj +++ b/build/Windows/MIDI2LR_App.vcxproj @@ -64,7 +64,7 @@ Disabled ProgramDatabase ..\..\external\JuceLibraryCode;..\..\external\JuceLibraryCode\modules;../../external/;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60104;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.3.0.0;JUCE_APP_VERSION_HEX=0x5030000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60105;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.3.0.1;JUCE_APP_VERSION_HEX=0x5030001;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) MultiThreadedDebugDLL true NotUsing @@ -105,7 +105,7 @@ Full ..\..\external\JuceLibraryCode;..\..\external\JuceLibraryCode\modules;../../external/;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60104;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.3.0.0;JUCE_APP_VERSION_HEX=0x5030000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x60105;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_WINRT_MIDI=0;JUCE_ASIO=0;JUCE_WASAPI=0;JUCE_DIRECTSOUND=0;JUCE_ALSA=0;JUCE_JACK=0;JUCE_BELA=0;JUCE_USE_ANDROID_OBOE=0;JUCE_USE_ANDROID_OPENSLES=0;JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS=0;JUCE_FORCE_DEBUG=0;JUCE_LOG_ASSERTIONS=0;JUCE_CATCH_UNHANDLED_EXCEPTIONS=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;_WIN32_WINNT=0x0A000007;WINVER=0x0A000007;NOMINMAX;WIN32_LEAN_AND_MEAN;JUCE_MODAL_LOOPS_PERMITTED=1;JUCER_VS2022_A3DCEFC2=1;JUCE_APP_VERSION=5.3.0.1;JUCE_APP_VERSION_HEX=0x5030001;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;%(PreprocessorDefinitions) MultiThreaded true NotUsing diff --git a/build/Windows/resources.rc b/build/Windows/resources.rc index 4fee5a078..aa5092823 100644 --- a/build/Windows/resources.rc +++ b/build/Windows/resources.rc @@ -9,7 +9,7 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 5,3,0,0 +FILEVERSION 5,3,0,1 BEGIN BLOCK "StringFileInfo" BEGIN @@ -18,9 +18,9 @@ BEGIN VALUE "CompanyName", "Rory Jaffe\0" VALUE "LegalCopyright", "Copyright (C) 2015 by Rory Jaffe.\0" VALUE "FileDescription", "MIDI2LR\0" - VALUE "FileVersion", "5.3.0.0\0" + VALUE "FileVersion", "5.3.0.1\0" VALUE "ProductName", "MIDI2LR\0" - VALUE "ProductVersion", "5.3.0.0\0" + VALUE "ProductVersion", "5.3.0.1\0" END END diff --git a/external/JuceLibraryCode/JuceHeader.h b/external/JuceLibraryCode/JuceHeader.h index f7ed95b22..d94658e21 100644 --- a/external/JuceLibraryCode/JuceHeader.h +++ b/external/JuceLibraryCode/JuceHeader.h @@ -38,7 +38,7 @@ namespace ProjectInfo { const char* const projectName = "MIDI2LR"; const char* const companyName = "Rory Jaffe"; - const char* const versionString = "5.3.0.0"; - const int versionNumber = 0x5030000; + const char* const versionString = "5.3.0.1"; + const int versionNumber = 0x5030001; } #endif diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp index eb2349e90..e64d8d8b9 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp @@ -36,6 +36,8 @@ void AudioProcessLoadMeasurer::reset (double sampleRate, int blockSize) cpuUsageProportion = 0; xruns = 0; + samplesPerBlock = blockSize; + if (sampleRate > 0.0 && blockSize > 0) { msPerSample = 1000.0 / sampleRate; @@ -77,6 +79,9 @@ AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p) AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p, int numSamplesInBlock) : owner (p), startTime (Time::getMillisecondCounterHiRes()), samplesInBlock (numSamplesInBlock) { + // numSamplesInBlock should never be zero. Did you remember to call AudioProcessLoadMeasurer::reset(), + // passing the expected samples per block? + jassert (numSamplesInBlock); } AudioProcessLoadMeasurer::ScopedTimer::~ScopedTimer() diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 7917c325c..96a4a24d8 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -104,13 +104,13 @@ namespace FloatVectorHelpers #define JUCE_BEGIN_VEC_OP \ using Mode = FloatVectorHelpers::ModeType::Mode; \ { \ - const int numLongOps = num / Mode::numParallel; + const auto numLongOps = num / Mode::numParallel; #define JUCE_FINISH_VEC_OP(normalOp) \ num &= (Mode::numParallel - 1); \ if (num == 0) return; \ } \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ JUCE_BEGIN_VEC_OP \ @@ -268,13 +268,13 @@ namespace FloatVectorHelpers using Mode = FloatVectorHelpers::ModeType::Mode; \ if (Mode::numParallel > 1) \ { \ - const int numLongOps = num / Mode::numParallel; + const auto numLongOps = num / Mode::numParallel; #define JUCE_FINISH_VEC_OP(normalOp) \ num &= (Mode::numParallel - 1); \ if (num == 0) return; \ } \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ JUCE_BEGIN_VEC_OP \ @@ -304,22 +304,22 @@ namespace FloatVectorHelpers //============================================================================== #else #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #endif //============================================================================== #define JUCE_VEC_LOOP(vecOp, srcLoad, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ + for (auto i = (decltype (numLongOps)) 0; i < numLongOps; ++i) \ { \ locals (srcLoad, dstLoad); \ dstStore (dest, vecOp); \ @@ -327,7 +327,7 @@ namespace FloatVectorHelpers } #define JUCE_VEC_LOOP_TWO_SOURCES(vecOp, src1Load, src2Load, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ + for (auto i = (decltype (numLongOps)) 0; i < numLongOps; ++i) \ { \ locals (src1Load, src2Load); \ dstStore (dest, vecOp); \ @@ -335,7 +335,7 @@ namespace FloatVectorHelpers } #define JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD(vecOp, src1Load, src2Load, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ + for (auto i = (decltype (numLongOps)) 0; i < numLongOps; ++i) \ { \ locals (src1Load, src2Load, dstLoad); \ dstStore (dest, vecOp); \ @@ -362,9 +362,10 @@ namespace FloatVectorHelpers using Type = typename Mode::Type; using ParallelType = typename Mode::ParallelType; - static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept + template + static Type findMinOrMax (const Type* src, Size num, const bool isMinimum) noexcept { - int numLongOps = num / Mode::numParallel; + auto numLongOps = num / Mode::numParallel; if (numLongOps > 1) { @@ -421,20 +422,24 @@ namespace FloatVectorHelpers num &= (Mode::numParallel - 1); src += Mode::numParallel; - for (int i = 0; i < num; ++i) + for (auto i = (decltype (num)) 0; i < num; ++i) result = isMinimum ? jmin (result, src[i]) : jmax (result, src[i]); return result; } - return isMinimum ? juce::findMinimum (src, num) - : juce::findMaximum (src, num); + if (num <= 0) + return 0; + + return isMinimum ? *std::min_element (src, src + num) + : *std::max_element (src, src + num); } - static Range findMinAndMax (const Type* src, int num) noexcept + template + static Range findMinAndMax (const Type* src, Size num) noexcept { - int numLongOps = num / Mode::numParallel; + auto numLongOps = num / Mode::numParallel; if (numLongOps > 1) { @@ -475,7 +480,7 @@ namespace FloatVectorHelpers num &= (Mode::numParallel - 1); src += Mode::numParallel; - for (int i = 0; i < num; ++i) + for (auto i = (decltype (num)) 0; i < num; ++i) result = result.getUnionWith (src[i]); return result; @@ -485,547 +490,941 @@ namespace FloatVectorHelpers } }; #endif -} //============================================================================== namespace { - #if JUCE_USE_VDSP_FRAMEWORK - // This casts away constness to account for slightly different vDSP function signatures - // in OSX 10.8 SDK and below. Can be safely removed once those SDKs are obsolete. - template - ValueType* osx108sdkCompatibilityCast (const ValueType* arg) noexcept { return const_cast (arg); } - #endif -} + template + void clear (float* dest, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclr (dest, 1, (vDSP_Length) num); + #else + zeromem (dest, (size_t) num * sizeof (float)); + #endif + } -//============================================================================== -void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclr (dest, 1, (size_t) num); - #else - zeromem (dest, (size_t) num * sizeof (float)); - #endif -} + template + void clear (double* dest, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclrD (dest, 1, (vDSP_Length) num); + #else + zeromem (dest, (size_t) num * sizeof (double)); + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclrD (dest, 1, (size_t) num); - #else - zeromem (dest, (size_t) num * sizeof (double)); - #endif -} + template + void fill (float* dest, float valueToFill, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vfill (&valueToFill, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, + val, + JUCE_LOAD_NONE, + const Mode::ParallelType val = Mode::load1 (valueToFill);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vfill (&valueToFill, dest, 1, (size_t) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, - const Mode::ParallelType val = Mode::load1 (valueToFill);) - #endif -} + template + void fill (double* dest, double valueToFill, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vfillD (&valueToFill, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, + val, + JUCE_LOAD_NONE, + const Mode::ParallelType val = Mode::load1 (valueToFill);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::fill (double* dest, double valueToFill, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vfillD (&valueToFill, dest, 1, (size_t) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, - const Mode::ParallelType val = Mode::load1 (valueToFill);) - #endif -} + template + void copyWithMultiply (float* dest, const float* src, float multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::copy (float* dest, const float* src, int num) noexcept -{ - memcpy (dest, src, (size_t) num * sizeof (float)); -} + template + void copyWithMultiply (double* dest, const double* src, double multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept -{ - memcpy (dest, src, (size_t) num * sizeof (double)); -} + template + void add (float* dest, float amount, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsadd (dest, 1, &amount, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, + Mode::add (d, amountToAdd), + JUCE_LOAD_DEST, + const Mode::ParallelType amountToAdd = Mode::load1 (amount);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} + template + void add (double* dest, double amount, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, + Mode::add (d, amountToAdd), + JUCE_LOAD_DEST, + const Mode::ParallelType amountToAdd = Mode::load1 (amount);) + } -void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} + template + void add (float* dest, const float* src, float amount, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsadd (src, 1, &amount, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, + Mode::add (am, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType am = Mode::load1 (amount);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsadd (dest, 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, - const Mode::ParallelType amountToAdd = Mode::load1 (amount);) - #endif -} + template + void add (double* dest, const double* src, double amount, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsaddD (src, 1, &amount, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, + Mode::add (am, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType am = Mode::load1 (amount);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, - const Mode::ParallelType amountToAdd = Mode::load1 (amount);) -} + template + void add (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], + Mode::add (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, float amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsadd (osx108sdkCompatibilityCast (src), 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType am = Mode::load1 (amount);) - #endif -} + template + void add (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], + Mode::add (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, double amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsaddD (osx108sdkCompatibilityCast (src), 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType am = Mode::load1 (amount);) - #endif -} + template + void add (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], + Mode::add (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} + template + void add (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], + Mode::add (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} + template + void subtract (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], + Mode::sub (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void subtract (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], + Mode::sub (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void subtract (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], + Mode::sub (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} + template + void subtract (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], + Mode::sub (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} + template + void addWithMultiply (float* dest, const float* src, float multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsma (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, + Mode::add (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void addWithMultiply (double* dest, const double* src, double multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmaD (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, + Mode::add (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void addWithMultiply (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vma ((float*) src1, 1, (float*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], + Mode::add (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsma (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} + template + void addWithMultiply (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmaD ((double*) src1, 1, (double*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], + Mode::add (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmaD (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} + template + void subtractWithMultiply (float* dest, const float* src, float multiplier, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, + Mode::sub (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vma ((float*) src1, 1, (float*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void subtractWithMultiply (double* dest, const double* src, double multiplier, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, + Mode::sub (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmaD ((double*) src1, 1, (double*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void subtractWithMultiply (float* dest, const float* src1, const float* src2, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], + Mode::sub (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + } -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, Mode::sub (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, + template + void subtractWithMultiply (double* dest, const double* src1, const double* src2, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], + Mode::sub (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + } + + template + void multiply (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], + Mode::mul (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } + + template + void multiply (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], + Mode::mul (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } + + template + void multiply (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], + Mode::mul (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void multiply (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], + Mode::mul (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void multiply (float* dest, float multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, + Mode::mul (d, mult), + JUCE_LOAD_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) -} + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, Mode::sub (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, + template + void multiply (double* dest, double multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, + Mode::mul (d, mult), + JUCE_LOAD_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) -} + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], Mode::sub (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) -} + template + void multiply (float* dest, const float* src, float multiplier, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + } + + template + void multiply (double* dest, const double* src, double multiplier, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + } + + template + void negate (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num); + #else + copyWithMultiply (dest, src, -1.0f, num); + #endif + } + + template + void negate (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num); + #else + copyWithMultiply (dest, src, -1.0f, num); + #endif + } + + template + void abs (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); + #else + FloatVectorHelpers::signMask32 signMask; + signMask.i = 0x7fffffffUL; + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = std::abs (src[i]), + Mode::bit_and (s, mask), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mask = Mode::load1 (signMask.f);) + + ignoreUnused (signMask); + #endif + } + + template + void abs (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); + #else + FloatVectorHelpers::signMask64 signMask; + signMask.i = 0x7fffffffffffffffULL; + + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = std::abs (src[i]), + Mode::bit_and (s, mask), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mask = Mode::load1 (signMask.d);) + + ignoreUnused (signMask); + #endif + } + + template + void min (float* dest, const float* src, float comp, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), + Mode::min (s, cmp), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) + } + + template + void min (double* dest, const double* src, double comp, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), + Mode::min (s, cmp), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) + } + + template + void min (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmin ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), + Mode::min (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void min (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vminD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), + Mode::min (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void max (float* dest, const float* src, float comp, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), + Mode::max (s, cmp), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) + } + + template + void max (double* dest, const double* src, double comp, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), + Mode::max (s, cmp), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) + } + + template + void max (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmax ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), + Mode::max (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void max (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmaxD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), + Mode::max (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void clip (float* dest, const float* src, float low, float high, Size num) noexcept + { + jassert (high >= low); + + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclip ((float*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), + Mode::max (Mode::min (s, hi), lo), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType lo = Mode::load1 (low); + const Mode::ParallelType hi = Mode::load1 (high);) + #endif + } + + template + void clip (double* dest, const double* src, double low, double high, Size num) noexcept + { + jassert (high >= low); + + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclipD ((double*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), + Mode::max (Mode::min (s, hi), lo), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType lo = Mode::load1 (low); + const Mode::ParallelType hi = Mode::load1 (high);) + #endif + } + + template + Range findMinAndMax (const float* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinAndMax (src, num); + #else + return Range::findMinAndMax (src, num); + #endif + } + + template + Range findMinAndMax (const double* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinAndMax (src, num); + #else + return Range::findMinAndMax (src, num); + #endif + } + + template + float findMinimum (const float* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); + #else + return juce::findMinimum (src, num); + #endif + } + + template + double findMinimum (const double* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); + #else + return juce::findMinimum (src, num); + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept + template + float findMaximum (const float* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); + #else + return juce::findMaximum (src, num); + #endif + } + + template + double findMaximum (const double* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); + #else + return juce::findMaximum (src, num); + #endif + } + + template + void convertFixedToFloat (float* dest, const int* src, float multiplier, Size num) noexcept + { + #if JUCE_USE_ARM_NEON + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, + vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), + JUCE_LOAD_NONE, + JUCE_INCREMENT_SRC_DEST, ) + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, + Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 (reinterpret_cast (src)))), + JUCE_LOAD_NONE, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } + +} // namespace +} // namespace FloatVectorHelpers + +//============================================================================== +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::clear (FloatType* dest, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], Mode::sub (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) + FloatVectorHelpers::clear (dest, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::fill (FloatType* dest, + FloatType valueToFill, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif + FloatVectorHelpers::fill (dest, valueToFill, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::copy (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif + memcpy (dest, src, (size_t) numValues * sizeof (FloatType)); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src1, const float* src2, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::copyWithMultiply (FloatType* dest, + const FloatType* src, + FloatType multiplier, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::copyWithMultiply (dest, src, multiplier, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src1, const double* src2, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::add (FloatType* dest, + FloatType amountToAdd, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::add (dest, amountToAdd, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::add (FloatType* dest, + const FloatType* src, + FloatType amount, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif + FloatVectorHelpers::add (dest, src, amount, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::add (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif + FloatVectorHelpers::add (dest, src, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, float multiplier, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::add (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) + FloatVectorHelpers::add (dest, src1, src2, num); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, double multiplier, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::subtract (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) + FloatVectorHelpers::subtract (dest, src, numValues); } -void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::subtract (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num); - #else - copyWithMultiply (dest, src, -1.0f, num); - #endif + FloatVectorHelpers::subtract (dest, src1, src2, num); } -void FloatVectorOperations::negate (double* dest, const double* src, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::addWithMultiply (FloatType* dest, + const FloatType* src, + FloatType multiplier, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num); - #else - copyWithMultiply (dest, src, -1.0f, num); - #endif + FloatVectorHelpers::addWithMultiply (dest, src, multiplier, numValues); } -void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::addWithMultiply (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); - #else - FloatVectorHelpers::signMask32 signMask; - signMask.i = 0x7fffffffUL; - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = std::abs (src[i]), Mode::bit_and (s, mask), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mask = Mode::load1 (signMask.f);) - - ignoreUnused (signMask); - #endif + FloatVectorHelpers::addWithMultiply (dest, src1, src2, num); } -void FloatVectorOperations::abs (double* dest, const double* src, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::subtractWithMultiply (FloatType* dest, + const FloatType* src, + FloatType multiplier, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); - #else - FloatVectorHelpers::signMask64 signMask; - signMask.i = 0x7fffffffffffffffULL; - - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = std::abs (src[i]), Mode::bit_and (s, mask), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mask = Mode::load1 (signMask.d);) - - ignoreUnused (signMask); - #endif + FloatVectorHelpers::subtractWithMultiply (dest, src, multiplier, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::subtractWithMultiply (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - #if JUCE_USE_ARM_NEON - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, - vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, ) - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, - Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 (reinterpret_cast (src)))), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif + FloatVectorHelpers::subtractWithMultiply (dest, src1, src2, num); } -void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src, float comp, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::multiply (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) + FloatVectorHelpers::multiply (dest, src, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src, double comp, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::multiply (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) + FloatVectorHelpers::multiply (dest, src1, src2, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src1, const float* src2, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::multiply (FloatType* dest, + FloatType multiplier, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmin ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::multiply (dest, multiplier, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src1, const double* src2, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::multiply (FloatType* dest, + const FloatType* src, + FloatType multiplier, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vminD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::multiply (dest, src, multiplier, num); } -void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src, float comp, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::negate (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) + FloatVectorHelpers::negate (dest, src, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src, double comp, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::abs (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) + FloatVectorHelpers::abs (dest, src, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src1, const float* src2, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::min (FloatType* dest, + const FloatType* src, + FloatType comp, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmax ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::min (dest, src, comp, num); } -void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src1, const double* src2, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::min (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmaxD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::min (dest, src1, src2, num); } -void JUCE_CALLTYPE FloatVectorOperations::clip (float* dest, const float* src, float low, float high, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::max (FloatType* dest, + const FloatType* src, + FloatType comp, + CountType num) noexcept { - jassert(high >= low); - - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclip ((float*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) - #endif + FloatVectorHelpers::max (dest, src, comp, num); } -void JUCE_CALLTYPE FloatVectorOperations::clip (double* dest, const double* src, double low, double high, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::max (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - jassert(high >= low); - - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclipD ((double*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) - #endif + FloatVectorHelpers::max (dest, src1, src2, num); } -Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept +template +void JUCE_CALLTYPE detail::FloatVectorOperationsBase::clip (FloatType* dest, + const FloatType* src, + FloatType low, + FloatType high, + CountType num) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinAndMax (src, num); - #else - return Range::findMinAndMax (src, num); - #endif + FloatVectorHelpers::clip (dest, src, low, high, num); } -Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const double* src, int num) noexcept +template +Range JUCE_CALLTYPE detail::FloatVectorOperationsBase::findMinAndMax (const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinAndMax (src, num); - #else - return Range::findMinAndMax (src, num); - #endif + return FloatVectorHelpers::findMinAndMax (src, numValues); } -float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept +template +FloatType JUCE_CALLTYPE detail::FloatVectorOperationsBase::findMinimum (const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); - #else - return juce::findMinimum (src, num); - #endif + return FloatVectorHelpers::findMinimum (src, numValues); } -double JUCE_CALLTYPE FloatVectorOperations::findMinimum (const double* src, int num) noexcept +template +FloatType JUCE_CALLTYPE detail::FloatVectorOperationsBase::findMaximum (const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); - #else - return juce::findMinimum (src, num); - #endif + return FloatVectorHelpers::findMaximum (src, numValues); } -float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept +template struct detail::FloatVectorOperationsBase; +template struct detail::FloatVectorOperationsBase; +template struct detail::FloatVectorOperationsBase; +template struct detail::FloatVectorOperationsBase; + +void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, size_t num) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); - #else - return juce::findMaximum (src, num); - #endif + FloatVectorHelpers::convertFixedToFloat (dest, src, multiplier, num); } -double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int num) noexcept +void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); - #else - return juce::findMaximum (src, num); - #endif + FloatVectorHelpers::convertFixedToFloat (dest, src, multiplier, num); } intptr_t JUCE_CALLTYPE FloatVectorOperations::getFpStatusRegister() noexcept @@ -1033,14 +1432,16 @@ intptr_t JUCE_CALLTYPE FloatVectorOperations::getFpStatusRegister() noexcept intptr_t fpsr = 0; #if JUCE_INTEL && JUCE_USE_SSE_INTRINSICS fpsr = static_cast (_mm_getcsr()); - #elif defined (__arm64__) || defined (__aarch64__) || JUCE_USE_ARM_NEON - #if defined (__arm64__) || defined (__aarch64__) - asm volatile("mrs %0, fpcr" : "=r" (fpsr)); + #elif defined(__arm64__) || defined(__aarch64__) || JUCE_USE_ARM_NEON + #if defined(__arm64__) || defined(__aarch64__) + asm volatile("mrs %0, fpcr" + : "=r"(fpsr)); #elif JUCE_USE_ARM_NEON - asm volatile("vmrs %0, fpscr" : "=r" (fpsr)); + asm volatile("vmrs %0, fpscr" + : "=r"(fpsr)); #endif #else - #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) + #if ! (defined(JUCE_INTEL) || defined(JUCE_ARM)) jassertfalse; // No support for getting the floating point status register for your platform #endif #endif @@ -1055,14 +1456,18 @@ void JUCE_CALLTYPE FloatVectorOperations::setFpStatusRegister (intptr_t fpsr) no // which aggressively optimises away the variable otherwise volatile auto fpsr_w = static_cast (fpsr); _mm_setcsr (fpsr_w); - #elif defined (__arm64__) || defined (__aarch64__) || JUCE_USE_ARM_NEON - #if defined (__arm64__) || defined (__aarch64__) - asm volatile("msr fpcr, %0" : : "ri" (fpsr)); + #elif defined(__arm64__) || defined(__aarch64__) || JUCE_USE_ARM_NEON + #if defined(__arm64__) || defined(__aarch64__) + asm volatile("msr fpcr, %0" + : + : "ri"(fpsr)); #elif JUCE_USE_ARM_NEON - asm volatile("vmsr fpscr, %0" : : "ri" (fpsr)); + asm volatile("vmsr fpscr, %0" + : + : "ri"(fpsr)); #endif #else - #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) + #if ! (defined(JUCE_INTEL) || defined(JUCE_ARM)) jassertfalse; // No support for getting the floating point status register for your platform #endif ignoreUnused (fpsr); @@ -1071,7 +1476,7 @@ void JUCE_CALLTYPE FloatVectorOperations::setFpStatusRegister (intptr_t fpsr) no void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined(__arm64__) || defined(__aarch64__)) #if JUCE_USE_SSE_INTRINSICS intptr_t mask = _MM_FLUSH_ZERO_MASK; #else /*JUCE_USE_ARM_NEON*/ @@ -1079,7 +1484,7 @@ void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnab #endif setFpStatusRegister ((getFpStatusRegister() & (~mask)) | (shouldEnable ? mask : 0)); #else - #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) + #if ! (defined(JUCE_INTEL) || defined(JUCE_ARM)) jassertfalse; // No support for flush to zero mode on your platform #endif ignoreUnused (shouldEnable); @@ -1088,7 +1493,7 @@ void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnab void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport (bool shouldDisable) noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined(__arm64__) || defined(__aarch64__)) #if JUCE_USE_SSE_INTRINSICS intptr_t mask = 0x8040; #else /*JUCE_USE_ARM_NEON*/ @@ -1099,7 +1504,7 @@ void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport (bool #else ignoreUnused (shouldDisable); - #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) + #if ! (defined(JUCE_INTEL) || defined(JUCE_ARM)) jassertfalse; // No support for disable denormals mode on your platform #endif #endif @@ -1107,7 +1512,7 @@ void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport (bool bool JUCE_CALLTYPE FloatVectorOperations::areDenormalsDisabled() noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined(__arm64__) || defined(__aarch64__)) #if JUCE_USE_SSE_INTRINSICS intptr_t mask = 0x8040; #else /*JUCE_USE_ARM_NEON*/ @@ -1122,7 +1527,7 @@ bool JUCE_CALLTYPE FloatVectorOperations::areDenormalsDisabled() noexcept ScopedNoDenormals::ScopedNoDenormals() noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined(__arm64__) || defined(__aarch64__)) #if JUCE_USE_SSE_INTRINSICS intptr_t mask = 0x8040; #else /*JUCE_USE_ARM_NEON*/ @@ -1136,9 +1541,9 @@ ScopedNoDenormals::ScopedNoDenormals() noexcept ScopedNoDenormals::~ScopedNoDenormals() noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined(__arm64__) || defined(__aarch64__)) FloatVectorOperations::setFpStatusRegister (fpsr); - #endif + #endif } diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h index c0bb4e422..b05d6f0f7 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -32,187 +32,123 @@ namespace juce #endif class ScopedNoDenormals; -//============================================================================== -/** - A collection of simple vector operations on arrays of floats, accelerated with - SIMD instructions where possible. - - @tags{Audio} -*/ -class JUCE_API FloatVectorOperations +#if ! DOXYGEN +namespace detail { -public: - //============================================================================== - /** Clears a vector of floats. */ - static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept; - - /** Clears a vector of doubles. */ - static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept; - - /** Copies a repeated value into a vector of floats. */ - static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept; - - /** Copies a repeated value into a vector of doubles. */ - static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept; - - /** Copies a vector of floats. */ - static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept; - - /** Copies a vector of doubles. */ - static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept; - - /** Copies a vector of floats, multiplying each value by a given multiplier */ - static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; - - /** Copies a vector of doubles, multiplying each value by a given multiplier */ - static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; - - /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept; - - /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept; - - /** Adds a fixed value to each source value and stores it in the destination array. */ - static void JUCE_CALLTYPE add (float* dest, const float* src, float amount, int numValues) noexcept; - - /** Adds a fixed value to each source value and stores it in the destination array. */ - static void JUCE_CALLTYPE add (double* dest, const double* src, double amount, int numValues) noexcept; - - /** Adds the source values to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; - - /** Adds the source values to the destination values. */ - static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept; - - /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE add (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE add (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Subtracts the source values from the destination values. */ - static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; - - /** Subtracts the source values from the destination values. */ - static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept; - - /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE subtract (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE subtract (double* dest, const double* src1, const double* src2, int num) noexcept; - /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; - - /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; - - /** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Multiplies the destination values by the source values. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept; - - /** Multiplies the destination values by the source values. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept; - - /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src1, const float* src2, int numValues) noexcept; - - /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src1, const double* src2, int numValues) noexcept; - - /** Multiplies each of the destination values by a fixed multiplier. */ - static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept; - - /** Multiplies each of the destination values by a fixed multiplier. */ - static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept; +template +struct FloatVectorOperationsBase +{ + static void JUCE_CALLTYPE clear (FloatType* dest, CountType numValues) noexcept; + static void JUCE_CALLTYPE fill (FloatType* dest, FloatType valueToFill, CountType numValues) noexcept; + static void JUCE_CALLTYPE copy (FloatType* dest, const FloatType* src, CountType numValues) noexcept; + static void JUCE_CALLTYPE copyWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; + static void JUCE_CALLTYPE add (FloatType* dest, FloatType amountToAdd, CountType numValues) noexcept; + static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src, FloatType amount, CountType numValues) noexcept; + static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src, CountType numValues) noexcept; + static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; + static void JUCE_CALLTYPE subtract (FloatType* dest, const FloatType* src, CountType numValues) noexcept; + static void JUCE_CALLTYPE subtract (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; + static void JUCE_CALLTYPE addWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; + static void JUCE_CALLTYPE addWithMultiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; + static void JUCE_CALLTYPE subtractWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; + static void JUCE_CALLTYPE subtractWithMultiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; + static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, CountType numValues) noexcept; + static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType numValues) noexcept; + static void JUCE_CALLTYPE multiply (FloatType* dest, FloatType multiplier, CountType numValues) noexcept; + static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType num) noexcept; + static void JUCE_CALLTYPE negate (FloatType* dest, const FloatType* src, CountType numValues) noexcept; + static void JUCE_CALLTYPE abs (FloatType* dest, const FloatType* src, CountType numValues) noexcept; + static void JUCE_CALLTYPE min (FloatType* dest, const FloatType* src, FloatType comp, CountType num) noexcept; + static void JUCE_CALLTYPE min (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; + static void JUCE_CALLTYPE max (FloatType* dest, const FloatType* src, FloatType comp, CountType num) noexcept; + static void JUCE_CALLTYPE max (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; + static void JUCE_CALLTYPE clip (FloatType* dest, const FloatType* src, FloatType low, FloatType high, CountType num) noexcept; + static Range JUCE_CALLTYPE findMinAndMax (const FloatType* src, CountType numValues) noexcept; + static FloatType JUCE_CALLTYPE findMinimum (const FloatType* src, CountType numValues) noexcept; + static FloatType JUCE_CALLTYPE findMaximum (const FloatType* src, CountType numValues) noexcept; +}; - /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src, float multiplier, int num) noexcept; +template +struct NameForwarder; - /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src, double multiplier, int num) noexcept; +template +struct NameForwarder : Head {}; - /** Copies a source vector to a destination, negating each value. */ - static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; +template +struct NameForwarder : Head, NameForwarder +{ + using Head::clear; + using NameForwarder::clear; - /** Copies a source vector to a destination, negating each value. */ - static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; + using Head::fill; + using NameForwarder::fill; - /** Copies a source vector to a destination, taking the absolute of each value. */ - static void JUCE_CALLTYPE abs (float* dest, const float* src, int numValues) noexcept; + using Head::copy; + using NameForwarder::copy; - /** Copies a source vector to a destination, taking the absolute of each value. */ - static void JUCE_CALLTYPE abs (double* dest, const double* src, int numValues) noexcept; + using Head::copyWithMultiply; + using NameForwarder::copyWithMultiply; - /** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ - static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; + using Head::add; + using NameForwarder::add; - /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE min (float* dest, const float* src, float comp, int num) noexcept; + using Head::subtract; + using NameForwarder::subtract; - /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE min (double* dest, const double* src, double comp, int num) noexcept; + using Head::addWithMultiply; + using NameForwarder::addWithMultiply; - /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE min (float* dest, const float* src1, const float* src2, int num) noexcept; + using Head::subtractWithMultiply; + using NameForwarder::subtractWithMultiply; - /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE min (double* dest, const double* src1, const double* src2, int num) noexcept; + using Head::multiply; + using NameForwarder::multiply; - /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE max (float* dest, const float* src, float comp, int num) noexcept; + using Head::negate; + using NameForwarder::negate; - /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE max (double* dest, const double* src, double comp, int num) noexcept; + using Head::abs; + using NameForwarder::abs; - /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE max (float* dest, const float* src1, const float* src2, int num) noexcept; + using Head::min; + using NameForwarder::min; - /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE max (double* dest, const double* src1, const double* src2, int num) noexcept; + using Head::max; + using NameForwarder::max; - /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ - static void JUCE_CALLTYPE clip (float* dest, const float* src, float low, float high, int num) noexcept; + using Head::clip; + using NameForwarder::clip; - /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ - static void JUCE_CALLTYPE clip (double* dest, const double* src, double low, double high, int num) noexcept; + using Head::findMinAndMax; + using NameForwarder::findMinAndMax; - /** Finds the minimum and maximum values in the given array. */ - static Range JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept; + using Head::findMinimum; + using NameForwarder::findMinimum; - /** Finds the minimum and maximum values in the given array. */ - static Range JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept; + using Head::findMaximum; + using NameForwarder::findMaximum; +}; - /** Finds the minimum value in the given array. */ - static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept; +} // namespace detail +#endif - /** Finds the minimum value in the given array. */ - static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept; +//============================================================================== +/** + A collection of simple vector operations on arrays of floats, accelerated with + SIMD instructions where possible. - /** Finds the maximum value in the given array. */ - static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept; + @tags{Audio} +*/ +class JUCE_API FloatVectorOperations : public detail::NameForwarder, + detail::FloatVectorOperationsBase, + detail::FloatVectorOperationsBase, + detail::FloatVectorOperationsBase> +{ +public: + static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept; - /** Finds the maximum value in the given array. */ - static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept; + static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, size_t num) noexcept; /** This method enables or disables the SSE/NEON flush-to-zero mode. */ static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h b/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h index c82b48868..d56f9c94b 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h @@ -32,7 +32,7 @@ ID: juce_audio_basics vendor: juce - version: 6.1.4 + version: 6.1.5 name: JUCE audio and MIDI data classes description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h index 179a6ed7f..03e5e2733 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h @@ -28,7 +28,7 @@ namespace universal_midi_packets { /** - This struct acts as a single-file namespace for Univeral MIDI Packet + This struct acts as a single-file namespace for Universal MIDI Packet functionality related to 7-bit SysEx. @tags{Audio} diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp index 0df6a783e..ec082c12c 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp @@ -43,16 +43,20 @@ MPEInstrument::MPEInstrument() noexcept mpeInstrumentFill (isMemberChannelSustained, false); pitchbendDimension.value = &MPENote::pitchbend; - pressureDimension.value = &MPENote::pressure; - timbreDimension.value = &MPENote::timbre; + pressureDimension.value = &MPENote::pressure; + timbreDimension.value = &MPENote::timbre; resetLastReceivedValues(); - legacyMode.isEnabled = false; - legacyMode.pitchbendRange = 2; legacyMode.channelRange = allChannels; } +MPEInstrument::MPEInstrument (MPEZoneLayout layout) + : MPEInstrument() +{ + setZoneLayout (layout); +} + MPEInstrument::~MPEInstrument() = default; //============================================================================== @@ -84,21 +88,30 @@ void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout) const ScopedLock sl (lock); legacyMode.isEnabled = false; - zoneLayout = newLayout; - resetLastReceivedValues(); + if (zoneLayout != newLayout) + { + zoneLayout = newLayout; + listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); }); + } } //============================================================================== void MPEInstrument::enableLegacyMode (int pitchbendRange, Range channelRange) { + if (legacyMode.isEnabled) + return; + releaseAllNotes(); const ScopedLock sl (lock); + legacyMode.isEnabled = true; legacyMode.pitchbendRange = pitchbendRange; legacyMode.channelRange = channelRange; + zoneLayout.clearAllZones(); + listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); }); } bool MPEInstrument::isLegacyModeEnabled() const noexcept @@ -117,7 +130,12 @@ void MPEInstrument::setLegacyModeChannelRange (Range channelRange) releaseAllNotes(); const ScopedLock sl (lock); - legacyMode.channelRange = channelRange; + + if (legacyMode.channelRange != channelRange) + { + legacyMode.channelRange = channelRange; + listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); }); + } } int MPEInstrument::getLegacyModePitchbendRange() const noexcept @@ -131,7 +149,12 @@ void MPEInstrument::setLegacyModePitchbendRange (int pitchbendRange) releaseAllNotes(); const ScopedLock sl (lock); - legacyMode.pitchbendRange = pitchbendRange; + + if (legacyMode.pitchbendRange != pitchbendRange) + { + legacyMode.pitchbendRange = pitchbendRange; + listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); }); + } } //============================================================================== @@ -242,7 +265,7 @@ void MPEInstrument::processMidiResetAllControllersMessage (const MidiMessage& me if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel())) { - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -260,7 +283,7 @@ void MPEInstrument::processMidiResetAllControllersMessage (const MidiMessage& me auto zone = (message.getChannel() == 1 ? zoneLayout.getLowerZone() : zoneLayout.getUpperZone()); - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -348,11 +371,11 @@ void MPEInstrument::noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity) { + const ScopedLock sl (lock); + if (notes.isEmpty() || ! isUsingChannel (midiChannel)) return; - const ScopedLock sl (lock); - if (auto* note = getNotePtr (midiChannel, midiNoteNumber)) { note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off; @@ -401,7 +424,7 @@ void MPEInstrument::polyAftertouch (int midiChannel, int midiNoteNumber, MPEValu { const ScopedLock sl (lock); - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -435,7 +458,7 @@ void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, M { if (dimension.trackingMode == allNotesOnChannel) { - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -464,7 +487,7 @@ void MPEInstrument::updateDimensionMaster (bool isLowerZone, MPEDimension& dimen if (! zone.isActive()) return; - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -573,7 +596,7 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool auto zone = (midiChannel == 1 ? zoneLayout.getLowerZone() : zoneLayout.getUpperZone()); - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -605,11 +628,15 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool if (! legacyMode.isEnabled) { if (zone.isLowerZone()) - for (auto i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i) + { + for (int i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i) isMemberChannelSustained[i - 1] = isDown; + } else - for (auto i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i) + { + for (int i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i) isMemberChannelSustained[i - 1] = isDown; + } } } } @@ -664,6 +691,17 @@ MPENote MPEInstrument::getNote (int index) const noexcept return notes[index]; } +MPENote MPEInstrument::getNoteWithID (uint16 noteID) const noexcept +{ + const ScopedLock sl (lock); + + for (auto& note : notes) + if (note.noteID == noteID) + return note; + + return {}; +} + //============================================================================== MPENote MPEInstrument::getMostRecentNote (int midiChannel) const noexcept { @@ -727,6 +765,8 @@ MPENote* MPEInstrument::getNotePtr (int midiChannel, TrackingMode mode) noexcept //============================================================================== const MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) const noexcept { + const ScopedLock sl (lock); + for (auto i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h index 6853f29f2..02cb82c29 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h @@ -38,10 +38,8 @@ namespace juce MPE. If you pass it a message, it will know what notes on what channels (if any) should be affected by that message. - The class has a Listener class with the three callbacks MPENoteAdded, - MPENoteChanged, and MPENoteFinished. Implement such a - Listener class to react to note changes and trigger some functionality for - your application that depends on the MPE note state. + The class has a Listener class that can be used to react to note and + state changes and trigger some functionality for your application. For example, you can use this class to write an MPE visualiser. If you want to write a real-time audio synth with MPE functionality, @@ -59,11 +57,14 @@ class JUCE_API MPEInstrument This will construct an MPE instrument with inactive lower and upper zones. - In order to process incoming MIDI, call setZoneLayout, define the layout - via MIDI RPN messages, or set the instrument to legacy mode. + In order to process incoming MIDI messages call setZoneLayout, use the MPEZoneLayout + constructor, define the layout via MIDI RPN messages, or set the instrument to legacy mode. */ MPEInstrument() noexcept; + /** Constructs an MPE instrument with the specified zone layout. */ + MPEInstrument (MPEZoneLayout layout); + /** Destructor. */ virtual ~MPEInstrument(); @@ -229,6 +230,9 @@ class JUCE_API MPEInstrument */ MPENote getNote (int midiChannel, int midiNoteNumber) const noexcept; + /** Returns the note with a given ID. */ + MPENote getNoteWithID (uint16 noteID) const noexcept; + /** Returns the most recent note that is playing on the given midiChannel (this will be the note which has received the most recent note-on without a corresponding note-off), if there is such a note. Otherwise, this returns an @@ -244,8 +248,8 @@ class JUCE_API MPEInstrument MPENote getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept; //============================================================================== - /** Derive from this class to be informed about any changes in the expressive - MIDI notes played by this instrument. + /** Derive from this class to be informed about any changes in the MPE notes played + by this instrument, and any changes to its zone layout. Note: This listener type receives its callbacks immediately, and not via the message thread (so you might be for example in the MIDI thread). @@ -297,6 +301,11 @@ class JUCE_API MPEInstrument and should therefore stop playing. */ virtual void noteReleased (MPENote finishedNote) { ignoreUnused (finishedNote); } + + /** Implement this callback to be informed whenever the MPE zone layout + or legacy mode settings of this instrument have been changed. + */ + virtual void zoneLayoutChanged() {} }; //============================================================================== @@ -307,7 +316,9 @@ class JUCE_API MPEInstrument void removeListener (Listener* listenerToRemove); //============================================================================== - /** Puts the instrument into legacy mode. + /** Puts the instrument into legacy mode. If legacy mode is already enabled this method + does nothing. + As a side effect, this will discard all currently playing notes, and call noteReleased for all of them. @@ -360,9 +371,9 @@ class JUCE_API MPEInstrument struct LegacyMode { - bool isEnabled; + bool isEnabled = false; Range channelRange; - int pitchbendRange; + int pitchbendRange = 2; }; struct MPEDimension diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.h b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.h index 67495b7ca..11bfedd61 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.h @@ -115,7 +115,7 @@ struct JUCE_API MPENote */ MPEValue noteOnVelocity { MPEValue::minValue() }; - /** Current per-note pitchbend of the note (in units of MIDI pitchwheel + /** Current per-note pitchbend of the note (in units of MIDI pitchwheel position). This dimension can be modulated while the note sounds. Note: This value is not aware of the currently used pitchbend range, diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp index 19b9387e9..469e18763 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp @@ -25,12 +25,10 @@ namespace juce MPESynthesiser::MPESynthesiser() { - MPEZoneLayout zoneLayout; - zoneLayout.setLowerZone (15); - setZoneLayout (zoneLayout); } -MPESynthesiser::MPESynthesiser (MPEInstrument* mpeInstrument) : MPESynthesiserBase (mpeInstrument) +MPESynthesiser::MPESynthesiser (MPEInstrument& mpeInstrument) + : MPESynthesiserBase (mpeInstrument) { } @@ -314,7 +312,7 @@ void MPESynthesiser::turnOffAllVoices (bool allowTailOff) } // finally make sure the MPE Instrument also doesn't have any notes anymore. - instrument->releaseAllNotes(); + instrument.releaseAllNotes(); } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h index 652dd7f0d..4237d03b2 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h @@ -65,11 +65,10 @@ class JUCE_API MPESynthesiser : public MPESynthesiserBase /** Constructor to pass to the synthesiser a custom MPEInstrument object to handle the MPE note state, MIDI channel assignment etc. (in case you need custom logic for this that goes beyond MIDI and MPE). - The synthesiser will take ownership of this object. @see MPESynthesiserBase, MPEInstrument */ - MPESynthesiser (MPEInstrument* instrumentToUse); + MPESynthesiser (MPEInstrument& instrumentToUse); /** Destructor. */ ~MPESynthesiser() override; @@ -303,7 +302,7 @@ class JUCE_API MPESynthesiser : public MPESynthesiserBase private: //============================================================================== - bool shouldStealVoices = false; + std::atomic shouldStealVoices { false }; uint32 lastNoteOnCounter = 0; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser) diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp index e00bf7b21..7108eaff9 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp @@ -24,80 +24,79 @@ namespace juce { MPESynthesiserBase::MPESynthesiserBase() - : instrument (new MPEInstrument) + : instrument (defaultInstrument) { - instrument->addListener (this); + instrument.addListener (this); } -MPESynthesiserBase::MPESynthesiserBase (MPEInstrument* inst) +MPESynthesiserBase::MPESynthesiserBase (MPEInstrument& inst) : instrument (inst) { - jassert (instrument != nullptr); - instrument->addListener (this); + instrument.addListener (this); } //============================================================================== MPEZoneLayout MPESynthesiserBase::getZoneLayout() const noexcept { - return instrument->getZoneLayout(); + return instrument.getZoneLayout(); } void MPESynthesiserBase::setZoneLayout (MPEZoneLayout newLayout) { - instrument->setZoneLayout (newLayout); + instrument.setZoneLayout (newLayout); } //============================================================================== void MPESynthesiserBase::enableLegacyMode (int pitchbendRange, Range channelRange) { - instrument->enableLegacyMode (pitchbendRange, channelRange); + instrument.enableLegacyMode (pitchbendRange, channelRange); } bool MPESynthesiserBase::isLegacyModeEnabled() const noexcept { - return instrument->isLegacyModeEnabled(); + return instrument.isLegacyModeEnabled(); } Range MPESynthesiserBase::getLegacyModeChannelRange() const noexcept { - return instrument->getLegacyModeChannelRange(); + return instrument.getLegacyModeChannelRange(); } void MPESynthesiserBase::setLegacyModeChannelRange (Range channelRange) { - instrument->setLegacyModeChannelRange (channelRange); + instrument.setLegacyModeChannelRange (channelRange); } int MPESynthesiserBase::getLegacyModePitchbendRange() const noexcept { - return instrument->getLegacyModePitchbendRange(); + return instrument.getLegacyModePitchbendRange(); } void MPESynthesiserBase::setLegacyModePitchbendRange (int pitchbendRange) { - instrument->setLegacyModePitchbendRange (pitchbendRange); + instrument.setLegacyModePitchbendRange (pitchbendRange); } //============================================================================== void MPESynthesiserBase::setPressureTrackingMode (TrackingMode modeToUse) { - instrument->setPressureTrackingMode (modeToUse); + instrument.setPressureTrackingMode (modeToUse); } void MPESynthesiserBase::setPitchbendTrackingMode (TrackingMode modeToUse) { - instrument->setPitchbendTrackingMode (modeToUse); + instrument.setPitchbendTrackingMode (modeToUse); } void MPESynthesiserBase::setTimbreTrackingMode (TrackingMode modeToUse) { - instrument->setTimbreTrackingMode (modeToUse); + instrument.setTimbreTrackingMode (modeToUse); } //============================================================================== void MPESynthesiserBase::handleMidiEvent (const MidiMessage& m) { - instrument->processNextMidiEvent (m); + instrument.processNextMidiEvent (m); } //============================================================================== @@ -148,7 +147,7 @@ void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate) if (sampleRate != newRate) { const ScopedLock sl (noteStateLock); - instrument->releaseAllNotes(); + instrument.releaseAllNotes(); sampleRate = newRate; } } diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h index a7978db55..6e8cab876 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h @@ -52,13 +52,12 @@ struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener /** Constructor. - If you use this constructor, the synthesiser will take ownership of the - provided instrument object, and will use it internally to handle the - MPE note state logic. + If you use this constructor, the synthesiser will use the provided instrument + object to handle the MPE note state logic. This is useful if you want to use an instance of your own class derived from MPEInstrument for the MPE logic. */ - MPESynthesiserBase (MPEInstrument* instrument); + MPESynthesiserBase (MPEInstrument& instrument); //============================================================================== /** Returns the synthesiser's internal MPE zone layout. @@ -200,10 +199,12 @@ struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener protected: //============================================================================== /** @internal */ - std::unique_ptr instrument; + MPEInstrument& instrument; private: //============================================================================== + MPEInstrument defaultInstrument { MPEZone (MPEZone::Type::lower, 15) }; + CriticalSection noteStateLock; double sampleRate = 0.0; int minimumSubBlockSize = 32; diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp index 24ecfbeb0..8eaa435a7 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp @@ -52,25 +52,25 @@ int MPEChannelAssigner::findMidiChannelForNewNote (int noteNumber) noexcept if (numChannels <= 1) return firstChannel; - for (auto ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) + for (int ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) { - if (midiChannels[ch].isFree() && midiChannels[ch].lastNotePlayed == noteNumber) + if (midiChannels[(size_t) ch].isFree() && midiChannels[(size_t) ch].lastNotePlayed == noteNumber) { midiChannelLastAssigned = ch; - midiChannels[ch].notes.add (noteNumber); + midiChannels[(size_t) ch].notes.add (noteNumber); return ch; } } - for (auto ch = midiChannelLastAssigned + channelIncrement; ; ch += channelIncrement) + for (int ch = midiChannelLastAssigned + channelIncrement; ; ch += channelIncrement) { if (ch == lastChannel + channelIncrement) // loop wrap-around ch = firstChannel; - if (midiChannels[ch].isFree()) + if (midiChannels[(size_t) ch].isFree()) { midiChannelLastAssigned = ch; - midiChannels[ch].notes.add (noteNumber); + midiChannels[(size_t) ch].notes.add (noteNumber); return ch; } @@ -79,11 +79,21 @@ int MPEChannelAssigner::findMidiChannelForNewNote (int noteNumber) noexcept } midiChannelLastAssigned = findMidiChannelPlayingClosestNonequalNote (noteNumber); - midiChannels[midiChannelLastAssigned].notes.add (noteNumber); + midiChannels[(size_t) midiChannelLastAssigned].notes.add (noteNumber); return midiChannelLastAssigned; } +int MPEChannelAssigner::findMidiChannelForExistingNote (int noteNumber) noexcept +{ + const auto iter = std::find_if (midiChannels.cbegin(), midiChannels.cend(), [&] (auto& ch) + { + return std::find (ch.notes.begin(), ch.notes.end(), noteNumber) != ch.notes.end(); + }); + + return iter != midiChannels.cend() ? (int) std::distance (midiChannels.cbegin(), iter) : -1; +} + void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel) { const auto removeNote = [] (MidiChannel& ch, int noteNum) @@ -99,7 +109,7 @@ void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel) if (midiChannel >= 0 && midiChannel <= 16) { - removeNote (midiChannels[midiChannel], noteNumber); + removeNote (midiChannels[(size_t) midiChannel], noteNumber); return; } @@ -126,9 +136,9 @@ int MPEChannelAssigner::findMidiChannelPlayingClosestNonequalNote (int noteNumbe auto channelWithClosestNote = firstChannel; int closestNoteDistance = 127; - for (auto ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) + for (int ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) { - for (auto note : midiChannels[ch].notes) + for (auto note : midiChannels[(size_t) ch].notes) { auto noteDistance = std::abs (note - noteNumber); @@ -296,24 +306,35 @@ struct MPEUtilsUnitTests : public UnitTest // check that channels are assigned in correct order int noteNum = 60; for (int ch = 2; ch <= 16; ++ch) - expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch); + { + expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch); + expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch); + + ++noteNum; + } // check that note-offs are processed channelAssigner.noteOff (60); expectEquals (channelAssigner.findMidiChannelForNewNote (60), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 2); channelAssigner.noteOff (61); expectEquals (channelAssigner.findMidiChannelForNewNote (61), 3); + expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 3); // check that assigned channel was last to play note channelAssigner.noteOff (65); channelAssigner.noteOff (66); expectEquals (channelAssigner.findMidiChannelForNewNote (66), 8); expectEquals (channelAssigner.findMidiChannelForNewNote (65), 7); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 8); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 7); // find closest channel playing nonequal note expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 2); // all notes off channelAssigner.allNotesOff(); @@ -323,10 +344,16 @@ struct MPEUtilsUnitTests : public UnitTest expectEquals (channelAssigner.findMidiChannelForNewNote (65), 7); expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 8); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 7); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 2); // normal assignment expectEquals (channelAssigner.findMidiChannelForNewNote (101), 3); expectEquals (channelAssigner.findMidiChannelForNewNote (20), 4); + expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 3); + expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 4); } // upper @@ -339,24 +366,35 @@ struct MPEUtilsUnitTests : public UnitTest // check that channels are assigned in correct order int noteNum = 60; for (int ch = 15; ch >= 1; --ch) - expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch); + { + expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch); + expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch); + + ++noteNum; + } // check that note-offs are processed channelAssigner.noteOff (60); expectEquals (channelAssigner.findMidiChannelForNewNote (60), 15); + expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 15); channelAssigner.noteOff (61); expectEquals (channelAssigner.findMidiChannelForNewNote (61), 14); + expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 14); // check that assigned channel was last to play note channelAssigner.noteOff (65); channelAssigner.noteOff (66); expectEquals (channelAssigner.findMidiChannelForNewNote (66), 9); expectEquals (channelAssigner.findMidiChannelForNewNote (65), 10); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 9); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 10); // find closest channel playing nonequal note expectEquals (channelAssigner.findMidiChannelForNewNote (80), 1); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 15); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 15); // all notes off channelAssigner.allNotesOff(); @@ -366,10 +404,16 @@ struct MPEUtilsUnitTests : public UnitTest expectEquals (channelAssigner.findMidiChannelForNewNote (65), 10); expectEquals (channelAssigner.findMidiChannelForNewNote (80), 1); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 15); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 9); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 10); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 15); // normal assignment expectEquals (channelAssigner.findMidiChannelForNewNote (101), 14); expectEquals (channelAssigner.findMidiChannelForNewNote (20), 13); + expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 14); + expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 13); } // legacy @@ -379,24 +423,35 @@ struct MPEUtilsUnitTests : public UnitTest // check that channels are assigned in correct order int noteNum = 60; for (int ch = 1; ch <= 16; ++ch) - expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch); + { + expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch); + expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch); + + ++noteNum; + } // check that note-offs are processed channelAssigner.noteOff (60); expectEquals (channelAssigner.findMidiChannelForNewNote (60), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 1); channelAssigner.noteOff (61); expectEquals (channelAssigner.findMidiChannelForNewNote (61), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 2); // check that assigned channel was last to play note channelAssigner.noteOff (65); channelAssigner.noteOff (66); expectEquals (channelAssigner.findMidiChannelForNewNote (66), 7); expectEquals (channelAssigner.findMidiChannelForNewNote (65), 6); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 7); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 6); // find closest channel playing nonequal note expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 1); // all notes off channelAssigner.allNotesOff(); @@ -406,10 +461,16 @@ struct MPEUtilsUnitTests : public UnitTest expectEquals (channelAssigner.findMidiChannelForNewNote (65), 6); expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 7); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 6); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 1); // normal assignment expectEquals (channelAssigner.findMidiChannelForNewNote (101), 2); expectEquals (channelAssigner.findMidiChannelForNewNote (20), 3); + expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 3); } } diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h index 6fcdafec4..a65d2404a 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h @@ -63,6 +63,11 @@ class MPEChannelAssigner */ int findMidiChannelForNewNote (int noteNumber) noexcept; + /** If a note has been added using findMidiChannelForNewNote() this will return the channel + to which it was assigned, otherwise it will return -1. + */ + int findMidiChannelForExistingNote (int initialNoteOnNumber) noexcept; + /** You must call this method for all note-offs that you receive so that this class can keep track of the currently playing notes internally. @@ -86,7 +91,7 @@ class MPEChannelAssigner int lastNotePlayed = -1; bool isFree() const noexcept { return notes.isEmpty(); } }; - MidiChannel midiChannels[17]; + std::array midiChannels; //============================================================================== int findMidiChannelPlayingClosestNonequalNote (int noteNumber) noexcept; diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp index 622dc894d..b89887713 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp @@ -43,6 +43,18 @@ MPEValue MPEValue::from14BitInt (int value) noexcept return { value }; } +MPEValue MPEValue::fromUnsignedFloat (float value) noexcept +{ + jassert (0.0f <= value && value <= 1.0f); + return { roundToInt (value * 16383.0f) }; +} + +MPEValue MPEValue::fromSignedFloat (float value) noexcept +{ + jassert (-1.0f <= value && value <= 1.0f); + return { roundToInt (((value + 1.0f) * 16383.0f) / 2.0f) }; +} + //============================================================================== MPEValue MPEValue::minValue() noexcept { return MPEValue::from7BitInt (0); } MPEValue MPEValue::centreValue() noexcept { return MPEValue::from7BitInt (64); } @@ -121,26 +133,34 @@ class MPEValueTests : public UnitTest beginTest ("zero/minimum value"); { - expectValuesConsistent (MPEValue::from7BitInt (0), 0, 0, -1.0f, 0.0f); - expectValuesConsistent (MPEValue::from14BitInt (0), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::from7BitInt (0), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::from14BitInt (0), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::fromUnsignedFloat (0.0f), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::fromSignedFloat (-1.0f), 0, 0, -1.0f, 0.0f); } beginTest ("maximum value"); { - expectValuesConsistent (MPEValue::from7BitInt (127), 127, 16383, 1.0f, 1.0f); - expectValuesConsistent (MPEValue::from14BitInt (16383), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::from7BitInt (127), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::from14BitInt (16383), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::fromUnsignedFloat (1.0f), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::fromSignedFloat (1.0f), 127, 16383, 1.0f, 1.0f); } beginTest ("centre value"); { - expectValuesConsistent (MPEValue::from7BitInt (64), 64, 8192, 0.0f, 0.5f); - expectValuesConsistent (MPEValue::from14BitInt (8192), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::from7BitInt (64), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::from14BitInt (8192), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::fromUnsignedFloat (0.5f), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::fromSignedFloat (0.0f), 64, 8192, 0.0f, 0.5f); } beginTest ("value halfway between min and centre"); { - expectValuesConsistent (MPEValue::from7BitInt (32), 32, 4096, -0.5f, 0.25f); - expectValuesConsistent (MPEValue::from14BitInt (4096), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::from7BitInt (32), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::from14BitInt (4096), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::fromUnsignedFloat (0.25f), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::fromSignedFloat (-0.5f), 32, 4096, -0.5f, 0.25f); } } diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.h b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.h index 1d9b826f0..3b4322b1f 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.h @@ -53,6 +53,12 @@ class JUCE_API MPEValue */ static MPEValue from14BitInt (int value) noexcept; + /** Constructs an MPEValue from a float between 0.0f and 1.0f. */ + static MPEValue fromUnsignedFloat (float value) noexcept; + + /** Constructs an MPEValue from a float between -1.0f and 1.0f. */ + static MPEValue fromSignedFloat (float value) noexcept; + /** Constructs an MPEValue corresponding to the centre value. */ static MPEValue centreValue() noexcept; diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp index 597f2f783..29b1ac3ec 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp @@ -23,7 +23,17 @@ namespace juce { -MPEZoneLayout::MPEZoneLayout() noexcept {} +MPEZoneLayout::MPEZoneLayout (MPEZone lower, MPEZone upper) + : lowerZone (lower), upperZone (upper) +{ +} + +MPEZoneLayout::MPEZoneLayout (MPEZone zone) + : lowerZone (zone.isLowerZone() ? zone : MPEZone()), + upperZone (! zone.isLowerZone() ? zone : MPEZone()) +{ +} + MPEZoneLayout::MPEZoneLayout (const MPEZoneLayout& other) : lowerZone (other.lowerZone), @@ -54,9 +64,9 @@ void MPEZoneLayout::setZone (bool isLower, int numMemberChannels, int perNotePit checkAndLimitZoneParameters (0, 96, masterPitchbendRange); if (isLower) - lowerZone = { true, numMemberChannels, perNotePitchbendRange, masterPitchbendRange }; + lowerZone = { MPEZone::Type::lower, numMemberChannels, perNotePitchbendRange, masterPitchbendRange }; else - upperZone = { false, numMemberChannels, perNotePitchbendRange, masterPitchbendRange }; + upperZone = { MPEZone::Type::upper, numMemberChannels, perNotePitchbendRange, masterPitchbendRange }; if (numMemberChannels > 0) { @@ -86,8 +96,8 @@ void MPEZoneLayout::setUpperZone (int numMemberChannels, int perNotePitchbendRan void MPEZoneLayout::clearAllZones() { - lowerZone = { true, 0 }; - upperZone = { false, 0 }; + lowerZone = { MPEZone::Type::lower, 0 }; + upperZone = { MPEZone::Type::upper, 0 }; sendLayoutChangeMessage(); } @@ -128,7 +138,7 @@ void MPEZoneLayout::processZoneLayoutRpnMessage (MidiRPNMessage rpn) } } -void MPEZoneLayout::updateMasterPitchbend (Zone& zone, int value) +void MPEZoneLayout::updateMasterPitchbend (MPEZone& zone, int value) { if (zone.masterPitchbendRange != value) { @@ -138,7 +148,7 @@ void MPEZoneLayout::updateMasterPitchbend (Zone& zone, int value) } } -void MPEZoneLayout::updatePerNotePitchbendRange (Zone& zone, int value) +void MPEZoneLayout::updatePerNotePitchbendRange (MPEZone& zone, int value) { if (zone.perNotePitchbendRange != value) { diff --git a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h index 9a49c2a24..1b1537b37 100644 --- a/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h +++ b/external/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h @@ -23,6 +23,83 @@ namespace juce { +//============================================================================== +/** + This struct represents an MPE zone. + + It can either be a lower or an upper zone, where: + - A lower zone encompasses master channel 1 and an arbitrary number of ascending + MIDI channels, increasing from channel 2. + - An upper zone encompasses master channel 16 and an arbitrary number of descending + MIDI channels, decreasing from channel 15. + + It also defines a pitchbend range (in semitones) to be applied for per-note pitchbends and + master pitchbends, respectively. +*/ +struct MPEZone +{ + enum class Type { lower, upper }; + + MPEZone() = default; + MPEZone (const MPEZone& other) = default; + + MPEZone (Type type, int memberChannels = 0, int perNotePitchbend = 48, int masterPitchbend = 2) + : zoneType (type), + numMemberChannels (memberChannels), + perNotePitchbendRange (perNotePitchbend), + masterPitchbendRange (masterPitchbend) + {} + + bool isLowerZone() const noexcept { return zoneType == Type::lower; } + bool isUpperZone() const noexcept { return zoneType == Type::upper; } + + bool isActive() const noexcept { return numMemberChannels > 0; } + + int getMasterChannel() const noexcept { return isLowerZone() ? lowerZoneMasterChannel : upperZoneMasterChannel; } + int getFirstMemberChannel() const noexcept { return isLowerZone() ? lowerZoneMasterChannel + 1 : upperZoneMasterChannel - 1; } + int getLastMemberChannel() const noexcept { return isLowerZone() ? (lowerZoneMasterChannel + numMemberChannels) + : (upperZoneMasterChannel - numMemberChannels); } + + bool isUsingChannelAsMemberChannel (int channel) const noexcept + { + return isLowerZone() ? (lowerZoneMasterChannel < channel && channel <= getLastMemberChannel()) + : (channel < upperZoneMasterChannel && getLastMemberChannel() <= channel); + } + + bool isUsing (int channel) const noexcept + { + return isUsingChannelAsMemberChannel (channel) || channel == getMasterChannel(); + } + + static auto tie (const MPEZone& z) + { + return std::tie (z.zoneType, + z.numMemberChannels, + z.perNotePitchbendRange, + z.masterPitchbendRange); + } + + bool operator== (const MPEZone& other) const + { + return tie (*this) == tie (other); + } + + bool operator!= (const MPEZone& other) const + { + return tie (*this) != tie (other); + } + + //============================================================================== + static constexpr int lowerZoneMasterChannel = 1, + upperZoneMasterChannel = 16; + + Type zoneType = Type::lower; + + int numMemberChannels = 0; + int perNotePitchbendRange = 48; + int masterPitchbendRange = 2; +}; + //============================================================================== /** This class represents the current MPE zone layout of a device capable of handling MPE. @@ -44,89 +121,28 @@ namespace juce class JUCE_API MPEZoneLayout { public: - /** Default constructor. + //============================================================================== + /** Creates a layout with inactive upper and lower zones. */ + MPEZoneLayout() = default; - This will create a layout with inactive lower and upper zones, representing - a device with MPE mode disabled. + /** Creates a layout with the given upper and lower zones. */ + MPEZoneLayout (MPEZone lower, MPEZone upper); - You can set the lower or upper MPE zones using the setZone() method. + /** Creates a layout with a single upper or lower zone, leaving the other zone uninitialised. */ + MPEZoneLayout (MPEZone singleZone); - @see setZone - */ - MPEZoneLayout() noexcept; - - /** Copy constuctor. - This will not copy the listeners registered to the MPEZoneLayout. - */ MPEZoneLayout (const MPEZoneLayout& other); - - /** Copy assignment operator. - This will not copy the listeners registered to the MPEZoneLayout. - */ MPEZoneLayout& operator= (const MPEZoneLayout& other); - //============================================================================== - /** - This struct represents an MPE zone. - - It can either be a lower or an upper zone, where: - - A lower zone encompasses master channel 1 and an arbitrary number of ascending - MIDI channels, increasing from channel 2. - - An upper zone encompasses master channel 16 and an arbitrary number of descending - MIDI channels, decreasing from channel 15. - - It also defines a pitchbend range (in semitones) to be applied for per-note pitchbends and - master pitchbends, respectively. - */ - struct Zone - { - Zone (const Zone& other) = default; - - bool isLowerZone() const noexcept { return lowerZone; } - bool isUpperZone() const noexcept { return ! lowerZone; } - - bool isActive() const noexcept { return numMemberChannels > 0; } - - int getMasterChannel() const noexcept { return lowerZone ? 1 : 16; } - int getFirstMemberChannel() const noexcept { return lowerZone ? 2 : 15; } - int getLastMemberChannel() const noexcept { return lowerZone ? (1 + numMemberChannels) - : (16 - numMemberChannels); } - - bool isUsingChannelAsMemberChannel (int channel) const noexcept - { - return lowerZone ? (channel > 1 && channel <= 1 + numMemberChannels) - : (channel < 16 && channel >= 16 - numMemberChannels); - } - - bool isUsing (int channel) const noexcept - { - return isUsingChannelAsMemberChannel (channel) || channel == getMasterChannel(); - } + bool operator== (const MPEZoneLayout& other) const { return lowerZone == other.lowerZone && upperZone == other.upperZone; } + bool operator!= (const MPEZoneLayout& other) const { return ! operator== (other); } - bool operator== (const Zone& other) const noexcept { return lowerZone == other.lowerZone - && numMemberChannels == other.numMemberChannels - && perNotePitchbendRange == other.perNotePitchbendRange - && masterPitchbendRange == other.masterPitchbendRange; } - - bool operator!= (const Zone& other) const noexcept { return ! operator== (other); } - - int numMemberChannels; - int perNotePitchbendRange; - int masterPitchbendRange; - - private: - friend class MPEZoneLayout; - - Zone (bool lower, int memberChans = 0, int perNotePb = 48, int masterPb = 2) noexcept - : numMemberChannels (memberChans), - perNotePitchbendRange (perNotePb), - masterPitchbendRange (masterPb), - lowerZone (lower) - { - } + //============================================================================== + /** Returns a struct representing the lower MPE zone. */ + MPEZone getLowerZone() const noexcept { return lowerZone; } - bool lowerZone; - }; + /** Returns a struct representing the upper MPE zone. */ + MPEZone getUpperZone() const noexcept { return upperZone; } /** Sets the lower zone of this layout. */ void setLowerZone (int numMemberChannels = 0, @@ -138,17 +154,14 @@ class JUCE_API MPEZoneLayout int perNotePitchbendRange = 48, int masterPitchbendRange = 2) noexcept; - /** Returns a struct representing the lower MPE zone. */ - const Zone getLowerZone() const noexcept { return lowerZone; } - - /** Returns a struct representing the upper MPE zone. */ - const Zone getUpperZone() const noexcept { return upperZone; } - /** Clears the lower and upper zones of this layout, making them both inactive and disabling MPE mode. */ void clearAllZones(); + /** Returns true if either of the zones are active. */ + bool isActive() const { return lowerZone.isActive() || upperZone.isActive(); } + //============================================================================== /** Pass incoming MIDI messages to an object of this class if you want the zone layout to properly react to MPE RPN messages like an @@ -200,10 +213,14 @@ class JUCE_API MPEZoneLayout /** Removes a listener. */ void removeListener (Listener* const listenerToRemove) noexcept; + #ifndef DOXYGEN + using Zone = MPEZone; + #endif + private: //============================================================================== - Zone lowerZone { true, 0 }; - Zone upperZone { false, 0 }; + MPEZone lowerZone { MPEZone::Type::lower, 0 }; + MPEZone upperZone { MPEZone::Type::upper, 0 }; MidiRPNDetector rpnDetector; ListenerList listeners; @@ -215,8 +232,8 @@ class JUCE_API MPEZoneLayout void processZoneLayoutRpnMessage (MidiRPNMessage); void processPitchbendRangeRpnMessage (MidiRPNMessage); - void updateMasterPitchbend (Zone&, int); - void updatePerNotePitchbendRange (Zone&, int); + void updateMasterPitchbend (MPEZone&, int); + void updatePerNotePitchbendRange (MPEZone&, int); void sendLayoutChangeMessage(); void checkAndLimitZoneParameters (int, int, int&) noexcept; diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index 9c69355e1..6e7e3398d 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -715,11 +715,7 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup currentDeviceType = currentAudioDevice->getTypeName(); currentAudioDevice->start (callbackHandler.get()); - - currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); - currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); - currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); - currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); + updateCurrentSetup(); for (int i = 0; i < availableDeviceTypes.size(); ++i) if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType) @@ -965,6 +961,8 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device loadMeasurer.reset (device->getCurrentSampleRate(), device->getCurrentBufferSizeSamples()); + updateCurrentSetup(); + { const ScopedLock sl (audioCallbackLock); @@ -972,7 +970,6 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device callbacks.getUnchecked(i)->audioDeviceAboutToStart (device); } - updateCurrentSetup(); sendChangeMessage(); } @@ -1444,6 +1441,48 @@ class AudioDeviceManagerTests : public UnitTest enableInputChannels (manager); closeDeviceByRequestingEmptyNames (manager); } + + beginTest ("AudioDeviceManager updates its current settings before notifying callbacks when device restarts itself"); + { + AudioDeviceManager manager; + auto deviceType = std::make_unique ("foo", + StringArray { "foo in a", "foo in b" }, + StringArray { "foo out a", "foo out b" }); + auto* ptr = deviceType.get(); + manager.addAudioDeviceType (std::move (deviceType)); + + AudioDeviceManager::AudioDeviceSetup setup; + setup.sampleRate = 48000.0; + setup.bufferSize = 256; + setup.inputDeviceName = "foo in a"; + setup.outputDeviceName = "foo out a"; + setup.useDefaultInputChannels = true; + setup.useDefaultOutputChannels = true; + manager.setAudioDeviceSetup (setup, true); + + const auto currentSetup = manager.getAudioDeviceSetup(); + expectEquals (currentSetup.sampleRate, setup.sampleRate); + expectEquals (currentSetup.bufferSize, setup.bufferSize); + + MockCallback callback; + manager.addAudioCallback (&callback); + + constexpr auto newSr = 10000.0; + constexpr auto newBs = 1024; + auto numCalls = 0; + + // Compilers disagree about whether newSr and newBs need to be captured + callback.aboutToStart = [&] + { + ++numCalls; + const auto current = manager.getAudioDeviceSetup(); + expectEquals (current.sampleRate, newSr); + expectEquals (current.bufferSize, newBs); + }; + + ptr->restartDevices (newSr, newBs); + expectEquals (numCalls, 1); + } } private: @@ -1608,11 +1647,26 @@ class AudioDeviceManagerTests : public UnitTest const String mockBName = "mockB"; const String emptyName = "empty"; - class MockDevice : public AudioIODevice + struct Restartable + { + virtual ~Restartable() = default; + virtual void restart (double newSr, int newBs) = 0; + }; + + class MockDevice : public AudioIODevice, + private Restartable { public: - MockDevice (String typeNameIn, String outNameIn, String inNameIn) - : AudioIODevice ("mock", typeNameIn), outName (outNameIn), inName (inNameIn) {} + MockDevice (ListenerList& l, String typeNameIn, String outNameIn, String inNameIn) + : AudioIODevice ("mock", typeNameIn), listeners (l), outName (outNameIn), inName (inNameIn) + { + listeners.add (this); + } + + ~MockDevice() override + { + listeners.remove (this); + } StringArray getOutputChannelNames() override { return { "o1", "o2", "o3" }; } StringArray getInputChannelNames() override { return { "i1", "i2", "i3" }; } @@ -1634,8 +1688,19 @@ class AudioDeviceManagerTests : public UnitTest void close() override { on = false; } bool isOpen() override { return on; } - void start (AudioIODeviceCallback*) override { playing = true; } - void stop() override { playing = false; } + void start (AudioIODeviceCallback* c) override + { + callback = c; + callback->audioDeviceAboutToStart (this); + playing = true; + } + + void stop() override + { + playing = false; + callback->audioDeviceStopped(); + } + bool isPlaying() override { return playing; } String getLastError() override { return {}; } @@ -1650,6 +1715,16 @@ class AudioDeviceManagerTests : public UnitTest int getInputLatencyInSamples() override { return 0; } private: + void restart (double newSr, int newBs) override + { + stop(); + close(); + open (inChannels, outChannels, newSr, newBs); + start (callback); + } + + ListenerList& listeners; + AudioIODeviceCallback* callback = nullptr; String outName, inName; BigInteger outChannels, inChannels; double sampleRate = 0.0; @@ -1668,6 +1743,12 @@ class AudioDeviceManagerTests : public UnitTest inNames (std::move (inputNames)), outNames (std::move (outputNames)) {} + ~MockDeviceType() override + { + // A Device outlived its DeviceType! + jassert (listeners.isEmpty()); + } + void scanForDevices() override {} StringArray getDeviceNames (bool isInput) const override @@ -1687,15 +1768,37 @@ class AudioDeviceManagerTests : public UnitTest AudioIODevice* createDevice (const String& outputName, const String& inputName) override { if (inNames.contains (inputName) || outNames.contains (outputName)) - return new MockDevice (getTypeName(), outputName, inputName); + return new MockDevice (listeners, getTypeName(), outputName, inputName); return nullptr; } + // Call this to emulate the device restarting itself with new settings. + // This might happen e.g. when a user changes the ASIO settings. + void restartDevices (double newSr, int newBs) + { + listeners.call ([&] (auto& l) { return l.restart (newSr, newBs); }); + } + private: const StringArray& getNames (bool isInput) const { return isInput ? inNames : outNames; } const StringArray inNames, outNames; + ListenerList listeners; + }; + + class MockCallback : public AudioIODeviceCallback + { + public: + std::function callback; + std::function aboutToStart; + std::function stopped; + std::function error; + + void audioDeviceIOCallback (const float**, int, float**, int, int) override { NullCheckedInvocation::invoke (callback); } + void audioDeviceAboutToStart (AudioIODevice*) override { NullCheckedInvocation::invoke (aboutToStart); } + void audioDeviceStopped() override { NullCheckedInvocation::invoke (stopped); } + void audioDeviceError (const String&) override { NullCheckedInvocation::invoke (error); } }; void initialiseManager (AudioDeviceManager& manager) diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h b/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h index 3416aa16d..d59d4a0e2 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h +++ b/external/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h @@ -32,7 +32,7 @@ ID: juce_audio_devices vendor: juce - version: 6.1.4 + version: 6.1.5 name: JUCE audio and MIDI I/O device classes description: Classes to play and record from audio and MIDI I/O devices website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index 07d1cb89b..9d066400d 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -50,7 +50,7 @@ namespace static void getDeviceSampleRates (snd_pcm_t* handle, Array& rates) { - const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; + const int ratesToTry[] = { 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; snd_pcm_hw_params_t* hwParams; snd_pcm_hw_params_alloca (&hwParams); diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index c6920b10d..3649accfd 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -294,7 +294,7 @@ class CoreAudioInternal : private Timer, if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) { - for (auto r : { 8000, 11025, 16000, 22050, 32000, + for (auto r : { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000 }) { @@ -683,10 +683,14 @@ class CoreAudioInternal : private Timer, { const ScopedLock sl (callbackLock); - if (! started) + if (callback == nullptr && callbackToNotify != nullptr) { - callback = nullptr; + callback = callbackToNotify; + callback->audioDeviceAboutToStart (&owner); + } + if (! started) + { if (deviceID != 0) { if (OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, this, &audioProcID))) @@ -702,14 +706,6 @@ class CoreAudioInternal : private Timer, } } } - - if (started) - { - callback = callbackToNotify; - - if (callback != nullptr) - callback->audioDeviceAboutToStart (&owner); - } } playing = started && callback != nullptr; diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index 4abef064b..2c346d80b 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -350,7 +350,7 @@ class ASIOAudioIODevice : public AudioIODevice, if (asioObject != nullptr) { - for (auto rate : { 8000, 11025, 16000, 22050, 32000, + for (auto rate : { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000 }) if (asioObject->canSampleRate ((double) rate) == 0) diff --git a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index 2e9c4d385..35991489b 100644 --- a/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/external/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -336,7 +336,28 @@ JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD" JUCE_COMCALL UnregisterAudioSessionNotification (IAudioSessionEvents*) = 0; }; +} // namespace juce + +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL (juce::IPropertyStore, 0x886d8eeb, 0x8cf2, 0x4446, 0x8d, 0x02, 0xcd, 0xba, 0x1d, 0xbd, 0xcf, 0x99) +__CRT_UUID_DECL (juce::IMMDevice, 0xD666063F, 0x1587, 0x4E43, 0x81, 0xF1, 0xB9, 0x48, 0xE8, 0x07, 0x36, 0x3F) +__CRT_UUID_DECL (juce::IMMEndpoint, 0x1BE09788, 0x6894, 0x4089, 0x85, 0x86, 0x9A, 0x2A, 0x6C, 0x26, 0x5A, 0xC5) +__CRT_UUID_DECL (juce::IMMNotificationClient, 0x7991EEC9, 0x7E89, 0x4D85, 0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0) +__CRT_UUID_DECL (juce::IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6) +__CRT_UUID_DECL (juce::MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E) +__CRT_UUID_DECL (juce::IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2) +__CRT_UUID_DECL (juce::IAudioClient2, 0x726778CD, 0xF60A, 0x4eda, 0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA) +__CRT_UUID_DECL (juce::IAudioClient3, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2) +__CRT_UUID_DECL (juce::IAudioCaptureClient, 0xC8ADBD64, 0xE71E, 0x48a0, 0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17) +__CRT_UUID_DECL (juce::IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2) +__CRT_UUID_DECL (juce::IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A) +__CRT_UUID_DECL (juce::IAudioSessionEvents, 0x24918ACC, 0x64B3, 0x37C1, 0x8C, 0xA9, 0x74, 0xA6, 0x6E, 0x99, 0x57, 0xA8) +__CRT_UUID_DECL (juce::IAudioSessionControl, 0xF4B1A599, 0x7266, 0x4319, 0xA8, 0xCA, 0xE7, 0x0A, 0xCB, 0x11, 0xE8, 0xCD) +#endif + //============================================================================== +namespace juce +{ namespace WasapiClasses { @@ -655,7 +676,7 @@ class WASAPIDeviceBase void querySupportedSampleRates (WAVEFORMATEXTENSIBLE format, ComSmartPtr& audioClient) { - for (auto rate : { 8000, 11025, 16000, 22050, 32000, + for (auto rate : { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000 }) { diff --git a/external/JuceLibraryCode/modules/juce_core/containers/juce_Array.h b/external/JuceLibraryCode/modules/juce_core/containers/juce_Array.h index 924173de8..23f1885d0 100644 --- a/external/JuceLibraryCode/modules/juce_core/containers/juce_Array.h +++ b/external/JuceLibraryCode/modules/juce_core/containers/juce_Array.h @@ -76,7 +76,7 @@ class Array { } - /** Initalises from a null-terminated raw array of values. + /** Initialises from a null-terminated raw array of values. @param data the data to copy from */ template @@ -86,7 +86,7 @@ class Array add (*data++); } - /** Initalises from a raw array of values. + /** Initialises from a raw array of values. @param data the data to copy from @param numValues the number of values in the array */ @@ -96,26 +96,26 @@ class Array values.addArray (data, numValues); } - /** Initalises an Array of size 1 containing a single element. */ + /** Initialises an Array of size 1 containing a single element. */ Array (const ElementType& singleElementToAdd) { add (singleElementToAdd); } - /** Initalises an Array of size 1 containing a single element. */ + /** Initialises an Array of size 1 containing a single element. */ Array (ElementType&& singleElementToAdd) { add (std::move (singleElementToAdd)); } - /** Initalises an Array from a list of items. */ + /** Initialises an Array from a list of items. */ template Array (const ElementType& firstNewElement, OtherElements&&... otherElements) { values.add (firstNewElement, std::forward (otherElements)...); } - /** Initalises an Array from a list of items. */ + /** Initialises an Array from a list of items. */ template Array (ElementType&& firstNewElement, OtherElements&&... otherElements) { diff --git a/external/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp b/external/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp index 2641f9302..08186495a 100644 --- a/external/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp +++ b/external/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp @@ -105,7 +105,7 @@ bool TemporaryFile::deleteTemporaryFile() const // Have a few attempts at deleting the file before giving up.. for (int i = 5; --i >= 0;) { - if (temporaryFile.deleteFile()) + if (temporaryFile.isDirectory() ? temporaryFile.deleteRecursively() : temporaryFile.deleteFile()) return true; Thread::sleep (50); diff --git a/external/JuceLibraryCode/modules/juce_core/juce_core.h b/external/JuceLibraryCode/modules/juce_core/juce_core.h index de3b331b0..12b5ba20e 100644 --- a/external/JuceLibraryCode/modules/juce_core/juce_core.h +++ b/external/JuceLibraryCode/modules/juce_core/juce_core.h @@ -32,7 +32,7 @@ ID: juce_core vendor: juce - version: 6.1.4 + version: 6.1.5 name: JUCE core classes description: The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_core/maths/juce_Range.h b/external/JuceLibraryCode/modules/juce_core/maths/juce_Range.h index 9e19a4e39..4e9966445 100644 --- a/external/JuceLibraryCode/modules/juce_core/maths/juce_Range.h +++ b/external/JuceLibraryCode/modules/juce_core/maths/juce_Range.h @@ -270,7 +270,8 @@ class Range } /** Scans an array of values for its min and max, and returns these as a Range. */ - static Range findMinAndMax (const ValueType* values, int numValues) noexcept + template ::value, int> = 0> + static Range findMinAndMax (const ValueType* values, Integral numValues) noexcept { if (numValues <= 0) return Range(); diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h b/external/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h index db28d82bd..96153bda0 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -132,7 +132,9 @@ #define STRICT 1 #define WIN32_LEAN_AND_MEAN 1 #if JUCE_MINGW - #define _WIN32_WINNT 0x0600 + #if ! defined (_WIN32_WINNT) + #define _WIN32_WINNT 0x0600 + #endif #else #define _WIN32_WINNT 0x0602 #endif diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm index cfbc4b8f2..4bb533e54 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm @@ -969,6 +969,7 @@ bool connect (WebInputStream::Listener* webInputListener, int numRetries = 0) if (! connection->start (owner, webInputListener)) { + const auto errorCode = connection->getErrorCode(); connection.reset(); if (@available (macOS 10.10, *)) @@ -976,7 +977,7 @@ bool connect (WebInputStream::Listener* webInputListener, int numRetries = 0) // Workaround for macOS versions below 10.10 where HTTPS POST requests with keep-alive // fail with the NSURLErrorNetworkConnectionLost error code. - if (numRetries == 0 && connection->getErrorCode() == NSURLErrorNetworkConnectionLost) + if (numRetries == 0 && errorCode == NSURLErrorNetworkConnectionLost) return connect (webInputListener, ++numRetries); return false; diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h index 30dcf08af..0daed1a26 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h @@ -23,7 +23,7 @@ namespace juce { -#if JUCE_MINGW || (! (defined (_MSC_VER) || defined (__uuidof))) +#if (JUCE_MINGW && JUCE_32BIT) || (! defined (_MSC_VER) && ! defined (__uuidof)) #ifdef __uuidof #undef __uuidof #endif @@ -47,7 +47,7 @@ namespace juce #else #define JUCE_DECLARE_UUID_GETTER(name, uuid) - #define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name + #define JUCE_COMCLASS(name, guid) struct DECLSPEC_UUID (guid) name #endif #define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp index b57bda5e2..ee95479bb 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp @@ -706,7 +706,8 @@ String File::getVersion() const //============================================================================== bool File::isSymbolicLink() const { - return (GetFileAttributes (fullPath.toWideCharPointer()) & FILE_ATTRIBUTE_REPARSE_POINT) != 0; + const auto attributes = WindowsFileHelpers::getAtts (fullPath); + return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0); } bool File::isShortcut() const diff --git a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp index eba859af7..03bbdcfdb 100644 --- a/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp +++ b/external/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp @@ -53,8 +53,6 @@ void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRI //============================================================================== -void JUCE_API juce_threadEntryPoint (void*); - static unsigned int STDMETHODCALLTYPE threadEntryProc (void* userData) { if (juce_messageWindowHandle != nullptr) diff --git a/external/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h b/external/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h index cb30e2146..7dec79c90 100644 --- a/external/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h +++ b/external/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h @@ -71,7 +71,7 @@ namespace juce #pragma intrinsic (__debugbreak) #endif #define JUCE_BREAK_IN_DEBUGGER { __debugbreak(); } -#elif JUCE_INTEL && (JUCE_GCC || JUCE_MAC) +#elif JUCE_INTEL && (JUCE_GCC || JUCE_CLANG || JUCE_MAC) #if JUCE_NO_INLINE_ASM #define JUCE_BREAK_IN_DEBUGGER { } #else diff --git a/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h b/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h index b44aa8ac7..70890b1c0 100644 --- a/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h +++ b/external/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h @@ -29,7 +29,7 @@ */ #define JUCE_MAJOR_VERSION 6 #define JUCE_MINOR_VERSION 1 -#define JUCE_BUILDNUMBER 4 +#define JUCE_BUILDNUMBER 5 /** Current JUCE version number. @@ -64,6 +64,7 @@ #include #include #include +#include //============================================================================== #include "juce_CompilerSupport.h" diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp index 7edfb7350..b541fc97b 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp @@ -167,7 +167,8 @@ void StringPairArray::minimiseStorageOverheads() values.minimiseStorageOverheads(); } -void StringPairArray::addMap (const std::map& toAdd) +template +void StringPairArray::addMapImpl (const Map& toAdd) { // If we just called `set` for each item in `toAdd`, that would // perform badly when adding to large StringPairArrays, as `set` @@ -201,6 +202,9 @@ void StringPairArray::addMap (const std::map& toAdd) } } +void StringPairArray::addUnorderedMap (const std::unordered_map& toAdd) { addMapImpl (toAdd); } +void StringPairArray::addMap (const std::map& toAdd) { addMapImpl (toAdd); } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS diff --git a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h index 2b270acf3..a1de4cc21 100644 --- a/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h +++ b/external/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h @@ -147,8 +147,14 @@ class JUCE_API StringPairArray /** Adds the contents of a map to this StringPairArray. */ void addMap (const std::map& mapToAdd); + /** Adds the contents of an unordered map to this StringPairArray. */ + void addUnorderedMap (const std::unordered_map& mapToAdd); + private: //============================================================================== + template + void addMapImpl (const Map& mapToAdd); + StringArray keys, values; bool ignoreCase; diff --git a/external/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp b/external/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp index 15481c4c8..be84489c1 100644 --- a/external/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp +++ b/external/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp @@ -379,8 +379,7 @@ String Time::getTimeZone() const { String zone[2]; - #if JUCE_WINDOWS - #if JUCE_MSVC || JUCE_CLANG + #if JUCE_WINDOWS && (JUCE_MSVC || JUCE_CLANG) _tzset(); for (int i = 0; i < 2; ++i) @@ -390,9 +389,6 @@ String Time::getTimeZone() const _get_tzname (&length, name, sizeof (name) - 1, i); zone[i] = name; } - #else - #warning "Can't find a replacement for tzset on mingw - ideas welcome!" - #endif #else tzset(); diff --git a/external/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h b/external/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h index b3aad16dd..124d7ba9f 100644 --- a/external/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h +++ b/external/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h @@ -125,7 +125,7 @@ class JUCE_API XmlDocument /** Sets a flag to change the treatment of empty text elements. If this is true (the default state), then any text elements that contain only - whitespace characters will be ingored during parsing. If you need to catch + whitespace characters will be ignored during parsing. If you need to catch whitespace-only text, then you should set this to false before calling the getDocumentElement() method. */ diff --git a/external/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp b/external/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp index 03fd7c5dd..fe1d61241 100644 --- a/external/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp +++ b/external/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp @@ -122,6 +122,19 @@ static int64 findCentralDirectoryFileHeader (InputStream& input, int& numEntries return 0; } +static bool hasSymbolicPart (const File& root, const File& f) +{ + jassert (root == f || f.isAChildOf (root)); + + for (auto p = f; p != root; p = p.getParentDirectory()) + { + if (p.isSymbolicLink()) + return true; + } + + return false; +} + //============================================================================== struct ZipFile::ZipInputStream : public InputStream { @@ -400,6 +413,14 @@ Result ZipFile::uncompressTo (const File& targetDirectory, } Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool shouldOverwriteFiles) +{ + return uncompressEntry (index, + targetDirectory, + shouldOverwriteFiles ? OverwriteFiles::yes : OverwriteFiles::no, + FollowSymlinks::no); +} + +Result ZipFile::uncompressEntry (int index, const File& targetDirectory, OverwriteFiles overwriteFiles, FollowSymlinks followSymlinks) { auto* zei = entries.getUnchecked (index); @@ -414,6 +435,9 @@ Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool sh auto targetFile = targetDirectory.getChildFile (entryPath); + if (! targetFile.isAChildOf (targetDirectory)) + return Result::fail ("Entry " + entryPath + " is outside the target directory"); + if (entryPath.endsWithChar ('/') || entryPath.endsWithChar ('\\')) return targetFile.createDirectory(); // (entry is a directory, not a file) @@ -424,13 +448,16 @@ Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool sh if (targetFile.exists()) { - if (! shouldOverwriteFiles) + if (overwriteFiles == OverwriteFiles::no) return Result::ok(); if (! targetFile.deleteFile()) return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName()); } + if (followSymlinks == FollowSymlinks::no && hasSymbolicPart (targetDirectory, targetFile.getParentDirectory())) + return Result::fail ("Parent directory leads through symlink for target file: " + targetFile.getFullPathName()); + if (! targetFile.getParentDirectory().createDirectory()) return Result::fail ("Failed to create target folder: " + targetFile.getParentDirectory().getFullPathName()); @@ -649,12 +676,9 @@ struct ZIPTests : public UnitTest : UnitTest ("ZIP", UnitTestCategories::compression) {} - void runTest() override + static MemoryBlock createZipMemoryBlock (const StringArray& entryNames) { - beginTest ("ZIP"); - ZipFile::Builder builder; - StringArray entryNames { "first", "second", "third" }; HashMap blocks; for (auto& entryName : entryNames) @@ -669,8 +693,61 @@ struct ZIPTests : public UnitTest MemoryBlock data; MemoryOutputStream mo (data, false); builder.writeToStream (mo, nullptr); + + return data; + } + + void runZipSlipTest() + { + const std::map testCases = { { "a", true }, +#if JUCE_WINDOWS + { "C:/b", false }, +#else + { "/b", false }, +#endif + { "c/d", true }, + { "../e/f", false }, + { "../../g/h", false }, + { "i/../j", true }, + { "k/l/../", true }, + { "m/n/../../", false }, + { "o/p/../../../", false } }; + + StringArray entryNames; + + for (const auto& testCase : testCases) + entryNames.add (testCase.first); + + TemporaryFile tmpDir; + tmpDir.getFile().createDirectory(); + auto data = createZipMemoryBlock (entryNames); MemoryInputStream mi (data, false); + ZipFile zip (mi); + + for (int i = 0; i < zip.getNumEntries(); ++i) + { + const auto result = zip.uncompressEntry (i, tmpDir.getFile()); + const auto caseIt = testCases.find (zip.getEntry (i)->filename); + if (caseIt != testCases.end()) + { + expect (result.wasOk() == caseIt->second, + zip.getEntry (i)->filename + " was unexpectedly " + (result.wasOk() ? "OK" : "not OK")); + } + else + { + expect (false); + } + } + } + + void runTest() override + { + beginTest ("ZIP"); + + StringArray entryNames { "first", "second", "third" }; + auto data = createZipMemoryBlock (entryNames); + MemoryInputStream mi (data, false); ZipFile zip (mi); expectEquals (zip.getNumEntries(), entryNames.size()); @@ -681,6 +758,9 @@ struct ZIPTests : public UnitTest std::unique_ptr input (zip.createStreamForEntry (*entry)); expectEquals (input->readEntireStreamAsString(), entryName); } + + beginTest ("ZipSlip"); + runZipSlipTest(); } }; diff --git a/external/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h b/external/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h index a022d32e0..326bf5840 100644 --- a/external/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h +++ b/external/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h @@ -179,6 +179,25 @@ class JUCE_API ZipFile const File& targetDirectory, bool shouldOverwriteFiles = true); + enum class OverwriteFiles { no, yes }; + enum class FollowSymlinks { no, yes }; + + /** Uncompresses one of the entries from the zip file. + + This will expand the entry and write it in a target directory. The entry's path is used to + determine which subfolder of the target should contain the new file. + + @param index the index of the entry to uncompress - this must be a valid index + between 0 and (getNumEntries() - 1). + @param targetDirectory the root folder to uncompress into + @param overwriteFiles whether to overwrite existing files with similarly-named ones + @param followSymlinks whether to follow symlinks inside the target directory + @returns success if all the files are successfully unzipped + */ + Result uncompressEntry (int index, + const File& targetDirectory, + OverwriteFiles overwriteFiles, + FollowSymlinks followSymlinks); //============================================================================== /** Used to create a new zip file. diff --git a/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h b/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h index 499e0c17a..5f298e3f5 100644 --- a/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h +++ b/external/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h @@ -35,7 +35,7 @@ ID: juce_data_structures vendor: juce - version: 6.1.4 + version: 6.1.5 name: JUCE data model helper classes description: Classes for undo/redo management, and smart data structures. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_events/juce_events.h b/external/JuceLibraryCode/modules/juce_events/juce_events.h index 1f396a303..023892b5e 100644 --- a/external/JuceLibraryCode/modules/juce_events/juce_events.h +++ b/external/JuceLibraryCode/modules/juce_events/juce_events.h @@ -32,7 +32,7 @@ ID: juce_events vendor: juce - version: 6.1.4 + version: 6.1.5 name: JUCE message and event handling classes description: Classes for running an application's main event loop and sending/receiving messages, timers, etc. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h b/external/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h index e2974d3ec..43d5ba8d8 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h +++ b/external/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h @@ -65,7 +65,7 @@ class JUCE_API ScaledImage ScaledImage (const Image& imageIn, double scaleIn) : image (imageIn), scaleFactor (scaleIn) {} - /** Returns the image at its original dimentions. */ + /** Returns the image at its original dimensions. */ Image getImage() const { return image; } /** Returns the image's scale. */ diff --git a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp index 5c0adb6e7..72105f7d5 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp @@ -54,6 +54,14 @@ #endif #if JUCE_USE_DIRECTWRITE || JUCE_DIRECT2D + /* This is a workaround for broken-by-default function definitions + in the MinGW headers. If you're using a newer distribution of MinGW, + then your headers may substitute the broken definitions with working definitions + when this flag is enabled. Unfortunately, not all MinGW headers contain this + workaround, so Direct2D remains disabled by default when building with MinGW. + */ + #define WIDL_EXPLICIT_AGGREGATE_RETURNS 1 + /* If you hit a compile error trying to include these files, you may need to update your version of the Windows SDK to the latest one. The DirectWrite and Direct2D headers are in the version 7 SDKs. diff --git a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h index f5de90ebf..a0a67a665 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h +++ b/external/JuceLibraryCode/modules/juce_graphics/juce_graphics.h @@ -35,7 +35,7 @@ ID: juce_graphics vendor: juce - version: 6.1.4 + version: 6.1.5 name: JUCE graphics classes description: Classes for 2D vector graphics, image loading/saving, font handling, etc. website: http://www.juce.com/juce diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp index 07f5db1de..6d3503cf7 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp @@ -201,6 +201,7 @@ namespace DirectWriteTypeLayout ComSmartPtr dwFont; auto hr = fontCollection.GetFontFromFontFace (glyphRun.fontFace, dwFont.resetAndGetPointerAddress()); + ignoreUnused (hr); jassert (dwFont != nullptr); ComSmartPtr dwFontFamily; @@ -289,6 +290,7 @@ namespace DirectWriteTypeLayout ComSmartPtr fontFamily; auto hr = fontCollection.GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); + ignoreUnused (hr); ComSmartPtr dwFont; uint32 fontFacesCount = 0; @@ -394,6 +396,7 @@ namespace DirectWriteTypeLayout UINT32 actualLineCount = 0; auto hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount); + ignoreUnused (hr); layout.ensureStorageAllocated ((int) actualLineCount); @@ -415,7 +418,7 @@ namespace DirectWriteTypeLayout line.stringRange = Range (lastLocation, lastLocation + (int) dwLineMetrics[i].length); line.lineOrigin.y += yAdjustment; yAdjustment += extraLineSpacing; - lastLocation += dwLineMetrics[i].length; + lastLocation += (int) dwLineMetrics[i].length; } } diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp index 12abed117..30db3082c 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp @@ -36,6 +36,7 @@ namespace uint32 index = 0; BOOL exists = false; auto hr = names->FindLocaleName (L"en-us", &index, &exists); + ignoreUnused (hr); if (! exists) index = 0; @@ -54,7 +55,7 @@ namespace jassert (family != nullptr); ComSmartPtr familyNames; auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); - jassert (SUCCEEDED (hr)); ignoreUnused (hr); + jassertquiet (SUCCEEDED (hr)); return getLocalisedName (familyNames); } @@ -63,7 +64,7 @@ namespace jassert (font != nullptr); ComSmartPtr faceNames; auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); - jassert (SUCCEEDED (hr)); ignoreUnused (hr); + jassertquiet (SUCCEEDED (hr)); return getLocalisedName (faceNames); } @@ -152,6 +153,7 @@ class WindowsDirectWriteTypeface : public Typeface uint32 fontIndex = 0; auto hr = fontCollection->FindFamilyName (font.getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound); + ignoreUnused (hr); if (! fontFound) fontIndex = 0; diff --git a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp index 3cd21b94d..b0773bc9f 100644 --- a/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp +++ b/external/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp @@ -235,6 +235,7 @@ StringArray Font::findAllTypefaceStyles (const String& family) BOOL fontFound = false; uint32 fontIndex = 0; auto hr = factories->systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound); + ignoreUnused (hr); if (! fontFound) fontIndex = 0; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityValueInterface.h b/external/JuceLibraryCode/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityValueInterface.h index 311a2ad16..4f074f101 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityValueInterface.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityValueInterface.h @@ -113,7 +113,7 @@ class JUCE_API AccessibilityValueInterface /** Returns the minimum value for this range. */ double getMinimumValue() const noexcept { return range.min; } - /** Returns the maxiumum value for this range. */ + /** Returns the maximum value for this range. */ double getMaximumValue() const noexcept { return range.max; } /** Returns the interval for this range. */ diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h b/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h index d4613264e..405b6999a 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h @@ -1475,6 +1475,23 @@ class JUCE_API Component : public MouseListener */ virtual std::unique_ptr createKeyboardFocusTraverser(); + /** Use this to indicate that the component should have an outline drawn around it + when it has keyboard focus. + + If this is set to true, then when the component gains keyboard focus the + LookAndFeel::createFocusOutlineForComponent() method will be used to draw an outline + around it. + + @see FocusOutline, hasFocusOutline + */ + void setHasFocusOutline (bool hasFocusOutline) noexcept { flags.hasFocusOutlineFlag = hasFocusOutline; } + + /** Returns true if this component should have a focus outline. + + @see FocusOutline, setHasFocusOutline + */ + bool hasFocusOutline() const noexcept { return flags.hasFocusOutlineFlag; } + //============================================================================== /** Returns true if the component (and all its parents) are enabled. @@ -2548,6 +2565,7 @@ class JUCE_API Component : public MouseListener bool isKeyboardFocusContainerFlag : 1; bool childKeyboardFocusedFlag : 1; bool dontFocusOnMouseClickFlag : 1; + bool hasFocusOutlineFlag : 1; bool alwaysOnTopFlag : 1; bool bufferToImageFlag : 1; bool bringToFrontOnClickFlag : 1; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp index 1e3ca9bf8..6aab61504 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp @@ -189,6 +189,24 @@ void Desktop::addFocusChangeListener (FocusChangeListener* l) { focusListen void Desktop::removeFocusChangeListener (FocusChangeListener* l) { focusListeners.remove (l); } void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); } +void Desktop::updateFocusOutline() +{ + if (auto* currentFocus = Component::getCurrentlyFocusedComponent()) + { + if (currentFocus->hasFocusOutline()) + { + focusOutline = currentFocus->getLookAndFeel().createFocusOutlineForComponent (*currentFocus); + + if (focusOutline != nullptr) + focusOutline->setOwner (currentFocus); + + return; + } + } + + focusOutline = nullptr; +} + void Desktop::handleAsyncUpdate() { // The component may be deleted during this operation, but we'll use a SafePointer rather than a @@ -197,6 +215,8 @@ void Desktop::handleAsyncUpdate() { l.globalFocusChanged (currentFocus.get()); }); + + updateFocusOutline(); } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h index 250f60b91..b7b7b58fc 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h @@ -436,6 +436,8 @@ class JUCE_API Desktop : private DeletedAtShutdown, std::unique_ptr defaultLookAndFeel; WeakReference currentLookAndFeel; + std::unique_ptr focusOutline; + Component* kioskModeComponent = nullptr; Rectangle kioskComponentOriginalBounds; bool kioskModeReentrant = false; @@ -458,6 +460,7 @@ class JUCE_API Desktop : private DeletedAtShutdown, void setKioskComponent (Component*, bool shouldBeEnabled, bool allowMenusAndBars); void triggerFocusCallback(); + void updateFocusOutline(); void handleAsyncUpdate() override; static Point getMousePositionFloat(); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp index abaadc2f9..1da5f321e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp @@ -67,11 +67,8 @@ #include #include #include - - #if ! JUCE_MINGW - #include - #include - #endif + #include + #include #if JUCE_WEB_BROWSER #include @@ -97,8 +94,6 @@ #endif #endif -#include - //============================================================================== #define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN \ jassert ((MessageManager::getInstanceWithoutCreating() != nullptr \ @@ -249,6 +244,7 @@ namespace juce #include "application/juce_Application.cpp" #include "misc/juce_BubbleComponent.cpp" #include "misc/juce_DropShadower.cpp" +#include "misc/juce_FocusOutline.cpp" #include "misc/juce_JUCESplashScreen.cpp" #include "layout/juce_FlexBox.cpp" @@ -310,26 +306,13 @@ JUCE_END_IGNORE_WARNINGS_GCC_LIKE #include "native/juce_mac_MouseCursor.mm" #elif JUCE_WINDOWS - - #if ! JUCE_MINGW - #include "native/accessibility/juce_win32_WindowsUIAWrapper.h" - #include "native/accessibility/juce_win32_AccessibilityElement.h" - #include "native/accessibility/juce_win32_UIAHelpers.h" - #include "native/accessibility/juce_win32_UIAProviders.h" - #include "native/accessibility/juce_win32_AccessibilityElement.cpp" - #include "native/accessibility/juce_win32_Accessibility.cpp" - #else - namespace juce - { - namespace WindowsAccessibility - { - long getUiaRootObjectId() { return -1; } - bool handleWmGetObject (AccessibilityHandler*, WPARAM, LPARAM, LRESULT*) { return false; } - void revokeUIAMapEntriesForWindow (HWND) {} - } - } - #endif - + #include "native/accessibility/juce_win32_ComInterfaces.h" + #include "native/accessibility/juce_win32_WindowsUIAWrapper.h" + #include "native/accessibility/juce_win32_AccessibilityElement.h" + #include "native/accessibility/juce_win32_UIAHelpers.h" + #include "native/accessibility/juce_win32_UIAProviders.h" + #include "native/accessibility/juce_win32_AccessibilityElement.cpp" + #include "native/accessibility/juce_win32_Accessibility.cpp" #include "native/juce_win32_Windowing.cpp" #include "native/juce_win32_DragAndDrop.cpp" #include "native/juce_win32_FileChooser.cpp" @@ -381,6 +364,34 @@ namespace juce //============================================================================== #if JUCE_WINDOWS +namespace juce +{ + +JUCE_COMCLASS (JuceIVirtualDesktopManager, "a5cd92ff-29be-454c-8d04-d82879fb3f1b") : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE IsWindowOnCurrentVirtualDesktop( + __RPC__in HWND topLevelWindow, + __RPC__out BOOL * onCurrentDesktop) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetWindowDesktopId( + __RPC__in HWND topLevelWindow, + __RPC__out GUID * desktopId) = 0; + + virtual HRESULT STDMETHODCALLTYPE MoveWindowToDesktop( + __RPC__in HWND topLevelWindow, + __RPC__in REFGUID desktopId) = 0; +}; + +JUCE_COMCLASS (JuceVirtualDesktopManager, "aa509086-5ca9-4c25-8f95-589d3c07b48a"); + +} // namespace juce + +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL (juce::JuceIVirtualDesktopManager, 0xa5cd92ff, 0x29be, 0x454c, 0x8d, 0x04, 0xd8, 0x28, 0x79, 0xfb, 0x3f, 0x1b) +__CRT_UUID_DECL (juce::JuceVirtualDesktopManager, 0xaa509086, 0x5ca9, 0x4c25, 0x8f, 0x95, 0x58, 0x9d, 0x3c, 0x07, 0xb4, 0x8a) +#endif + bool juce::isWindowOnCurrentVirtualDesktop (void* x) { if (x == nullptr) @@ -388,36 +399,16 @@ bool juce::isWindowOnCurrentVirtualDesktop (void* x) static auto* desktopManager = [] { - // IVirtualDesktopManager Copied from ShObjdl_core.h, because it may not be defined - MIDL_INTERFACE ("a5cd92ff-29be-454c-8d04-d82879fb3f1b") - juce_IVirtualDesktopManager : public IUnknown - { - public: - virtual HRESULT STDMETHODCALLTYPE IsWindowOnCurrentVirtualDesktop( - __RPC__in HWND topLevelWindow, - __RPC__out BOOL * onCurrentDesktop) = 0; - - virtual HRESULT STDMETHODCALLTYPE GetWindowDesktopId( - __RPC__in HWND topLevelWindow, - __RPC__out GUID * desktopId) = 0; - - virtual HRESULT STDMETHODCALLTYPE MoveWindowToDesktop( - __RPC__in HWND topLevelWindow, - __RPC__in REFGUID desktopId) = 0; - }; - - juce_IVirtualDesktopManager* result = nullptr; + JuceIVirtualDesktopManager* result = nullptr; JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - class DECLSPEC_UUID("aa509086-5ca9-4c25-8f95-589d3c07b48a") juce_VirtualDesktopManager; - - if (SUCCEEDED (CoCreateInstance (__uuidof (juce_VirtualDesktopManager), nullptr, CLSCTX_ALL, IID_PPV_ARGS (&result)))) + if (SUCCEEDED (CoCreateInstance (__uuidof (JuceVirtualDesktopManager), nullptr, CLSCTX_ALL, IID_PPV_ARGS (&result)))) return result; JUCE_END_IGNORE_WARNINGS_GCC_LIKE - return static_cast (nullptr); + return static_cast (nullptr); }(); BOOL current = false; @@ -428,6 +419,7 @@ bool juce::isWindowOnCurrentVirtualDesktop (void* x) return true; } + #else bool juce::isWindowOnCurrentVirtualDesktop (void*) { return true; } juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h index a92fc233b..d0529dc89 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h @@ -35,7 +35,7 @@ ID: juce_gui_basics vendor: juce - version: 6.1.4 + version: 6.1.5 name: JUCE GUI core classes description: Basic user-interface components and related classes. website: http://www.juce.com/juce @@ -161,6 +161,7 @@ namespace juce class FlexBox; class Grid; + class FocusOutline; #if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX Image createSnapshotOfNativeWindow (void* nativeWindowHandle); @@ -259,6 +260,7 @@ namespace juce #include "menus/juce_BurgerMenuComponent.h" #include "buttons/juce_ToolbarButton.h" #include "misc/juce_DropShadower.h" +#include "misc/juce_FocusOutline.h" #include "misc/juce_JUCESplashScreen.h" #include "widgets/juce_TreeView.h" #include "windows/juce_TopLevelWindow.h" diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h index 00fe20b78..a559d8ee7 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h @@ -328,7 +328,7 @@ class JUCE_API MultiDocumentPanel : public Component, operation you could give a value of false to the callback to abort the close operation. If your component is based on the FileBasedDocument class, then you'd probably want - to call FileBasedDocument::saveIfNeededAndUserAgreesAsync() and call the calback with + to call FileBasedDocument::saveIfNeededAndUserAgreesAsync() and call the callback with true if this returned FileBasedDocument::savedOk. @see closeDocumentAsync, FileBasedDocument::saveIfNeededAndUserAgreesAsync() diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h index 9123740d4..8fa2b3f9b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h @@ -153,7 +153,9 @@ class JUCE_API LookAndFeel : public ScrollBar::LookAndFeelMethods, Colour findColour (int colourId) const noexcept; /** Registers a colour to be used for a particular purpose. + For more details, see the comments for findColour(). + @see findColour, Component::findColour, Component::setColour */ void setColour (int colourId, Colour colour) noexcept; @@ -165,22 +167,27 @@ class JUCE_API LookAndFeel : public ScrollBar::LookAndFeelMethods, //============================================================================== /** Returns the typeface that should be used for a given font. + The default implementation just does what you'd expect it to, but you can override this if you want to intercept fonts and use your own custom typeface object. + @see setDefaultTypeface */ virtual Typeface::Ptr getTypefaceForFont (const Font&); /** Allows you to supply a default typeface that will be returned as the default sans-serif font. + Instead of a typeface object, you can specify a typeface by name using the setDefaultSansSerifTypefaceName() method. + You can perform more complex typeface substitutions by overloading getTypefaceForFont() but this lets you easily set a global typeface. */ void setDefaultSansSerifTypeface (Typeface::Ptr newDefaultTypeface); /** Allows you to change the default sans-serif font. + If you need to supply your own Typeface object for any of the default fonts, rather than just supplying the name (e.g. if you want to use an embedded font), then you can instead call setDefaultSansSerifTypeface() with an object to use. @@ -188,39 +195,64 @@ class JUCE_API LookAndFeel : public ScrollBar::LookAndFeelMethods, void setDefaultSansSerifTypefaceName (const String& newName); //============================================================================== - /** Override this to get the chance to swap a component's mouse cursor for a - customised one. + /** Sets whether native alert windows (if available) or standard JUCE AlertWindows + drawn with AlertWindow::LookAndFeelMethods will be used. + + @see isUsingNativeAlertWindows */ - virtual MouseCursor getMouseCursorFor (Component&); + void setUsingNativeAlertWindows (bool shouldUseNativeAlerts); - //============================================================================== - /** Creates a new graphics context object. */ - virtual std::unique_ptr createGraphicsContext (const Image& imageToRenderOn, - Point origin, - const RectangleList& initialClip); + /** Returns true if native alert windows will be used (if available). - void setUsingNativeAlertWindows (bool shouldUseNativeAlerts); + The default setting for this is false. + + @see setUsingNativeAlertWindows + */ bool isUsingNativeAlertWindows(); //============================================================================== /** Draws a small image that spins to indicate that something's happening. + This method should use the current time to animate itself, so just keep repainting it every so often. */ virtual void drawSpinningWaitAnimation (Graphics&, const Colour& colour, int x, int y, int w, int h) = 0; - //============================================================================== /** Returns a tick shape for use in yes/no boxes, etc. */ virtual Path getTickShape (float height) = 0; + /** Returns a cross shape for use in yes/no boxes, etc. */ virtual Path getCrossShape (float height) = 0; - //============================================================================== - virtual DropShadower* createDropShadowerForComponent (Component*) = 0; + /** Creates a drop-shadower for a given component, if required. + + @see DropShadower + */ + virtual std::unique_ptr createDropShadowerForComponent (Component&) = 0; + + /** Creates a focus outline for a given component, if required. + + @see FocusOutline + */ + virtual std::unique_ptr createFocusOutlineForComponent (Component&) = 0; //============================================================================== - /** Plays the system's default 'beep' noise, to alert the user about something very important. */ + /** Override this to get the chance to swap a component's mouse cursor for a + customised one. + + @see MouseCursor + */ + virtual MouseCursor getMouseCursorFor (Component&); + + /** Creates a new graphics context object. */ + virtual std::unique_ptr createGraphicsContext (const Image& imageToRenderOn, + Point origin, + const RectangleList& initialClip); + + /** Plays the system's default 'beep' noise, to alert the user about something + very important. This is only supported on some platforms. + */ virtual void playAlertSound(); private: diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 800a79f0d..be243790f 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -188,15 +188,22 @@ LookAndFeel_V2::LookAndFeel_V2() 0x1000440, /*LassoComponent::lassoFillColourId*/ 0x66dddddd, 0x1000441, /*LassoComponent::lassoOutlineColourId*/ 0x99111111, + 0x1004000, /*KeyboardComponentBase::upDownButtonBackgroundColourId*/ 0xffd3d3d3, + 0x1004001, /*KeyboardComponentBase::upDownButtonArrowColourId*/ 0xff000000, + 0x1005000, /*MidiKeyboardComponent::whiteNoteColourId*/ 0xffffffff, 0x1005001, /*MidiKeyboardComponent::blackNoteColourId*/ 0xff000000, 0x1005002, /*MidiKeyboardComponent::keySeparatorLineColourId*/ 0x66000000, 0x1005003, /*MidiKeyboardComponent::mouseOverKeyOverlayColourId*/ 0x80ffff00, 0x1005004, /*MidiKeyboardComponent::keyDownOverlayColourId*/ 0xffb6b600, 0x1005005, /*MidiKeyboardComponent::textLabelColourId*/ 0xff000000, - 0x1005006, /*MidiKeyboardComponent::upDownButtonBackgroundColourId*/ 0xffd3d3d3, - 0x1005007, /*MidiKeyboardComponent::upDownButtonArrowColourId*/ 0xff000000, - 0x1005008, /*MidiKeyboardComponent::shadowColourId*/ 0x4c000000, + 0x1005006, /*MidiKeyboardComponent::shadowColourId*/ 0x4c000000, + + 0x1006000, /*MPEKeyboardComponent::whiteNoteColourId*/ 0xff1a1c27, + 0x1006001, /*MPEKeyboardComponent::blackNoteColourId*/ 0x99f1f1f1, + 0x1006002, /*MPEKeyboardComponent::textLabelColourId*/ 0xfff1f1f1, + 0x1006003, /*MPEKeyboardComponent::noteCircleFillColourId*/ 0x99ba00ff, + 0x1006004, /*MPEKeyboardComponent::noteCircleOutlineColourId*/ 0xfff1f1f1, 0x1004500, /*CodeEditorComponent::backgroundColourId*/ 0xffffffff, 0x1004502, /*CodeEditorComponent::highlightColourId*/ textHighlightColour, @@ -2056,9 +2063,28 @@ int LookAndFeel_V2::getDefaultMenuBarHeight() } //============================================================================== -DropShadower* LookAndFeel_V2::createDropShadowerForComponent (Component*) +std::unique_ptr LookAndFeel_V2::createDropShadowerForComponent (Component&) { - return new DropShadower (DropShadow (Colours::black.withAlpha (0.4f), 10, Point (0, 2))); + return std::make_unique (DropShadow (Colours::black.withAlpha (0.4f), 10, Point (0, 2))); +} + +std::unique_ptr LookAndFeel_V2::createFocusOutlineForComponent (Component&) +{ + struct WindowProperties : public FocusOutline::OutlineWindowProperties + { + Rectangle getOutlineBounds (Component& c) override + { + return c.getScreenBounds(); + } + + void drawOutline (Graphics& g, int width, int height) override + { + g.setColour (Colours::yellow.withAlpha (0.6f)); + g.drawRoundedRectangle ({ (float) width, (float) height }, 3.0f, 3.0f); + } + }; + + return std::make_unique (std::make_unique()); } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h index d1cea998f..2c2266ce3 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -299,7 +299,8 @@ class JUCE_API LookAndFeel_V2 : public LookAndFeel bool positionTitleBarButtonsOnLeft) override; //============================================================================== - DropShadower* createDropShadowerForComponent (Component*) override; + std::unique_ptr createDropShadowerForComponent (Component&) override; + std::unique_ptr createFocusOutlineForComponent (Component&) override; //============================================================================== void drawStretchableLayoutResizerBar (Graphics&, int w, int h, bool isVerticalBar, diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp index 3b6e78642..beacf7e22 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp @@ -1438,15 +1438,22 @@ void LookAndFeel_V4::initialiseColours() 0x1000440, /*LassoComponent::lassoFillColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).getARGB(), 0x1000441, /*LassoComponent::lassoOutlineColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(), + 0x1004000, /*KeyboardComponentBase::upDownButtonBackgroundColourId*/ 0xffd3d3d3, + 0x1004001, /*KeyboardComponentBase::upDownButtonArrowColourId*/ 0xff000000, + 0x1005000, /*MidiKeyboardComponent::whiteNoteColourId*/ 0xffffffff, 0x1005001, /*MidiKeyboardComponent::blackNoteColourId*/ 0xff000000, 0x1005002, /*MidiKeyboardComponent::keySeparatorLineColourId*/ 0x66000000, 0x1005003, /*MidiKeyboardComponent::mouseOverKeyOverlayColourId*/ 0x80ffff00, 0x1005004, /*MidiKeyboardComponent::keyDownOverlayColourId*/ 0xffb6b600, 0x1005005, /*MidiKeyboardComponent::textLabelColourId*/ 0xff000000, - 0x1005006, /*MidiKeyboardComponent::upDownButtonBackgroundColourId*/ 0xffd3d3d3, - 0x1005007, /*MidiKeyboardComponent::upDownButtonArrowColourId*/ 0xff000000, - 0x1005008, /*MidiKeyboardComponent::shadowColourId*/ 0x4c000000, + 0x1005006, /*MidiKeyboardComponent::shadowColourId*/ 0x4c000000, + + 0x1006000, /*MPEKeyboardComponent::whiteNoteColourId*/ 0xff1a1c27, + 0x1006001, /*MPEKeyboardComponent::blackNoteColourId*/ 0x99f1f1f1, + 0x1006002, /*MPEKeyboardComponent::textLabelColourId*/ 0xfff1f1f1, + 0x1006003, /*MPEKeyboardComponent::noteCircleFillColourId*/ 0x99ba00ff, + 0x1006004, /*MPEKeyboardComponent::noteCircleOutlineColourId*/ 0xfff1f1f1, 0x1004500, /*CodeEditorComponent::backgroundColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(), 0x1004502, /*CodeEditorComponent::highlightColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).withAlpha (0.4f).getARGB(), diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index 951542abd..649d3aa2e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -41,8 +41,20 @@ struct PopupMenu::HelperClasses class MouseSourceState; struct MenuWindow; -static bool canBeTriggered (const PopupMenu::Item& item) noexcept { return item.isEnabled && item.itemID != 0 && ! item.isSectionHeader; } -static bool hasActiveSubMenu (const PopupMenu::Item& item) noexcept { return item.isEnabled && item.subMenu != nullptr && item.subMenu->items.size() > 0; } +static bool canBeTriggered (const PopupMenu::Item& item) noexcept +{ + return item.isEnabled + && item.itemID != 0 + && ! item.isSectionHeader + && (item.customComponent == nullptr || item.customComponent->isTriggeredAutomatically()); +} + +static bool hasActiveSubMenu (const PopupMenu::Item& item) noexcept +{ + return item.isEnabled + && item.subMenu != nullptr + && item.subMenu->items.size() > 0; +} //============================================================================== struct HeaderItemComponent : public PopupMenu::CustomComponent @@ -73,6 +85,11 @@ struct HeaderItemComponent : public PopupMenu::CustomComponent idealWidth += idealWidth / 4; } + std::unique_ptr createAccessibilityHandler() override + { + return nullptr; + } + const Options& options; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HeaderItemComponent) @@ -81,9 +98,7 @@ struct HeaderItemComponent : public PopupMenu::CustomComponent //============================================================================== struct ItemComponent : public Component { - ItemComponent (const PopupMenu::Item& i, - const PopupMenu::Options& o, - MenuWindow& parent) + ItemComponent (const PopupMenu::Item& i, const PopupMenu::Options& o, MenuWindow& parent) : item (i), parentWindow (parent), options (o), customComp (i.customComponent) { if (item.isSectionHeader) @@ -164,17 +179,48 @@ struct ItemComponent : public Component } } + static bool isAccessibilityHandlerRequired (const PopupMenu::Item& item) + { + return item.isSectionHeader || hasActiveSubMenu (item) || canBeTriggered (item); + } + PopupMenu::Item item; private: + class ValueInterface : public AccessibilityValueInterface + { + public: + ValueInterface() = default; + + bool isReadOnly() const override { return true; } + + double getCurrentValue() const override + { + return 1.0; + } + + String getCurrentValueAsString() const override + { + return TRANS ("Checked"); + } + + void setValue (double) override {} + void setValueAsString (const String&) override {} + + AccessibleValueRange getRange() const override { return {}; } + }; + //============================================================================== class ItemAccessibilityHandler : public AccessibilityHandler { public: explicit ItemAccessibilityHandler (ItemComponent& itemComponentToWrap) : AccessibilityHandler (itemComponentToWrap, - AccessibilityRole::menuItem, - getAccessibilityActions (*this, itemComponentToWrap)), + isAccessibilityHandlerRequired (itemComponentToWrap.item) ? AccessibilityRole::menuItem + : AccessibilityRole::ignored, + getAccessibilityActions (*this, itemComponentToWrap), + AccessibilityHandler::Interfaces { itemComponentToWrap.item.isTicked ? std::make_unique() + : nullptr }), itemComponent (itemComponentToWrap) { } @@ -195,6 +241,9 @@ struct ItemComponent : public Component : state.withExpandable().withCollapsed(); } + if (itemComponent.item.isTicked) + state = state.withChecked(); + return state.isFocused() ? state.withSelected() : state; } @@ -209,12 +258,6 @@ struct ItemComponent : public Component item.parentWindow.setCurrentlyHighlightedChild (&item); }; - auto onPress = [&item] - { - item.parentWindow.setCurrentlyHighlightedChild (&item); - item.parentWindow.triggerCurrentlyHighlightedItem(); - }; - auto onToggle = [&handler, &item, onFocus] { if (handler.getCurrentState().isSelected()) @@ -224,9 +267,17 @@ struct ItemComponent : public Component }; auto actions = AccessibilityActions().addAction (AccessibilityActionType::focus, std::move (onFocus)) - .addAction (AccessibilityActionType::press, std::move (onPress)) .addAction (AccessibilityActionType::toggle, std::move (onToggle)); + if (canBeTriggered (item.item)) + { + actions.addAction (AccessibilityActionType::press, [&item] + { + item.parentWindow.setCurrentlyHighlightedChild (&item); + item.parentWindow.triggerCurrentlyHighlightedItem(); + }); + } + if (hasActiveSubMenu (item.item)) { auto showSubMenu = [&item] @@ -1176,10 +1227,7 @@ struct MenuWindow : public Component void triggerCurrentlyHighlightedItem() { - if (currentChild != nullptr - && canBeTriggered (currentChild->item) - && (currentChild->item.customComponent == nullptr - || currentChild->item.customComponent->isTriggeredAutomatically())) + if (currentChild != nullptr && canBeTriggered (currentChild->item)) { dismissMenu (¤tChild->item); } @@ -1720,7 +1768,7 @@ PopupMenu::Item&& PopupMenu::Item::setImage (std::unique_ptr newImage) void PopupMenu::addItem (Item newItem) { // An ID of 0 is used as a return value to indicate that the user - // didn't pick anything, so you shouldn't use it as the ID for an item.. + // didn't pick anything, so you shouldn't use it as the ID for an item. jassert (newItem.itemID != 0 || newItem.isSeparator || newItem.isSectionHeader || newItem.subMenu != nullptr); @@ -1828,12 +1876,23 @@ void PopupMenu::addColouredItem (int itemResultID, String itemText, Colour itemT void PopupMenu::addCustomItem (int itemResultID, std::unique_ptr cc, - std::unique_ptr subMenu) + std::unique_ptr subMenu, + const String& itemTitle) { Item i; + i.text = itemTitle; i.itemID = itemResultID; i.customComponent = cc.release(); i.subMenu.reset (createCopyIfNotNull (subMenu.get())); + + // If this assertion is hit, this item will be visible to screen readers but with + // no name, which may be confusing to users. + // It's probably a good idea to add a title for this menu item that describes + // the meaning of the item, or the contents of the submenu, as appropriate. + // If you don't want this menu item to be press-able directly, pass "false" to the + // constructor of the CustomComponent. + jassert (! (HelperClasses::ItemComponent::isAccessibilityHandlerRequired (i) && itemTitle.isEmpty())); + addItem (std::move (i)); } @@ -1841,11 +1900,12 @@ void PopupMenu::addCustomItem (int itemResultID, Component& customComponent, int idealWidth, int idealHeight, bool triggerMenuItemAutomaticallyWhenClicked, - std::unique_ptr subMenu) + std::unique_ptr subMenu, + const String& itemTitle) { auto comp = std::make_unique (customComponent, idealWidth, idealHeight, triggerMenuItemAutomaticallyWhenClicked); - addCustomItem (itemResultID, std::move (comp), std::move (subMenu)); + addCustomItem (itemResultID, std::move (comp), std::move (subMenu), itemTitle); } void PopupMenu::addSubMenu (String subMenuName, PopupMenu subMenu, bool isActive) @@ -2211,15 +2271,13 @@ void PopupMenu::setItem (CustomComponent& c, const Item* itemToUse) } //============================================================================== +PopupMenu::CustomComponent::CustomComponent() : CustomComponent (true) {} + PopupMenu::CustomComponent::CustomComponent (bool autoTrigger) : triggeredAutomatically (autoTrigger) { } -PopupMenu::CustomComponent::~CustomComponent() -{ -} - void PopupMenu::CustomComponent::setHighlighted (bool shouldBeHighlighted) { isHighlighted = shouldBeHighlighted; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h index f731b349c..1ea8507e1 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h @@ -39,20 +39,22 @@ namespace juce m.addItem (1, "item 1"); m.addItem (2, "item 2"); - const int result = m.show(); - - if (result == 0) - { - // user dismissed the menu without picking anything - } - else if (result == 1) - { - // user picked item 1 - } - else if (result == 2) - { - // user picked item 2 - } + m.showMenuAsync (PopupMenu::Options(), + [] (int result) + { + if (result == 0) + { + // user dismissed the menu without picking anything + } + else if (result == 1) + { + // user picked item 1 + } + else if (result == 2) + { + // user picked item 2 + } + }); } @endcode @@ -68,9 +70,7 @@ namespace juce mainMenu.addItem (3, "item 3"); mainMenu.addSubMenu ("other choices", subMenu); - const int result = m.show(); - - ...etc + m.showMenuAsync (...); } @endcode @@ -333,11 +333,15 @@ class JUCE_API PopupMenu Note that native macOS menus do not support custom components. + itemTitle will be used as the fallback text for this item, and will + be exposed to screen reader clients. + @see CustomComponent */ void addCustomItem (int itemResultID, std::unique_ptr customComponent, - std::unique_ptr optionalSubMenu = nullptr); + std::unique_ptr optionalSubMenu = nullptr, + const String& itemTitle = {}); /** Appends a custom menu item that can't be used to trigger a result. @@ -350,6 +354,9 @@ class JUCE_API PopupMenu menu ID specified in itemResultID. If this is false, the menu item can't be triggered, so itemResultID is not used. + itemTitle will be used as the fallback text for this item, and will + be exposed to screen reader clients. + Note that native macOS menus do not support custom components. */ void addCustomItem (int itemResultID, @@ -357,7 +364,8 @@ class JUCE_API PopupMenu int idealWidth, int idealHeight, bool triggerMenuItemAutomaticallyWhenClicked, - std::unique_ptr optionalSubMenu = nullptr); + std::unique_ptr optionalSubMenu = nullptr, + const String& itemTitle = {}); /** Appends a sub-menu. @@ -820,15 +828,22 @@ class JUCE_API PopupMenu public SingleThreadedReferenceCountedObject { public: + /** Creates a custom item that is triggered automatically. */ + CustomComponent(); + /** Creates a custom item. + If isTriggeredAutomatically is true, then the menu will automatically detect a mouse-click on this component and use that to invoke the menu item. If it's false, then it's up to your class to manually trigger the item when it wants to. - */ - CustomComponent (bool isTriggeredAutomatically = true); - /** Destructor. */ - ~CustomComponent() override; + If isTriggeredAutomatically is true, then an accessibility handler 'wrapper' + will be created for the item that allows pressing, focusing, and toggling. + If isTriggeredAutomatically is false, and the item has no submenu, then + no accessibility wrapper will be created and your component must be + independently accessible. + */ + explicit CustomComponent (bool isTriggeredAutomatically); /** Returns a rectangle with the size that this component would like to have. diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.h b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.h index e56be3484..201dc94d8 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_DropShadower.h @@ -55,17 +55,8 @@ class JUCE_API DropShadower : private ComponentListener /** Attaches the DropShadower to the component you want to shadow. */ void setOwner (Component* componentToFollow); - private: //============================================================================== - class ShadowWindow; - - WeakReference owner; - OwnedArray shadowWindows; - DropShadow shadow; - bool reentrant = false; - WeakReference lastParentComp; - void componentMovedOrResized (Component&, bool, bool) override; void componentBroughtToFront (Component&) override; void componentChildrenChanged (Component&) override; @@ -75,6 +66,14 @@ class JUCE_API DropShadower : private ComponentListener void updateParent(); void updateShadows(); + class ShadowWindow; + + WeakReference owner; + OwnedArray shadowWindows; + DropShadow shadow; + bool reentrant = false; + WeakReference lastParentComp; + class ParentVisibilityChangedListener; std::unique_ptr visibilityChangedListener; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_FocusOutline.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_FocusOutline.cpp new file mode 100644 index 000000000..5f328e15a --- /dev/null +++ b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_FocusOutline.cpp @@ -0,0 +1,185 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +//============================================================================== +struct OutlineWindowComponent : public Component +{ + OutlineWindowComponent (Component* c, FocusOutline::OutlineWindowProperties& p) + : target (c), props (p) + { + setVisible (true); + setInterceptsMouseClicks (false, false); + + if (target->isOnDesktop()) + { + setSize (1, 1); + addToDesktop (ComponentPeer::windowIgnoresMouseClicks + | ComponentPeer::windowIsTemporary + | ComponentPeer::windowIgnoresKeyPresses); + } + else if (auto* parent = target->getParentComponent()) + { + auto targetIndex = parent->getIndexOfChildComponent (target); + parent->addChildComponent (this, targetIndex + 1); + } + } + + void paint (Graphics& g) override + { + if (target != nullptr) + props.drawOutline (g, getWidth(), getHeight()); + } + + void resized() override + { + repaint(); + } + + float getDesktopScaleFactor() const override + { + return target != nullptr ? target->getDesktopScaleFactor() + : Component::getDesktopScaleFactor(); + } + +private: + WeakReference target; + FocusOutline::OutlineWindowProperties& props; + + JUCE_DECLARE_NON_COPYABLE (OutlineWindowComponent) +}; + +//============================================================================== +FocusOutline::FocusOutline (std::unique_ptr props) + : properties (std::move (props)) +{ +} + +FocusOutline::~FocusOutline() +{ + if (owner != nullptr) + owner->removeComponentListener (this); + + if (lastParentComp != nullptr) + lastParentComp->removeComponentListener (this); +} + +void FocusOutline::setOwner (Component* componentToFollow) +{ + if (componentToFollow != owner) + { + if (owner != nullptr) + owner->removeComponentListener (this); + + owner = componentToFollow; + + if (owner != nullptr) + owner->addComponentListener (this); + + updateParent(); + updateOutlineWindow(); + } +} + +void FocusOutline::componentMovedOrResized (Component& c, bool, bool) +{ + if (owner == &c) + updateOutlineWindow(); +} + +void FocusOutline::componentBroughtToFront (Component& c) +{ + if (owner == &c) + updateOutlineWindow(); +} + +void FocusOutline::componentParentHierarchyChanged (Component& c) +{ + if (owner == &c) + { + updateParent(); + updateOutlineWindow(); + } +} + +void FocusOutline::componentVisibilityChanged (Component& c) +{ + if (owner == &c) + updateOutlineWindow(); +} + +void FocusOutline::updateParent() +{ + lastParentComp = (owner != nullptr ? owner->getParentComponent() + : nullptr); +} + +void FocusOutline::updateOutlineWindow() +{ + if (reentrant) + return; + + const ScopedValueSetter setter (reentrant, true); + + if (owner == nullptr) + { + outlineWindow = nullptr; + return; + } + + if (owner->isShowing() + && owner->getWidth() > 0 && owner->getHeight() > 0) + { + if (outlineWindow == nullptr) + outlineWindow = std::make_unique (owner, *properties); + + WeakReference deletionChecker (outlineWindow.get()); + + outlineWindow->setAlwaysOnTop (owner->isAlwaysOnTop()); + + if (deletionChecker == nullptr) + return; + + const auto windowBounds = [this] + { + const auto bounds = properties->getOutlineBounds (*owner); + + if (lastParentComp != nullptr) + return lastParentComp->getLocalArea (nullptr, bounds); + + return bounds; + }(); + + outlineWindow->setBounds (windowBounds); + } + else + { + outlineWindow = nullptr; + } +} + +} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_FocusOutline.h b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_FocusOutline.h new file mode 100644 index 000000000..32c626f9e --- /dev/null +++ b/external/JuceLibraryCode/modules/juce_gui_basics/misc/juce_FocusOutline.h @@ -0,0 +1,100 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +/** + Adds a focus outline to a component. + + This object creates and manages a component that sits on top of a target + component. It will track the position of the target component and will be + brought to the front with the tracked component. + + Use the Component::setHasFocusOutline() method to indicate that a component + should have a focus outline drawn around it, and when it receives keyboard + focus one of these objects will be created using the + LookAndFeel::createFocusOutlineForComponent() method. You can override this + method in your own LookAndFeel classes to draw a custom outline if required. + + @tags{GUI} +*/ +class JUCE_API FocusOutline : private ComponentListener +{ +public: + //============================================================================== + /** Defines the focus outline window properties. + + Pass an instance of one of these to the FocusOutline constructor to control + the bounds for the outline window and how it is drawn. + */ + struct JUCE_API OutlineWindowProperties + { + virtual ~OutlineWindowProperties() = default; + + /** Return the bounds for the outline window in screen coordinates. */ + virtual Rectangle getOutlineBounds (Component& focusedComponent) = 0; + + /** This method will be called to draw the focus outline. */ + virtual void drawOutline (Graphics&, int width, int height) = 0; + }; + + //============================================================================== + /** Creates a FocusOutline. + + Call setOwner to attach it to a component. + */ + FocusOutline (std::unique_ptr props); + + /** Destructor. */ + ~FocusOutline() override; + + /** Attaches the outline to a component. */ + void setOwner (Component* componentToFollow); + +private: + //============================================================================== + void componentMovedOrResized (Component&, bool, bool) override; + void componentBroughtToFront (Component&) override; + void componentParentHierarchyChanged (Component&) override; + void componentVisibilityChanged (Component&) override; + + void updateOutlineWindow(); + void updateParent(); + + //============================================================================== + std::unique_ptr properties; + + WeakReference owner; + std::unique_ptr outlineWindow; + WeakReference lastParentComp; + + bool reentrant = false; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FocusOutline) +}; + +} // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.cpp index de476a849..75710f9b0 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseCursor.cpp @@ -43,7 +43,7 @@ class MouseCursor::SharedCursorHandle standard (false) { // your hotspot needs to be within the bounds of the image! - jassert (image.getImage().getBounds().contains (hotSpot)); + jassert (image.getScaledBounds().toNearestInt().contains (hotSpot)); } static std::shared_ptr createStandard (const MouseCursor::StandardCursorType type) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_android_Accessibility.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_android_Accessibility.cpp index 9c954e743..16f920748 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_android_Accessibility.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_android_Accessibility.cpp @@ -354,7 +354,8 @@ class AccessibilityNativeHandle isSelected ? ACTION_CLEAR_SELECTION : ACTION_SELECT); } - if (accessibilityHandler.getActions().contains (AccessibilityActionType::press)) + if ((accessibilityHandler.getCurrentState().isCheckable() && accessibilityHandler.getActions().contains (AccessibilityActionType::toggle)) + || accessibilityHandler.getActions().contains (AccessibilityActionType::press)) { env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setClickable, @@ -480,7 +481,8 @@ class AccessibilityNativeHandle case ACTION_CLICK: { - if (accessibilityHandler.getActions().invoke (AccessibilityActionType::press)) + if ((accessibilityHandler.getCurrentState().isCheckable() && accessibilityHandler.getActions().invoke (AccessibilityActionType::toggle)) + || accessibilityHandler.getActions().invoke (AccessibilityActionType::press)) { sendAccessibilityEventImpl (accessibilityHandler, TYPE_VIEW_CLICKED, 0); return true; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm index f567dcd8c..9bb320d4b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm @@ -399,11 +399,11 @@ static BOOL accessibilityPerformActivate (id self, SEL) { if (auto* handler = getHandler (self)) { - // occasionaly VoiceOver sends accessibilityActivate to the wrong element, so we first query + // Occasionally VoiceOver sends accessibilityActivate to the wrong element, so we first query // which element it thinks has focus and forward the event on to that element if it differs id focusedElement = UIAccessibilityFocusedElement (UIAccessibilityNotificationVoiceOverIdentifier); - if (! [(id) handler->getNativeImplementation() isEqual: focusedElement]) + if (focusedElement != nullptr && ! [(id) handler->getNativeImplementation() isEqual: focusedElement]) return [focusedElement accessibilityActivate]; if (handler->hasFocus (false)) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm index 8e2d33a39..5bce418b8 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm @@ -797,7 +797,8 @@ static BOOL getIsAccessibilitySelectorAllowed (id self, SEL, SEL selector) continue; if (selector == @selector (accessibilityPerformPress)) - return handler->getActions().contains (AccessibilityActionType::press); + return (handler->getCurrentState().isCheckable() && handler->getActions().contains (AccessibilityActionType::toggle)) + || handler->getActions().contains (AccessibilityActionType::press); if (selector == @selector (accessibilityPerformShowMenu)) return handler->getActions().contains (AccessibilityActionType::showMenu); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm index c3050709e..f46e2f152 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm @@ -126,6 +126,10 @@ static BOOL performActionIfSupported (id self, AccessibilityActionType actionTyp static BOOL accessibilityPerformPress (id self, SEL) { + if (auto* handler = getHandler (self)) + if (handler->getCurrentState().isCheckable() && handler->getActions().invoke (AccessibilityActionType::toggle)) + return YES; + return performActionIfSupported (self, AccessibilityActionType::press); } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp index 7432e3766..744cd033e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp @@ -159,7 +159,7 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte || eventType == InternalAccessibilityEvent::elementDestroyed) { if (auto* parent = handler.getParent()) - sendAccessibilityAutomationEvent (*parent, UIA_LayoutInvalidatedEventId); + sendAccessibilityAutomationEvent (*parent, ComTypes::UIA_LayoutInvalidatedEventId); return; } @@ -176,9 +176,9 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte { switch (eventType) { - case InternalAccessibilityEvent::focusChanged: return UIA_AutomationFocusChangedEventId; - case InternalAccessibilityEvent::windowOpened: return UIA_Window_WindowOpenedEventId; - case InternalAccessibilityEvent::windowClosed: return UIA_Window_WindowClosedEventId; + case InternalAccessibilityEvent::focusChanged: return ComTypes::UIA_AutomationFocusChangedEventId; + case InternalAccessibilityEvent::windowOpened: return ComTypes::UIA_Window_WindowOpenedEventId; + case InternalAccessibilityEvent::windowClosed: return ComTypes::UIA_Window_WindowClosedEventId; case InternalAccessibilityEvent::elementCreated: case InternalAccessibilityEvent::elementDestroyed: case InternalAccessibilityEvent::elementMovedOrResized: break; @@ -219,10 +219,10 @@ void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventTyp { switch (eventType) { - case AccessibilityEvent::textSelectionChanged: return UIA_Text_TextSelectionChangedEventId; - case AccessibilityEvent::textChanged: return UIA_Text_TextChangedEventId; - case AccessibilityEvent::structureChanged: return UIA_StructureChangedEventId; - case AccessibilityEvent::rowSelectionChanged: return UIA_SelectionItem_ElementSelectedEventId; + case AccessibilityEvent::textSelectionChanged: return ComTypes::UIA_Text_TextSelectionChangedEventId; + case AccessibilityEvent::textChanged: return ComTypes::UIA_Text_TextChangedEventId; + case AccessibilityEvent::structureChanged: return ComTypes::UIA_StructureChangedEventId; + case AccessibilityEvent::rowSelectionChanged: return ComTypes::UIA_SelectionItem_ElementSelectedEventId; case AccessibilityEvent::titleChanged: case AccessibilityEvent::valueChanged: break; } @@ -238,7 +238,7 @@ struct SpVoiceWrapper : public DeletedAtShutdown { SpVoiceWrapper() { - auto hr = voice.CoCreateInstance (CLSID_SpVoice); + auto hr = voice.CoCreateInstance (ComTypes::CLSID_SpVoice); jassertquiet (SUCCEEDED (hr)); } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp index 13a7e1a1b..364434da2 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp @@ -57,41 +57,41 @@ static auto roleToControlTypeId (AccessibilityRole roleType) case AccessibilityRole::popupMenu: case AccessibilityRole::dialogWindow: case AccessibilityRole::splashScreen: - case AccessibilityRole::window: return UIA_WindowControlTypeId; + case AccessibilityRole::window: return ComTypes::UIA_WindowControlTypeId; case AccessibilityRole::label: - case AccessibilityRole::staticText: return UIA_TextControlTypeId; + case AccessibilityRole::staticText: return ComTypes::UIA_TextControlTypeId; case AccessibilityRole::column: - case AccessibilityRole::row: return UIA_HeaderItemControlTypeId; - - case AccessibilityRole::button: return UIA_ButtonControlTypeId; - case AccessibilityRole::toggleButton: return UIA_CheckBoxControlTypeId; - case AccessibilityRole::radioButton: return UIA_RadioButtonControlTypeId; - case AccessibilityRole::comboBox: return UIA_ComboBoxControlTypeId; - case AccessibilityRole::image: return UIA_ImageControlTypeId; - case AccessibilityRole::slider: return UIA_SliderControlTypeId; - case AccessibilityRole::editableText: return UIA_EditControlTypeId; - case AccessibilityRole::menuItem: return UIA_MenuItemControlTypeId; - case AccessibilityRole::menuBar: return UIA_MenuBarControlTypeId; - case AccessibilityRole::table: return UIA_TableControlTypeId; - case AccessibilityRole::tableHeader: return UIA_HeaderControlTypeId; - case AccessibilityRole::cell: return UIA_DataItemControlTypeId; - case AccessibilityRole::hyperlink: return UIA_HyperlinkControlTypeId; - case AccessibilityRole::list: return UIA_ListControlTypeId; - case AccessibilityRole::listItem: return UIA_ListItemControlTypeId; - case AccessibilityRole::tree: return UIA_TreeControlTypeId; - case AccessibilityRole::treeItem: return UIA_TreeItemControlTypeId; - case AccessibilityRole::progressBar: return UIA_ProgressBarControlTypeId; - case AccessibilityRole::group: return UIA_GroupControlTypeId; - case AccessibilityRole::scrollBar: return UIA_ScrollBarControlTypeId; - case AccessibilityRole::tooltip: return UIA_ToolTipControlTypeId; + case AccessibilityRole::row: return ComTypes::UIA_HeaderItemControlTypeId; + + case AccessibilityRole::button: return ComTypes::UIA_ButtonControlTypeId; + case AccessibilityRole::toggleButton: return ComTypes::UIA_CheckBoxControlTypeId; + case AccessibilityRole::radioButton: return ComTypes::UIA_RadioButtonControlTypeId; + case AccessibilityRole::comboBox: return ComTypes::UIA_ComboBoxControlTypeId; + case AccessibilityRole::image: return ComTypes::UIA_ImageControlTypeId; + case AccessibilityRole::slider: return ComTypes::UIA_SliderControlTypeId; + case AccessibilityRole::editableText: return ComTypes::UIA_EditControlTypeId; + case AccessibilityRole::menuItem: return ComTypes::UIA_MenuItemControlTypeId; + case AccessibilityRole::menuBar: return ComTypes::UIA_MenuBarControlTypeId; + case AccessibilityRole::table: return ComTypes::UIA_TableControlTypeId; + case AccessibilityRole::tableHeader: return ComTypes::UIA_HeaderControlTypeId; + case AccessibilityRole::cell: return ComTypes::UIA_DataItemControlTypeId; + case AccessibilityRole::hyperlink: return ComTypes::UIA_HyperlinkControlTypeId; + case AccessibilityRole::list: return ComTypes::UIA_ListControlTypeId; + case AccessibilityRole::listItem: return ComTypes::UIA_ListItemControlTypeId; + case AccessibilityRole::tree: return ComTypes::UIA_TreeControlTypeId; + case AccessibilityRole::treeItem: return ComTypes::UIA_TreeItemControlTypeId; + case AccessibilityRole::progressBar: return ComTypes::UIA_ProgressBarControlTypeId; + case AccessibilityRole::group: return ComTypes::UIA_GroupControlTypeId; + case AccessibilityRole::scrollBar: return ComTypes::UIA_ScrollBarControlTypeId; + case AccessibilityRole::tooltip: return ComTypes::UIA_ToolTipControlTypeId; case AccessibilityRole::ignored: case AccessibilityRole::unspecified: break; }; - return UIA_CustomControlTypeId; + return ComTypes::UIA_CustomControlTypeId; } //============================================================================== @@ -109,7 +109,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::QueryInterface (REFIID refId, void** r if (! isElementValid()) return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE; - if ((refId == __uuidof (IRawElementProviderFragmentRoot) && ! isFragmentRoot())) + if ((refId == __uuidof (ComTypes::IRawElementProviderFragmentRoot) && ! isFragmentRoot())) return E_NOINTERFACE; return ComBaseClassHelper::QueryInterface (refId, result); @@ -133,7 +133,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::get_ProviderOptions (ProviderOptions* if (options == nullptr) return E_INVALIDARG; - *options = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading; + *options = (ProviderOptions) (ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading); return S_OK; } @@ -148,36 +148,36 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUn switch (pId) { - case UIA_WindowPatternId: + case ComTypes::UIA_WindowPatternId: { if (fragmentRoot) return new UIAWindowProvider (this); break; } - case UIA_TransformPatternId: + case ComTypes::UIA_TransformPatternId: { if (fragmentRoot) return new UIATransformProvider (this); break; } - case UIA_TextPatternId: - case UIA_TextPattern2Id: + case ComTypes::UIA_TextPatternId: + case ComTypes::UIA_TextPattern2Id: { if (accessibilityHandler.getTextInterface() != nullptr) return new UIATextProvider (this); break; } - case UIA_ValuePatternId: + case ComTypes::UIA_ValuePatternId: { if (accessibilityHandler.getValueInterface() != nullptr) return new UIAValueProvider (this); break; } - case UIA_RangeValuePatternId: + case ComTypes::UIA_RangeValuePatternId: { if (accessibilityHandler.getValueInterface() != nullptr && accessibilityHandler.getValueInterface()->getRange().isValid()) @@ -187,17 +187,18 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUn break; } - case UIA_TogglePatternId: + case ComTypes::UIA_TogglePatternId: { - if (accessibilityHandler.getActions().contains (AccessibilityActionType::toggle) - && accessibilityHandler.getCurrentState().isCheckable()) + if (accessibilityHandler.getCurrentState().isCheckable() + && (accessibilityHandler.getActions().contains (AccessibilityActionType::toggle) + || accessibilityHandler.getActions().contains (AccessibilityActionType::press))) { return new UIAToggleProvider (this); } break; } - case UIA_SelectionPatternId: + case ComTypes::UIA_SelectionPatternId: { if (role == AccessibilityRole::list || role == AccessibilityRole::popupMenu @@ -208,7 +209,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUn break; } - case UIA_SelectionItemPatternId: + case ComTypes::UIA_SelectionItemPatternId: { auto state = accessibilityHandler.getCurrentState(); @@ -220,28 +221,28 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUn break; } - case UIA_GridPatternId: + case ComTypes::UIA_GridPatternId: { if (accessibilityHandler.getTableInterface() != nullptr) return new UIAGridProvider (this); break; } - case UIA_GridItemPatternId: + case ComTypes::UIA_GridItemPatternId: { if (accessibilityHandler.getCellInterface() != nullptr) return new UIAGridItemProvider (this); break; } - case UIA_InvokePatternId: + case ComTypes::UIA_InvokePatternId: { if (accessibilityHandler.getActions().contains (AccessibilityActionType::press)) return new UIAInvokeProvider (this); break; } - case UIA_ExpandCollapsePatternId: + case ComTypes::UIA_ExpandCollapsePatternId: { if (accessibilityHandler.getActions().contains (AccessibilityActionType::showMenu) && accessibilityHandler.getCurrentState().isExpandable()) @@ -312,7 +313,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyI VariantHelpers::setBool (textInterface->isDisplayingProtectedText(), pRetVal); break; - case UIA_IsPeripheralPropertyId: + case ComTypes::UIA_IsPeripheralPropertyId: VariantHelpers::setBool (role == AccessibilityRole::tooltip || role == AccessibilityRole::popupMenu || role == AccessibilityRole::splashScreen, @@ -338,27 +339,27 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyI } //============================================================================== -JUCE_COMRESULT AccessibilityNativeHandle::Navigate (NavigateDirection direction, IRawElementProviderFragment** pRetVal) +JUCE_COMRESULT AccessibilityNativeHandle::Navigate (ComTypes::NavigateDirection direction, ComTypes::IRawElementProviderFragment** pRetVal) { return withCheckedComArgs (pRetVal, *this, [&] { auto* handler = [&]() -> AccessibilityHandler* { - if (direction == NavigateDirection_Parent) + if (direction == ComTypes::NavigateDirection_Parent) return accessibilityHandler.getParent(); - if (direction == NavigateDirection_FirstChild - || direction == NavigateDirection_LastChild) + if (direction == ComTypes::NavigateDirection_FirstChild + || direction == ComTypes::NavigateDirection_LastChild) { auto children = accessibilityHandler.getChildren(); return children.empty() ? nullptr - : (direction == NavigateDirection_FirstChild ? children.front() - : children.back()); + : (direction == ComTypes::NavigateDirection_FirstChild ? children.front() + : children.back()); } - if (direction == NavigateDirection_NextSibling - || direction == NavigateDirection_PreviousSibling) + if (direction == ComTypes::NavigateDirection_NextSibling + || direction == ComTypes::NavigateDirection_PreviousSibling) { if (auto* parent = accessibilityHandler.getParent()) { @@ -368,10 +369,10 @@ JUCE_COMRESULT AccessibilityNativeHandle::Navigate (NavigateDirection direction, if (iter == siblings.end()) return nullptr; - if (direction == NavigateDirection_NextSibling && iter != std::prev (siblings.cend())) + if (direction == ComTypes::NavigateDirection_NextSibling && iter != std::prev (siblings.cend())) return *std::next (iter); - if (direction == NavigateDirection_PreviousSibling && iter != siblings.cbegin()) + if (direction == ComTypes::NavigateDirection_PreviousSibling && iter != siblings.cbegin()) return *std::prev (iter); } } @@ -412,7 +413,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetRuntimeId (SAFEARRAY** pRetVal) }); } -JUCE_COMRESULT AccessibilityNativeHandle::get_BoundingRectangle (UiaRect* pRetVal) +JUCE_COMRESULT AccessibilityNativeHandle::get_BoundingRectangle (ComTypes::UiaRect* pRetVal) { return withCheckedComArgs (pRetVal, *this, [&] { @@ -451,7 +452,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::SetFocus() return S_OK; } -JUCE_COMRESULT AccessibilityNativeHandle::get_FragmentRoot (IRawElementProviderFragmentRoot** pRetVal) +JUCE_COMRESULT AccessibilityNativeHandle::get_FragmentRoot (ComTypes::IRawElementProviderFragmentRoot** pRetVal) { return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT { @@ -477,7 +478,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::get_FragmentRoot (IRawElementProviderF } //============================================================================== -JUCE_COMRESULT AccessibilityNativeHandle::ElementProviderFromPoint (double x, double y, IRawElementProviderFragment** pRetVal) +JUCE_COMRESULT AccessibilityNativeHandle::ElementProviderFromPoint (double x, double y, ComTypes::IRawElementProviderFragment** pRetVal) { return withCheckedComArgs (pRetVal, *this, [&] { @@ -499,7 +500,7 @@ JUCE_COMRESULT AccessibilityNativeHandle::ElementProviderFromPoint (double x, do }); } -JUCE_COMRESULT AccessibilityNativeHandle::GetFocus (IRawElementProviderFragment** pRetVal) +JUCE_COMRESULT AccessibilityNativeHandle::GetFocus (ComTypes::IRawElementProviderFragment** pRetVal) { return withCheckedComArgs (pRetVal, *this, [&] { diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h index e9852a050..e9986ede5 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h @@ -30,8 +30,8 @@ namespace juce #define UIA_IsDialogPropertyId 30174 class AccessibilityNativeHandle : public ComBaseClassHelper + ComTypes::IRawElementProviderFragment, + ComTypes::IRawElementProviderFragmentRoot> { public: explicit AccessibilityNativeHandle (AccessibilityHandler& handler); @@ -51,15 +51,15 @@ class AccessibilityNativeHandle : public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIAExpandCollapseProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT Expand() override @@ -47,13 +44,13 @@ class UIAExpandCollapseProvider : public UIAProviderBase, return invokeShowMenu(); } - JUCE_COMRESULT get_ExpandCollapseState (ExpandCollapseState* pRetVal) override + JUCE_COMRESULT get_ExpandCollapseState (ComTypes::ExpandCollapseState* pRetVal) override { return withCheckedComArgs (pRetVal, *this, [&] { *pRetVal = getHandler().getCurrentState().isExpanded() - ? ExpandCollapseState_Expanded - : ExpandCollapseState_Collapsed; + ? ComTypes::ExpandCollapseState_Expanded + : ComTypes::ExpandCollapseState_Collapsed; return S_OK; }); @@ -70,8 +67,8 @@ class UIAExpandCollapseProvider : public UIAProviderBase, if (handler.getActions().invoke (AccessibilityActionType::showMenu)) { sendAccessibilityAutomationEvent (handler, handler.getCurrentState().isExpanded() - ? UIA_MenuOpenedEventId - : UIA_MenuClosedEventId); + ? ComTypes::UIA_MenuOpenedEventId + : ComTypes::UIA_MenuClosedEventId); return S_OK; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridItemProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridItemProvider.h index ab3b69ea4..a074340af 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridItemProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridItemProvider.h @@ -28,13 +28,10 @@ namespace juce //============================================================================== class UIAGridItemProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIAGridItemProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT get_Row (int* pRetVal) override diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridProvider.h index 17f250e0f..b9c34959b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridProvider.h @@ -28,13 +28,10 @@ namespace juce //============================================================================== class UIAGridProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIAGridProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT GetItem (int row, int column, IRawElementProviderSimple** pRetVal) override diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAInvokeProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAInvokeProvider.h index 1f45871d3..0e6636033 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAInvokeProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAInvokeProvider.h @@ -28,13 +28,10 @@ namespace juce //============================================================================== class UIAInvokeProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIAInvokeProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT Invoke() override @@ -47,7 +44,7 @@ class UIAInvokeProvider : public UIAProviderBase, if (handler.getActions().invoke (AccessibilityActionType::press)) { if (isElementValid()) - sendAccessibilityAutomationEvent (handler, UIA_Invoke_InvokedEventId); + sendAccessibilityAutomationEvent (handler, ComTypes::UIA_Invoke_InvokedEventId); return S_OK; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIARangeValueProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIARangeValueProvider.h index 1ebd19364..b1866f71b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIARangeValueProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIARangeValueProvider.h @@ -28,13 +28,10 @@ namespace juce //============================================================================== class UIARangeValueProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIARangeValueProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT SetValue (double val) override diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIASelectionProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIASelectionProvider.h index f8add26cc..830eba330 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIASelectionProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIASelectionProvider.h @@ -28,21 +28,13 @@ namespace juce JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") -JUCE_COMCLASS (ISelectionProvider2, "14f68475-ee1c-44f6-a869-d239381f0fe7") : public ISelectionProvider -{ - JUCE_COMCALL get_FirstSelectedItem (IRawElementProviderSimple** retVal) = 0; - JUCE_COMCALL get_LastSelectedItem (IRawElementProviderSimple** retVal) = 0; - JUCE_COMCALL get_CurrentSelectedItem (IRawElementProviderSimple** retVal) = 0; - JUCE_COMCALL get_ItemCount (int* retVal) = 0; -}; - //============================================================================== class UIASelectionItemProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIASelectionItemProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle), + explicit UIASelectionItemProvider (AccessibilityNativeHandle* handle) + : UIAProviderBase (handle), isRadioButton (getHandler().getRole() == AccessibilityRole::radioButton) { } @@ -58,7 +50,7 @@ class UIASelectionItemProvider : public UIAProviderBase, if (isRadioButton) { handler.getActions().invoke (AccessibilityActionType::press); - sendAccessibilityAutomationEvent (handler, UIA_SelectionItem_ElementSelectedEventId); + sendAccessibilityAutomationEvent (handler, ComTypes::UIA_SelectionItem_ElementSelectedEventId); return S_OK; } @@ -136,22 +128,19 @@ class UIASelectionItemProvider : public UIAProviderBase, //============================================================================== class UIASelectionProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIASelectionProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT QueryInterface (REFIID iid, void** result) override { - if (iid == _uuidof (IUnknown) || iid == _uuidof (ISelectionProvider)) - return castToType (result); + if (iid == __uuidof (IUnknown) || iid == __uuidof (ComTypes::ISelectionProvider)) + return castToType (result); - if (iid == _uuidof (ISelectionProvider2)) - return castToType (result); + if (iid == __uuidof (ComTypes::ISelectionProvider2)) + return castToType (result); *result = nullptr; return E_NOINTERFACE; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIATextProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIATextProvider.h index 2d107d0a7..2a2bfc5b9 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIATextProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIATextProvider.h @@ -28,24 +28,21 @@ namespace juce //============================================================================== class UIATextProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIATextProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT QueryInterface (REFIID iid, void** result) override { JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - if (iid == _uuidof (IUnknown) || iid == _uuidof (ITextProvider)) - return castToType (result); + if (iid == __uuidof (IUnknown) || iid == __uuidof (ComTypes::ITextProvider)) + return castToType (result); - if (iid == _uuidof (ITextProvider2)) - return castToType (result); + if (iid == __uuidof (ComTypes::ITextProvider2)) + return castToType (result); *result = nullptr; return E_NOINTERFACE; @@ -54,7 +51,7 @@ class UIATextProvider : public UIAProviderBase, } //============================================================================= - JUCE_COMRESULT get_DocumentRange (ITextRangeProvider** pRetVal) override + JUCE_COMRESULT get_DocumentRange (ComTypes::ITextRangeProvider** pRetVal) override { return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface) { @@ -63,11 +60,11 @@ class UIATextProvider : public UIAProviderBase, }); } - JUCE_COMRESULT get_SupportedTextSelection (SupportedTextSelection* pRetVal) override + JUCE_COMRESULT get_SupportedTextSelection (ComTypes::SupportedTextSelection* pRetVal) override { return withCheckedComArgs (pRetVal, *this, [&] { - *pRetVal = SupportedTextSelection_Single; + *pRetVal = ComTypes::SupportedTextSelection_Single; return S_OK; }); } @@ -124,7 +121,7 @@ class UIATextProvider : public UIAProviderBase, }); } - JUCE_COMRESULT RangeFromChild (IRawElementProviderSimple*, ITextRangeProvider** pRetVal) override + JUCE_COMRESULT RangeFromChild (IRawElementProviderSimple*, ComTypes::ITextRangeProvider** pRetVal) override { return withCheckedComArgs (pRetVal, *this, [] { @@ -132,7 +129,7 @@ class UIATextProvider : public UIAProviderBase, }); } - JUCE_COMRESULT RangeFromPoint (UiaPoint point, ITextRangeProvider** pRetVal) override + JUCE_COMRESULT RangeFromPoint (ComTypes::UiaPoint point, ComTypes::ITextRangeProvider** pRetVal) override { return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface) { @@ -146,7 +143,7 @@ class UIATextProvider : public UIAProviderBase, } //============================================================================== - JUCE_COMRESULT GetCaretRange (BOOL* isActive, ITextRangeProvider** pRetVal) override + JUCE_COMRESULT GetCaretRange (BOOL* isActive, ComTypes::ITextRangeProvider** pRetVal) override { return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface) { @@ -159,7 +156,7 @@ class UIATextProvider : public UIAProviderBase, }); } - JUCE_COMRESULT RangeFromAnnotation (IRawElementProviderSimple*, ITextRangeProvider** pRetVal) override + JUCE_COMRESULT RangeFromAnnotation (IRawElementProviderSimple*, ComTypes::ITextRangeProvider** pRetVal) override { return withCheckedComArgs (pRetVal, *this, [] { @@ -183,7 +180,7 @@ class UIATextProvider : public UIAProviderBase, //============================================================================== class UIATextRangeProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: UIATextRangeProvider (UIATextProvider& textProvider, Range range) @@ -202,7 +199,7 @@ class UIATextProvider : public UIAProviderBase, return Select(); } - JUCE_COMRESULT Clone (ITextRangeProvider** pRetVal) override + JUCE_COMRESULT Clone (ComTypes::ITextRangeProvider** pRetVal) override { return withCheckedComArgs (pRetVal, *this, [&] { @@ -211,7 +208,7 @@ class UIATextProvider : public UIAProviderBase, }); } - JUCE_COMRESULT Compare (ITextRangeProvider* range, BOOL* pRetVal) override + JUCE_COMRESULT Compare (ComTypes::ITextRangeProvider* range, BOOL* pRetVal) override { return withCheckedComArgs (pRetVal, *this, [&] { @@ -220,9 +217,9 @@ class UIATextProvider : public UIAProviderBase, }); } - JUCE_COMRESULT CompareEndpoints (TextPatternRangeEndpoint endpoint, - ITextRangeProvider* targetRange, - TextPatternRangeEndpoint targetEndpoint, + JUCE_COMRESULT CompareEndpoints (ComTypes::TextPatternRangeEndpoint endpoint, + ComTypes::ITextRangeProvider* targetRange, + ComTypes::TextPatternRangeEndpoint targetEndpoint, int* pRetVal) override { if (targetRange == nullptr) @@ -230,19 +227,19 @@ class UIATextProvider : public UIAProviderBase, return withCheckedComArgs (pRetVal, *this, [&] { - auto offset = (endpoint == TextPatternRangeEndpoint_Start ? selectionRange.getStart() - : selectionRange.getEnd()); + auto offset = (endpoint == ComTypes::TextPatternRangeEndpoint_Start ? selectionRange.getStart() + : selectionRange.getEnd()); auto otherRange = static_cast (targetRange)->getSelectionRange(); - auto otherOffset = (targetEndpoint == TextPatternRangeEndpoint_Start ? otherRange.getStart() - : otherRange.getEnd()); + auto otherOffset = (targetEndpoint == ComTypes::TextPatternRangeEndpoint_Start ? otherRange.getStart() + : otherRange.getEnd()); *pRetVal = offset - otherOffset; return S_OK; }); } - JUCE_COMRESULT ExpandToEnclosingUnit (TextUnit unit) override + JUCE_COMRESULT ExpandToEnclosingUnit (ComTypes::TextUnit unit) override { if (! isElementValid()) return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE; @@ -269,7 +266,7 @@ class UIATextProvider : public UIAProviderBase, return (HRESULT) UIA_E_NOTSUPPORTED; } - JUCE_COMRESULT FindAttribute (TEXTATTRIBUTEID, VARIANT, BOOL, ITextRangeProvider** pRetVal) override + JUCE_COMRESULT FindAttribute (TEXTATTRIBUTEID, VARIANT, BOOL, ComTypes::ITextRangeProvider** pRetVal) override { return withCheckedComArgs (pRetVal, *this, [] { @@ -278,7 +275,7 @@ class UIATextProvider : public UIAProviderBase, } JUCE_COMRESULT FindText (BSTR text, BOOL backward, BOOL ignoreCase, - ITextRangeProvider** pRetVal) override + ComTypes::ITextRangeProvider** pRetVal) override { return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface) { @@ -303,25 +300,25 @@ class UIATextProvider : public UIAProviderBase, switch (attributeId) { - case UIA_IsReadOnlyAttributeId: + case ComTypes::UIA_IsReadOnlyAttributeId: { VariantHelpers::setBool (textInterface.isReadOnly(), pRetVal); break; } - case UIA_CaretPositionAttributeId: + case ComTypes::UIA_CaretPositionAttributeId: { auto cursorPos = textInterface.getTextInsertionOffset(); auto caretPos = [&] { if (cursorPos == 0) - return CaretPosition_BeginningOfLine; + return ComTypes::CaretPosition_BeginningOfLine; if (cursorPos == textInterface.getTotalNumCharacters()) - return CaretPosition_EndOfLine; + return ComTypes::CaretPosition_EndOfLine; - return CaretPosition_Unknown; + return ComTypes::CaretPosition_Unknown; }(); VariantHelpers::setInt (caretPos, pRetVal); @@ -412,28 +409,28 @@ class UIATextProvider : public UIAProviderBase, }); } - JUCE_COMRESULT Move (TextUnit unit, int count, int* pRetVal) override + JUCE_COMRESULT Move (ComTypes::TextUnit unit, int count, int* pRetVal) override { return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface&) { if (count > 0) { - MoveEndpointByUnit (TextPatternRangeEndpoint_End, unit, count, pRetVal); - MoveEndpointByUnit (TextPatternRangeEndpoint_Start, unit, count, pRetVal); + MoveEndpointByUnit (ComTypes::TextPatternRangeEndpoint_End, unit, count, pRetVal); + MoveEndpointByUnit (ComTypes::TextPatternRangeEndpoint_Start, unit, count, pRetVal); } else if (count < 0) { - MoveEndpointByUnit (TextPatternRangeEndpoint_Start, unit, count, pRetVal); - MoveEndpointByUnit (TextPatternRangeEndpoint_End, unit, count, pRetVal); + MoveEndpointByUnit (ComTypes::TextPatternRangeEndpoint_Start, unit, count, pRetVal); + MoveEndpointByUnit (ComTypes::TextPatternRangeEndpoint_End, unit, count, pRetVal); } return S_OK; }); } - JUCE_COMRESULT MoveEndpointByRange (TextPatternRangeEndpoint endpoint, - ITextRangeProvider* targetRange, - TextPatternRangeEndpoint targetEndpoint) override + JUCE_COMRESULT MoveEndpointByRange (ComTypes::TextPatternRangeEndpoint endpoint, + ComTypes::ITextRangeProvider* targetRange, + ComTypes::TextPatternRangeEndpoint targetEndpoint) override { if (targetRange == nullptr) return E_INVALIDARG; @@ -441,11 +438,11 @@ class UIATextProvider : public UIAProviderBase, if (! isElementValid()) return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE; - if (auto* textInterface = owner->getHandler().getTextInterface()) + if (owner->getHandler().getTextInterface() != nullptr) { auto otherRange = static_cast (targetRange)->getSelectionRange(); - auto targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start ? otherRange.getStart() - : otherRange.getEnd()); + auto targetPoint = (targetEndpoint == ComTypes::TextPatternRangeEndpoint_Start ? otherRange.getStart() + : otherRange.getEnd()); setEndpointChecked (endpoint, targetPoint); return S_OK; @@ -454,8 +451,8 @@ class UIATextProvider : public UIAProviderBase, return (HRESULT) UIA_E_NOTSUPPORTED; } - JUCE_COMRESULT MoveEndpointByUnit (TextPatternRangeEndpoint endpoint, - TextUnit unit, + JUCE_COMRESULT MoveEndpointByUnit (ComTypes::TextPatternRangeEndpoint endpoint, + ComTypes::TextUnit unit, int count, int* pRetVal) override { @@ -464,8 +461,8 @@ class UIATextProvider : public UIAProviderBase, if (count == 0 || textInterface.getTotalNumCharacters() == 0) return S_OK; - auto endpointToMove = (endpoint == TextPatternRangeEndpoint_Start ? selectionRange.getStart() - : selectionRange.getEnd()); + auto endpointToMove = (endpoint == ComTypes::TextPatternRangeEndpoint_Start ? selectionRange.getStart() + : selectionRange.getEnd()); const auto direction = (count > 0 ? AccessibilityTextHelpers::Direction::forwards : AccessibilityTextHelpers::Direction::backwards); @@ -536,23 +533,23 @@ class UIATextProvider : public UIAProviderBase, } private: - static AccessibilityTextHelpers::BoundaryType getBoundaryType (TextUnit unit) + static AccessibilityTextHelpers::BoundaryType getBoundaryType (ComTypes::TextUnit unit) { switch (unit) { - case TextUnit_Character: + case ComTypes::TextUnit_Character: return AccessibilityTextHelpers::BoundaryType::character; - case TextUnit_Format: - case TextUnit_Word: + case ComTypes::TextUnit_Format: + case ComTypes::TextUnit_Word: return AccessibilityTextHelpers::BoundaryType::word; - case TextUnit_Line: + case ComTypes::TextUnit_Line: return AccessibilityTextHelpers::BoundaryType::line; - case TextUnit_Paragraph: - case TextUnit_Page: - case TextUnit_Document: + case ComTypes::TextUnit_Paragraph: + case ComTypes::TextUnit_Page: + case ComTypes::TextUnit_Document: return AccessibilityTextHelpers::BoundaryType::document; }; @@ -560,9 +557,9 @@ class UIATextProvider : public UIAProviderBase, return AccessibilityTextHelpers::BoundaryType::character; } - void setEndpointChecked (TextPatternRangeEndpoint endpoint, int newEndpoint) + void setEndpointChecked (ComTypes::TextPatternRangeEndpoint endpoint, int newEndpoint) { - if (endpoint == TextPatternRangeEndpoint_Start) + if (endpoint == ComTypes::TextPatternRangeEndpoint_Start) { if (selectionRange.getEnd() < newEndpoint) selectionRange.setEnd (newEndpoint); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAToggleProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAToggleProvider.h index 25604345d..949d6a0b2 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAToggleProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAToggleProvider.h @@ -28,13 +28,10 @@ namespace juce //============================================================================== class UIAToggleProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIAToggleProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT Toggle() override @@ -44,7 +41,8 @@ class UIAToggleProvider : public UIAProviderBase, const auto& handler = getHandler(); - if (handler.getActions().invoke (AccessibilityActionType::toggle)) + if (handler.getActions().invoke (AccessibilityActionType::toggle) + || handler.getActions().invoke (AccessibilityActionType::press)) { VARIANT newValue; VariantHelpers::setInt (getCurrentToggleState(), &newValue); @@ -57,7 +55,7 @@ class UIAToggleProvider : public UIAProviderBase, return (HRESULT) UIA_E_NOTSUPPORTED; } - JUCE_COMRESULT get_ToggleState (ToggleState* pRetVal) override + JUCE_COMRESULT get_ToggleState (ComTypes::ToggleState* pRetVal) override { return withCheckedComArgs (pRetVal, *this, [&] { @@ -67,10 +65,10 @@ class UIAToggleProvider : public UIAProviderBase, } private: - ToggleState getCurrentToggleState() const + ComTypes::ToggleState getCurrentToggleState() const { - return getHandler().getCurrentState().isChecked() ? ToggleState_On - : ToggleState_Off; + return getHandler().getCurrentState().isChecked() ? ComTypes::ToggleState_On + : ComTypes::ToggleState_Off; } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIATransformProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIATransformProvider.h index 92e1cdb75..e0d581dc6 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIATransformProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIATransformProvider.h @@ -28,13 +28,10 @@ namespace juce //============================================================================== class UIATransformProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIATransformProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT Move (double x, double y) override diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAValueProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAValueProvider.h index 5873d7843..fc9a837e8 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAValueProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAValueProvider.h @@ -28,13 +28,10 @@ namespace juce //============================================================================== class UIAValueProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - UIAValueProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== JUCE_COMRESULT SetValue (LPCWSTR val) override diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAWindowProvider.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAWindowProvider.h index cf98179d6..da5b2365a 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAWindowProvider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_UIAWindowProvider.h @@ -28,16 +28,13 @@ namespace juce //============================================================================== class UIAWindowProvider : public UIAProviderBase, - public ComBaseClassHelper + public ComBaseClassHelper { public: - explicit UIAWindowProvider (AccessibilityNativeHandle* nativeHandle) - : UIAProviderBase (nativeHandle) - { - } + using UIAProviderBase::UIAProviderBase; //============================================================================== - JUCE_COMRESULT SetVisualState (WindowVisualState state) override + JUCE_COMRESULT SetVisualState (ComTypes::WindowVisualState state) override { if (! isElementValid()) return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE; @@ -46,15 +43,15 @@ class UIAWindowProvider : public UIAProviderBase, { switch (state) { - case WindowVisualState_Maximized: + case ComTypes::WindowVisualState_Maximized: peer->setFullScreen (true); break; - case WindowVisualState_Minimized: + case ComTypes::WindowVisualState_Minimized: peer->setMinimised (true); break; - case WindowVisualState_Normal: + case ComTypes::WindowVisualState_Normal: peer->setFullScreen (false); peer->setMinimised (false); break; @@ -133,18 +130,18 @@ class UIAWindowProvider : public UIAProviderBase, }); } - JUCE_COMRESULT get_WindowVisualState (WindowVisualState* pRetVal) override + JUCE_COMRESULT get_WindowVisualState (ComTypes::WindowVisualState* pRetVal) override { return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT { if (auto* peer = getPeer()) { if (peer->isFullScreen()) - *pRetVal = WindowVisualState_Maximized; + *pRetVal = ComTypes::WindowVisualState_Maximized; else if (peer->isMinimised()) - *pRetVal = WindowVisualState_Minimized; + *pRetVal = ComTypes::WindowVisualState_Minimized; else - *pRetVal = WindowVisualState_Normal; + *pRetVal = ComTypes::WindowVisualState_Normal; return S_OK; } @@ -153,15 +150,15 @@ class UIAWindowProvider : public UIAProviderBase, }); } - JUCE_COMRESULT get_WindowInteractionState (WindowInteractionState* pRetVal) override + JUCE_COMRESULT get_WindowInteractionState (ComTypes::WindowInteractionState* pRetVal) override { return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT { if (auto* peer = getPeer()) { *pRetVal = peer->getComponent().isCurrentlyBlockedByAnotherModalComponent() - ? WindowInteractionState::WindowInteractionState_BlockedByModalWindow - : WindowInteractionState::WindowInteractionState_Running; + ? ComTypes::WindowInteractionState::WindowInteractionState_BlockedByModalWindow + : ComTypes::WindowInteractionState::WindowInteractionState_Running; return S_OK; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h index 2f7991178..ad17710e4 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h @@ -127,7 +127,9 @@ class WindowsUIAWrapper : public DeletedAtShutdown template static FuncType getUiaFunction (HMODULE module, LPCSTR funcName) { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type") return (FuncType) GetProcAddress (module, funcName); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/java/app/com/rmsl/juce/ComponentPeerView.java b/external/JuceLibraryCode/modules/juce_gui_basics/native/java/app/com/rmsl/juce/ComponentPeerView.java index d0f522bb1..1af59c64d 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/java/app/com/rmsl/juce/ComponentPeerView.java +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/java/app/com/rmsl/juce/ComponentPeerView.java @@ -144,6 +144,7 @@ public boolean isOpaque () private native void handleMouseDown (long host, int index, float x, float y, long time); private native void handleMouseDrag (long host, int index, float x, float y, long time); private native void handleMouseUp (long host, int index, float x, float y, long time); + private native void handleAccessibilityHover (long host, int action, float x, float y, long time); @Override public boolean onTouchEvent (MotionEvent event) @@ -227,35 +228,17 @@ public boolean onTouchEvent (MotionEvent event) @Override public boolean onHoverEvent (MotionEvent event) { - if (host == 0 || ! accessibilityManager.isTouchExplorationEnabled ()) - return false; - - int action = event.getAction (); - long time = event.getEventTime (); - - switch (action & MotionEvent.ACTION_MASK) // simulate "mouse" events when touch exploration is enabled + if (accessibilityManager.isTouchExplorationEnabled()) { - case MotionEvent.ACTION_HOVER_ENTER: - handleMouseDown (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); - return true; - - case MotionEvent.ACTION_HOVER_EXIT: - handleMouseUp (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); - return true; - - case MotionEvent.ACTION_HOVER_MOVE: - handleMouseDrag (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); - return true; - - default: - break; + handleAccessibilityHover (host, event.getActionMasked(), event.getRawX(), event.getRawY(), event.getEventTime()); + return true; } return false; } //============================================================================== - private native void handleKeyDown (long host, int keycode, int textchar); + private native void handleKeyDown (long host, int keycode, int textchar, int kbFlags); private native void handleKeyUp (long host, int keycode, int textchar); private native void handleBackButton (long host); private native void handleKeyboardHidden (long host); @@ -300,7 +283,7 @@ public boolean onKeyDown (int keyCode, KeyEvent event) return super.onKeyDown (keyCode, event); case KeyEvent.KEYCODE_BACK: { - backButtonPressed(); + backButtonPressed (); return true; } @@ -308,7 +291,10 @@ public boolean onKeyDown (int keyCode, KeyEvent event) break; } - handleKeyDown (host, keyCode, event.getUnicodeChar ()); + handleKeyDown (host, + keyCode, + event.getUnicodeChar (), + event.getMetaState ()); return true; } @@ -334,7 +320,11 @@ public boolean onKeyMultiple (int keyCode, int count, KeyEvent event) if (event.getCharacters () != null) { int utf8Char = event.getCharacters ().codePointAt (0); - handleKeyDown (host, utf8Char, utf8Char); + + handleKeyDown (host, + keyCode, + utf8Char, + event.getMetaState ()); return true; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_android_Windowing.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_android_Windowing.cpp index 5709cb2be..396b35fde 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_android_Windowing.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_android_Windowing.cpp @@ -28,195 +28,101 @@ namespace juce // This byte-code is generated from native/java/com/rmsl/juce/ComponentPeerView.java with min sdk version 16 // See juce_core/native/java/README.txt on how to generate this byte-code. static const uint8 javaComponentPeerView[] = -{31,139,8,8,244,59,63,97,0,3,74,97,118,97,68,101,120,66,121,116,101,67,111,100,101,46,100,101,120,0,165,155,9,120,91,197,181, -199,207,204,213,98,203,178,45,203,137,237,36,86,44,59,155,226,196,182,156,61,56,64,118,226,196,89,136,29,67,236,180,141,108,95, -219,74,228,43,69,146,55,66,31,41,80,8,75,41,75,216,67,129,54,165,27,143,165,44,31,229,81,186,1,165,101,41,180,240,160,45,180, -208,242,74,88,74,83,118,104,31,188,255,153,59,178,229,44,77,219,231,124,191,123,206,204,156,153,59,119,230,204,153,185,215,113, -151,57,228,9,207,157,79,197,179,155,15,182,174,201,59,197,108,45,216,252,173,174,187,158,170,250,166,177,120,119,238,69,203,47, -157,71,148,32,162,161,214,121,126,210,63,143,207,37,10,8,59,127,17,120,197,65,180,28,242,33,39,81,25,228,11,57,68,103,64,238, -207,37,66,17,197,242,137,14,76,39,122,19,242,242,0,209,62,112,53,184,14,124,21,220,2,30,4,63,2,15,131,71,193,227,224,41,240,43, -240,28,120,31,20,78,38,242,131,18,48,17,76,6,83,64,8,212,128,85,96,55,184,10,220,9,30,7,111,2,111,5,209,66,112,42,216,1,206, -2,87,130,219,192,207,193,123,192,19,36,154,10,182,129,207,131,59,192,43,160,188,146,168,9,164,192,141,224,73,80,88,69,116,26, -184,28,220,3,126,13,62,2,229,83,136,26,192,22,208,11,46,6,223,5,63,5,175,131,79,64,96,42,81,61,216,14,206,4,87,128,3,224,94,240, -0,248,57,120,2,60,13,158,7,47,128,87,192,235,224,16,120,15,124,4,62,1,198,52,162,28,80,0,252,96,34,8,128,42,16,2,53,96,30,56, -1,156,4,86,128,117,96,51,104,5,109,192,2,231,128,47,131,235,193,55,192,93,224,7,224,49,240,28,248,35,248,11,248,59,200,195,60, -86,130,153,96,1,88,10,218,65,55,136,130,51,193,185,224,98,176,15,220,48,221,158,251,59,193,131,224,73,240,34,248,19,248,51,240, -204,32,242,129,98,48,17,44,1,167,128,13,96,27,136,130,243,192,1,112,59,120,4,60,11,126,15,222,0,31,1,71,136,104,2,168,5,139, -193,90,112,58,136,128,29,96,16,92,0,174,5,183,129,239,131,39,192,27,224,175,224,3,240,49,200,155,137,241,3,117,224,36,176,14, -68,64,18,156,3,46,7,55,130,187,193,131,224,57,240,50,120,21,188,14,222,7,127,3,222,106,162,73,96,54,88,8,86,129,141,224,52,208, -9,250,192,94,112,5,216,15,238,1,15,129,199,192,111,192,75,224,32,56,4,222,1,31,1,57,11,237,130,137,160,10,132,64,45,88,6,90,65, -47,216,13,174,1,223,1,15,130,231,192,239,193,91,224,19,174,63,155,168,20,76,7,117,96,49,88,3,90,193,103,65,15,216,5,134,192, -185,224,10,112,29,248,30,120,0,60,12,94,3,238,26,248,28,40,6,83,64,61,88,11,218,64,47,56,27,92,11,238,5,63,1,191,4,191,3,111, -129,191,131,188,90,140,15,168,6,43,193,90,176,9,156,14,182,131,110,176,19,236,2,187,193,127,128,47,130,75,192,62,240,117,112, -7,248,49,120,26,188,8,94,2,127,4,175,130,55,192,95,192,59,224,3,224,66,12,66,72,162,60,80,2,74,201,142,83,19,192,68,48,9,148, -3,132,24,66,184,32,132,3,194,210,39,44,117,194,178,38,44,91,194,114,35,184,63,193,101,9,46,71,112,23,194,84,19,166,134,48,180, -132,97,33,60,26,225,118,84,7,194,160,30,204,1,8,151,132,48,74,243,193,2,176,80,199,205,197,224,4,208,0,150,128,19,193,73,224, -100,176,20,44,35,59,174,174,0,171,192,26,112,26,136,128,14,208,9,186,200,126,190,204,143,91,203,15,39,217,207,44,116,218,163, -117,206,231,113,144,58,223,155,209,49,0,133,89,109,249,245,152,229,232,252,2,157,95,168,203,50,250,120,173,251,244,184,114,251, -197,90,247,233,186,101,89,109,242,88,151,149,219,58,143,113,80,219,76,207,106,103,166,110,167,72,235,33,216,140,211,122,184, -220,190,39,143,249,34,221,14,235,75,117,59,53,186,157,241,122,30,214,148,219,253,225,185,216,84,110,143,225,28,109,211,168,117, -190,215,90,173,159,14,155,117,90,231,251,54,105,189,11,250,122,173,167,161,111,208,250,30,232,27,181,126,49,244,77,90,223,7, -125,179,214,247,103,229,31,200,210,111,133,222,162,245,187,179,242,127,152,165,63,154,165,63,149,213,230,243,89,249,47,65,111, -214,250,193,172,252,131,147,70,117,30,231,83,181,30,206,106,231,80,150,61,143,225,150,204,152,64,111,205,140,85,96,212,198,151, -165,151,5,108,127,156,171,199,243,116,173,7,145,191,85,235,161,44,125,30,244,54,173,47,133,222,174,245,53,208,183,105,189,5,250, -103,181,190,29,250,103,180,222,155,149,207,126,245,57,173,39,144,191,93,235,123,178,236,47,14,176,111,11,26,34,91,230,10,94, -235,211,40,73,182,188,75,73,65,247,106,121,159,150,223,211,242,126,45,255,75,219,63,162,134,33,72,46,97,203,66,193,177,98,46, -61,76,44,43,200,45,56,110,216,229,21,186,188,2,37,126,193,126,94,162,206,74,65,172,180,187,149,172,160,135,148,12,208,47,148, -156,75,255,163,100,30,29,84,126,62,157,86,67,58,17,121,254,66,188,38,93,244,101,37,199,211,13,144,185,136,92,134,146,115,233, -109,226,181,61,67,165,243,116,126,30,162,192,59,234,185,237,116,33,238,123,137,146,37,116,169,78,127,133,199,81,231,251,112,74, -187,66,167,175,81,210,160,107,201,94,139,215,105,121,189,146,130,190,166,229,1,226,181,39,105,159,146,33,250,42,241,58,155,170, -238,87,140,200,246,123,37,23,209,75,74,78,162,63,18,175,201,217,148,214,242,16,113,44,158,79,63,34,142,21,94,122,74,201,9,244, -22,251,149,238,247,4,172,108,150,229,136,142,223,85,114,38,189,175,228,73,42,152,5,176,194,13,53,47,121,116,142,146,101,116,190, -78,239,85,114,33,189,174,230,167,70,217,77,70,139,47,18,207,139,93,175,2,249,166,150,221,74,22,82,143,146,19,72,8,91,74,53, -127,211,148,125,16,51,210,171,100,45,69,149,92,74,59,148,44,166,157,90,198,148,60,153,250,148,156,64,131,90,158,169,228,2,218, -163,100,144,46,84,210,79,23,43,153,79,95,82,210,69,151,107,63,217,167,203,175,84,114,30,93,165,100,46,237,87,210,67,55,106,187, -155,148,204,161,155,149,180,231,33,136,17,186,69,203,111,104,249,77,37,39,211,183,180,252,182,206,255,142,246,195,91,181,252, -79,45,111,211,126,122,187,146,39,208,29,74,206,167,31,106,249,99,37,171,232,87,74,86,210,51,90,62,171,229,127,235,242,231,116, -250,121,45,127,173,100,1,253,70,201,233,244,91,37,103,209,11,74,158,72,191,83,210,246,159,160,246,31,78,191,172,228,28,250,131, -146,182,63,177,253,43,74,54,208,155,74,214,211,187,90,190,167,100,53,125,168,228,68,250,88,201,48,253,77,167,255,174,237,254, -87,203,79,116,249,167,122,92,124,130,247,164,82,186,154,88,46,163,31,168,117,41,233,251,74,22,81,129,224,125,199,246,211,89,88, -17,151,17,41,47,251,188,146,146,126,70,188,255,140,163,7,137,207,2,19,180,159,218,107,41,179,31,226,117,137,110,66,140,254, -178,222,144,38,235,124,222,207,56,118,115,249,125,144,183,233,242,10,93,127,86,86,253,71,81,254,35,93,30,36,123,207,228,253,238, -144,174,255,60,228,107,186,156,247,249,19,160,47,155,99,159,5,214,205,177,243,182,67,246,0,67,151,239,2,187,181,205,185,42, -223,80,250,199,181,246,153,160,93,120,40,225,227,222,180,203,60,204,101,14,90,225,123,57,235,236,115,145,95,180,116,120,104,151, -111,14,242,243,100,9,94,1,235,165,116,44,150,14,114,251,138,168,165,35,143,118,97,139,72,98,213,179,93,75,167,109,107,80,158, -99,215,166,121,180,188,223,35,23,203,63,127,106,249,120,60,220,190,208,171,14,125,255,73,117,246,249,164,10,35,154,240,13,171, -167,229,123,115,191,167,213,217,231,171,99,221,219,160,34,225,247,207,157,49,225,200,123,134,231,211,114,167,23,190,118,8,247, -228,253,193,35,172,224,52,68,39,175,8,253,233,72,235,5,202,58,244,26,199,89,135,234,79,67,157,61,119,45,9,219,142,75,74,164,58, -115,200,197,176,241,35,174,180,236,242,100,151,75,113,174,184,210,125,203,160,107,33,158,204,67,161,67,6,250,203,207,120,74, -29,199,117,140,172,63,31,207,152,70,158,159,182,249,188,234,252,149,131,20,63,235,166,58,123,31,45,118,141,163,42,233,133,29, -143,66,75,42,159,90,230,22,144,229,91,14,43,175,88,44,150,140,230,5,87,99,47,25,181,56,25,22,30,99,243,84,126,23,20,116,103,203, -101,100,56,230,111,159,72,91,82,24,7,163,132,252,198,54,104,25,251,150,116,62,250,93,139,39,241,58,184,255,14,53,14,115,105,133, -147,237,23,27,110,90,184,221,73,254,98,174,195,247,26,143,117,110,249,198,241,147,29,118,143,208,147,60,151,236,115,187,235, -236,243,90,187,191,64,63,103,21,198,48,17,156,9,15,111,247,129,226,66,245,204,66,253,35,186,168,206,254,150,144,240,241,169,174, -96,36,255,138,145,252,176,202,151,250,196,123,125,157,237,207,45,190,66,181,38,216,131,248,190,95,171,179,207,188,45,193,66, -244,145,79,214,120,206,74,31,122,61,17,119,46,24,177,251,246,49,237,38,41,187,2,120,60,63,201,157,176,123,156,253,206,237,47, -73,4,75,16,95,171,28,121,232,141,11,189,106,223,87,130,84,5,37,194,179,104,95,110,251,190,98,164,2,72,77,85,169,113,104,247,36, -204,181,215,49,209,25,71,187,152,65,232,21,142,18,212,43,162,114,135,19,121,187,176,207,122,28,139,28,159,161,10,15,167,121,63, -110,219,239,135,214,71,195,180,237,250,241,208,186,177,23,89,193,30,117,237,197,190,93,133,54,19,62,181,15,59,198,209,148,101, -243,104,186,224,211,158,21,116,160,231,45,220,39,39,214,111,208,73,220,162,27,119,245,27,150,175,95,247,228,3,242,79,159,210, -189,0,207,211,145,83,74,235,115,92,110,127,105,121,78,158,210,172,112,130,90,92,94,99,145,49,142,252,210,63,117,202,202,133,228, -119,238,242,165,48,14,94,215,122,151,195,233,31,239,87,210,10,15,209,21,14,246,52,246,110,126,42,187,247,251,40,116,115,190,35, -244,46,120,27,28,2,191,1,188,57,24,14,117,198,31,253,217,115,50,253,75,233,195,127,236,114,62,39,150,96,61,242,247,40,62,183, -72,35,116,173,152,113,181,168,189,82,200,217,55,136,234,235,5,233,232,130,53,28,182,125,166,57,232,87,113,214,161,34,31,98,75, -88,251,88,184,137,42,141,2,172,121,167,138,119,181,97,59,198,38,182,152,84,185,194,163,252,57,83,167,33,83,22,94,138,58,94,229, -131,153,178,149,35,101,75,70,202,220,234,148,135,119,12,93,54,224,58,85,69,6,110,211,169,215,253,182,176,189,102,154,151,250, -179,226,139,1,95,228,156,68,184,145,28,34,244,241,232,243,116,233,182,248,157,183,13,207,84,160,251,192,63,59,194,118,236,247, -251,10,71,214,76,58,156,89,51,227,84,159,50,249,103,232,252,45,190,241,234,254,82,175,189,61,97,251,189,210,242,173,84,125,85, -177,14,237,225,60,248,137,161,239,115,94,216,222,71,10,84,61,251,173,243,146,172,60,151,142,223,87,30,231,217,78,209,207,150, -177,255,202,113,236,215,140,140,133,221,143,111,28,165,31,183,31,37,239,222,172,60,135,190,215,247,195,246,183,0,191,224,175, -5,237,97,73,182,52,104,107,61,226,222,161,173,97,55,109,13,187,116,110,46,252,24,51,233,219,26,118,160,60,7,235,113,2,250,131, -51,159,40,210,207,203,115,242,120,216,222,171,143,222,255,150,229,120,99,216,188,137,28,167,134,62,100,223,48,148,255,61,115, -204,58,115,87,125,250,169,122,238,165,13,228,136,112,29,47,238,195,123,245,203,168,195,103,84,255,4,63,53,127,21,245,120,71,20, -210,177,8,123,120,203,205,69,152,187,114,158,59,236,22,6,113,164,243,98,173,120,114,44,95,62,100,158,235,217,47,124,74,179,4, -127,135,8,189,221,124,51,199,157,66,46,71,188,240,177,116,88,216,207,33,157,238,155,6,114,248,141,88,92,26,250,221,241,236,214, -219,118,63,63,158,221,70,219,142,95,208,132,67,125,215,224,49,224,249,224,243,19,127,111,201,69,138,199,50,191,222,254,222,226, -247,53,119,140,62,225,98,97,239,188,179,221,252,45,130,75,44,95,30,198,196,35,18,167,174,165,233,169,208,59,241,112,41,13,228, -96,247,125,203,242,45,194,204,132,248,101,65,218,99,157,249,62,196,223,23,242,53,30,148,240,253,66,245,246,119,6,191,24,185,159, -62,99,20,138,197,238,92,220,39,23,125,243,72,255,148,57,115,220,20,95,90,70,3,215,120,68,232,109,203,231,225,40,43,23,169, -211,141,173,91,193,1,220,195,163,250,151,216,132,126,25,254,162,208,139,246,156,243,189,86,214,219,223,50,198,206,185,253,100, -156,199,207,228,194,189,18,155,215,193,91,252,190,208,123,164,35,9,246,221,250,81,95,206,213,49,107,123,189,237,67,241,211,113, -18,26,200,110,117,17,238,105,175,157,56,214,14,215,41,212,62,180,11,197,229,104,168,249,105,88,27,60,186,194,80,163,43,10,133, -165,206,38,158,92,246,152,2,202,115,63,123,25,123,204,251,106,116,66,127,109,254,165,31,146,103,121,178,154,93,222,57,61,78, -158,101,72,151,251,105,246,6,156,112,138,66,47,28,223,114,163,109,249,211,227,91,110,128,165,229,227,61,218,147,231,47,90,56, -169,154,252,149,83,202,231,195,219,150,209,19,228,159,56,255,254,74,226,86,184,141,219,184,13,196,70,72,225,119,174,116,122,157, -103,111,121,100,188,21,44,182,115,74,86,186,188,174,179,187,31,41,201,180,251,188,7,51,249,18,250,113,206,56,193,47,11,247,92, -246,178,71,228,44,246,148,209,63,251,4,51,169,251,211,236,254,252,59,61,225,150,66,191,253,215,123,176,94,247,96,218,255,187, -7,235,85,15,120,109,218,95,28,217,183,56,254,213,105,201,235,148,191,117,164,148,239,73,117,86,58,88,111,127,31,69,31,113,222, -112,195,251,39,26,31,145,127,220,148,149,56,111,56,59,92,56,111,168,179,67,130,230,56,248,252,234,70,191,243,177,151,128,191, -251,253,83,170,112,218,48,248,180,145,131,19,197,122,135,52,248,148,177,75,134,222,202,151,161,55,193,235,224,32,251,123,17,250, -198,223,25,189,28,109,229,236,146,234,50,35,20,154,49,187,182,58,43,206,143,155,51,186,54,12,157,91,62,199,62,123,47,146,110, -226,17,217,9,139,2,242,47,13,253,141,159,79,191,103,204,177,191,47,241,217,141,79,217,19,229,3,234,236,86,137,187,85,224,189, -113,17,34,135,229,27,196,10,245,136,19,69,49,70,219,10,79,161,48,158,120,149,178,183,194,85,228,115,180,212,23,227,68,61,91,181, -159,41,241,75,43,92,73,62,105,151,213,240,248,190,109,255,206,140,127,50,223,113,249,59,233,135,249,52,250,65,86,255,120,15,75, -151,31,150,230,250,227,200,222,243,139,208,59,161,243,152,160,150,57,186,220,175,247,247,137,58,191,66,75,169,9,234,241,154, -69,11,85,126,141,206,175,161,5,90,10,21,67,133,254,231,164,209,179,132,212,190,145,57,67,216,186,123,228,155,180,196,232,142, -230,179,204,87,105,135,238,131,75,151,185,96,39,117,158,91,203,92,45,51,223,180,11,84,79,72,159,101,248,185,230,234,254,7,85, -254,92,237,183,115,71,122,106,215,159,167,229,124,93,79,232,243,23,203,124,125,127,214,11,71,202,11,85,255,164,182,200,60,95, -233,72,63,236,182,125,250,126,193,17,75,251,185,130,218,90,144,253,110,47,66,132,119,55,215,146,168,21,77,159,68,242,164,6,42, -89,17,239,75,196,45,211,74,111,50,205,100,107,212,28,172,221,17,25,136,144,88,77,114,117,35,137,70,146,141,51,1,212,181,36,215, -54,81,96,109,127,167,185,172,179,211,76,165,162,29,209,88,52,61,188,33,222,101,110,74,198,7,162,93,102,146,74,215,153,195,29, -241,72,178,107,101,52,213,23,77,165,154,162,169,180,105,161,64,52,145,108,66,107,77,104,166,169,137,140,38,36,112,89,203,151, -38,42,105,138,88,93,201,120,180,171,46,146,72,212,45,235,76,71,7,208,114,3,205,27,155,159,72,196,162,157,145,116,52,110,77,205, -216,52,69,187,205,206,225,206,152,185,34,18,139,117,68,58,119,166,26,104,194,177,106,101,23,117,198,45,244,44,93,183,130,229, -80,58,187,168,39,25,73,244,70,59,83,117,43,34,214,64,4,13,78,62,74,81,60,22,79,174,142,198,210,102,242,216,229,235,35,233,100, -116,168,129,102,254,195,242,49,77,149,29,105,186,41,18,181,208,191,210,35,75,54,155,157,40,40,30,41,136,167,234,150,247,91,93, -49,179,129,198,101,103,54,46,143,90,93,220,250,104,27,3,152,234,58,76,214,170,1,147,27,159,56,182,96,125,156,135,75,151,205, -28,91,198,78,50,117,163,181,58,222,217,159,90,209,27,177,122,204,204,36,103,119,101,196,52,251,145,70,50,79,73,198,251,19,13, -180,224,200,146,150,164,105,110,236,72,153,201,1,51,137,187,156,18,139,119,68,98,77,145,225,120,127,122,244,54,21,255,184,94, -3,213,143,53,136,100,251,107,221,24,239,93,31,177,34,61,92,101,206,63,93,133,29,190,209,234,142,31,209,255,227,212,201,44,146, -6,170,29,91,47,106,37,250,211,125,102,186,55,222,85,183,60,146,66,227,72,195,47,45,76,175,242,218,105,199,182,95,213,21,77,199, -147,118,119,170,143,109,118,68,147,53,199,177,93,175,244,145,209,57,185,169,51,222,87,151,236,75,197,234,118,32,0,212,29,17,54, -166,254,195,184,208,64,171,143,219,192,49,34,199,212,177,51,187,248,223,109,167,129,42,143,87,181,129,170,154,186,34,177,129, -232,206,186,136,101,197,211,42,102,212,173,178,58,99,241,84,212,234,89,17,139,164,84,48,56,210,166,17,3,155,212,229,149,71,41, -95,111,246,117,104,3,19,38,129,163,152,52,71,123,172,72,186,63,105,242,130,225,24,92,23,195,218,170,195,10,75,54,155,187,250, -77,171,19,37,69,217,37,246,237,170,178,178,26,99,49,179,39,18,179,167,97,213,80,167,153,176,39,123,234,81,108,146,61,253,125, -120,246,44,171,226,108,43,4,197,30,123,208,70,51,55,196,155,251,59,123,109,207,200,170,231,207,50,217,216,177,67,197,164,64,86, -94,179,217,217,159,132,67,28,163,74,51,98,160,213,195,30,57,154,151,52,187,99,104,7,221,24,136,219,161,187,37,146,236,49,179, -123,59,241,40,230,118,215,26,104,188,93,214,159,142,198,234,150,37,147,145,97,118,130,6,42,204,202,230,28,242,29,150,129,119, -220,150,173,155,86,145,55,219,231,72,180,146,108,109,36,103,107,35,126,160,174,37,87,235,218,198,213,171,215,146,3,178,145,175, -188,163,181,174,109,67,33,43,188,171,181,170,172,166,54,148,54,181,97,207,107,109,67,173,54,213,130,104,35,163,141,235,225, -210,196,106,19,57,219,214,178,238,128,192,62,217,198,185,216,31,93,109,77,42,219,201,18,249,237,216,148,219,27,201,223,126,164, -23,20,183,31,101,18,60,118,64,154,26,14,135,71,244,250,44,125,78,150,62,55,75,159,151,165,207,207,210,23,100,233,11,179,244, -69,208,243,108,125,117,44,210,147,162,252,49,145,144,198,69,142,18,113,201,21,81,161,136,107,178,108,138,116,152,49,202,137,232, -125,157,38,68,186,186,142,30,255,41,55,162,157,55,69,162,131,138,120,215,95,222,159,78,199,173,77,73,220,198,236,34,87,71, -28,201,62,72,181,27,146,171,83,109,228,228,238,84,251,85,23,57,113,96,136,36,41,175,147,99,84,28,219,235,178,52,39,70,182,100, -42,80,137,150,100,196,74,117,199,147,125,148,207,199,5,236,195,41,101,141,134,236,83,3,26,138,247,35,61,169,51,105,70,210,71, -198,63,142,203,228,232,138,118,119,147,48,201,105,242,134,74,193,110,236,198,71,53,77,45,31,110,225,86,115,217,66,109,176,228, -236,86,194,219,61,186,221,118,81,158,74,113,200,106,236,162,73,88,23,99,90,91,157,85,88,126,120,225,152,211,90,174,42,85,179, -48,158,213,209,147,146,62,21,81,62,178,57,4,97,90,204,36,94,144,56,201,30,71,30,214,180,145,151,87,38,63,90,75,180,207,84,141, -174,49,163,61,189,105,42,134,170,118,147,236,46,113,102,147,94,213,27,173,102,140,156,105,169,74,246,234,165,66,168,106,148, -17,48,213,224,122,71,51,80,221,141,212,230,200,224,233,25,101,43,229,177,18,143,167,185,125,242,33,209,60,12,71,233,107,198,234, -141,118,154,84,128,156,45,86,148,167,154,159,68,221,255,240,19,131,122,206,214,232,136,199,114,157,211,48,7,241,193,150,248, -78,116,175,124,36,173,140,98,38,54,151,68,44,50,188,58,25,193,19,59,80,122,186,186,110,37,209,75,133,152,37,120,29,70,115,83, -164,159,221,209,55,146,177,217,76,193,113,71,114,150,143,120,46,229,219,57,216,188,86,198,7,177,40,70,146,91,18,52,110,36,161, -54,182,53,209,174,46,244,73,223,102,125,28,247,80,117,198,100,36,35,61,153,54,85,6,154,209,109,170,227,36,21,235,132,153,100, -255,214,78,144,211,27,73,217,110,87,218,11,7,105,142,119,235,233,75,198,251,236,199,135,9,106,43,39,117,244,198,17,66,69,148, -60,152,245,141,42,42,167,200,136,246,245,81,33,191,95,68,35,177,21,145,68,106,61,134,157,242,117,70,179,25,91,101,117,141,148, -35,217,156,142,36,225,239,234,248,209,50,156,48,201,171,212,207,217,71,17,202,193,205,90,35,177,126,44,225,40,182,130,157,38, -110,150,106,180,82,233,8,182,67,148,166,54,38,34,216,27,105,66,52,213,18,199,222,180,106,40,129,101,171,92,107,149,21,193,60, -117,161,237,148,158,51,114,239,52,135,87,112,127,74,119,30,227,45,37,63,83,208,220,203,35,234,140,169,160,148,143,153,54,147, -220,189,13,56,47,144,35,102,118,167,201,21,51,173,158,116,47,185,116,87,133,69,14,139,189,193,109,153,131,27,88,201,177,50,17, -192,107,101,175,58,87,188,131,67,9,57,226,177,174,94,117,29,164,162,184,149,121,163,89,161,66,9,214,201,104,214,74,51,149,78, -198,135,217,113,70,51,181,115,101,213,204,120,215,164,209,172,230,200,128,153,25,47,140,117,218,204,182,87,131,63,182,137,230, -116,60,145,64,86,41,66,128,234,199,97,7,71,116,222,130,111,13,82,126,60,251,13,128,10,226,99,66,53,121,227,214,154,56,22,150, -10,11,148,27,183,50,142,157,175,212,245,253,177,116,52,193,83,162,146,112,206,28,222,1,84,85,88,52,71,207,48,51,177,14,45,217, -83,171,90,114,197,237,9,119,219,242,115,168,215,143,224,157,70,108,114,38,148,99,123,18,145,36,44,85,48,200,79,140,113,111,103, -66,5,239,64,34,158,232,143,29,51,92,139,36,185,147,246,59,37,85,37,205,30,246,141,228,177,95,55,169,60,105,246,225,81,237,199, -223,104,29,182,87,57,147,42,18,26,41,51,77,5,41,142,153,35,47,123,228,69,90,13,34,123,42,149,102,167,26,237,135,86,171,135,171, -101,157,201,85,181,166,140,71,82,25,82,71,125,27,163,241,169,76,40,220,18,205,138,109,147,142,154,205,135,225,8,54,193,148,29, -28,149,3,231,167,198,4,69,79,38,25,179,251,116,90,52,22,219,16,79,43,119,240,166,176,96,50,1,10,21,145,26,137,30,48,102,71,179, -251,133,163,30,138,225,101,163,201,178,148,221,155,198,209,123,233,39,117,168,237,197,145,238,141,166,200,197,215,169,97,45,235, -145,203,155,141,129,150,160,242,64,228,244,167,187,23,169,8,47,6,200,57,160,162,134,91,137,141,221,228,224,183,28,42,228,107, -182,115,229,114,70,75,124,75,202,36,223,192,17,123,194,64,52,153,238,143,196,244,190,229,25,24,29,10,49,72,98,136,228,80,24,212, -131,57,96,46,152,71,98,152,110,119,72,186,92,186,11,218,106,232,94,135,216,39,221,1,153,247,180,28,10,236,48,232,23,162,236, -15,235,232,167,14,249,69,137,252,2,122,194,33,46,20,238,192,99,242,140,192,219,6,93,44,170,107,46,119,18,189,224,48,246,201,228, -128,187,224,39,21,244,103,41,8,77,45,161,119,37,185,103,183,27,242,45,145,127,129,33,94,23,197,181,67,31,24,244,69,33,119, -44,17,69,69,209,37,50,21,112,210,18,145,231,66,125,105,119,64,46,216,42,215,13,214,208,175,165,184,134,239,118,184,252,3,154, -44,184,155,94,181,197,18,227,108,241,170,184,71,184,103,203,167,169,65,238,149,131,242,41,49,56,36,159,223,253,210,94,33,157, -158,101,53,75,106,151,44,57,169,221,160,179,60,103,26,98,143,152,191,228,150,74,195,120,76,132,69,233,248,240,100,67,62,42,164, -40,42,117,74,121,42,122,226,20,78,195,229,145,179,14,56,61,46,114,9,151,116,25,213,213,114,96,182,83,86,203,212,108,90,104, -247,96,161,188,70,94,171,20,7,43,215,201,235,199,230,142,40,206,76,241,126,78,238,231,30,95,88,78,3,16,235,232,45,67,94,45,111, -224,252,55,28,200,160,189,60,98,244,138,193,215,125,14,153,62,3,242,18,135,72,67,124,104,176,64,205,27,85,250,162,114,218,171, -13,174,179,13,206,182,197,245,134,56,136,121,89,183,174,166,109,93,91,45,157,37,118,171,90,247,27,242,75,242,187,2,21,39,213, -208,159,132,56,159,7,171,236,19,185,51,176,206,200,73,202,38,195,117,129,52,100,94,175,40,46,148,131,1,92,131,172,13,5,232,235, -82,62,196,214,129,243,165,63,32,19,1,89,216,32,251,15,108,147,195,107,233,81,105,60,36,206,81,133,50,255,60,57,16,184,170,125, -199,249,240,5,41,219,232,65,93,173,224,252,96,249,85,116,151,225,60,243,103,226,66,249,182,24,70,23,191,109,56,174,146,183,136, -95,137,95,162,252,196,243,219,232,106,97,155,202,71,41,32,239,63,11,93,202,229,46,185,185,75,63,161,173,114,233,108,116,38,108, -227,107,50,242,30,23,242,196,37,134,247,77,177,224,68,33,12,207,189,66,214,136,242,252,147,157,30,167,183,222,153,183,195,229, -169,21,197,37,114,119,195,18,151,247,68,17,24,207,249,99,51,229,106,17,40,160,159,25,226,74,140,125,208,16,231,202,176,240,151, -72,239,108,217,23,168,48,232,60,81,51,217,73,74,153,53,197,73,47,135,171,233,97,67,60,137,46,210,107,134,24,116,23,68,203,233, -2,41,46,66,229,135,12,186,79,4,106,119,172,27,218,62,225,124,146,149,226,75,178,44,40,171,164,229,240,63,46,74,243,229,84,100, -148,187,75,69,233,210,210,220,210,149,165,178,180,186,212,97,91,85,40,43,9,171,83,70,236,11,228,20,182,23,101,147,109,69,150, -5,202,42,72,10,135,231,210,160,240,79,253,194,30,199,129,242,105,226,161,114,33,14,4,132,248,33,120,96,50,138,133,87,138,75,131, -51,247,236,113,236,173,168,22,183,86,144,225,162,2,174,33,252,179,80,231,80,80,236,13,222,84,137,203,125,124,121,170,82,200, -131,224,210,42,114,149,78,42,242,195,247,253,246,191,122,24,223,87,5,147,115,167,227,178,159,47,119,243,229,41,190,188,201,151, -189,51,28,103,75,18,128,165,91,235,255,12,227,128,7,44,18,7,102,8,241,12,56,52,3,3,31,26,47,246,135,132,184,27,60,15,222,4, -123,102,10,113,7,120,9,188,11,246,86,11,177,31,28,2,7,102,193,14,60,80,35,196,173,181,66,92,83,39,28,207,128,131,117,176,9,59, -197,43,243,96,55,95,138,115,23,72,241,205,5,250,251,114,246,239,10,88,102,254,78,135,191,67,103,254,86,135,191,79,103,254,94, -39,243,187,78,254,155,29,254,54,157,249,187,29,23,141,254,237,142,225,179,203,248,119,17,34,104,255,142,103,106,1,108,130,182, -13,255,31,54,225,179,127,183,195,255,111,77,6,237,251,242,223,250,24,218,158,255,239,152,35,104,255,142,225,132,57,246,47,29, -184,174,250,191,111,62,187,175,252,119,69,255,7,207,224,91,10,144,52,0,0,0,0}; +{ 31,139,8,8,217,126,216,97,0,3,74,97,118,97,68,101,120,66,121,116,101,67,111,100,101,46,100,101,120,0,165,155,11,124,212,213,149,199,207,189,255,255,204,36,147,215,100,18,146,64,18,50,9,175,16,72,102,8,111,19,148,183,6,18,64,18,16,18,171,76,146,127,146, + 129,201,127,134,153,73,72,124,21,149,143,96,173,21,45,82,21,109,177,165,182,110,109,215,90,219,181,22,181,93,93,215,173,186,85,107,87,180,90,31,69,197,150,90,180,86,105,117,117,127,247,49,147,9,143,210,118,195,231,59,231,252,207,125,223,123,238,185,247, + 63,33,221,214,144,59,48,115,54,125,253,172,186,247,86,249,42,166,141,253,112,218,236,231,195,83,47,91,120,183,99,205,99,123,255,208,184,125,22,81,148,136,134,214,207,242,146,254,57,56,147,168,146,41,251,60,240,129,73,180,4,242,121,7,81,9,228,241,12,162, + 203,33,31,200,36,66,18,237,207,33,218,52,153,200,151,75,212,94,78,116,33,184,24,116,130,205,192,6,215,128,107,193,245,96,55,216,3,110,1,251,192,215,192,195,224,5,240,18,248,13,120,3,188,5,126,15,142,129,63,131,156,241,232,7,184,8,12,131,61,224,126,240, + 60,248,12,140,173,32,170,7,231,130,207,129,65,112,19,120,8,60,7,142,130,137,62,162,101,96,8,220,3,14,131,194,74,162,6,208,11,246,130,23,64,69,21,198,2,46,7,119,130,159,129,195,192,152,64,84,14,2,160,13,92,2,110,0,247,129,199,193,155,224,47,160,122,34, + 209,82,208,1,194,224,10,176,3,220,4,110,6,183,130,253,224,0,184,7,220,7,126,12,30,1,143,129,39,192,211,224,57,240,2,120,5,188,14,222,1,71,193,123,224,35,240,9,48,38,17,101,130,28,224,5,227,64,37,152,4,166,130,5,96,45,184,8,108,6,131,224,42,240,37,112, + 27,184,11,220,7,30,6,79,130,151,192,49,112,28,56,176,174,121,160,6,204,6,243,192,74,208,10,58,64,23,8,131,56,216,14,174,7,119,128,123,192,15,193,65,240,34,120,21,188,1,222,1,89,83,136,138,64,57,152,6,230,129,117,32,14,174,0,55,128,187,192,15,193,35,224, + 73,240,34,56,12,254,2,114,171,137,202,192,20,48,15,44,7,171,193,6,96,129,109,96,39,184,29,124,7,252,4,188,8,94,5,111,130,35,128,79,37,42,0,147,192,92,176,28,180,130,16,184,4,92,7,110,3,223,2,7,193,227,224,85,240,14,248,35,120,31,124,10,140,26,248,15, + 152,0,102,129,115,64,11,184,0,92,12,182,128,1,176,27,236,3,7,192,67,224,41,240,75,240,6,56,2,142,129,227,224,99,192,166,17,185,65,33,168,2,53,32,0,230,128,38,112,17,136,130,171,193,126,240,3,240,56,120,21,188,13,62,4,206,233,40,15,42,64,29,152,11,22, + 129,53,224,34,208,3,34,96,24,108,7,95,4,251,192,215,193,207,192,99,224,105,240,30,200,171,197,122,129,82,48,13,204,7,107,65,39,136,130,47,128,59,193,195,224,73,240,18,120,11,124,8,204,58,204,51,152,0,234,65,51,88,11,54,128,77,160,15,216,32,14,134,193, + 213,224,26,112,61,216,11,110,7,223,5,15,128,159,131,23,193,155,224,8,248,61,248,35,248,19,248,8,124,12,62,3,78,196,36,132,40,202,2,69,160,152,84,220,26,11,198,129,82,80,6,16,82,8,97,131,16,22,8,97,128,176,237,9,91,156,176,125,9,219,138,224,254,4,119, + 37,184,28,193,101,8,203,77,88,30,194,244,18,166,133,48,60,66,115,228,7,1,48,3,212,3,132,79,66,88,165,217,96,14,152,171,227,232,124,112,22,104,0,141,96,1,56,27,156,3,22,130,69,96,49,169,88,187,12,156,7,54,128,78,208,5,186,129,69,106,124,201,31,151,150, + 247,151,170,49,51,253,236,214,186,176,139,121,224,218,158,173,245,131,176,231,165,213,229,213,115,246,152,182,231,106,123,158,78,75,234,99,180,238,209,243,42,234,47,208,250,83,186,108,73,90,157,98,174,159,47,85,186,152,227,151,117,158,201,105,245,76, + 213,245,228,107,253,48,244,66,173,31,45,85,109,138,57,255,64,215,35,244,79,116,61,181,186,158,49,122,29,204,50,213,31,177,22,217,101,106,14,235,117,158,38,173,139,182,86,104,189,16,121,86,106,93,180,219,172,117,31,236,45,90,15,64,95,165,245,70,232,171, + 181,126,30,244,53,90,111,131,126,190,214,47,132,222,170,245,238,52,123,56,77,79,64,95,167,245,203,210,236,187,210,244,221,105,250,45,105,117,238,79,179,127,27,122,155,214,239,77,179,31,40,29,209,197,156,175,213,186,152,207,100,61,15,164,229,23,243,185, + 94,235,63,133,253,2,173,63,145,150,231,80,154,254,90,153,242,205,153,122,110,55,106,253,8,236,237,90,63,150,166,127,2,189,67,235,25,226,142,160,117,15,244,207,105,189,76,220,27,180,94,13,253,34,173,7,210,236,194,199,54,105,125,30,236,65,173,159,151,150, + 191,173,92,248,57,163,97,82,50,135,137,125,63,137,226,164,228,191,73,201,232,65,45,15,106,249,144,150,15,107,249,136,206,255,115,18,177,194,71,110,166,100,1,19,113,99,38,253,23,9,89,65,89,76,196,16,149,94,161,211,43,144,82,196,132,207,23,209,86,225,79, + 216,117,15,72,89,65,79,72,89,78,191,148,114,38,189,35,101,22,29,149,62,63,153,150,67,58,16,129,254,68,98,127,58,233,70,41,199,208,126,200,76,68,49,67,202,89,244,33,137,125,94,45,159,179,180,61,11,17,225,35,57,110,245,156,135,118,111,144,178,136,110,210, + 207,183,106,121,167,152,127,157,46,228,110,41,77,186,89,63,239,147,210,160,219,73,237,211,59,180,252,170,148,140,238,210,242,91,36,246,37,167,189,82,78,165,111,146,216,131,147,100,251,5,136,120,191,149,114,62,29,150,178,148,222,38,177,95,107,105,64,203, + 15,72,196,233,57,244,31,36,226,72,54,61,47,229,88,122,159,68,44,81,227,24,139,136,43,100,25,162,230,143,164,172,161,191,74,121,14,153,114,93,2,50,189,28,51,176,67,202,18,218,165,159,175,149,114,30,189,43,215,171,78,230,27,143,26,95,39,177,78,170,92,5, + 236,61,90,246,74,153,71,125,82,142,37,7,83,210,41,215,115,178,204,239,195,10,133,164,244,211,102,41,23,209,22,41,11,40,172,101,191,148,11,201,150,114,44,13,105,121,185,148,115,233,74,41,43,233,58,41,189,116,189,148,57,244,37,41,157,180,71,251,205,94, + 157,254,21,41,103,211,45,82,102,210,215,164,116,211,215,117,190,111,72,153,65,7,164,84,235,32,252,236,110,41,203,232,95,180,252,142,150,247,104,127,252,174,148,227,233,123,218,254,175,186,220,189,90,126,95,203,251,164,244,209,15,164,108,160,251,165,156, + 67,143,105,249,184,148,19,232,5,41,171,232,144,150,47,106,249,146,78,255,181,126,126,89,203,87,164,204,165,223,72,57,133,94,149,114,58,189,38,229,217,244,134,148,202,143,124,218,143,196,243,155,122,255,188,37,165,242,43,145,255,136,148,141,116,76,202, + 122,58,174,229,95,164,156,70,159,72,57,142,62,149,114,6,125,166,159,137,169,124,76,75,206,84,186,193,212,188,20,50,113,110,21,211,109,36,228,98,122,84,238,87,78,63,147,50,159,188,76,156,77,202,95,167,97,103,124,153,196,249,100,208,21,82,114,122,154,196, + 25,85,72,255,78,226,190,48,86,251,171,218,83,201,51,19,175,88,116,33,98,220,21,250,208,26,175,237,226,204,19,49,93,164,39,32,239,208,233,21,186,252,180,180,242,59,144,126,175,78,247,145,58,87,197,153,120,143,46,191,7,242,144,78,23,119,129,169,208,3,245, + 234,190,112,86,189,178,173,134,92,15,12,157,190,9,108,214,121,226,210,110,72,157,251,213,189,161,131,185,41,234,17,55,168,14,158,133,181,204,64,45,162,173,28,191,186,59,121,89,91,167,155,182,122,102,193,158,197,139,240,218,56,131,115,115,62,55,201,229, + 201,167,182,206,44,218,234,171,160,24,118,191,200,215,214,165,242,26,148,101,110,93,51,135,22,15,184,249,124,254,135,207,108,143,24,143,203,83,253,182,169,219,159,224,87,119,152,42,204,104,212,115,153,28,173,104,91,244,187,214,175,238,96,167,107,219, + 160,124,230,245,206,156,50,246,228,54,3,115,105,177,35,27,49,227,24,218,20,113,202,205,108,223,20,68,169,108,86,253,214,201,185,231,201,220,213,239,136,248,107,202,254,44,241,171,181,107,139,170,124,34,165,136,203,123,9,159,143,60,94,196,151,182,173, + 238,244,116,206,118,176,155,93,119,109,115,206,199,200,16,215,143,25,232,175,24,227,106,191,136,247,152,89,111,14,198,184,13,54,47,93,232,201,150,119,180,12,60,137,177,110,240,171,179,182,192,89,72,85,60,27,249,196,44,180,197,115,168,109,102,46,217,158, + 165,200,149,205,230,179,198,17,155,239,60,236,197,145,28,139,144,195,109,172,157,40,222,21,25,125,191,237,70,50,204,217,155,198,209,186,56,230,193,40,34,175,113,33,180,100,254,182,68,14,250,29,192,72,178,77,209,127,83,206,195,108,90,226,16,249,231,27, + 46,154,187,201,65,222,2,81,70,180,85,140,56,103,123,138,196,200,78,104,163,250,191,197,90,10,159,187,218,175,238,116,29,222,92,61,206,42,204,97,212,39,246,82,135,7,20,228,201,49,51,249,143,232,203,126,245,253,67,212,35,110,123,185,41,251,190,148,189, + 94,218,185,190,21,127,195,175,252,185,205,147,39,247,132,240,32,209,238,119,252,234,94,220,230,203,67,31,197,205,27,227,172,244,160,215,101,104,57,55,149,239,190,211,230,43,151,249,114,225,241,98,36,63,70,190,167,132,223,185,188,69,81,95,9,226,111,149, + 153,133,222,56,209,171,142,61,69,120,170,160,104,160,150,246,100,118,236,41,192,83,57,158,38,203,167,66,212,187,16,107,157,109,142,115,68,81,47,86,16,122,133,89,132,114,249,84,102,58,96,75,32,190,187,205,121,230,231,168,194,45,158,241,158,74,237,183, + 123,161,69,113,227,185,112,223,24,104,33,156,77,182,111,179,252,220,130,243,190,10,117,70,61,242,60,54,11,105,194,162,89,52,153,137,27,161,237,51,209,243,54,209,39,7,246,175,207,65,162,70,23,90,245,26,182,103,72,247,228,99,242,78,158,208,51,7,227,233, + 204,40,166,150,12,167,203,91,92,150,145,37,53,59,16,167,54,103,182,49,207,40,36,47,247,78,156,176,116,46,121,29,91,61,131,152,135,108,103,139,211,116,120,199,120,165,180,3,151,210,151,77,225,105,194,187,197,168,84,239,247,80,245,157,57,102,245,7,224, + 125,112,12,188,4,196,225,97,152,242,61,96,228,103,251,57,244,15,61,159,248,163,210,197,93,178,8,251,241,18,82,247,23,110,84,223,202,166,124,133,213,221,204,248,244,59,88,205,62,70,58,186,224,221,47,160,124,166,213,231,149,113,214,148,145,15,239,125,1, + 237,99,129,213,84,105,228,98,207,59,100,188,155,27,80,49,54,186,174,143,42,151,184,165,63,39,203,44,77,166,5,22,163,76,182,244,193,100,90,75,42,237,236,84,154,75,222,254,16,135,117,218,160,179,77,70,6,81,167,67,239,123,43,160,246,76,235,66,111,90,124, + 49,224,139,194,18,13,52,227,118,84,253,215,145,241,244,235,186,196,123,113,59,198,148,171,251,32,126,226,1,21,251,189,158,188,212,158,185,60,144,220,51,133,178,79,73,251,213,218,190,206,51,70,182,207,245,222,187,54,160,222,61,109,207,114,217,87,25,235, + 80,159,151,170,63,53,116,59,55,4,212,57,146,43,203,169,55,211,175,164,217,156,58,126,127,245,12,99,91,161,199,150,204,255,173,51,228,95,153,154,11,213,143,123,79,209,143,7,78,97,123,36,205,102,234,182,30,15,168,239,11,188,76,124,163,208,17,224,164,164, + 65,27,103,32,238,29,219,24,112,209,198,128,83,91,51,225,199,88,73,207,198,128,137,244,12,236,199,177,232,79,21,249,88,190,30,175,88,147,95,5,212,89,125,234,254,183,45,46,162,232,218,86,50,207,175,62,46,124,195,144,254,247,202,105,203,204,92,246,217,103, + 114,220,11,23,144,25,20,101,220,104,71,156,213,191,11,168,239,57,218,182,231,99,157,196,41,229,198,73,55,142,90,175,68,20,241,228,98,215,187,177,251,189,66,154,182,167,64,72,135,141,53,204,164,44,167,107,251,96,70,147,246,17,177,166,110,180,37,124,244, + 163,128,250,174,195,235,105,141,161,39,226,140,101,56,99,153,58,233,166,139,87,62,18,41,54,206,45,85,95,150,144,78,215,86,182,99,208,217,130,249,173,254,67,36,48,150,118,102,226,228,59,98,123,206,194,8,171,197,69,143,171,113,38,191,191,17,178,64,147, + 173,207,218,49,51,212,123,159,151,90,175,66,219,78,217,182,115,62,51,208,242,252,204,76,180,137,158,227,4,247,86,213,87,186,40,178,112,28,221,242,32,90,121,223,246,184,97,207,102,243,152,184,89,40,221,246,13,11,191,117,136,154,68,31,179,68,31,175,100, + 123,84,31,189,158,234,23,212,220,139,245,154,57,67,125,239,48,122,238,213,136,133,77,140,213,73,110,30,93,187,10,171,134,178,127,38,189,163,137,22,205,24,241,169,76,29,59,86,207,80,107,25,217,80,74,109,131,233,181,206,67,155,202,135,99,240,97,81,38,79, + 175,229,38,36,151,161,162,214,103,145,219,16,35,103,134,156,117,150,199,108,79,14,234,119,103,138,181,203,165,44,215,175,110,252,140,166,177,15,165,215,86,191,215,250,156,23,210,246,229,227,140,21,235,44,98,129,88,151,2,33,157,174,103,7,51,214,136,17, + 231,87,191,124,230,156,107,85,206,255,60,115,206,243,145,211,246,136,211,210,157,229,205,159,91,90,67,222,202,9,101,179,113,18,44,193,253,216,59,110,246,131,149,36,106,17,117,124,79,212,225,43,20,146,121,29,75,29,217,142,171,214,61,62,198,246,141,81, + 150,162,165,206,108,231,85,61,143,23,37,235,61,228,118,179,234,215,208,143,171,113,83,199,165,254,135,55,190,238,102,25,243,221,37,244,247,142,96,42,245,124,150,222,159,127,166,39,162,166,234,95,255,227,61,88,163,123,48,233,255,221,131,53,178,7,88,102, + 166,190,29,20,190,37,226,144,95,75,17,191,196,187,91,92,250,30,151,119,150,23,102,168,239,50,209,71,156,251,46,202,230,227,140,255,37,111,225,132,165,56,247,29,157,78,156,251,242,12,143,83,189,41,238,145,46,244,59,7,251,31,124,226,245,78,168,194,169, + 111,136,83,63,3,39,123,139,201,13,113,218,111,229,213,239,230,240,234,163,224,119,224,136,240,247,124,244,173,84,238,93,68,61,62,189,168,166,196,168,174,158,50,189,174,38,45,222,26,245,35,123,195,208,214,172,122,117,7,158,199,93,36,102,196,70,142,92, + 242,46,172,254,88,140,79,197,228,194,122,21,7,196,29,74,220,118,199,241,159,202,59,84,37,90,171,192,251,219,60,68,15,219,115,9,118,168,155,45,96,5,152,109,59,48,137,2,24,241,185,50,191,29,152,72,30,179,109,70,1,110,182,117,178,254,100,138,151,219,129, + 9,228,225,42,205,47,230,247,125,146,191,239,18,63,201,239,92,197,123,206,116,216,102,37,191,60,213,63,11,79,120,94,115,194,179,40,95,72,234,236,205,71,239,152,182,9,124,90,102,232,116,175,62,103,199,105,123,133,150,92,227,211,243,53,141,230,74,123,173, + 182,215,34,58,43,201,100,60,101,250,159,131,70,206,116,174,125,35,121,150,155,41,157,105,187,43,245,93,50,199,76,179,84,126,33,115,82,101,132,116,234,52,39,242,113,109,115,105,153,169,101,242,187,232,92,217,43,210,247,11,49,198,153,122,44,62,105,159, + 169,125,120,102,170,215,170,252,44,45,103,235,114,201,126,10,153,163,219,23,122,94,42,61,47,109,76,57,169,177,22,167,250,161,234,246,232,246,124,169,156,106,92,62,157,155,145,122,223,102,213,132,247,41,103,99,200,14,37,206,38,126,118,3,21,45,137,244, + 71,35,182,101,39,214,88,86,108,125,200,218,86,183,57,56,24,36,182,156,248,242,38,98,77,196,155,166,2,168,43,136,175,104,166,242,21,3,93,214,162,174,46,43,30,15,117,134,194,161,196,240,170,72,183,181,38,22,25,12,117,91,49,42,94,105,13,119,70,130,177,238, + 165,161,120,127,40,30,111,14,197,19,150,141,4,214,76,188,25,181,53,163,154,230,102,50,154,241,128,143,21,226,163,153,138,154,131,118,119,44,18,234,246,7,163,81,255,162,174,68,104,16,53,55,208,172,209,246,104,52,28,234,10,38,66,17,123,98,50,79,115,168, + 199,234,26,238,10,91,75,130,225,112,103,176,107,75,188,129,198,158,174,84,122,82,87,196,70,207,18,254,37,66,14,37,210,147,122,99,193,104,95,168,43,238,95,18,180,7,131,168,112,252,41,146,34,225,72,108,121,40,156,176,98,167,79,111,9,38,98,161,161,6,154, + 250,55,211,71,85,85,114,114,214,53,193,144,141,254,21,159,156,178,214,234,66,66,65,42,33,18,247,47,30,176,187,195,86,3,21,166,27,155,22,135,236,110,81,251,72,29,131,88,106,63,22,107,217,160,37,42,31,55,58,161,37,34,166,75,167,77,29,157,38,156,100,226, + 106,123,121,164,107,32,190,164,47,104,247,90,201,69,78,239,74,42,107,250,144,82,198,115,99,145,129,104,3,205,57,57,165,45,102,89,171,59,227,86,108,208,138,161,149,115,195,145,206,96,184,57,56,28,25,72,140,52,83,241,183,203,53,208,140,209,25,130,233,254, + 234,31,229,189,45,65,59,216,43,138,212,255,221,69,132,195,55,217,61,145,147,250,127,134,50,201,77,210,64,117,163,203,133,236,232,64,162,223,74,244,69,186,253,139,131,113,84,142,103,248,165,141,229,149,94,59,233,244,249,151,117,135,18,145,152,234,78,205, + 233,179,157,84,101,237,25,242,182,72,61,53,59,231,52,119,69,250,253,177,254,120,216,191,25,1,192,127,82,216,152,248,55,227,66,3,45,63,99,5,167,137,28,19,71,175,236,252,127,182,158,6,170,60,83,209,6,170,106,238,14,134,7,67,91,252,65,219,142,36,100,204, + 240,47,179,187,194,145,120,200,238,93,18,14,198,101,48,56,57,79,19,38,54,166,211,43,79,145,222,98,245,119,234,12,22,178,148,159,34,75,107,168,215,14,38,6,98,150,216,48,34,6,251,195,216,91,126,236,176,88,171,181,117,192,178,187,144,146,159,158,162,154, + 171,74,51,53,133,195,86,111,48,172,150,97,217,80,151,21,85,139,61,241,20,121,98,189,3,253,24,123,90,174,130,244,92,8,138,189,106,210,70,140,171,34,173,3,93,125,202,51,210,202,121,211,178,172,238,220,44,99,82,121,154,173,213,234,26,136,193,33,78,83,164, + 21,49,208,238,21,30,57,98,139,89,61,97,212,131,110,12,70,84,232,110,11,198,122,173,244,222,142,59,69,118,213,181,6,26,163,210,6,18,161,176,127,81,44,22,28,22,78,208,64,121,105,102,97,33,207,9,134,6,50,219,54,174,89,70,217,233,62,71,108,61,241,245,77, + 228,88,223,132,31,168,43,200,185,126,69,211,242,229,120,155,135,20,9,43,68,2,30,196,193,182,126,69,59,242,8,69,28,110,235,165,169,185,29,169,205,237,56,250,214,183,163,112,187,172,136,181,147,209,46,202,225,163,89,168,205,228,104,95,33,116,19,2,199,101, + 187,176,226,152,116,182,55,75,179,67,72,216,59,112,54,119,52,145,183,227,100,103,40,232,56,197,90,184,85,92,154,24,8,4,82,250,140,52,189,62,77,159,153,166,207,74,211,103,167,233,115,210,244,185,105,250,60,232,89,74,95,30,14,246,198,41,103,84,64,164,194, + 224,41,2,47,57,131,50,34,137,146,66,54,7,59,173,48,101,4,245,241,78,99,131,221,221,167,62,6,40,51,168,125,56,78,172,147,242,197,225,191,120,32,145,136,216,107,98,104,198,234,38,103,103,4,143,253,144,242,80,36,103,151,60,207,201,213,37,143,173,110,114, + 224,222,16,140,81,86,151,8,85,17,156,178,139,18,226,33,117,50,83,174,124,104,139,5,237,120,79,36,214,79,57,226,214,128,227,56,46,115,163,34,117,121,64,69,145,1,60,151,118,197,172,96,226,228,48,40,194,51,153,221,161,158,30,98,22,57,44,113,174,146,175, + 7,135,242,41,179,198,23,15,183,137,90,51,69,14,121,206,146,163,71,138,236,158,145,83,183,155,178,228,147,136,92,77,221,84,138,237,49,170,182,229,105,137,101,39,38,142,186,180,101,202,84,185,10,121,41,181,37,24,223,130,54,198,8,195,200,13,74,223,150,40, + 7,102,17,154,176,78,86,44,78,25,226,81,184,32,185,133,166,51,101,139,29,43,198,218,22,234,183,100,43,231,89,161,222,190,4,21,64,149,167,76,122,31,133,177,89,239,246,213,118,43,166,210,178,101,21,216,213,193,86,68,73,85,133,218,227,178,163,114,17,16,86, + 229,220,103,143,24,80,153,11,79,107,131,219,54,36,149,141,148,37,148,72,36,33,90,35,15,30,90,135,225,71,253,173,216,227,161,46,139,114,97,89,103,135,132,39,136,113,201,222,156,120,175,144,163,94,31,74,57,180,40,115,1,150,40,178,173,45,178,5,157,45,75, + 61,203,76,97,11,71,80,52,28,28,94,30,11,98,252,38,82,55,200,207,141,196,250,168,4,139,8,167,28,181,42,231,69,68,43,121,58,37,26,93,19,28,16,126,236,73,25,214,90,113,120,124,202,178,56,229,242,148,163,44,56,252,150,70,182,97,55,165,30,215,69,169,48,245, + 32,15,198,243,66,221,221,232,173,110,166,37,130,54,100,153,81,134,88,176,55,89,167,52,160,26,93,167,188,142,82,129,126,176,98,98,99,104,239,201,232,11,198,149,191,22,247,193,179,90,35,61,122,153,99,145,126,53,49,200,130,210,210,187,205,190,8,66,48,11, + 145,27,222,177,90,70,245,56,25,161,254,126,202,19,239,39,161,96,120,73,48,26,111,193,130,80,142,54,180,90,225,101,118,119,42,29,143,240,139,24,54,138,188,190,180,13,71,45,202,150,234,197,234,42,67,25,104,108,125,48,60,128,189,31,194,81,178,197,66,99, + 241,38,59,158,8,226,56,69,106,124,117,52,136,179,149,198,134,226,109,17,156,109,203,134,162,216,239,210,5,151,217,65,172,96,55,234,142,235,213,36,215,22,107,120,137,232,79,241,150,211,188,229,228,36,19,90,251,196,140,58,194,50,154,229,192,7,172,152,232, + 222,42,220,55,200,12,91,61,9,114,134,45,187,55,209,71,78,221,85,102,147,105,11,63,113,217,214,182,85,66,201,176,147,161,35,219,78,223,174,206,72,167,136,65,100,70,194,221,125,242,115,27,229,71,236,228,27,209,18,25,131,176,159,70,76,75,173,120,34,22,25, + 22,142,51,98,212,206,149,86,50,233,93,165,35,166,214,224,160,149,156,47,181,7,211,242,203,201,31,93,69,107,34,18,141,194,84,140,80,33,251,113,194,197,19,157,183,225,91,219,40,39,146,254,6,65,185,145,81,49,158,178,35,182,220,12,50,124,80,102,196,78,58, + 118,142,84,91,6,194,137,80,84,44,137,124,132,115,102,136,163,67,22,69,142,214,208,37,86,50,72,162,38,181,180,178,38,103,68,45,184,75,201,139,81,110,0,81,63,129,24,230,136,74,199,118,71,131,49,228,148,97,34,39,58,202,189,29,81,25,245,203,163,145,232,64, + 248,180,113,158,197,200,21,83,239,164,84,21,179,122,133,111,196,78,255,186,74,101,49,171,31,67,85,195,95,109,159,112,200,57,98,50,98,26,113,43,65,185,113,17,91,83,47,139,148,141,103,57,137,194,83,169,56,253,169,73,13,90,238,30,81,44,237,78,47,139,53, + 39,61,146,74,240,116,202,183,57,26,19,79,6,201,117,161,180,168,87,122,74,179,184,76,7,113,122,198,85,216,148,14,156,19,31,21,46,221,201,199,176,234,211,5,161,112,120,85,36,33,221,33,59,142,13,147,12,80,40,136,167,84,244,64,102,225,104,170,95,184,42,34, + 25,94,54,242,88,18,87,189,105,26,105,75,143,212,148,199,144,153,232,11,197,201,41,62,39,6,180,156,1,171,56,148,12,212,4,85,76,68,198,64,162,103,158,140,253,108,144,28,131,50,106,184,164,88,221,67,166,120,75,162,60,241,153,238,92,153,194,208,22,89,23, + 183,200,51,120,210,105,49,24,138,37,6,130,97,125,190,185,7,71,166,130,109,35,54,68,124,40,0,102,128,122,48,19,204,34,54,76,223,52,57,237,229,174,220,246,90,186,199,100,183,112,87,57,207,122,150,15,149,111,54,232,151,172,228,141,149,244,144,201,175,229, + 176,231,210,163,38,187,142,185,202,159,228,151,148,191,111,208,245,172,166,246,38,7,209,115,166,113,11,143,111,115,229,62,90,65,191,229,140,80,85,35,253,142,147,107,122,135,193,223,103,57,215,26,236,93,86,80,55,244,145,65,215,48,190,185,145,229,231,135, + 26,121,188,220,65,141,44,203,137,242,92,117,128,207,217,200,87,110,171,165,95,112,118,187,104,237,68,121,8,85,230,222,79,175,40,209,104,92,205,126,207,126,204,92,211,249,179,212,192,63,101,219,248,51,108,219,16,127,227,210,215,118,49,238,112,47,170,109, + 172,107,108,60,187,195,160,237,204,125,153,193,174,100,179,27,239,170,52,140,95,176,0,43,30,19,24,111,240,167,24,103,249,197,14,206,207,71,87,28,204,97,56,221,124,218,1,135,219,73,78,230,228,78,163,166,134,15,78,119,240,26,30,159,78,115,85,23,230,242, + 219,249,29,82,49,133,242,85,254,181,209,214,148,226,72,38,239,23,143,215,137,46,127,161,140,6,33,86,210,97,131,239,227,119,10,251,235,38,12,180,75,76,25,189,100,136,207,47,152,124,224,82,200,29,38,27,128,120,215,16,2,37,111,150,207,215,149,209,167,134, + 202,176,91,101,56,46,51,208,141,6,59,138,133,89,185,178,182,125,101,123,29,125,158,93,38,75,221,107,240,27,249,143,24,10,150,214,210,219,140,237,130,186,139,21,229,249,104,15,231,79,136,199,242,157,124,76,57,239,47,231,121,13,117,43,45,62,176,226,66, + 62,188,130,30,228,198,19,108,135,76,231,249,215,240,88,249,222,142,205,187,12,199,110,206,253,214,57,116,175,46,155,187,211,87,182,151,238,54,28,151,63,205,190,196,63,100,151,160,35,251,13,243,54,126,55,123,129,253,15,210,23,236,108,167,91,84,171,59, + 249,19,84,206,31,252,124,249,74,35,51,206,155,13,215,245,220,224,143,210,70,190,112,58,43,200,11,40,60,205,70,214,51,140,47,104,52,178,143,177,57,11,24,51,220,15,50,94,203,202,114,206,113,184,29,217,51,28,89,155,157,238,58,86,80,196,47,109,104,116,102, + 47,96,229,99,132,125,180,145,47,103,229,185,244,136,193,110,197,12,251,12,182,139,7,152,183,136,103,79,199,40,43,12,218,201,106,199,59,72,42,211,38,56,232,245,64,13,253,196,96,207,161,139,244,170,193,134,92,185,161,50,58,206,216,13,40,252,152,65,7,89, + 121,221,230,149,67,155,198,238,36,94,201,110,228,37,62,94,197,35,166,247,25,86,156,195,39,194,80,230,42,102,197,11,139,51,139,151,22,243,226,154,98,83,229,170,144,185,224,143,197,231,166,242,231,242,9,34,63,43,25,175,20,94,82,94,82,65,156,153,238,221, + 62,230,157,124,229,118,243,80,217,20,118,188,140,177,67,229,140,125,0,142,141,71,50,203,230,108,183,111,218,246,237,230,193,138,233,236,181,10,50,156,148,43,74,48,111,45,202,28,168,100,187,124,207,139,143,163,226,99,71,21,227,183,131,199,170,200,89,82, + 154,239,133,135,123,213,191,153,200,124,180,10,89,30,152,140,143,103,196,199,17,241,177,99,10,62,246,139,143,131,83,204,171,56,49,32,164,75,235,127,47,133,192,13,206,98,135,166,96,201,171,25,59,80,109,176,7,170,139,216,51,208,143,128,47,78,101,108,63, + 184,31,28,6,123,106,24,251,54,56,8,158,1,7,166,97,232,224,139,211,25,123,13,188,92,203,216,243,117,204,220,237,103,230,1,63,210,252,14,182,127,22,99,247,204,230,236,167,224,240,108,79,218,239,7,146,50,249,119,57,226,251,234,228,223,230,136,239,177,147, + 127,159,147,252,93,169,248,27,29,241,29,118,242,239,116,156,52,242,183,58,134,71,127,191,157,139,58,125,234,247,66,27,160,59,125,42,143,248,255,103,204,163,236,226,255,156,113,159,106,87,252,109,143,161,243,139,255,247,101,250,212,239,37,166,214,171, + 95,84,136,178,242,255,173,121,84,95,197,223,17,253,31,67,245,215,66,128,52,0,0,0,0 }; //============================================================================== #if JUCE_PUSH_NOTIFICATIONS && JUCE_MODULE_AVAILABLE_juce_gui_extra @@ -309,6 +215,135 @@ static BorderSize androidDisplayCutoutToBorderSize (LocalRef displ getInset (AndroidDisplayCutout.getSafeInsetRight) }; } +/* The usage of the KeyPress class relies on its keyCode member having the standard ASCII values + represent ASCII keycodes. However in the native Android keycodes the values for special keys + e.g. RETURN, F1-F12 overlap with the ASCII range. Hence we need to translate them. +*/ +static constexpr int translateAndroidKeyCode (int keyCode) noexcept +{ + switch (keyCode) + { + case 7: return '0'; + case 8: return '1'; + case 9: return '2'; + case 10: return '3'; + case 11: return '4'; + case 12: return '5'; + case 13: return '6'; + case 14: return '7'; + case 15: return '8'; + case 16: return '9'; + case 17: return '*'; + case 18: return '#'; + case 19: return KeyPress::upKey; // KEYCODE_DPAD_UP + case 20: return KeyPress::downKey; // KEYCODE_DPAD_DOWN + case 21: return KeyPress::leftKey; // KEYCODE_DPAD_LEFT + case 22: return KeyPress::rightKey; // KEYCODE_DPAD_RIGHT + case 29: return 'A'; + case 30: return 'B'; + case 31: return 'C'; + case 32: return 'D'; + case 33: return 'E'; + case 34: return 'F'; + case 35: return 'G'; + case 36: return 'H'; + case 37: return 'I'; + case 38: return 'J'; + case 39: return 'K'; + case 40: return 'L'; + case 41: return 'M'; + case 42: return 'N'; + case 43: return 'O'; + case 44: return 'P'; + case 45: return 'Q'; + case 46: return 'R'; + case 47: return 'S'; + case 48: return 'T'; + case 49: return 'U'; + case 50: return 'V'; + case 51: return 'W'; + case 52: return 'X'; + case 53: return 'Y'; + case 54: return 'Z'; + case 55: return ','; + case 56: return '.'; + case 61: return KeyPress::tabKey; // KEYCODE_TAB + case 62: return KeyPress::spaceKey; // KEYCODE_SPACE + case 66: return KeyPress::returnKey; // KEYCODE_ENTER + case 67: return KeyPress::backspaceKey; // KEYCODE_DEL + case 68: return '`'; + case 69: return '-'; + case 70: return '='; + case 71: return '['; + case 72: return ']'; + case 73: return '\\'; + case 74: return ';'; + case 75: return '\''; + case 76: return '/'; + case 77: return '@'; + case 81: return '+'; + case 85: return KeyPress::playKey; // KEYCODE_MEDIA_PLAY_PAUSE + case 86: return KeyPress::stopKey; // KEYCODE_MEDIA_STOP + case 87: return KeyPress::fastForwardKey; // KEYCODE_MEDIA_NEXT + case 88: return KeyPress::rewindKey; // KEYCODE_MEDIA_PREVIOUS + case 92: return KeyPress::pageUpKey; // KEYCODE_PAGE_UP + case 93: return KeyPress::pageDownKey; // KEYCODE_PAGE_DOWN + case 111: return KeyPress::escapeKey; // KEYCODE_ESCAPE + case 112: return KeyPress::deleteKey; // KEYCODE_FORWARD_DEL + case 122: return KeyPress::homeKey; // KEYCODE_MOVE_HOME + case 123: return KeyPress::endKey; // KEYCODE_MOVE_END + case 124: return KeyPress::insertKey; // KEYCODE_INSERT + case 131: return KeyPress::F1Key; // KEYCODE_F1 + case 132: return KeyPress::F2Key; // KEYCODE_F2 + case 133: return KeyPress::F3Key; // KEYCODE_F3 + case 134: return KeyPress::F4Key; // KEYCODE_F4 + case 135: return KeyPress::F5Key; // KEYCODE_F5 + case 136: return KeyPress::F6Key; // KEYCODE_F6 + case 137: return KeyPress::F7Key; // KEYCODE_F7 + case 138: return KeyPress::F8Key; // KEYCODE_F8 + case 139: return KeyPress::F9Key; // KEYCODE_F9 + case 140: return KeyPress::F10Key; // KEYCODE_F10 + case 141: return KeyPress::F11Key; // KEYCODE_F11 + case 142: return KeyPress::F12Key; // KEYCODE_F12 + case 144: return '0'; + case 145: return '1'; + case 146: return '2'; + case 147: return '3'; + case 148: return '4'; + case 149: return '5'; + case 150: return '6'; + case 151: return '7'; + case 152: return '8'; + case 153: return '9'; + case 154: return '/'; + case 155: return '*'; + case 156: return '-'; + case 157: return '+'; + case 158: return '.'; + case 159: return ','; + case 161: return '='; + case 162: return '('; + case 163: return ')'; + + default: return 0; + } +} + +static constexpr int translateAndroidKeyboardFlags (int javaFlags) noexcept +{ + constexpr int metaShiftOn = 0x1; + constexpr int metaAltOn = 0x02; + constexpr int metaCtrlOn = 0x1000; + + int flags = 0; + + if ((javaFlags & metaShiftOn) != 0) flags |= ModifierKeys::shiftModifier; + if ((javaFlags & metaAltOn) != 0) flags |= ModifierKeys::altModifier; + if ((javaFlags & metaCtrlOn) != 0) flags |= ModifierKeys::ctrlModifier; + + return flags; +} + //============================================================================== class AndroidComponentPeer : public ComponentPeer, private Timer @@ -689,9 +724,42 @@ class AndroidComponentPeer : public ComponentPeer, index); } - void handleKeyDownCallback (int k, int kc) + void handleAccessibilityHoverCallback (int command, Point sysPos, int64) + { + enum + { + TYPE_VIEW_HOVER_ENTER = 0x00000080, + TYPE_VIEW_HOVER_EXIT = 0x00000100, + + ACTION_HOVER_ENTER = 0x00000009, + ACTION_HOVER_MOVE = 0x00000007, + ACTION_HOVER_EXIT = 0x0000000a + }; + + if (auto* topHandler = component.getAccessibilityHandler()) + { + if (auto* virtualHandler = topHandler->getChildAt ((sysPos / scale).roundToInt())) + { + switch (command) + { + case ACTION_HOVER_ENTER: + case ACTION_HOVER_MOVE: + sendAccessibilityEventImpl (*virtualHandler, TYPE_VIEW_HOVER_ENTER, 0); + break; + + case ACTION_HOVER_EXIT: + sendAccessibilityEventImpl (*virtualHandler, TYPE_VIEW_HOVER_EXIT, 0); + break; + } + } + } + } + + void handleKeyDownCallback (int k, int kc, int kbFlags) { - handleKeyPress (k, static_cast (kc)); + ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withOnlyMouseButtons() + .withFlags (translateAndroidKeyboardFlags (kbFlags)); + handleKeyPress (translateAndroidKeyCode (k), static_cast (kc)); } void handleKeyUpCallback (int /*k*/, int /*kc*/) @@ -984,7 +1052,8 @@ class AndroidComponentPeer : public ComponentPeer, CALLBACK (handleMouseDownJni, "handleMouseDown", "(JIFFJ)V") \ CALLBACK (handleMouseDragJni, "handleMouseDrag", "(JIFFJ)V") \ CALLBACK (handleMouseUpJni, "handleMouseUp", "(JIFFJ)V") \ - CALLBACK (handleKeyDownJni, "handleKeyDown", "(JII)V") \ + CALLBACK (handleAccessibleHoverJni, "handleAccessibilityHover", "(JIFFJ)V") \ + CALLBACK (handleKeyDownJni, "handleKeyDown", "(JIII)V") \ CALLBACK (handleKeyUpJni, "handleKeyUp", "(JII)V") \ CALLBACK (handleBackButtonJni, "handleBackButton", "(J)V") \ CALLBACK (handleKeyboardHiddenJni, "handleKeyboardHidden", "(J)V") \ @@ -1004,9 +1073,10 @@ class AndroidComponentPeer : public ComponentPeer, static void JNICALL handleMouseDownJni (JNIEnv*, jobject /*view*/, jlong host, jint i, jfloat x, jfloat y, jlong time) { if (auto* myself = reinterpret_cast (host)) myself->handleMouseDownCallback (i, Point ((float) x, (float) y), (int64) time); } static void JNICALL handleMouseDragJni (JNIEnv*, jobject /*view*/, jlong host, jint i, jfloat x, jfloat y, jlong time) { if (auto* myself = reinterpret_cast (host)) myself->handleMouseDragCallback (i, Point ((float) x, (float) y), (int64) time); } static void JNICALL handleMouseUpJni (JNIEnv*, jobject /*view*/, jlong host, jint i, jfloat x, jfloat y, jlong time) { if (auto* myself = reinterpret_cast (host)) myself->handleMouseUpCallback (i, Point ((float) x, (float) y), (int64) time); } + static void JNICALL handleAccessibleHoverJni(JNIEnv*, jobject /*view*/, jlong host, jint c, jfloat x, jfloat y, jlong time) { if (auto* myself = reinterpret_cast (host)) myself->handleAccessibilityHoverCallback ((int) c, Point ((float) x, (float) y), (int64) time); } static void JNICALL viewSizeChangedJni (JNIEnv*, jobject /*view*/, jlong host) { if (auto* myself = reinterpret_cast (host)) myself->handleMovedOrResized(); } static void JNICALL focusChangedJni (JNIEnv*, jobject /*view*/, jlong host, jboolean hasFocus) { if (auto* myself = reinterpret_cast (host)) myself->handleFocusChangeCallback (hasFocus); } - static void JNICALL handleKeyDownJni (JNIEnv*, jobject /*view*/, jlong host, jint k, jint kc) { if (auto* myself = reinterpret_cast (host)) myself->handleKeyDownCallback ((int) k, (int) kc); } + static void JNICALL handleKeyDownJni (JNIEnv*, jobject /*view*/, jlong host, jint k, jint kc, jint kbFlags) { if (auto* myself = reinterpret_cast (host)) myself->handleKeyDownCallback ((int) k, (int) kc, (int) kbFlags); } static void JNICALL handleKeyUpJni (JNIEnv*, jobject /*view*/, jlong host, jint k, jint kc) { if (auto* myself = reinterpret_cast (host)) myself->handleKeyUpCallback ((int) k, (int) kc); } static void JNICALL handleBackButtonJni (JNIEnv*, jobject /*view*/, jlong host) { if (auto* myself = reinterpret_cast (host)) myself->handleBackButtonCallback(); } static void JNICALL handleKeyboardHiddenJni (JNIEnv*, jobject /*view*/, jlong host) { if (auto* myself = reinterpret_cast (host)) myself->handleKeyboardHiddenCallback(); } @@ -1933,80 +2003,80 @@ String SystemClipboard::getTextFromClipboard() } //============================================================================== -const int extendedKeyModifier = 0x10000; - -const int KeyPress::spaceKey = ' '; -const int KeyPress::returnKey = 66; -const int KeyPress::escapeKey = 4; -const int KeyPress::backspaceKey = 67; -const int KeyPress::leftKey = extendedKeyModifier + 1; -const int KeyPress::rightKey = extendedKeyModifier + 2; -const int KeyPress::upKey = extendedKeyModifier + 3; -const int KeyPress::downKey = extendedKeyModifier + 4; -const int KeyPress::pageUpKey = extendedKeyModifier + 5; -const int KeyPress::pageDownKey = extendedKeyModifier + 6; -const int KeyPress::endKey = extendedKeyModifier + 7; -const int KeyPress::homeKey = extendedKeyModifier + 8; -const int KeyPress::deleteKey = extendedKeyModifier + 9; -const int KeyPress::insertKey = -1; -const int KeyPress::tabKey = 61; -const int KeyPress::F1Key = extendedKeyModifier + 10; -const int KeyPress::F2Key = extendedKeyModifier + 11; -const int KeyPress::F3Key = extendedKeyModifier + 12; -const int KeyPress::F4Key = extendedKeyModifier + 13; -const int KeyPress::F5Key = extendedKeyModifier + 14; -const int KeyPress::F6Key = extendedKeyModifier + 16; -const int KeyPress::F7Key = extendedKeyModifier + 17; -const int KeyPress::F8Key = extendedKeyModifier + 18; -const int KeyPress::F9Key = extendedKeyModifier + 19; -const int KeyPress::F10Key = extendedKeyModifier + 20; -const int KeyPress::F11Key = extendedKeyModifier + 21; -const int KeyPress::F12Key = extendedKeyModifier + 22; -const int KeyPress::F13Key = extendedKeyModifier + 23; -const int KeyPress::F14Key = extendedKeyModifier + 24; -const int KeyPress::F15Key = extendedKeyModifier + 25; -const int KeyPress::F16Key = extendedKeyModifier + 26; -const int KeyPress::F17Key = extendedKeyModifier + 50; -const int KeyPress::F18Key = extendedKeyModifier + 51; -const int KeyPress::F19Key = extendedKeyModifier + 52; -const int KeyPress::F20Key = extendedKeyModifier + 53; -const int KeyPress::F21Key = extendedKeyModifier + 54; -const int KeyPress::F22Key = extendedKeyModifier + 55; -const int KeyPress::F23Key = extendedKeyModifier + 56; -const int KeyPress::F24Key = extendedKeyModifier + 57; -const int KeyPress::F25Key = extendedKeyModifier + 58; -const int KeyPress::F26Key = extendedKeyModifier + 59; -const int KeyPress::F27Key = extendedKeyModifier + 60; -const int KeyPress::F28Key = extendedKeyModifier + 61; -const int KeyPress::F29Key = extendedKeyModifier + 62; -const int KeyPress::F30Key = extendedKeyModifier + 63; -const int KeyPress::F31Key = extendedKeyModifier + 64; -const int KeyPress::F32Key = extendedKeyModifier + 65; -const int KeyPress::F33Key = extendedKeyModifier + 66; -const int KeyPress::F34Key = extendedKeyModifier + 67; -const int KeyPress::F35Key = extendedKeyModifier + 68; -const int KeyPress::numberPad0 = extendedKeyModifier + 27; -const int KeyPress::numberPad1 = extendedKeyModifier + 28; -const int KeyPress::numberPad2 = extendedKeyModifier + 29; -const int KeyPress::numberPad3 = extendedKeyModifier + 30; -const int KeyPress::numberPad4 = extendedKeyModifier + 31; -const int KeyPress::numberPad5 = extendedKeyModifier + 32; -const int KeyPress::numberPad6 = extendedKeyModifier + 33; -const int KeyPress::numberPad7 = extendedKeyModifier + 34; -const int KeyPress::numberPad8 = extendedKeyModifier + 35; -const int KeyPress::numberPad9 = extendedKeyModifier + 36; -const int KeyPress::numberPadAdd = extendedKeyModifier + 37; -const int KeyPress::numberPadSubtract = extendedKeyModifier + 38; -const int KeyPress::numberPadMultiply = extendedKeyModifier + 39; -const int KeyPress::numberPadDivide = extendedKeyModifier + 40; -const int KeyPress::numberPadSeparator = extendedKeyModifier + 41; -const int KeyPress::numberPadDecimalPoint = extendedKeyModifier + 42; -const int KeyPress::numberPadEquals = extendedKeyModifier + 43; -const int KeyPress::numberPadDelete = extendedKeyModifier + 44; -const int KeyPress::playKey = extendedKeyModifier + 45; -const int KeyPress::stopKey = extendedKeyModifier + 46; -const int KeyPress::fastForwardKey = extendedKeyModifier + 47; -const int KeyPress::rewindKey = extendedKeyModifier + 48; +constexpr int extendedKeyModifier = 0x10000; + +const int KeyPress::spaceKey = ' '; +const int KeyPress::returnKey = extendedKeyModifier + 2; +const int KeyPress::escapeKey = extendedKeyModifier + 3; +const int KeyPress::backspaceKey = extendedKeyModifier + 4; +const int KeyPress::leftKey = extendedKeyModifier + 5; +const int KeyPress::rightKey = extendedKeyModifier + 6; +const int KeyPress::upKey = extendedKeyModifier + 7; +const int KeyPress::downKey = extendedKeyModifier + 8; +const int KeyPress::pageUpKey = extendedKeyModifier + 9; +const int KeyPress::pageDownKey = extendedKeyModifier + 10; +const int KeyPress::endKey = extendedKeyModifier + 11; +const int KeyPress::homeKey = extendedKeyModifier + 12; +const int KeyPress::deleteKey = extendedKeyModifier + 13; +const int KeyPress::insertKey = extendedKeyModifier + 14; +const int KeyPress::tabKey = extendedKeyModifier + 15; +const int KeyPress::F1Key = extendedKeyModifier + 16; +const int KeyPress::F2Key = extendedKeyModifier + 17; +const int KeyPress::F3Key = extendedKeyModifier + 18; +const int KeyPress::F4Key = extendedKeyModifier + 19; +const int KeyPress::F5Key = extendedKeyModifier + 20; +const int KeyPress::F6Key = extendedKeyModifier + 21; +const int KeyPress::F7Key = extendedKeyModifier + 22; +const int KeyPress::F8Key = extendedKeyModifier + 23; +const int KeyPress::F9Key = extendedKeyModifier + 24; +const int KeyPress::F10Key = extendedKeyModifier + 25; +const int KeyPress::F11Key = extendedKeyModifier + 26; +const int KeyPress::F12Key = extendedKeyModifier + 27; +const int KeyPress::F13Key = extendedKeyModifier + 28; +const int KeyPress::F14Key = extendedKeyModifier + 29; +const int KeyPress::F15Key = extendedKeyModifier + 30; +const int KeyPress::F16Key = extendedKeyModifier + 31; +const int KeyPress::F17Key = extendedKeyModifier + 32; +const int KeyPress::F18Key = extendedKeyModifier + 33; +const int KeyPress::F19Key = extendedKeyModifier + 34; +const int KeyPress::F20Key = extendedKeyModifier + 35; +const int KeyPress::F21Key = extendedKeyModifier + 36; +const int KeyPress::F22Key = extendedKeyModifier + 37; +const int KeyPress::F23Key = extendedKeyModifier + 38; +const int KeyPress::F24Key = extendedKeyModifier + 39; +const int KeyPress::F25Key = extendedKeyModifier + 40; +const int KeyPress::F26Key = extendedKeyModifier + 41; +const int KeyPress::F27Key = extendedKeyModifier + 42; +const int KeyPress::F28Key = extendedKeyModifier + 43; +const int KeyPress::F29Key = extendedKeyModifier + 44; +const int KeyPress::F30Key = extendedKeyModifier + 45; +const int KeyPress::F31Key = extendedKeyModifier + 46; +const int KeyPress::F32Key = extendedKeyModifier + 47; +const int KeyPress::F33Key = extendedKeyModifier + 48; +const int KeyPress::F34Key = extendedKeyModifier + 49; +const int KeyPress::F35Key = extendedKeyModifier + 50; +const int KeyPress::numberPad0 = extendedKeyModifier + 51; +const int KeyPress::numberPad1 = extendedKeyModifier + 52; +const int KeyPress::numberPad2 = extendedKeyModifier + 53; +const int KeyPress::numberPad3 = extendedKeyModifier + 54; +const int KeyPress::numberPad4 = extendedKeyModifier + 55; +const int KeyPress::numberPad5 = extendedKeyModifier + 56; +const int KeyPress::numberPad6 = extendedKeyModifier + 57; +const int KeyPress::numberPad7 = extendedKeyModifier + 58; +const int KeyPress::numberPad8 = extendedKeyModifier + 59; +const int KeyPress::numberPad9 = extendedKeyModifier + 60; +const int KeyPress::numberPadAdd = extendedKeyModifier + 61; +const int KeyPress::numberPadSubtract = extendedKeyModifier + 62; +const int KeyPress::numberPadMultiply = extendedKeyModifier + 63; +const int KeyPress::numberPadDivide = extendedKeyModifier + 64; +const int KeyPress::numberPadSeparator = extendedKeyModifier + 65; +const int KeyPress::numberPadDecimalPoint = extendedKeyModifier + 66; +const int KeyPress::numberPadEquals = extendedKeyModifier + 67; +const int KeyPress::numberPadDelete = extendedKeyModifier + 68; +const int KeyPress::playKey = extendedKeyModifier + 69; +const int KeyPress::stopKey = extendedKeyModifier + 70; +const int KeyPress::fastForwardKey = extendedKeyModifier + 71; +const int KeyPress::rewindKey = extendedKeyModifier + 72; //============================================================================== #ifdef JUCE_PUSH_NOTIFICATIONS_ACTIVITY diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_common_MimeTypes.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_common_MimeTypes.cpp index 2cd7c35d5..726fb4b6e 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_common_MimeTypes.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_common_MimeTypes.cpp @@ -434,7 +434,7 @@ MimeTypeTableEntry MimeTypeTableEntry::table[641] = {"psd", "application/octet-stream"}, {"pvu", "paleovu/x-pv"}, {"pwz", "application/vnd.ms-powerpoint"}, - {"py", "text/x-script.phyton"}, + {"py", "text/x-script.python"}, {"pyc", "application/x-bytecode.python"}, {"qcp", "audio/vnd.qcelp"}, {"qd3", "x-world/x-3dmf"}, diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_FileChooser.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_FileChooser.mm index c3dea9a44..579cc9b48 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_FileChooser.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_FileChooser.mm @@ -131,6 +131,16 @@ Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). ~Native() override { exitModalState (0); + + // Our old peer may not have received a becomeFirstResponder call at this point, + // so the static currentlyFocusedPeer may be null. + // We'll try to find an appropriate peer to focus. + + for (auto i = 0; i < ComponentPeer::getNumPeers(); ++i) + if (auto* p = ComponentPeer::getPeer (i)) + if (p != getPeer()) + if (auto* view = (UIView*) p->getNativeHandle()) + [view becomeFirstResponder]; } void launch() override @@ -300,9 +310,8 @@ void didPickDocumentAtURL (NSURL* url) void pickerWasCancelled() { cancelPendingUpdate(); - owner.finished ({}); - exitModalState (0); + // Calling owner.finished will delete this Pimpl instance, so don't call any more member functions here! } //============================================================================== diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm index 301ace17c..362a7e023 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm @@ -283,6 +283,11 @@ static int64 getMouseTime (UIEvent* e) noexcept static MultiTouchMapper currentTouches; private: + void appStyleChanged() override + { + [controller setNeedsStatusBarAppearanceUpdate]; + } + //============================================================================== class AsyncRepaintMessage : public CallbackMessage { @@ -395,6 +400,24 @@ - (BOOL) prefersHomeIndicatorAutoHidden - (UIStatusBarStyle) preferredStatusBarStyle { + #if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 + if (@available (iOS 13.0, *)) + { + if (auto* peer = getViewPeer (self)) + { + switch (peer->getAppStyle()) + { + case ComponentPeer::Style::automatic: + return UIStatusBarStyleDefault; + case ComponentPeer::Style::light: + return UIStatusBarStyleDarkContent; + case ComponentPeer::Style::dark: + return UIStatusBarStyleLightContent; + } + } + } + #endif + return UIStatusBarStyleDefault; } @@ -680,8 +703,13 @@ - (void) becomeKeyWindow Desktop::getInstance().addFocusChangeListener (this); } +static UIViewComponentPeer* currentlyFocusedPeer = nullptr; + UIViewComponentPeer::~UIViewComponentPeer() { + if (currentlyFocusedPeer == this) + currentlyFocusedPeer = nullptr; + currentTouches.deleteAllTouchesForPeer (this); Desktop::getInstance().removeFocusChangeListener (this); @@ -1007,8 +1035,6 @@ static float getTouchForce (UITouch* touch) noexcept #endif //============================================================================== -static UIViewComponentPeer* currentlyFocusedPeer = nullptr; - void UIViewComponentPeer::viewFocusGain() { if (currentlyFocusedPeer != this) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index e8c9991f7..5ffe3b2cd 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -40,6 +40,66 @@ - (float)deviceDeltaY; namespace juce { +//============================================================================== +static constexpr int translateVirtualToAsciiKeyCode (int keyCode) noexcept +{ + switch (keyCode) + { + // The virtual keycodes are from HIToolbox/Events.h + case 0x00: return 'A'; + case 0x01: return 'S'; + case 0x02: return 'D'; + case 0x03: return 'F'; + case 0x04: return 'H'; + case 0x05: return 'G'; + case 0x06: return 'Z'; + case 0x07: return 'X'; + case 0x08: return 'C'; + case 0x09: return 'V'; + case 0x0B: return 'B'; + case 0x0C: return 'Q'; + case 0x0D: return 'W'; + case 0x0E: return 'E'; + case 0x0F: return 'R'; + case 0x10: return 'Y'; + case 0x11: return 'T'; + case 0x12: return '1'; + case 0x13: return '2'; + case 0x14: return '3'; + case 0x15: return '4'; + case 0x16: return '6'; + case 0x17: return '5'; + case 0x18: return '='; // kVK_ANSI_Equal + case 0x19: return '9'; + case 0x1A: return '7'; + case 0x1B: return '-'; // kVK_ANSI_Minus + case 0x1C: return '8'; + case 0x1D: return '0'; + case 0x1E: return ']'; // kVK_ANSI_RightBracket + case 0x1F: return 'O'; + case 0x20: return 'U'; + case 0x21: return '['; // kVK_ANSI_LeftBracket + case 0x22: return 'I'; + case 0x23: return 'P'; + case 0x25: return 'L'; + case 0x26: return 'J'; + case 0x27: return '"'; // kVK_ANSI_Quote + case 0x28: return 'K'; + case 0x29: return ';'; // kVK_ANSI_Semicolon + case 0x2A: return '\\'; // kVK_ANSI_Backslash + case 0x2B: return ','; // kVK_ANSI_Comma + case 0x2C: return '/'; // kVK_ANSI_Slash + case 0x2D: return 'N'; + case 0x2E: return 'M'; + case 0x2F: return '.'; // kVK_ANSI_Period + case 0x32: return '`'; // kVK_ANSI_Grave + + default: return keyCode; + } +} + +constexpr int extendedKeyModifier = 0x30000; + //============================================================================== class NSViewComponentPeer : public ComponentPeer, private Timer @@ -137,8 +197,7 @@ - (float)deviceDeltaY; [window setExcludedFromWindowsMenu: (windowStyleFlags & windowIsTemporary) != 0]; [window setIgnoresMouseEvents: (windowStyleFlags & windowIgnoresMouseClicks) != 0]; - if ((windowStyleFlags & windowHasMaximiseButton) == windowHasMaximiseButton) - [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; + setCollectionBehaviour (false); [window setRestorable: NO]; @@ -347,22 +406,42 @@ bool isMinimised() const override return [window isMiniaturized]; } + NSWindowCollectionBehavior getCollectionBehavior (bool forceFullScreen) const + { + if (forceFullScreen) + return NSWindowCollectionBehaviorFullScreenPrimary; + + // Some SDK versions don't define NSWindowCollectionBehaviorFullScreenNone + constexpr auto fullScreenNone = (NSUInteger) (1 << 9); + + return (getStyleFlags() & (windowHasMaximiseButton | windowIsResizable)) == (windowHasMaximiseButton | windowIsResizable) + ? NSWindowCollectionBehaviorFullScreenPrimary + : fullScreenNone; + } + + void setCollectionBehaviour (bool forceFullScreen) const + { + [window setCollectionBehavior: getCollectionBehavior (forceFullScreen)]; + } + void setFullScreen (bool shouldBeFullScreen) override { - if (! isSharedWindow) - { - if (isMinimised()) - setMinimised (false); + if (isSharedWindow) + return; - if (hasNativeTitleBar()) - { - if (shouldBeFullScreen != isFullScreen()) - [window toggleFullScreen: nil]; - } - else - { - [window zoom: nil]; - } + setCollectionBehaviour (shouldBeFullScreen); + + if (isMinimised()) + setMinimised (false); + + if (hasNativeTitleBar()) + { + if (shouldBeFullScreen != isFullScreen()) + [window toggleFullScreen: nil]; + } + else + { + [window zoom: nil]; } } @@ -1259,6 +1338,19 @@ static int getKeyCodeFromEvent (NSEvent* ev) else keyCode = (int) CharacterFunctions::toUpperCase ((juce_wchar) keyCode); + // The purpose of the keyCode is to provide information about non-printing characters to facilitate + // keyboard control over the application. + // + // So when keyCode is decoded as a printing character outside the ASCII range we need to replace it. + // This holds when the keyCode is larger than 0xff and not part one of the two MacOS specific + // non-printing ranges. + if (keyCode > 0xff + && ! (keyCode >= NSUpArrowFunctionKey && keyCode <= NSModeSwitchFunctionKey) + && ! (keyCode >= extendedKeyModifier)) + { + keyCode = translateVirtualToAsciiKeyCode ([ev keyCode]); + } + if (([ev modifierFlags] & NSEventModifierFlagNumericPad) != 0) { const int numPadConversions[] = { '0', KeyPress::numberPad0, '1', KeyPress::numberPad1, @@ -1458,6 +1550,15 @@ void grabFocus() override void textInputRequired (Point, TextInputTarget&) override {} + void dismissPendingTextInput() override + { + stringBeingComposed.clear(); + const auto* inputContext = [NSTextInputContext currentInputContext]; + + if (inputContext != nil) + [inputContext discardMarkedText]; + } + void resetWindowPresentation() { if (hasNativeTitleBar()) @@ -1467,6 +1568,7 @@ void resetWindowPresentation() } [NSApp setPresentationOptions: NSApplicationPresentationDefault]; + setCollectionBehaviour (isFullScreen()); } void setHasChangedSinceSaved (bool b) override @@ -2215,6 +2317,8 @@ static void callOnOwner (id self, Func&& func, Args&&... args) addMethod (@selector (accessibilityRole), getAccessibilityRole); addMethod (@selector (accessibilitySubrole), getAccessibilitySubrole); + addMethod (@selector (keyDown:), keyDown); + addMethod (@selector (window:shouldDragDocumentWithEvent:from:withPasteboard:), shouldAllowIconDrag); addProtocol (@protocol (NSWindowDelegate)); @@ -2226,23 +2330,52 @@ static void callOnOwner (id self, Func&& func, Args&&... args) //============================================================================== static BOOL isFlipped (id, SEL) { return true; } - static NSRect windowWillUseStandardFrame (id self, SEL, NSWindow*, NSRect) + // Key events will be processed by the peer's component. + // If the component is unable to use the event, it will be re-sent + // to performKeyEquivalent. + // performKeyEquivalent will send the event to the view's superclass, + // which will try passing the event to the main menu. + // If the event still hasn't been processed, it will be passed to the + // next responder in the chain, which will be the NSWindow for a peer + // that is on the desktop. + // If the NSWindow still doesn't handle the event, the Apple docs imply + // that the event should be sent to the NSApp for processing, but this + // doesn't seem to happen for keyDown events. + // Instead, the NSWindow attempts to process the event, fails, and + // triggers an annoying NSBeep. + // Overriding keyDown to "handle" the event seems to suppress the beep. + static void keyDown (id, SEL, NSEvent* ev) + { + ignoreUnused (ev); + + #if JUCE_DEBUG_UNHANDLED_KEYPRESSES + DBG ("unhandled key down event with keycode: " << [ev keyCode]); + #endif + } + + static NSRect windowWillUseStandardFrame (id self, SEL, NSWindow*, NSRect r) { if (auto* owner = getOwner (self)) { if (auto* constrainer = owner->getConstrainer()) { - return flippedScreenRect (makeNSRect (owner->getFrameSize().addedTo (owner->getComponent().getScreenBounds() - .withWidth (constrainer->getMaximumWidth()) - .withHeight (constrainer->getMaximumHeight())))); + const auto originalBounds = owner->getFrameSize().addedTo (owner->getComponent().getScreenBounds()).toFloat(); + const auto expanded = originalBounds.withWidth ((float) constrainer->getMaximumWidth()) + .withHeight ((float) constrainer->getMaximumHeight()); + const auto constrained = expanded.constrainedWithin (convertToRectFloat (flippedScreenRect (r))); + return flippedScreenRect (makeNSRect (constrained)); } } - return makeNSRect (Rectangle (10000, 10000)); + return r; } - static BOOL windowShouldZoomToFrame (id, SEL, NSWindow* window, NSRect frame) + static BOOL windowShouldZoomToFrame (id self, SEL, NSWindow* window, NSRect frame) { + if (auto* owner = getOwner (self)) + if (owner->hasNativeTitleBar() && (owner->getStyleFlags() & ComponentPeer::windowIsResizable) == 0) + return NO; + return convertToRectFloat ([window frame]).withZeroOrigin() != convertToRectFloat (frame).withZeroOrigin(); } @@ -2463,7 +2596,7 @@ static NSAccessibilityRole getAccessibilitySubrole (id self, SEL) else if (! shouldBeEnabled) [NSApp setPresentationOptions: NSApplicationPresentationDefault]; - [peer->window toggleFullScreen: nil]; + peer->setFullScreen (true); } else { @@ -2542,27 +2675,27 @@ static NSAccessibilityRole getAccessibilitySubrole (id self, SEL) const int KeyPress::F34Key = NSF34FunctionKey; const int KeyPress::F35Key = NSF35FunctionKey; -const int KeyPress::numberPad0 = 0x30020; -const int KeyPress::numberPad1 = 0x30021; -const int KeyPress::numberPad2 = 0x30022; -const int KeyPress::numberPad3 = 0x30023; -const int KeyPress::numberPad4 = 0x30024; -const int KeyPress::numberPad5 = 0x30025; -const int KeyPress::numberPad6 = 0x30026; -const int KeyPress::numberPad7 = 0x30027; -const int KeyPress::numberPad8 = 0x30028; -const int KeyPress::numberPad9 = 0x30029; -const int KeyPress::numberPadAdd = 0x3002a; -const int KeyPress::numberPadSubtract = 0x3002b; -const int KeyPress::numberPadMultiply = 0x3002c; -const int KeyPress::numberPadDivide = 0x3002d; -const int KeyPress::numberPadSeparator = 0x3002e; -const int KeyPress::numberPadDecimalPoint = 0x3002f; -const int KeyPress::numberPadEquals = 0x30030; -const int KeyPress::numberPadDelete = 0x30031; -const int KeyPress::playKey = 0x30000; -const int KeyPress::stopKey = 0x30001; -const int KeyPress::fastForwardKey = 0x30002; -const int KeyPress::rewindKey = 0x30003; +const int KeyPress::numberPad0 = extendedKeyModifier + 0x20; +const int KeyPress::numberPad1 = extendedKeyModifier + 0x21; +const int KeyPress::numberPad2 = extendedKeyModifier + 0x22; +const int KeyPress::numberPad3 = extendedKeyModifier + 0x23; +const int KeyPress::numberPad4 = extendedKeyModifier + 0x24; +const int KeyPress::numberPad5 = extendedKeyModifier + 0x25; +const int KeyPress::numberPad6 = extendedKeyModifier + 0x26; +const int KeyPress::numberPad7 = extendedKeyModifier + 0x27; +const int KeyPress::numberPad8 = extendedKeyModifier + 0x28; +const int KeyPress::numberPad9 = extendedKeyModifier + 0x29; +const int KeyPress::numberPadAdd = extendedKeyModifier + 0x2a; +const int KeyPress::numberPadSubtract = extendedKeyModifier + 0x2b; +const int KeyPress::numberPadMultiply = extendedKeyModifier + 0x2c; +const int KeyPress::numberPadDivide = extendedKeyModifier + 0x2d; +const int KeyPress::numberPadSeparator = extendedKeyModifier + 0x2e; +const int KeyPress::numberPadDecimalPoint = extendedKeyModifier + 0x2f; +const int KeyPress::numberPadEquals = extendedKeyModifier + 0x30; +const int KeyPress::numberPadDelete = extendedKeyModifier + 0x31; +const int KeyPress::playKey = extendedKeyModifier + 0x00; +const int KeyPress::stopKey = extendedKeyModifier + 0x01; +const int KeyPress::fastForwardKey = extendedKeyModifier + 0x02; +const int KeyPress::rewindKey = extendedKeyModifier + 0x03; } // namespace juce diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp index d8a6bbd49..7f2597f4d 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp @@ -23,10 +23,6 @@ ============================================================================== */ -#if JUCE_MINGW -LWSTDAPI IUnknown_GetWindow (IUnknown* punk, HWND* phwnd); -#endif - namespace juce { @@ -253,7 +249,9 @@ class Win32NativeFileChooser : private Thread JUCE_COMRESULT updateHwnd (IFileDialog* d) { HWND hwnd = nullptr; - IUnknown_GetWindow (d, &hwnd); + + if (auto window = ComSmartPtr { d }.getInterface()) + window->GetWindow (&hwnd); ScopedLock lock (owner.deletingDialog); @@ -501,13 +499,11 @@ class Win32NativeFileChooser : private Thread const Remover remover (*this); - #if ! JUCE_MINGW if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista && customComponent == nullptr) { return openDialogVistaAndUp (async); } - #endif return openDialogPreVista (async); } @@ -781,9 +777,9 @@ class FileChooser::Native : public std::enable_shared_from_this, public FileChooser::Pimpl { public: - Native (FileChooser& fileChooser, int flags, FilePreviewComponent* previewComp) + Native (FileChooser& fileChooser, int flagsIn, FilePreviewComponent* previewComp) : owner (fileChooser), - nativeFileChooser (std::make_unique (this, flags, previewComp, fileChooser.startingFile, + nativeFileChooser (std::make_unique (this, flagsIn, previewComp, fileChooser.startingFile, fileChooser.title, fileChooser.filters)) { auto mainMon = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index b5757476a..ea546ad48 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -1133,7 +1133,6 @@ namespace IconConverters if (auto* dc = ::CreateCompatibleDC (deviceContext.dc)) { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfour-char-constants") BITMAPV5HEADER header = {}; header.bV5Size = sizeof (BITMAPV5HEADER); header.bV5Width = bm.bmWidth; @@ -1145,15 +1144,8 @@ namespace IconConverters header.bV5GreenMask = 0x0000FF00; header.bV5BlueMask = 0x000000FF; header.bV5AlphaMask = 0xFF000000; - - #if JUCE_MINGW - header.bV5CSType = 'Win '; - #else - header.bV5CSType = LCS_WINDOWS_COLOR_SPACE; - #endif - + header.bV5CSType = 0x57696E20; // 'Win ' header.bV5Intent = LCS_GM_IMAGES; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE uint32* bitmapImageData = nullptr; @@ -1244,6 +1236,15 @@ JUCE_IUNKNOWNCLASS (ITipInvocation, "37c994e7-432b-4834-a2f7-dce1f13b834b") JUCE_COMCALL Toggle (HWND) = 0; }; +} // namespace juce + +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL (juce::ITipInvocation, 0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b) +#endif + +namespace juce +{ + struct OnScreenKeyboard : public DeletedAtShutdown, private Timer { @@ -1358,7 +1359,15 @@ JUCE_COMCLASS (IUIViewSettings, "c63657f6-8850-470d-88f8-455e16ea2c26") : publi JUCE_COMCALL GetUserInteractionMode (UserInteractionMode*) = 0; }; +} // namespace juce +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL (juce::IUIViewSettingsInterop, 0x3694dbf9, 0x8f68, 0x44be, 0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6) +__CRT_UUID_DECL (juce::IUIViewSettings, 0xc63657f6, 0x8850, 0x470d, 0x88, 0xf8, 0x45, 0x5e, 0x16, 0xea, 0x2c, 0x26) +#endif + +namespace juce +{ struct UWPUIViewSettings { UWPUIViewSettings() @@ -2443,7 +2452,7 @@ class HWNDComponentPeer : public ComponentPeer, if (! component.isCurrentlyModal() && (styleFlags & windowHasDropShadow) != 0 && ((! hasTitleBar()) || SystemStats::getOperatingSystemType() < SystemStats::WinVista)) { - shadower.reset (component.getLookAndFeel().createDropShadowerForComponent (&component)); + shadower = component.getLookAndFeel().createDropShadowerForComponent (component); if (shadower != nullptr) shadower->setOwner (&component); @@ -4528,8 +4537,8 @@ class PreVistaMessageBox : public WindowsMessageBoxBase public: PreVistaMessageBox (const MessageBoxOptions& opts, UINT extraFlags, - std::unique_ptr&& callback) - : WindowsMessageBoxBase (opts.getAssociatedComponent(), std::move (callback)), + std::unique_ptr&& cb) + : WindowsMessageBoxBase (opts.getAssociatedComponent(), std::move (cb)), flags (extraFlags | getMessageBoxFlags (opts.getIconType())), title (opts.getTitle()), message (opts.getMessage()) { @@ -4581,8 +4590,8 @@ class WindowsTaskDialog : public WindowsMessageBoxBase { public: WindowsTaskDialog (const MessageBoxOptions& opts, - std::unique_ptr&& callback) - : WindowsMessageBoxBase (opts.getAssociatedComponent(), std::move (callback)), + std::unique_ptr&& cb) + : WindowsMessageBoxBase (opts.getAssociatedComponent(), std::move (cb)), iconType (opts.getIconType()), title (opts.getTitle()), message (opts.getMessage()), button1 (opts.getButtonText (0)), button2 (opts.getButtonText (1)), button3 (opts.getButtonText (2)) @@ -4594,6 +4603,7 @@ class WindowsTaskDialog : public WindowsMessageBoxBase TASKDIALOGCONFIG config{}; config.cbSize = sizeof (config); + config.hwndParent = getParentHWND(); config.pszWindowTitle = title.toWideCharPointer(); config.pszContent = message.toWideCharPointer(); config.hInstance = (HINSTANCE) Process::getCurrentModuleInstanceHandle(); @@ -5199,17 +5209,18 @@ class MouseCursor::PlatformSpecificHandle if (iter != cursorsBySize.end()) return iter->second; - const auto img = info.image.getImage(); - const auto imgW = jmax (1, img.getWidth()); - const auto imgH = jmax (1, img.getHeight()); - + const auto logicalSize = info.image.getScaledBounds(); const auto scale = (float) size / (float) unityCursorSize; - const auto scaleToUse = scale * jmin (1.0f, jmin ((float) unityCursorSize / (float) imgW, - (float) unityCursorSize / (float) imgH)) / info.image.getScale(); - const auto rescaled = img.rescaled (roundToInt (scaleToUse * (float) imgW), - roundToInt (scaleToUse * (float) imgH)); - const auto hx = jlimit (0, rescaled.getWidth(), roundToInt (scaleToUse * (float) info.hotspot.x)); - const auto hy = jlimit (0, rescaled.getHeight(), roundToInt (scaleToUse * (float) info.hotspot.y)); + const auto physicalSize = logicalSize * scale; + + const auto& image = info.image.getImage(); + const auto rescaled = image.rescaled (roundToInt ((float) physicalSize.getWidth()), + roundToInt ((float) physicalSize.getHeight())); + + const auto effectiveScale = rescaled.getWidth() / logicalSize.getWidth(); + + const auto hx = jlimit (0, rescaled.getWidth(), roundToInt ((float) info.hotspot.x * effectiveScale)); + const auto hy = jlimit (0, rescaled.getHeight(), roundToInt ((float) info.hotspot.y * effectiveScale)); return cursorsBySize.emplace (size, IconConverters::createHICONFromImage (rescaled, false, hx, hy)).first->second; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp index 48d0a5187..548b17ff7 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp @@ -179,15 +179,21 @@ XWindowSystemUtilities::GetXProperty::~GetXProperty() } //============================================================================== -XWindowSystemUtilities::XSettings::XSettings (::Display* d) - : display (d) +std::unique_ptr XWindowSystemUtilities::XSettings::createXSettings (::Display* d) { - settingsAtom = Atoms::getCreating (display, "_XSETTINGS_SETTINGS"); + const auto settingsAtom = Atoms::getCreating (d, "_XSETTINGS_SETTINGS"); + const auto settingsWindow = X11Symbols::getInstance()->xGetSelectionOwner (d, + Atoms::getCreating (d, "_XSETTINGS_S0")); - settingsWindow = X11Symbols::getInstance()->xGetSelectionOwner (display, - Atoms::getCreating (display, "_XSETTINGS_S0")); + if (settingsWindow == None) + return {}; + + return rawToUniquePtr (new XWindowSystemUtilities::XSettings (d, settingsWindow, settingsAtom)); +} - jassert (settingsWindow != None); +XWindowSystemUtilities::XSettings::XSettings (::Display* d, ::Window settingsWindowIn, Atom settingsAtomIn) + : display (d), settingsWindow (settingsWindowIn), settingsAtom (settingsAtomIn) +{ update(); } @@ -1345,18 +1351,22 @@ namespace ClipboardHelpers { auto localContent = XWindowSystem::getInstance()->getLocalClipboardContent(); - // translate to utf8 - numDataItems = localContent.getNumBytesAsUTF8() + 1; - data.calloc (numDataItems); - localContent.copyToUTF8 (data, numDataItems); - propertyFormat = 8; // bits/item + // Translate to utf8 + numDataItems = localContent.getNumBytesAsUTF8(); + auto numBytesRequiredToStore = numDataItems + 1; + data.calloc (numBytesRequiredToStore); + localContent.copyToUTF8 (data, numBytesRequiredToStore); + propertyFormat = 8; // bits per item } else if (evt.target == atoms.targets) { - // another application wants to know what we are able to send + // Another application wants to know what we are able to send + numDataItems = 2; - propertyFormat = 32; // atoms are 32-bit - data.calloc (numDataItems * 4); + data.calloc (numDataItems * sizeof (Atom)); + + // Atoms are flagged as 32-bit irrespective of sizeof (Atom) + propertyFormat = 32; auto* dataAtoms = unalignedPointerCast (data.getData()); @@ -1380,7 +1390,7 @@ namespace ClipboardHelpers { X11Symbols::getInstance()->xChangeProperty (evt.display, evt.requestor, evt.property, evt.target, - propertyFormat /* 8 or 32 */, PropModeReplace, + propertyFormat, PropModeReplace, reinterpret_cast (data.getData()), (int) numDataItems); reply.property = evt.property; // " == success" } @@ -3030,11 +3040,12 @@ long XWindowSystem::getUserTime (::Window windowH) const void XWindowSystem::initialiseXSettings() { - xSettings = std::make_unique (display); + xSettings = XWindowSystemUtilities::XSettings::createXSettings (display); - X11Symbols::getInstance()->xSelectInput (display, - xSettings->getSettingsWindow(), - StructureNotifyMask | PropertyChangeMask); + if (xSettings != nullptr) + X11Symbols::getInstance()->xSelectInput (display, + xSettings->getSettingsWindow(), + StructureNotifyMask | PropertyChangeMask); } XWindowSystem::DisplayVisuals::DisplayVisuals (::Display* xDisplay) diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h index 1dee48047..15befd2b6 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h @@ -130,7 +130,7 @@ namespace XWindowSystemUtilities class XSettings { public: - explicit XSettings (::Display*); + static std::unique_ptr createXSettings (::Display*); //============================================================================== void update(); @@ -158,6 +158,8 @@ namespace XWindowSystemUtilities std::unordered_map settings; ListenerList listeners; + XSettings (::Display*, Atom, ::Window); + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XSettings) }; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.h b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.h index 3443b6b51..b7d4af46d 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.h @@ -889,7 +889,7 @@ class JUCE_API Slider : public Component, //============================================================================== /** An RAII class for sending slider listener drag messages. - This is useful if you are programatically updating the slider's value and want + This is useful if you are programmatically updating the slider's value and want to imitate a mouse event, for example in a custom AccessibilityHandler. @see Slider::Listener diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp index 33328e535..27861ab8b 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TextEditor.cpp @@ -1836,6 +1836,9 @@ void TextEditor::mouseDown (const MouseEvent& e) { moveCaretTo (getTextIndexAt (e.x, e.y), e.mods.isShiftDown()); + + if (auto* peer = getPeer()) + peer->dismissPendingTextInput(); } else { @@ -1956,6 +1959,10 @@ bool TextEditor::moveCaretWithTransaction (const int newPos, const bool selectin { newTransaction(); moveCaretTo (newPos, selecting); + + if (auto* peer = getPeer()) + peer->dismissPendingTextInput(); + return true; } diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp index c29c6d658..24a1b9c2a 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -313,6 +313,9 @@ class TreeView::ContentComponent : public Component, if (iter != itemComponents.end()) { + if (itemUnderMouse == iter->get()) + itemUnderMouse = nullptr; + if (isMouseDraggingInChildComp (*(iter->get()))) owner.hideDragHighlight(); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h index ffe8f2ccb..4aeb67e95 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h @@ -418,6 +418,34 @@ class JUCE_API ComponentPeer */ virtual void setHasChangedSinceSaved (bool) {} + + enum class Style + { + /** A style that matches the system-wide style. */ + automatic, + + /** A light style, which will probably use dark text on a light background. */ + light, + + /** A dark style, which will probably use light text on a dark background. */ + dark + }; + + /** On operating systems that support it, this will update the style of this + peer as requested. + + Note that this will not update the theme system-wide. This will only + update UI elements so that they display appropriately for this peer! + */ + void setAppStyle (Style s) + { + if (std::exchange (style, s) != style) + appStyleChanged(); + } + + /** Returns the style requested for this app. */ + Style getAppStyle() const { return style; } + protected: //============================================================================== static void forceDisplayUpdate(); @@ -428,9 +456,12 @@ class JUCE_API ComponentPeer ComponentBoundsConstrainer* constrainer = nullptr; static std::function getNativeRealtimeModifiers; ListenerList scaleFactorListeners; + Style style = Style::automatic; private: //============================================================================== + virtual void appStyleChanged() {} + Component* getTargetForKeyPress(); WeakReference lastFocusedComponent, dragAndDropTargetComponent; diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp index 03f7abdff..bd9e2a5ef 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp @@ -71,13 +71,13 @@ void TooltipWindow::mouseEnter (const MouseEvent& e) void TooltipWindow::mouseDown (const MouseEvent&) { if (isVisible()) - dismissalMouseEventOccured = true; + dismissalMouseEventOccurred = true; } void TooltipWindow::mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) { if (isVisible()) - dismissalMouseEventOccured = true; + dismissalMouseEventOccurred = true; } void TooltipWindow::updatePosition (const String& tip, Point pos, Rectangle parentArea) @@ -145,7 +145,7 @@ void TooltipWindow::displayTipInternal (Point screenPos, const String& tip, toFront (false); manuallyShownTip = shownManually == ShownManually::yes ? tip : String(); - dismissalMouseEventOccured = false; + dismissalMouseEventOccurred = false; } } @@ -168,7 +168,7 @@ void TooltipWindow::hideTip() { tipShowing = {}; manuallyShownTip = {}; - dismissalMouseEventOccured = false; + dismissalMouseEventOccurred = false; removeFromDesktop(); setVisible (false); @@ -201,7 +201,7 @@ void TooltipWindow::timerCallback() if (manuallyShownTip.isNotEmpty()) { - if (dismissalMouseEventOccured || newComp == nullptr) + if (dismissalMouseEventOccurred || newComp == nullptr) hideTip(); return; @@ -218,7 +218,7 @@ void TooltipWindow::timerCallback() const auto tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse); const auto now = Time::getApproximateMillisecondCounter(); - if (tipChanged || dismissalMouseEventOccured || mouseMovedQuickly) + if (tipChanged || dismissalMouseEventOccurred || mouseMovedQuickly) lastCompChangeTime = now; const auto showTip = [this, &mouseSource, &mousePos, &newTip] @@ -231,7 +231,7 @@ void TooltipWindow::timerCallback() { // if a tip is currently visible (or has just disappeared), update to a new one // immediately if needed.. - if (newComp == nullptr || dismissalMouseEventOccured || newTip.isEmpty()) + if (newComp == nullptr || dismissalMouseEventOccurred || newTip.isEmpty()) hideTip(); else if (tipChanged) showTip(); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.h b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.h index dae2fa4de..d0e21e4b0 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.h +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.h @@ -140,7 +140,7 @@ class JUCE_API TooltipWindow : public Component, String tipShowing, lastTipUnderMouse, manuallyShownTip; int millisecondsBeforeTipAppears; unsigned int lastCompChangeTime = 0, lastHideTime = 0; - bool reentrant = false, dismissalMouseEventOccured = false; + bool reentrant = false, dismissalMouseEventOccurred = false; enum ShownManually { yes, no }; void displayTipInternal (Point, const String&, ShownManually); diff --git a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp index ba53da1fb..5ec63d174 100644 --- a/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp +++ b/external/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp @@ -150,7 +150,7 @@ TopLevelWindow::TopLevelWindow (const String& name, const bool shouldAddToDeskto TopLevelWindow::~TopLevelWindow() { - shadower.reset(); + shadower = nullptr; TopLevelWindowManager::getInstance()->removeWindow (this); } @@ -213,7 +213,7 @@ void TopLevelWindow::setDropShadowEnabled (const bool useShadow) if (isOnDesktop()) { - shadower.reset(); + shadower = nullptr; Component::addToDesktop (getDesktopWindowStyleFlags()); } else @@ -222,7 +222,7 @@ void TopLevelWindow::setDropShadowEnabled (const bool useShadow) { if (shadower == nullptr) { - shadower.reset (getLookAndFeel().createDropShadowerForComponent (this)); + shadower = getLookAndFeel().createDropShadowerForComponent (*this); if (shadower != nullptr) shadower->setOwner (this); @@ -230,7 +230,7 @@ void TopLevelWindow::setDropShadowEnabled (const bool useShadow) } else { - shadower.reset(); + shadower = nullptr; } } } @@ -257,7 +257,7 @@ void TopLevelWindow::recreateDesktopWindow() void TopLevelWindow::addToDesktop() { - shadower.reset(); + shadower = nullptr; Component::addToDesktop (getDesktopWindowStyleFlags()); setDropShadowEnabled (isDropShadowEnabled()); // force an update to clear away any fake shadows if necessary. }