From 2a0e12e13627821cc8f02ea5eba320671cb2d408 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Oct 2014 22:59:37 +0100 Subject: [PATCH] Update juce --- .../buffers/juce_FloatVectorOperations.cpp | 129 ++++++++++++- .../buffers/juce_FloatVectorOperations.h | 30 +++ .../juce_audio_basics/juce_audio_basics.cpp | 2 + .../juce_audio_basics/midi/juce_MidiBuffer.h | 10 +- .../midi/juce_MidiMessage.cpp | 2 +- .../sources/juce_ResamplingAudioSource.cpp | 3 +- .../synthesisers/juce_Synthesiser.cpp | 97 +++++++--- .../synthesisers/juce_Synthesiser.h | 86 ++++++--- .../native/juce_linux_ALSA.cpp | 2 +- .../codecs/juce_WavAudioFormat.cpp | 175 ++++++++++++++--- .../codecs/juce_WavAudioFormat.h | 3 + .../format/juce_AudioFormatReader.cpp | 123 +++++------- .../format/juce_AudioFormatReader.h | 28 ++- .../sampler/juce_Sampler.cpp | 10 +- .../juce_audio_formats/sampler/juce_Sampler.h | 6 +- .../juce_AudioUnitPluginFormat.mm | 12 +- .../format_types/juce_LADSPAPluginFormat.cpp | 2 +- .../format_types/juce_VST3PluginFormat.cpp | 6 +- .../juce_audio_processors.h | 1 + .../processors/juce_AudioProcessor.cpp | 181 ++++++++++++++++-- .../processors/juce_AudioProcessor.h | 33 +++- .../processors/juce_AudioProcessorParameter.h | 158 +++++++++++++++ .../juce_core/containers/juce_OwnedArray.h | 2 +- .../files/juce_WildcardFileFilter.cpp | 53 +++-- .../juce_core/files/juce_WildcardFileFilter.h | 4 - .../juce_core/javascript/juce_Javascript.cpp | 28 +-- .../juce_core/javascript/juce_Javascript.h | 7 +- source/modules/juce_core/juce_core.cpp | 19 +- source/modules/juce_core/juce_core.h | 5 +- .../juce_core/maths/juce_NormalisableRange.h | 169 ++++++++++++++++ .../juce_core/memory/juce_MemoryBlock.cpp | 6 +- .../juce_core/memory/juce_ScopedPointer.h | 8 +- .../native/juce_BasicNativeHeaders.h | 27 --- .../juce_core/native/juce_linux_Network.cpp | 37 ++-- .../juce_core/native/juce_posix_SharedCode.h | 51 ++--- .../juce_core/native/juce_win32_Network.cpp | 46 +++-- .../juce_core/native/juce_win32_Threads.cpp | 5 - .../modules/juce_core/network/juce_Socket.cpp | 12 +- source/modules/juce_core/network/juce_URL.cpp | 9 +- source/modules/juce_core/network/juce_URL.h | 3 + .../streams/juce_MemoryOutputStream.cpp | 2 +- .../juce_core/system/juce_PlatformDefs.h | 60 +++++- .../juce_core/system/juce_StandardHeader.h | 1 + .../juce_core/system/juce_SystemStats.cpp | 2 +- .../juce_core/system/juce_TargetPlatform.h | 6 +- source/modules/juce_core/text/juce_String.h | 6 +- .../juce_core/threads/juce_ChildProcess.cpp | 5 - .../juce_core/threads/juce_ChildProcess.h | 2 - .../modules/juce_core/xml/juce_XmlElement.cpp | 2 +- .../app_properties/juce_PropertiesFile.cpp | 2 +- .../undomanager/juce_UndoManager.cpp | 48 +++-- .../undomanager/juce_UndoManager.h | 47 +++-- .../values/juce_ValueTree.h | 5 +- .../messages/juce_MessageManager.cpp | 19 ++ .../messages/juce_MessageManager.h | 7 + .../native/juce_linux_Messaging.cpp | 2 +- .../contexts/juce_GraphicsContext.h | 6 +- ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 2 +- .../image_formats/juce_PNGLoader.cpp | 7 +- .../image_formats/pnglib/pngread.c | 2 +- .../juce_graphics/images/juce_Image.cpp | 6 +- .../modules/juce_graphics/images/juce_Image.h | 6 +- .../juce_gui_basics/buttons/juce_Button.cpp | 12 +- .../juce_gui_basics/buttons/juce_Button.h | 10 +- .../juce_ApplicationCommandManager.cpp | 39 ++-- .../commands/juce_ApplicationCommandManager.h | 1 + .../commands/juce_KeyPressMappingSet.cpp | 2 +- .../components/juce_Component.h | 9 +- .../drawables/juce_SVGParser.cpp | 136 +++++++------ .../layout/juce_ComponentAnimator.cpp | 2 +- .../lookandfeel/juce_LookAndFeel_V2.cpp | 13 ++ .../lookandfeel/juce_LookAndFeel_V2.h | 3 + .../menus/juce_MenuBarComponent.cpp | 30 +-- .../menus/juce_MenuBarComponent.h | 9 +- .../juce_gui_basics/menus/juce_MenuBarModel.h | 14 +- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 9 +- .../juce_gui_basics/menus/juce_PopupMenu.h | 3 + .../mouse/juce_DragAndDropContainer.cpp | 37 +++- .../mouse/juce_DragAndDropContainer.h | 6 + .../native/juce_ios_UIViewComponentPeer.mm | 123 +++++++----- .../native/juce_linux_FileChooser.cpp | 12 +- .../native/juce_linux_Windowing.cpp | 6 +- .../native/juce_win32_Windowing.cpp | 48 +++-- .../juce_BooleanPropertyComponent.cpp | 2 +- .../juce_BooleanPropertyComponent.h | 18 ++ .../properties/juce_ChoicePropertyComponent.h | 8 +- .../properties/juce_SliderPropertyComponent.h | 4 + .../juce_gui_basics/widgets/juce_Label.cpp | 2 - .../juce_gui_basics/widgets/juce_Label.h | 2 +- .../juce_gui_basics/widgets/juce_ListBox.cpp | 16 +- .../juce_gui_basics/widgets/juce_ListBox.h | 18 +- .../juce_gui_basics/widgets/juce_Slider.cpp | 13 +- .../widgets/juce_TableListBox.cpp | 25 +-- .../widgets/juce_TableListBox.h | 37 ++-- .../juce_gui_basics/widgets/juce_Toolbar.cpp | 8 +- .../juce_gui_basics/widgets/juce_TreeView.cpp | 9 +- .../windows/juce_CallOutBox.cpp | 12 +- .../juce_gui_basics/windows/juce_CallOutBox.h | 11 ++ .../windows/juce_DialogWindow.h | 2 +- .../windows/juce_TopLevelWindow.h | 2 +- .../code_editor/juce_CodeDocument.cpp | 18 +- .../code_editor/juce_CodeEditorComponent.cpp | 6 +- .../native/juce_mac_NSViewComponent.mm | 5 +- .../native/juce_win32_WebBrowserComponent.cpp | 6 + 104 files changed, 1844 insertions(+), 739 deletions(-) create mode 100644 source/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h create mode 100644 source/modules/juce_core/maths/juce_NormalisableRange.h diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 308ac8457..2fc6898cb 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -24,8 +24,9 @@ namespace FloatVectorHelpers { - #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest)); - #define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); + #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest)); + #define JUCE_INCREMENT_SRC1_SRC2_DEST dest += (16 / sizeof (*dest)); src1 += (16 / sizeof (*dest)); src2 += (16 / sizeof (*dest)); + #define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); #if JUCE_USE_SSE_INTRINSICS static bool sse2Present = false; @@ -122,6 +123,17 @@ namespace FloatVectorHelpers } \ JUCE_FINISH_VEC_OP (normalOp) + #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + { \ + Mode::ParallelType (&loadSrc1) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src1) ? Mode::loadA : Mode::loadU; \ + Mode::ParallelType (&loadSrc2) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src2) ? Mode::loadA : Mode::loadU; \ + void (&storeDst) (Mode::Type* dest, Mode::ParallelType a) = FloatVectorHelpers::isAligned (dest) ? Mode::storeA : Mode::storeU; \ + JUCE_VEC_LOOP_TWO_SOURCES (vecOp, loadSrc1, loadSrc2, storeDst, locals, increment); \ + } \ + JUCE_FINISH_VEC_OP (normalOp) + //============================================================================== #elif JUCE_USE_ARM_NEON @@ -193,6 +205,12 @@ namespace FloatVectorHelpers JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ JUCE_FINISH_VEC_OP (normalOp) + #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + JUCE_FINISH_VEC_OP (normalOp) + //============================================================================== #else #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ @@ -201,6 +219,8 @@ namespace FloatVectorHelpers #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ for (int i = 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; #endif //============================================================================== @@ -212,10 +232,19 @@ namespace FloatVectorHelpers increment; \ } + #define JUCE_VEC_LOOP_TWO_SOURCES(vecOp, src1Load, src2Load, dstStore, locals, increment) \ + for (int i = 0; i < numLongOps; ++i) \ + { \ + locals (src1Load, src2Load); \ + dstStore (dest, vecOp); \ + increment; \ + } + #define JUCE_LOAD_NONE(srcLoad, dstLoad) - #define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); - #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src); - #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); + #define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); + #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src); + #define JUCE_LOAD_SRC1_SRC2(src1Load, src2Load) const Mode::ParallelType s1 = src1Load (src1), s2 = src2Load (src2); + #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON template struct ModeType { typedef BasicOps32 Mode; }; @@ -443,6 +472,28 @@ void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int const Mode::ParallelType amountToAdd = Mode::load1 (amount);) } +void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float* src, float amount, int 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 (double* dest, double* src, double amount, int 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 (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK @@ -461,6 +512,24 @@ void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, #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 +} + +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 +} + void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK @@ -479,6 +548,24 @@ void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* #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 +} + +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 +} + void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept { JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), @@ -511,6 +598,24 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* #endif } +void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src1, const float* src2, int 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 +} + +void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src1, const double* src2, int 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 +} + void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK @@ -531,6 +636,20 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multipl #endif } +void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, float multiplier, int 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);) +} + +void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, double multiplier, int 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);) +} + void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h index 54821d28c..0b3fcb67c 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -65,18 +65,36 @@ public: /** 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, 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, 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; @@ -89,12 +107,24 @@ public: /** 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; + /** 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; + + /** 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; + /** Copies a source vector to a destination, negating each value. */ static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; diff --git a/source/modules/juce_audio_basics/juce_audio_basics.cpp b/source/modules/juce_audio_basics/juce_audio_basics.cpp index 0e13f7d8a..685e7c4c2 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/source/modules/juce_audio_basics/juce_audio_basics.cpp @@ -57,7 +57,9 @@ #endif #if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK + #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) #include + #undef Point #else #undef JUCE_USE_VDSP_FRAMEWORK #endif diff --git a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h index 7dfc4588b..ecbc56a21 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h @@ -186,9 +186,10 @@ public: /** Retrieves a copy of the next event from the buffer. - @param result on return, this will be the message (the MidiMessage's timestamp - is not set) - @param samplePosition on return, this will be the position of the event + @param result on return, this will be the message. The MidiMessage's timestamp + is set to the same value as samplePosition. + @param samplePosition on return, this will be the position of the event, as a + sample index in the buffer @returns true if an event was found, or false if the iterator has reached the end of the buffer */ @@ -203,7 +204,8 @@ public: temporarily until the MidiBuffer is altered. @param numBytesOfMidiData on return, this is the number of bytes of data used by the midi message - @param samplePosition on return, this will be the position of the event + @param samplePosition on return, this will be the position of the event, as a + sample index in the buffer @returns true if an event was found, or false if the iterator has reached the end of the buffer */ diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index bf5e19e9a..07e2ccd27 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -661,7 +661,7 @@ String MidiMessage::getTextFromTextMetaEvent() const MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) { - jassert (type > 0 && type < 16) + jassert (type > 0 && type < 16); MidiMessage result; diff --git a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp index 44c3dada0..6a0704e0f 100644 --- a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp +++ b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp @@ -230,7 +230,8 @@ void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double void ResamplingAudioSource::resetFilters() { - filterStates.clear ((size_t) numChannels); + if (filterStates != nullptr) + filterStates.clear ((size_t) numChannels); } void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 7a72d0083..6a889b4b3 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -163,10 +163,7 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu : numSamples; if (numThisTime > 0) - { - for (int i = voices.size(); --i >= 0;) - voices.getUnchecked (i)->renderNextBlock (outputBuffer, startSample, numThisTime); - } + renderVoices (outputBuffer, startSample, numThisTime); if (useEvent) handleMidiEvent (m); @@ -176,6 +173,12 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu } } +void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples) +{ + for (int i = voices.size(); --i >= 0;) + voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples); +} + void Synthesiser::handleMidiEvent (const MidiMessage& m) { if (m.isNoteOn()) @@ -184,7 +187,7 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) } else if (m.isNoteOff()) { - noteOff (m.getChannel(), m.getNoteNumber(), true); + noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true); } else if (m.isAllNotesOff() || m.isAllSoundOff()) { @@ -230,10 +233,10 @@ void Synthesiser::noteOn (const int midiChannel, if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel)) - stopVoice (voice, true); + stopVoice (voice, 1.0f, true); } - startVoice (findFreeVoice (sound, shouldStealNotes), + startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes), sound, midiChannel, midiNoteNumber, velocity); } } @@ -248,7 +251,7 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice, if (voice != nullptr && sound != nullptr) { if (voice->currentlyPlayingSound != nullptr) - voice->stopNote (false); + voice->stopNote (0.0f, false); voice->startNote (midiNoteNumber, velocity, sound, lastPitchWheelValues [midiChannel - 1]); @@ -261,11 +264,11 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice, } } -void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff) +void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff) { jassert (voice != nullptr); - voice->stopNote (allowTailOff); + voice->stopNote (velocity, allowTailOff); // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()! jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0)); @@ -273,6 +276,7 @@ void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff) void Synthesiser::noteOff (const int midiChannel, const int midiNoteNumber, + const float velocity, const bool allowTailOff) { const ScopedLock sl (lock); @@ -291,7 +295,7 @@ void Synthesiser::noteOff (const int midiChannel, voice->keyIsDown = false; if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown)) - stopVoice (voice, allowTailOff); + stopVoice (voice, velocity, allowTailOff); } } } @@ -307,7 +311,7 @@ void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff) SynthesiserVoice* const voice = voices.getUnchecked (i); if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) - voice->stopNote (allowTailOff); + voice->stopNote (1.0f, allowTailOff); } sustainPedalsDown.clear(); @@ -379,7 +383,7 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) SynthesiserVoice* const voice = voices.getUnchecked (i); if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown) - stopVoice (voice, true); + stopVoice (voice, 1.0f, true); } sustainPedalsDown.clearBit (midiChannel); @@ -400,7 +404,7 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown) if (isDown) voice->sostenutoPedalDown = true; else if (voice->sostenutoPedalDown) - stopVoice (voice, true); + stopVoice (voice, 1.0f, true); } } } @@ -412,7 +416,9 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) } //============================================================================== -SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, const bool stealIfNoneAvailable) const +SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, + int midiChannel, int midiNoteNumber, + const bool stealIfNoneAvailable) const { const ScopedLock sl (lock); @@ -425,25 +431,68 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, con } if (stealIfNoneAvailable) - return findVoiceToSteal (soundToPlay); + return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber); return nullptr; } -SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay) const +struct VoiceAgeSorter +{ + static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept + { + return v1->wasStartedBefore (*v2) ? 1 : (v2->wasStartedBefore (*v1) ? -1 : 0); + } +}; + +SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, + int /*midiChannel*/, int midiNoteNumber) const { - // currently this just steals the one that's been playing the longest, but could be made a bit smarter.. - SynthesiserVoice* oldest = nullptr; + SynthesiserVoice* bottom = nullptr; + SynthesiserVoice* top = nullptr; + + // this is a list of voices we can steal, sorted by how long they've been running + Array usableVoices; + usableVoices.ensureStorageAllocated (voices.size()); for (int i = 0; i < voices.size(); ++i) { SynthesiserVoice* const voice = voices.getUnchecked (i); - if (voice->canPlaySound (soundToPlay) - && (oldest == nullptr || voice->wasStartedBefore (*oldest))) - oldest = voice; + if (voice->canPlaySound (soundToPlay)) + { + VoiceAgeSorter sorter; + usableVoices.addSorted (sorter, voice); + + const int note = voice->getCurrentlyPlayingNote(); + + if (bottom == nullptr || note < bottom->getCurrentlyPlayingNote()) + bottom = voice; + + if (top == nullptr || note > top->getCurrentlyPlayingNote()) + top = voice; + } + } + + jassert (bottom != nullptr && top != nullptr); + + // The oldest note that's playing with the target pitch playing is ideal.. + for (int i = 0; i < usableVoices.size(); ++i) + { + SynthesiserVoice* const voice = usableVoices.getUnchecked (i); + + if (voice->getCurrentlyPlayingNote() == midiNoteNumber) + return voice; + } + + // ..otherwise, look for the oldest note that isn't the top or bottom note.. + for (int i = 0; i < usableVoices.size(); ++i) + { + SynthesiserVoice* const voice = usableVoices.getUnchecked (i); + + if (voice != bottom && voice != top) + return voice; } - jassert (oldest != nullptr); - return oldest; + // ..otherwise, there's only one or two voices to choose from - we'll return the top one.. + return top; } diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index e765697cb..6a32cedaa 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -55,14 +55,14 @@ public: The Synthesiser will use this information when deciding which sounds to trigger for a given note. */ - virtual bool appliesToNote (const int midiNoteNumber) = 0; + virtual bool appliesToNote (int midiNoteNumber) = 0; /** Returns true if the sound should be triggered by midi events on a given channel. The Synthesiser will use this information when deciding which sounds to trigger for a given note. */ - virtual bool appliesToChannel (const int midiChannel) = 0; + virtual bool appliesToChannel (int midiChannel) = 0; /** The class is reference-counted, so this is a handy pointer class for it. */ typedef ReferenceCountedObjectPtr Ptr; @@ -127,6 +127,8 @@ public: This will be called during the rendering callback, so must be fast and thread-safe. + The velocity indicates how quickly the note was released - 0 is slowly, 1 is quickly. + If allowTailOff is false or the voice doesn't want to tail-off, then it must stop all sound immediately, and must call clearCurrentNote() to reset the state of this voice and allow the synth to reassign it another sound. @@ -136,7 +138,7 @@ public: finishes playing (during the rendering callback), it must make sure that it calls clearCurrentNote(). */ - virtual void stopNote (bool allowTailOff) = 0; + virtual void stopNote (float velocity, bool allowTailOff) = 0; /** Called to let the voice know that the pitch wheel has been moved. This will be called during the rendering callback, so must be fast and thread-safe. @@ -173,13 +175,6 @@ public: int startSample, int numSamples) = 0; - /** Returns true if the voice is currently playing a sound which is mapped to the given - midi channel. - - If it's not currently playing, this will return false. - */ - bool isPlayingChannel (int midiChannel) const; - /** Changes the voice's reference sample rate. The rate is set so that subclasses know the output rate and can set their pitch @@ -188,7 +183,19 @@ public: This method is called by the synth, and subclasses can access the current rate with the currentSampleRate member. */ - void setCurrentPlaybackSampleRate (double newRate); + virtual void setCurrentPlaybackSampleRate (double newRate); + + /** Returns the current target sample rate at which rendering is being done. + Subclasses may need to know this so that they can pitch things correctly. + */ + double getSampleRate() const noexcept { return currentSampleRate; } + + /** Returns true if the voice is currently playing a sound which is mapped to the given + midi channel. + + If it's not currently playing, this will return false. + */ + bool isPlayingChannel (int midiChannel) const; /** Returns true if the key that triggered this voice is still held down. Note that the voice may still be playing after the key was released (e.g because the @@ -203,13 +210,6 @@ public: bool wasStartedBefore (const SynthesiserVoice& other) const noexcept; protected: - //============================================================================== - /** Returns the current target sample rate at which rendering is being done. - - This is available for subclasses so they can pitch things correctly. - */ - double getSampleRate() const { return currentSampleRate; } - /** Resets the state of this voice after a sound has finished playing. The subclass must call this when it finishes playing a note and becomes available @@ -235,6 +235,11 @@ private: SynthesiserSound::Ptr currentlyPlayingSound; bool keyIsDown, sostenutoPedalDown; + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // Note the new parameters for this method. + virtual int stopNote (bool) { return 0; } + #endif + JUCE_LEAK_DETECTOR (SynthesiserVoice) }; @@ -268,8 +273,7 @@ class JUCE_API Synthesiser public: //============================================================================== /** Creates a new synthesiser. - - You'll need to add some sounds and voices before it'll make any sound.. + You'll need to add some sounds and voices before it'll make any sound. */ Synthesiser(); @@ -365,6 +369,7 @@ public: */ virtual void noteOff (int midiChannel, int midiNoteNumber, + float velocity, bool allowTailOff); /** Turns off all notes. @@ -444,7 +449,7 @@ public: This value is propagated to the voices so that they can use it to render the correct pitches. */ - void setCurrentPlaybackSampleRate (double sampleRate); + virtual void setCurrentPlaybackSampleRate (double sampleRate); /** Creates the next block of audio output. @@ -463,6 +468,11 @@ public: int startSample, int numSamples); + /** Returns the current target sample rate at which rendering is being done. + Subclasses may need to know this so that they can pitch things correctly. + */ + double getSampleRate() const noexcept { return sampleRate; } + protected: //============================================================================== /** This is used to control access to the rendering callback and the note trigger methods. */ @@ -474,21 +484,34 @@ protected: /** The last pitch-wheel values for each midi channel. */ int lastPitchWheelValues [16]; - /** Searches through the voices to find one that's not currently playing, and which - can play the given sound. + /** Renders the voices for the given range. + By default this just calls renderNextBlock() on each voice, but you may need + to override it to handle custom cases. + */ + virtual void renderVoices (AudioSampleBuffer& outputAudio, + int startSample, int numSamples); + + /** Searches through the voices to find one that's not currently playing, and + which can play the given sound. Returns nullptr if all voices are busy and stealing isn't enabled. - This can be overridden to implement custom voice-stealing algorithms. + To implement a custom note-stealing algorithm, you can either override this + method, or (preferably) override findVoiceToSteal(). */ virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay, - const bool stealIfNoneAvailable) const; + int midiChannel, + int midiNoteNumber, + bool stealIfNoneAvailable) const; /** Chooses a voice that is most suitable for being re-used. - The default method returns the one that has been playing for the longest, but - you may want to override this and do something more cunning instead. + The default method will attempt to find the oldest voice that isn't the + bottom or top note being played. If that's not suitable for your synth, + you can override this method and do something more cunning instead. */ - virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay) const; + virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay, + int midiChannel, + int midiNoteNumber) const; /** Starts a specified voice playing a particular sound. @@ -511,11 +534,14 @@ private: bool shouldStealNotes; BigInteger sustainPedalsDown; - void stopVoice (SynthesiserVoice*, bool allowTailOff); + void stopVoice (SynthesiserVoice*, float velocity, bool allowTailOff); #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note the new parameters for this method. + // 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/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index 9bc7ecf4b..091ffbab5 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -41,7 +41,7 @@ namespace return err; } #else - #define JUCE_ALSA_LOG(x) + #define JUCE_ALSA_LOG(x) {} #define JUCE_CHECKED_RESULT(x) (x) #endif diff --git a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 1da4e0379..473e7445b 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -65,6 +65,7 @@ const char* const WavAudioFormat::acidNumerator = "acid numerator"; const char* const WavAudioFormat::acidTempo = "acid tempo"; const char* const WavAudioFormat::ISRC = "ISRC"; +const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; //============================================================================== namespace WavFileHelpers @@ -471,6 +472,46 @@ namespace WavFileHelpers } } + //============================================================================== + namespace ListInfoChunk + { + static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName) + { + const String value (values.getValue (paramName, String())); + + if (value.isEmpty()) + return false; + + const int valueLength = (int) value.getNumBytesAsUTF8() + 1; + const int chunkLength = valueLength + (valueLength & 1); + + out.writeInt (chunkName (paramName)); + out.writeInt (chunkLength); + out.write (value.toUTF8(), (size_t) valueLength); + + if ((out.getDataSize() & 1) != 0) + out.writeByte (0); + + return true; + } + + static MemoryBlock createFrom (const StringPairArray& values) + { + static const char* params[] = { "INAM", "IART", "IPRD", "IPRT", "ISFT", + "ISRC", "IGNR", "ICMT", "ICOP", "ICRD" }; + + MemoryOutputStream out; + out.writeInt (chunkName ("INFO")); + bool anyParamsDefined = false; + + for (int i = 0; i < numElementsInArray (params); ++i) + if (writeValue (values, out, params[i])) + anyParamsDefined = true; + + return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock(); + } + } + //============================================================================== struct AcidChunk { @@ -481,6 +522,38 @@ namespace WavFileHelpers input.read (this, (int) jmin (sizeof (*this), length)); } + AcidChunk (const StringPairArray& values) + { + zerostruct (*this); + + flags = getFlagIfPresent (values, WavAudioFormat::acidOneShot, 0x01) + | getFlagIfPresent (values, WavAudioFormat::acidRootSet, 0x02) + | getFlagIfPresent (values, WavAudioFormat::acidStretch, 0x04) + | getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08) + | getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10); + + if (values[WavAudioFormat::acidRootSet].getIntValue() != 0) + rootNote = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidRootNote].getIntValue()); + + numBeats = ByteOrder::swapIfBigEndian ((uint32) values[WavAudioFormat::acidBeats].getIntValue()); + meterDenominator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidDenominator].getIntValue()); + meterNumerator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidNumerator].getIntValue()); + + if (values.containsKey (WavAudioFormat::acidTempo)) + tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue()); + } + + static MemoryBlock createFrom (const StringPairArray& values) + { + return AcidChunk (values).toMemoryBlock(); + } + + MemoryBlock toMemoryBlock() const + { + return (flags != 0 || rootNote != 0 || numBeats != 0 || meterDenominator != 0 || meterNumerator != 0) + ? MemoryBlock (this, sizeof (*this)) : MemoryBlock(); + } + void addToMetadata (StringPairArray& values) const { setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01); @@ -490,30 +563,65 @@ namespace WavFileHelpers setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10); if (flags & 0x02) // root note set - values.set (WavAudioFormat::acidRootNote, String (rootNote)); + values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote))); - values.set (WavAudioFormat::acidBeats, String (numBeats)); - values.set (WavAudioFormat::acidDenominator, String (meterDenominator)); - values.set (WavAudioFormat::acidNumerator, String (meterNumerator)); - values.set (WavAudioFormat::acidTempo, String (tempo)); + values.set (WavAudioFormat::acidBeats, String (ByteOrder::swapIfBigEndian (numBeats))); + values.set (WavAudioFormat::acidDenominator, String (ByteOrder::swapIfBigEndian (meterDenominator))); + values.set (WavAudioFormat::acidNumerator, String (ByteOrder::swapIfBigEndian (meterNumerator))); + values.set (WavAudioFormat::acidTempo, String (swapFloatByteOrder (tempo))); } - void setBoolFlag (StringPairArray& values, const char* name, int32 mask) const + void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const { - values.set (name, (flags & mask) ? "1" : "0"); + values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0"); + } + + static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag) + { + return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0; + } + + static float swapFloatByteOrder (const float x) noexcept + { + #ifdef JUCE_BIG_ENDIAN + union { uint32 asInt; float asFloat; } n; + n.asFloat = x; + n.asInt = ByteOrder::swap (n.asInt); + return n.asFloat; + #else + return x; + #endif } - int32 flags; - int16 rootNote; - int16 reserved1; + uint32 flags; + uint16 rootNote; + uint16 reserved1; float reserved2; - int32 numBeats; - int16 meterDenominator; - int16 meterNumerator; + uint32 numBeats; + uint16 meterDenominator; + uint16 meterNumerator; float tempo; } JUCE_PACKED; + //============================================================================== + struct TracktionChunk + { + static MemoryBlock createFrom (const StringPairArray& values) + { + const String s = values[WavAudioFormat::tracktionLoopInfo]; + MemoryBlock data; + + if (s.isNotEmpty()) + { + MemoryOutputStream os (data, false); + os.writeString (s); + } + + return data; + } + }; + //============================================================================== namespace AXMLChunk { @@ -816,6 +924,12 @@ public: { AcidChunk (*input, length).addToMetadata (metadataValues); } + else if (chunkType == chunkName ("Trkn")) + { + MemoryBlock tracktion; + input->readIntoMemoryBlock (tracktion, (ssize_t) length); + metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString()); + } else if (chunkEnd <= input->getPosition()) { break; @@ -913,12 +1027,15 @@ public: // key should be removed (or set to "WAV") once this has been done jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF"); - bwavChunk = BWAVChunk::createFrom (metadataValues); - axmlChunk = AXMLChunk::createFrom (metadataValues); - smplChunk = SMPLChunk::createFrom (metadataValues); - instChunk = InstChunk::createFrom (metadataValues); - cueChunk = CueChunk ::createFrom (metadataValues); - listChunk = ListChunk::createFrom (metadataValues); + bwavChunk = BWAVChunk::createFrom (metadataValues); + axmlChunk = AXMLChunk::createFrom (metadataValues); + smplChunk = SMPLChunk::createFrom (metadataValues); + instChunk = InstChunk::createFrom (metadataValues); + cueChunk = CueChunk ::createFrom (metadataValues); + listChunk = ListChunk::createFrom (metadataValues); + listInfoChunk = ListInfoChunk::createFrom (metadataValues); + acidChunk = AcidChunk::createFrom (metadataValues); + trckChunk = TracktionChunk::createFrom (metadataValues); } headerPosition = out->getPosition(); @@ -981,7 +1098,7 @@ public: } private: - MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk; + MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk; uint64 lengthInSamples, bytesWritten; int64 headerPosition; bool writeFailed; @@ -1033,6 +1150,9 @@ private: + chunkSize (instChunk) + chunkSize (cueChunk) + chunkSize (listChunk) + + chunkSize (listInfoChunk) + + chunkSize (acidChunk) + + chunkSize (trckChunk) + (8 + 28)); // (ds64 chunk) riffChunkSize += (riffChunkSize & 1); @@ -1106,12 +1226,15 @@ private: output->write (subFormat.data4, sizeof (subFormat.data4)); } - writeChunk (bwavChunk, chunkName ("bext")); - writeChunk (axmlChunk, chunkName ("axml")); - writeChunk (smplChunk, chunkName ("smpl")); - writeChunk (instChunk, chunkName ("inst"), 7); - writeChunk (cueChunk, chunkName ("cue ")); - writeChunk (listChunk, chunkName ("LIST")); + writeChunk (bwavChunk, chunkName ("bext")); + writeChunk (axmlChunk, chunkName ("axml")); + writeChunk (smplChunk, chunkName ("smpl")); + writeChunk (instChunk, chunkName ("inst"), 7); + writeChunk (cueChunk, chunkName ("cue ")); + writeChunk (listChunk, chunkName ("LIST")); + writeChunk (listInfoChunk, chunkName ("LIST")); + writeChunk (acidChunk, chunkName ("acid")); + writeChunk (trckChunk, chunkName ("Trkn")); writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); diff --git a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h index f80695c0a..62de05a89 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h +++ b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h @@ -135,6 +135,9 @@ public: /** Metadata property name used when reading an ISRC code from an AXML chunk. */ static const char* const ISRC; + /** Metadata property name used when reading a WAV file with a Tracktion chunk. */ + static const char* const tracktionLoopInfo; + //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index 071f82508..5db3149ef 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -173,102 +173,75 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer, } } -template -static Range getChannelMinAndMax (SampleType* channel, int numSamples) noexcept -{ - return Range::findMinAndMax (channel, numSamples); -} - -static Range getChannelMinAndMax (float* channel, int numSamples) noexcept -{ - return FloatVectorOperations::findMinAndMax (channel, numSamples); -} - -template -static void getStereoMinAndMax (SampleType* const* channels, const int numChannels, const int numSamples, - SampleType& lmin, SampleType& lmax, SampleType& rmin, SampleType& rmax) -{ - Range range (getChannelMinAndMax (channels[0], numSamples)); - lmax = jmax (lmax, range.getEnd()); - lmin = jmin (lmin, range.getStart()); - - if (numChannels > 1) - { - range = getChannelMinAndMax (channels[1], numSamples); - rmax = jmax (rmax, range.getEnd()); - rmin = jmin (rmin, range.getStart()); - } - else - { - rmax = lmax; - rmin = lmin; - } -} - void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, - float& lowestLeft, float& highestLeft, - float& lowestRight, float& highestRight) + Range* const results, const int channelsToRead) { + jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels); + if (numSamples <= 0) { - lowestLeft = 0; - lowestRight = 0; - highestLeft = 0; - highestRight = 0; + for (int i = 0; i < channelsToRead; ++i) + results[i] = Range(); + return; } const int bufferSize = (int) jmin (numSamples, (int64) 4096); - AudioSampleBuffer tempSampleBuffer ((int) numChannels, bufferSize); + AudioSampleBuffer tempSampleBuffer ((int) channelsToRead, bufferSize); float* const* const floatBuffer = tempSampleBuffer.getArrayOfWritePointers(); int* const* intBuffer = reinterpret_cast (floatBuffer); + bool isFirstBlock = true; - if (usesFloatingPointData) + while (numSamples > 0) { - float lmin = 1.0e6f; - float lmax = -lmin; - float rmin = lmin; - float rmax = lmax; + const int numToDo = (int) jmin (numSamples, (int64) bufferSize); + if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false)) + break; - while (numSamples > 0) + for (int i = 0; i < channelsToRead; ++i) { - const int numToDo = (int) jmin (numSamples, (int64) bufferSize); - if (! read (intBuffer, 2, startSampleInFile, numToDo, false)) - break; + Range r; + + if (usesFloatingPointData) + { + r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo); + } + else + { + Range intRange (Range::findMinAndMax (intBuffer[i], numToDo)); - numSamples -= numToDo; - startSampleInFile += numToDo; - getStereoMinAndMax (floatBuffer, (int) numChannels, numToDo, lmin, lmax, rmin, rmax); + r = Range (intRange.getStart() / (float) std::numeric_limits::max(), + intRange.getEnd() / (float) std::numeric_limits::max()); + } + + results[i] = isFirstBlock ? r : results[i].getUnionWith (r); } - lowestLeft = lmin; - highestLeft = lmax; - lowestRight = rmin; - highestRight = rmax; + isFirstBlock = false; + numSamples -= numToDo; + startSampleInFile += numToDo; } - else - { - int lmax = std::numeric_limits::min(); - int lmin = std::numeric_limits::max(); - int rmax = std::numeric_limits::min(); - int rmin = std::numeric_limits::max(); - - while (numSamples > 0) - { - const int numToDo = (int) jmin (numSamples, (int64) bufferSize); - if (! read (intBuffer, 2, startSampleInFile, numToDo, false)) - break; +} - numSamples -= numToDo; - startSampleInFile += numToDo; - getStereoMinAndMax (intBuffer, (int) numChannels, numToDo, lmin, lmax, rmin, rmax); - } +void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, + float& lowestLeft, float& highestLeft, + float& lowestRight, float& highestRight) +{ + Range levels[2]; + readMaxLevels (startSampleInFile, numSamples, levels, jmin (2, (int) numChannels)); + lowestLeft = levels[0].getStart(); + highestLeft = levels[0].getEnd(); - lowestLeft = lmin / (float) std::numeric_limits::max(); - highestLeft = lmax / (float) std::numeric_limits::max(); - lowestRight = rmin / (float) std::numeric_limits::max(); - highestRight = rmax / (float) std::numeric_limits::max(); + if (numChannels > 1) + { + lowestRight = levels[1].getStart(); + highestRight = levels[1].getEnd(); + } + else + { + lowestRight = lowestLeft; + highestRight = highestLeft; } } diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.h b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.h index d7cdd9c86..065388a77 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.h +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.h @@ -121,6 +121,25 @@ public: bool useReaderLeftChan, bool useReaderRightChan); + /** Finds the highest and lowest sample levels from a section of the audio stream. + + This will read a block of samples from the stream, and measure the + highest and lowest sample levels from the channels in that section, returning + these as normalised floating-point levels. + + @param startSample the offset into the audio stream to start reading from. It's + ok for this to be beyond the start or end of the stream. + @param numSamples how many samples to read + @param results this array will be filled with Range values for each channel. + The array must contain numChannels elements. + @param numChannelsToRead the number of channels of data to scan. This must be + more than zero, but not more than the total number of channels + that the reader contains + @see read + */ + virtual void readMaxLevels (int64 startSample, int64 numSamples, + Range* results, int numChannelsToRead); + /** Finds the highest and lowest sample levels from a section of the audio stream. This will read a block of samples from the stream, and measure the @@ -138,12 +157,9 @@ public: channel (if there is one) @see read */ - virtual void readMaxLevels (int64 startSample, - int64 numSamples, - float& lowestLeft, - float& highestLeft, - float& lowestRight, - float& highestRight); + virtual void readMaxLevels (int64 startSample, int64 numSamples, + float& lowestLeft, float& highestLeft, + float& lowestRight, float& highestRight); /** Scans the source looking for a sample whose magnitude is in a specified range. diff --git a/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp b/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp index 9dbe7b3c5..4e7583e0a 100644 --- a/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp +++ b/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp @@ -59,12 +59,12 @@ SamplerSound::~SamplerSound() { } -bool SamplerSound::appliesToNote (const int midiNoteNumber) +bool SamplerSound::appliesToNote (int midiNoteNumber) { return midiNotes [midiNoteNumber]; } -bool SamplerSound::appliesToChannel (const int /*midiChannel*/) +bool SamplerSound::appliesToChannel (int /*midiChannel*/) { return true; } @@ -127,7 +127,7 @@ void SamplerVoice::startNote (const int midiNoteNumber, } } -void SamplerVoice::stopNote (const bool allowTailOff) +void SamplerVoice::stopNote (float /*velocity*/, bool allowTailOff) { if (allowTailOff) { @@ -197,7 +197,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa if (attackReleaseLevel <= 0.0f) { - stopNote (false); + stopNote (0.0f, false); break; } } @@ -216,7 +216,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa if (sourceSamplePosition > playingSound->length) { - stopNote (false); + stopNote (0.0f, false); break; } } diff --git a/source/modules/juce_audio_formats/sampler/juce_Sampler.h b/source/modules/juce_audio_formats/sampler/juce_Sampler.h index 51133add9..f801bf232 100644 --- a/source/modules/juce_audio_formats/sampler/juce_Sampler.h +++ b/source/modules/juce_audio_formats/sampler/juce_Sampler.h @@ -82,8 +82,8 @@ public: //============================================================================== - bool appliesToNote (const int midiNoteNumber) override; - bool appliesToChannel (const int midiChannel) override; + bool appliesToNote (int midiNoteNumber) override; + bool appliesToChannel (int midiChannel) override; private: @@ -124,7 +124,7 @@ public: bool canPlaySound (SynthesiserSound*) override; void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; - void stopNote (bool allowTailOff) override; + void stopNote (float velocity, bool allowTailOff) override; void pitchWheelMoved (int newValue); void controllerMoved (int controllerNumber, int newValue) override; diff --git a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index 798a66234..2877509e8 100644 --- a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -345,11 +345,21 @@ public: refreshParameterList(); updateNumChannels(); producesMidiMessages = canProduceMidiOutput(); - setPluginCallbacks(); setPlayConfigDetails (numInputBusChannels * numInputBusses, numOutputBusChannels * numOutputBusses, rate, blockSize); setLatencySamples (0); + + if (parameters.size() == 0) + { + // some plugins crash if initialiseAudioUnit() is called too soon (sigh..), so we'll + // only call it here if it seems like they it's one of the awkward plugins that can + // only create their parameters after it has been initialised. + initialiseAudioUnit(); + refreshParameterList(); + } + + setPluginCallbacks(); } //============================================================================== diff --git a/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp index 8479bc3b3..1bb2413ec 100644 --- a/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp @@ -26,7 +26,7 @@ } // (juce namespace) -#include "ladspa.h" +#include namespace juce { diff --git a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index e640ca714..a7416264c 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -2112,7 +2112,11 @@ private: MemoryBlock mem; if (mem.fromBase64Encoding (state->getAllSubText())) - stream = new Steinberg::MemoryStream (mem.getData(), (TSize) mem.getSize()); + { + stream = new Steinberg::MemoryStream(); + stream->setSize ((TSize) mem.getSize()); + mem.copyTo (stream->getData(), 0, mem.getSize()); + } } return stream; diff --git a/source/modules/juce_audio_processors/juce_audio_processors.h b/source/modules/juce_audio_processors/juce_audio_processors.h index 3854d2af5..281165680 100644 --- a/source/modules/juce_audio_processors/juce_audio_processors.h +++ b/source/modules/juce_audio_processors/juce_audio_processors.h @@ -76,6 +76,7 @@ class AudioProcessor; #include "processors/juce_AudioPlayHead.h" #include "processors/juce_AudioProcessorEditor.h" #include "processors/juce_AudioProcessorListener.h" +#include "processors/juce_AudioProcessorParameter.h" #include "processors/juce_AudioProcessor.h" #include "processors/juce_PluginDescription.h" #include "processors/juce_AudioPluginInstance.h" diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 068a96547..0dffb423e 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -120,19 +120,6 @@ void AudioProcessor::setParameterNotifyingHost (const int parameterIndex, sendParamChangeMessageToListeners (parameterIndex, newValue); } -String AudioProcessor::getParameterName (int parameterIndex, int maximumStringLength) -{ - return getParameterName (parameterIndex).substring (0, maximumStringLength); -} - -String AudioProcessor::getParameterText (int parameterIndex, int maximumStringLength) -{ - return getParameterText (parameterIndex).substring (0, maximumStringLength); -} - -int AudioProcessor::getParameterNumSteps (int /*parameterIndex*/) { return 0x7fffffff; } -float AudioProcessor::getParameterDefaultValue (int /*parameterIndex*/) { return 0.0f; } - AudioProcessorListener* AudioProcessor::getListenerLocked (const int index) const noexcept { const ScopedLock sl (listenerLock); @@ -203,10 +190,129 @@ void AudioProcessor::updateHostDisplay() l->audioProcessorChanged (this); } -String AudioProcessor::getParameterLabel (int) const { return String(); } -bool AudioProcessor::isParameterOrientationInverted (int) const { return false; } -bool AudioProcessor::isParameterAutomatable (int) const { return true; } -bool AudioProcessor::isMetaParameter (int) const { return false; } +const OwnedArray& AudioProcessor::getParameters() const noexcept +{ + return managedParameters; +} + +int AudioProcessor::getNumParameters() +{ + return managedParameters.size(); +} + +float AudioProcessor::getParameter (int index) +{ + if (AudioProcessorParameter* p = getParamChecked (index)) + return p->getValue(); + + return 0; +} + +void AudioProcessor::setParameter (int index, float newValue) +{ + if (AudioProcessorParameter* p = getParamChecked (index)) + p->setValue (newValue); +} + +float AudioProcessor::getParameterDefaultValue (int index) +{ + if (AudioProcessorParameter* p = managedParameters[index]) + return p->getDefaultValue(); + + return 0; +} + +const String AudioProcessor::getParameterName (int index) +{ + if (AudioProcessorParameter* p = getParamChecked (index)) + return p->getName (512); + + return String(); +} + +String AudioProcessor::getParameterName (int index, int maximumStringLength) +{ + if (AudioProcessorParameter* p = managedParameters[index]) + return p->getName (maximumStringLength); + + return getParameterName (index).substring (0, maximumStringLength); +} + +const String AudioProcessor::getParameterText (int index) +{ + return getParameterText (index, 1024); +} + +String AudioProcessor::getParameterText (int index, int maximumStringLength) +{ + if (AudioProcessorParameter* p = managedParameters[index]) + return p->getText (p->getValue(), maximumStringLength); + + return getParameterText (index).substring (0, maximumStringLength); +} + +int AudioProcessor::getParameterNumSteps (int index) +{ + if (AudioProcessorParameter* p = managedParameters[index]) + return p->getNumSteps(); + + return AudioProcessor::getDefaultNumParameterSteps(); +} + +int AudioProcessor::getDefaultNumParameterSteps() noexcept +{ + return 0x7fffffff; +} + +String AudioProcessor::getParameterLabel (int index) const +{ + if (AudioProcessorParameter* p = managedParameters[index]) + return p->getLabel(); + + return String(); +} + +bool AudioProcessor::isParameterAutomatable (int index) const +{ + if (AudioProcessorParameter* p = managedParameters[index]) + return p->isAutomatable(); + + return true; +} + +bool AudioProcessor::isParameterOrientationInverted (int index) const +{ + if (AudioProcessorParameter* p = managedParameters[index]) + return p->isOrientationInverted(); + + return false; +} + +bool AudioProcessor::isMetaParameter (int index) const +{ + if (AudioProcessorParameter* p = managedParameters[index]) + return p->isMetaParameter(); + + return false; +} + +AudioProcessorParameter* AudioProcessor::getParamChecked (int index) const noexcept +{ + AudioProcessorParameter* p = managedParameters[index]; + + // If you hit this, then you're either trying to access parameters that are out-of-range, + // or you're not using addParameter and the managed parameter list, but have failed + // to override some essential virtual methods and implement them appropriately. + jassert (p != nullptr); + return p; +} + +void AudioProcessor::addParameter (AudioProcessorParameter* p) +{ + p->processor = this; + p->parameterIndex = managedParameters.size(); + managedParameters.add (p); +} void AudioProcessor::suspendProcessing (const bool shouldBeSuspended) { @@ -299,6 +405,47 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeIn void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {} void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {} +//============================================================================== +AudioProcessorParameter::AudioProcessorParameter() noexcept + : processor (nullptr), parameterIndex (-1) +{} + +AudioProcessorParameter::~AudioProcessorParameter() {} + +void AudioProcessorParameter::setValueNotifyingHost (float newValue) +{ + // This method can't be used until the parameter has been attached to a processor! + jassert (processor != nullptr && parameterIndex >= 0); + + return processor->setParameterNotifyingHost (parameterIndex, newValue); +} + +void AudioProcessorParameter::beginChangeGesture() +{ + // This method can't be used until the parameter has been attached to a processor! + jassert (processor != nullptr && parameterIndex >= 0); + + processor->beginParameterChangeGesture (parameterIndex); +} + +void AudioProcessorParameter::endChangeGesture() +{ + // This method can't be used until the parameter has been attached to a processor! + jassert (processor != nullptr && parameterIndex >= 0); + + processor->endParameterChangeGesture (parameterIndex); +} + +bool AudioProcessorParameter::isOrientationInverted() const { return false; } +bool AudioProcessorParameter::isAutomatable() const { return true; } +bool AudioProcessorParameter::isMetaParameter() const { return false; } +int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); } + +String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const +{ + return String (value, 2); +} + //============================================================================== bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept { diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h index 9d1ba93ec..9fe9911db 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -379,10 +379,10 @@ public: /** This must return the correct value immediately after the object has been created, and mustn't change the number of parameters later. */ - virtual int getNumParameters() = 0; + virtual int getNumParameters(); /** Returns the name of a particular parameter. */ - virtual const String getParameterName (int parameterIndex) = 0; + virtual const String getParameterName (int parameterIndex); /** Called by the host to find out the value of one of the filter's parameters. @@ -392,10 +392,10 @@ public: It's also likely to be called by non-UI threads, so the code in here should be thread-aware. */ - virtual float getParameter (int parameterIndex) = 0; + virtual float getParameter (int parameterIndex); /** Returns the value of a parameter as a text string. */ - virtual const String getParameterText (int parameterIndex) = 0; + virtual const String getParameterText (int parameterIndex); /** Returns the name of a parameter as a text string with a preferred maximum length. If you want to provide customised short versions of your parameter names that @@ -416,12 +416,18 @@ public: virtual String getParameterText (int parameterIndex, int maximumStringLength); /** Returns the number of discrete steps that this parameter can represent. - The default return value if you don't implement this method is 0x7fffffff. + The default return value if you don't implement this method is + AudioProcessor::getDefaultNumParameterSteps(). If your parameter is boolean, then you may want to make this return 2. The value that is returned may or may not be used, depending on the host. */ virtual int getParameterNumSteps (int parameterIndex); + /** Returns the default number of steps for a parameter. + @see getParameterNumSteps + */ + static int getDefaultNumParameterSteps() noexcept; + /** Returns the default value for the parameter. By default, this just returns 0. The value that is returned may or may not be used, depending on the host. @@ -451,7 +457,7 @@ public: The value passed will be between 0 and 1.0. */ - virtual void setParameter (int parameterIndex, float newValue) = 0; + virtual void setParameter (int parameterIndex, float newValue); /** Your filter can call this when it needs to change one of its parameters. @@ -503,6 +509,16 @@ public: */ void updateHostDisplay(); + //============================================================================== + /** Adds a parameter to the list. + The parameter object will be managed and deleted automatically by the list + when no longer needed. + */ + void addParameter (AudioProcessorParameter*); + + /** Returns the current list of parameters. */ + const OwnedArray& getParameters() const noexcept; + //============================================================================== /** Returns the number of preset programs the filter supports. @@ -582,7 +598,7 @@ public: //============================================================================== /** LV2 specific calls, saving/restore as string. */ virtual String getStateInformationString () { return String::empty; } - virtual void setStateInformationString (const String& data) {} + virtual void setStateInformationString (const String&) {} //============================================================================== /** Adds a listener that will be called when an aspect of this processor changes. */ @@ -668,6 +684,9 @@ private: CriticalSection callbackLock, listenerLock; String inputSpeakerArrangement, outputSpeakerArrangement; + OwnedArray managedParameters; + AudioProcessorParameter* getParamChecked (int) const noexcept; + #if JUCE_DEBUG BigInteger changingParams; #endif diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h new file mode 100644 index 000000000..8b4d2cf32 --- /dev/null +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h @@ -0,0 +1,158 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED +#define JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED + + +//============================================================================== +/** An abstract base class for parameter objects that can be added to an + AudioProcessor. + + @see AudioProcessor::addParameter +*/ +class JUCE_API AudioProcessorParameter +{ +public: + AudioProcessorParameter() noexcept; + + /** Destructor. */ + virtual ~AudioProcessorParameter(); + + /** Called by the host to find out the value of this parameter. + + Hosts will expect the value returned to be between 0 and 1.0. + + This could be called quite frequently, so try to make your code efficient. + It's also likely to be called by non-UI threads, so the code in here should + be thread-aware. + */ + virtual float getValue() const = 0; + + /** The host will call this method to change the value of one of the filter's parameters. + + The host may call this at any time, including during the audio processing + callback, so the filter has to process this very fast and avoid blocking. + + If you want to set the value of a parameter internally, e.g. from your + editor component, then don't call this directly - instead, use the + setValueNotifyingHost() method, which will also send a message to + the host telling it about the change. If the message isn't sent, the host + won't be able to automate your parameters properly. + + The value passed will be between 0 and 1.0. + */ + virtual void setValue (float newValue) = 0; + + /** Your filter can call this when it needs to change one of its parameters. + + This could happen when the editor or some other internal operation changes + a parameter. This method will call the setParameter() method to change the + value, and will then send a message to the host telling it about the change. + + Note that to make sure the host correctly handles automation, you should call + the beginChangeGesture() and endChangeGesture() methods to tell the host when + the user has started and stopped changing the parameter. + */ + void setValueNotifyingHost (float newValue); + + /** Sends a signal to the host to tell it that the user is about to start changing this + parameter. + This allows the host to know when a parameter is actively being held by the user, and + it may use this information to help it record automation. + If you call this, it must be matched by a later call to endChangeGesture(). + */ + void beginChangeGesture(); + + /** Tells the host that the user has finished changing this parameter. + This allows the host to know when a parameter is actively being held by the user, + and it may use this information to help it record automation. + A call to this method must follow a call to beginChangeGesture(). + */ + void endChangeGesture(); + + /** This should return the default value for this parameter. */ + virtual float getDefaultValue() const = 0; + + /** Returns the name to display for this parameter, which should be made + to fit within the given string length. + */ + virtual String getName (int maximumStringLength) const = 0; + + /** Some parameters may be able to return a label string for + their units. For example "Hz" or "%". + */ + virtual String getLabel() const = 0; + + /** Returns the number of discrete interval steps that this parameter's range + should be quantised into. + + If you want a continuous range of values, don't override this method, and allow + the default implementation to return AudioProcessor::getDefaultNumParameterSteps(). + If your parameter is boolean, then you may want to make this return 2. + The value that is returned may or may not be used, depending on the host. + */ + virtual int getNumSteps() const; + + /** Returns a textual version of the supplied parameter value. + The default implementation just returns the floating point value + as a string, but this could do anything you need for a custom type + of value. + */ + virtual String getText (float value, int /*maximumStringLength*/) const; + + /** Should parse a string and return the appropriate value for it. */ + virtual float getValueForText (const String& text) const = 0; + + /** This can be overridden to tell the host that this parameter operates in the + reverse direction. + (Not all plugin formats or hosts will actually use this information). + */ + virtual bool isOrientationInverted() const; + + /** Returns true if the host can automate this parameter. + By default, this returns true. + */ + virtual bool isAutomatable() const; + + /** Should return true if this parameter is a "meta" parameter. + A meta-parameter is a parameter that changes other params. It is used + by some hosts (e.g. AudioUnit hosts). + By default this returns false. + */ + virtual bool isMetaParameter() const; + + /** Returns the index of this parameter in its parent processor's parameter list. */ + int getParameterIndex() const noexcept { return parameterIndex; } + +private: + friend class AudioProcessor; + AudioProcessor* processor; + int parameterIndex; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameter) +}; + + +#endif // JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED diff --git a/source/modules/juce_core/containers/juce_OwnedArray.h b/source/modules/juce_core/containers/juce_OwnedArray.h index a116df875..be7448c8d 100644 --- a/source/modules/juce_core/containers/juce_OwnedArray.h +++ b/source/modules/juce_core/containers/juce_OwnedArray.h @@ -830,7 +830,7 @@ public: This will use a comparator object to sort the elements into order. The object passed must have a method of the form: @code - int compareElements (ElementType first, ElementType second); + int compareElements (ElementType* first, ElementType* second); @endcode ..and this method must return: diff --git a/source/modules/juce_core/files/juce_WildcardFileFilter.cpp b/source/modules/juce_core/files/juce_WildcardFileFilter.cpp index 1fef4553a..f18a2eb9a 100644 --- a/source/modules/juce_core/files/juce_WildcardFileFilter.cpp +++ b/source/modules/juce_core/files/juce_WildcardFileFilter.cpp @@ -26,32 +26,7 @@ ============================================================================== */ -WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns, - const String& directoryWildcardPatterns, - const String& desc) - : FileFilter (desc.isEmpty() ? fileWildcardPatterns - : (desc + " (" + fileWildcardPatterns + ")")) -{ - parse (fileWildcardPatterns, fileWildcards); - parse (directoryWildcardPatterns, directoryWildcards); -} - -WildcardFileFilter::~WildcardFileFilter() -{ -} - -bool WildcardFileFilter::isFileSuitable (const File& file) const -{ - return match (file, fileWildcards); -} - -bool WildcardFileFilter::isDirectorySuitable (const File& file) const -{ - return match (file, directoryWildcards); -} - -//============================================================================== -void WildcardFileFilter::parse (const String& pattern, StringArray& result) +static void parseWildcard (const String& pattern, StringArray& result) { result.addTokens (pattern.toLowerCase(), ";,", "\"'"); @@ -65,7 +40,7 @@ void WildcardFileFilter::parse (const String& pattern, StringArray& result) result.set (i, "*"); } -bool WildcardFileFilter::match (const File& file, const StringArray& wildcards) +static bool matchWildcard (const File& file, const StringArray& wildcards) { const String filename (file.getFileName()); @@ -75,3 +50,27 @@ bool WildcardFileFilter::match (const File& file, const StringArray& wildcards) return false; } + +WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns, + const String& directoryWildcardPatterns, + const String& desc) + : FileFilter (desc.isEmpty() ? fileWildcardPatterns + : (desc + " (" + fileWildcardPatterns + ")")) +{ + parseWildcard (fileWildcardPatterns, fileWildcards); + parseWildcard (directoryWildcardPatterns, directoryWildcards); +} + +WildcardFileFilter::~WildcardFileFilter() +{ +} + +bool WildcardFileFilter::isFileSuitable (const File& file) const +{ + return matchWildcard (file, fileWildcards); +} + +bool WildcardFileFilter::isDirectorySuitable (const File& file) const +{ + return matchWildcard (file, directoryWildcards); +} diff --git a/source/modules/juce_core/files/juce_WildcardFileFilter.h b/source/modules/juce_core/files/juce_WildcardFileFilter.h index 166ae4acb..fb398ce63 100644 --- a/source/modules/juce_core/files/juce_WildcardFileFilter.h +++ b/source/modules/juce_core/files/juce_WildcardFileFilter.h @@ -75,12 +75,8 @@ private: //============================================================================== StringArray fileWildcards, directoryWildcards; - static void parse (const String& pattern, StringArray& result); - static bool match (const File& file, const StringArray& wildcards); - JUCE_LEAK_DETECTOR (WildcardFileFilter) }; - #endif // JUCE_WILDCARDFILEFILTER_H_INCLUDED diff --git a/source/modules/juce_core/javascript/juce_Javascript.cpp b/source/modules/juce_core/javascript/juce_Javascript.cpp index bbcedb7ba..e78bb3711 100644 --- a/source/modules/juce_core/javascript/juce_Javascript.cpp +++ b/source/modules/juce_core/javascript/juce_Javascript.cpp @@ -102,6 +102,7 @@ struct JavascriptEngine::RootObject : public DynamicObject static bool isNumericOrUndefined (const var& v) { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool() || v.isUndefined(); } static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s, 8); return b.toInt64(); } static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; } + static var* getPropertyPointer (DynamicObject* o, Identifier i) { return o->getProperties().getVarPointer (i); } //============================================================================== struct CodeLocation @@ -139,13 +140,13 @@ struct JavascriptEngine::RootObject : public DynamicObject { if (DynamicObject* o = targetObject.getDynamicObject()) { - if (var* prop = o->getProperties().getVarPointer (functionName)) + if (const var* prop = getPropertyPointer (o, functionName)) return *prop; for (DynamicObject* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr; p = p->getProperty (getPrototypeIdentifier()).getDynamicObject()) { - if (var* prop = p->getProperties().getVarPointer (functionName)) + if (const var* prop = getPropertyPointer (p, functionName)) return *prop; } } @@ -168,14 +169,14 @@ struct JavascriptEngine::RootObject : public DynamicObject var* findRootClassProperty (Identifier className, Identifier propName) const { if (DynamicObject* cls = root->getProperty (className).getDynamicObject()) - return cls->getProperties().getVarPointer (propName); + return getPropertyPointer (cls, propName); return nullptr; } var findSymbolInParentScopes (Identifier name) const { - if (var* v = scope->getProperties().getVarPointer (name)) + if (const var* v = getPropertyPointer (scope, name)) return *v; return parent != nullptr ? parent->findSymbolInParentScopes (name) @@ -184,13 +185,11 @@ struct JavascriptEngine::RootObject : public DynamicObject bool findAndInvokeMethod (Identifier function, const var::NativeFunctionArgs& args, var& result) const { - const NamedValueSet& props = scope->getProperties(); - DynamicObject* target = args.thisObject.getDynamicObject(); if (target == nullptr || target == scope) { - if (const var* m = props.getVarPointer (function)) + if (const var* m = getPropertyPointer (scope, function)) { if (FunctionObject* fo = dynamic_cast (m->getObject())) { @@ -200,6 +199,8 @@ struct JavascriptEngine::RootObject : public DynamicObject } } + const NamedValueSet& props = scope->getProperties(); + for (int i = 0; i < props.size(); ++i) if (DynamicObject* o = props.getValueAt (i).getDynamicObject()) if (Scope (this, root, o).findAndInvokeMethod (function, args, result)) @@ -353,7 +354,7 @@ struct JavascriptEngine::RootObject : public DynamicObject void assign (const Scope& s, const var& newValue) const override { - if (var* v = s.scope->getProperties().getVarPointer (name)) + if (var* v = getPropertyPointer (s.scope, name)) *v = newValue; else s.root->setProperty (name, newValue); @@ -378,7 +379,7 @@ struct JavascriptEngine::RootObject : public DynamicObject } if (DynamicObject* o = p.getDynamicObject()) - if (var* v = o->getProperties().getVarPointer (child)) + if (const var* v = getPropertyPointer (o, child)) return *v; return var::undefined(); @@ -544,7 +545,7 @@ struct JavascriptEngine::RootObject : public DynamicObject { DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {} var getWithDoubles (double a, double b) const override { return b != 0 ? a / b : std::numeric_limits::infinity(); } - var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a / b) : var (std::numeric_limits::infinity()); } + var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a / (double) b) : var (std::numeric_limits::infinity()); } }; struct ModuloOp : public BinaryOperator @@ -1658,7 +1659,7 @@ JavascriptEngine::JavascriptEngine() : maximumExecutionTime (15.0), root (new R JavascriptEngine::~JavascriptEngine() {} -void JavascriptEngine::prepareTimeout() const { root->timeout = Time::getCurrentTime() + maximumExecutionTime; } +void JavascriptEngine::prepareTimeout() const noexcept { root->timeout = Time::getCurrentTime() + maximumExecutionTime; } void JavascriptEngine::registerNativeObject (Identifier name, DynamicObject* object) { @@ -1714,6 +1715,11 @@ var JavascriptEngine::callFunction (Identifier function, const var::NativeFuncti return returnVal; } +const NamedValueSet& JavascriptEngine::getRootObjectProperties() const noexcept +{ + return root->getProperties(); +} + #if JUCE_MSVC #pragma warning (pop) #endif diff --git a/source/modules/juce_core/javascript/juce_Javascript.h b/source/modules/juce_core/javascript/juce_Javascript.h index 42368a4fa..eafc3cdb6 100644 --- a/source/modules/juce_core/javascript/juce_Javascript.h +++ b/source/modules/juce_core/javascript/juce_Javascript.h @@ -96,10 +96,13 @@ public: */ RelativeTime maximumExecutionTime; + /** Provides access to the set of properties of the root namespace object. */ + const NamedValueSet& getRootObjectProperties() const noexcept; + private: JUCE_PUBLIC_IN_DLL_BUILD (struct RootObject) - ReferenceCountedObjectPtr root; - void prepareTimeout() const; + const ReferenceCountedObjectPtr root; + void prepareTimeout() const noexcept; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine) }; diff --git a/source/modules/juce_core/juce_core.cpp b/source/modules/juce_core/juce_core.cpp index b15fdd5c9..7cfc66453 100644 --- a/source/modules/juce_core/juce_core.cpp +++ b/source/modules/juce_core/juce_core.cpp @@ -71,16 +71,19 @@ #endif #else - #if JUCE_LINUX || JUCE_ANDROID || JUCE_HAIKU + #if JUCE_LINUX || JUCE_ANDROID #include #include - #if ! JUCE_HAIKU - #include - #endif + #include #include #include #endif + #if JUCE_LINUX + #include + #include + #endif + #include #include #include @@ -90,14 +93,12 @@ #include #include - #if JUCE_LINUX + #if ! JUCE_ANDROID #include - #include #endif #endif #if JUCE_MAC || JUCE_IOS - #include #include #include #endif @@ -216,10 +217,6 @@ namespace juce #include "native/juce_android_SystemStats.cpp" #include "native/juce_android_Threads.cpp" -//============================================================================== -#elif JUCE_HAIKU -// TODO - #endif #include "threads/juce_ChildProcess.cpp" diff --git a/source/modules/juce_core/juce_core.h b/source/modules/juce_core/juce_core.h index 626e2817c..323529b70 100644 --- a/source/modules/juce_core/juce_core.h +++ b/source/modules/juce_core/juce_core.h @@ -129,10 +129,6 @@ #define JUCE_STRING_UTF_TYPE 8 #endif -#ifndef JUCE_USE_VFORK - #define JUCE_USE_VFORK 0 -#endif - //============================================================================= //============================================================================= #if JUCE_MSVC @@ -198,6 +194,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe #include "threads/juce_ScopedLock.h" #include "threads/juce_CriticalSection.h" #include "maths/juce_Range.h" +#include "maths/juce_NormalisableRange.h" #include "containers/juce_ElementComparator.h" #include "containers/juce_ArrayAllocationBase.h" #include "containers/juce_Array.h" diff --git a/source/modules/juce_core/maths/juce_NormalisableRange.h b/source/modules/juce_core/maths/juce_NormalisableRange.h new file mode 100644 index 000000000..800c61630 --- /dev/null +++ b/source/modules/juce_core/maths/juce_NormalisableRange.h @@ -0,0 +1,169 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + 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. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_NORMALISABLERANGE_H_INCLUDED +#define JUCE_NORMALISABLERANGE_H_INCLUDED + + +//============================================================================== +/** + Represents a mapping between an arbitrary range of values and a + normalised 0->1 range. + + The properties of the mapping also include an optional snapping interval + and skew-factor. + + @see Range +*/ +template +class NormalisableRange +{ +public: + /** Creates a continuous range that performs a dummy mapping. */ + NormalisableRange() noexcept : start(), end (1), interval(), skew (static_cast (1)) {} + + /** Creates a copy of another range. */ + NormalisableRange (const NormalisableRange& other) noexcept + : start (other.start), end (other.end), + interval (other.interval), skew (other.skew) + { + checkInvariants(); + } + + /** Creates a copy of another range. */ + NormalisableRange& operator= (const NormalisableRange& other) noexcept + { + start = other.start; + end = other.end; + interval = other.interval; + skew = other.skew; + checkInvariants(); + return *this; + } + + /** Creates a NormalisableRange with a given range, interval and skew factor. */ + NormalisableRange (ValueType rangeStart, + ValueType rangeEnd, + ValueType intervalValue, + ValueType skewFactor) noexcept + : start (rangeStart), end (rangeEnd), + interval (intervalValue), skew (skewFactor) + { + checkInvariants(); + } + + /** Creates a NormalisableRange with a given range and interval, but a dummy skew-factor. */ + NormalisableRange (ValueType rangeStart, + ValueType rangeEnd, + ValueType intervalValue) noexcept + : start (rangeStart), end (rangeEnd), + interval (intervalValue), skew (static_cast (1)) + { + checkInvariants(); + } + + /** Creates a NormalisableRange with a given range, continuous interval, but a dummy skew-factor. */ + NormalisableRange (ValueType rangeStart, + ValueType rangeEnd) noexcept + : start (rangeStart), end (rangeEnd), + interval(), skew (static_cast (1)) + { + checkInvariants(); + } + + /** Uses the properties of this mapping to convert a non-normalised value to + its 0->1 representation. + */ + ValueType convertTo0to1 (ValueType v) const noexcept + { + ValueType proportion = (v - start) / (end - start); + + if (skew != static_cast (1)) + proportion = pow (proportion, skew); + + return proportion; + } + + /** Uses the properties of this mapping to convert a normalised 0->1 value to + its full-range representation. + */ + ValueType convertFrom0to1 (ValueType proportion) const noexcept + { + if (skew != static_cast (1) && proportion > ValueType()) + proportion = exp (log (proportion) / skew); + + return start + (end - start) * proportion; + } + + /** Takes a non-normalised value and snaps it based on the interval property of + this NormalisedRange. */ + ValueType snapToLegalValue (ValueType v) const noexcept + { + if (interval > ValueType()) + v = start + interval * std::floor ((v - start) / interval + static_cast (0.5)); + + if (v <= start || end <= start) + return start; + + if (v >= end) + return end; + + return v; + } + + /** The start of the non-normalised range. */ + ValueType start; + + /** The end of the non-normalised range. */ + ValueType end; + + /** The snapping interval that should be used (in non-normalised value). Use 0 for a continuous range. */ + ValueType interval; + + /** An optional skew factor that alters the way values are distribute across the range. + + The skew factor lets you skew the mapping logarithmically so that larger or smaller + values are given a larger proportion of the avilable space. + + A factor of 1.0 has no skewing effect at all. If the factor is < 1.0, the lower end + of the range will fill more of the slider's length; if the factor is > 1.0, the upper + end of the range will be expanded. + */ + ValueType skew; + +private: + void checkInvariants() const + { + jassert (end > start); + jassert (interval >= ValueType()); + jassert (skew > ValueType()); + } +}; + + +#endif // JUCE_NORMALISABLERANGE_H_INCLUDED diff --git a/source/modules/juce_core/memory/juce_MemoryBlock.cpp b/source/modules/juce_core/memory/juce_MemoryBlock.cpp index a82703713..6097c7e7e 100644 --- a/source/modules/juce_core/memory/juce_MemoryBlock.cpp +++ b/source/modules/juce_core/memory/juce_MemoryBlock.cpp @@ -88,14 +88,14 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept - : data (static_cast &&> (other.data)), + : data (static_cast&&> (other.data)), size (other.size) { } MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept { - data = static_cast &&> (other.data); + data = static_cast&&> (other.data); size = other.size; return *this; } @@ -346,7 +346,7 @@ void MemoryBlock::loadFromHexString (StringRef hex) if (c == 0) { - setSize (static_cast (dest - data)); + setSize (static_cast (dest - data)); return; } } diff --git a/source/modules/juce_core/memory/juce_ScopedPointer.h b/source/modules/juce_core/memory/juce_ScopedPointer.h index a7fcff0dd..1aa8031e1 100644 --- a/source/modules/juce_core/memory/juce_ScopedPointer.h +++ b/source/modules/juce_core/memory/juce_ScopedPointer.h @@ -182,11 +182,11 @@ public: /** Swaps this object with that of another ScopedPointer. The two objects simply exchange their pointers. */ - void swapWith (ScopedPointer & other) noexcept + void swapWith (ScopedPointer& other) noexcept { // Two ScopedPointers should never be able to refer to the same object - if // this happens, you must have done something dodgy! - jassert (object != other.object || this == other.getAddress()); + jassert (object != other.object || this == other.getAddress() || object == nullptr); std::swap (object, other.object); } @@ -231,7 +231,7 @@ private: template bool operator== (const ScopedPointer& pointer1, ObjectType* const pointer2) noexcept { - return static_cast (pointer1) == pointer2; + return static_cast (pointer1) == pointer2; } /** Compares a ScopedPointer with another pointer. @@ -240,7 +240,7 @@ bool operator== (const ScopedPointer& pointer1, ObjectType* const po template bool operator!= (const ScopedPointer& pointer1, ObjectType* const pointer2) noexcept { - return static_cast (pointer1) != pointer2; + return static_cast (pointer1) != pointer2; } //============================================================================== diff --git a/source/modules/juce_core/native/juce_BasicNativeHeaders.h b/source/modules/juce_core/native/juce_BasicNativeHeaders.h index 3bdb3846f..55c0c8c30 100644 --- a/source/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/source/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -212,33 +212,6 @@ #include #include #include - -//============================================================================== -#elif JUCE_HAIKU - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include #endif // Need to clear various moronic redefinitions made by system headers.. diff --git a/source/modules/juce_core/native/juce_linux_Network.cpp b/source/modules/juce_core/native/juce_linux_Network.cpp index 7543a77d6..6871aa546 100644 --- a/source/modules/juce_core/native/juce_linux_Network.cpp +++ b/source/modules/juce_core/native/juce_linux_Network.cpp @@ -31,26 +31,26 @@ void MACAddress::findAllAddresses (Array& result) const int s = socket (AF_INET, SOCK_DGRAM, 0); if (s != -1) { - char buf [1024]; - struct ifconf ifc; - ifc.ifc_len = sizeof (buf); - ifc.ifc_buf = buf; - ioctl (s, SIOCGIFCONF, &ifc); + struct ifaddrs* addrs = nullptr; - for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i) + if (getifaddrs (&addrs) != -1) { - struct ifreq ifr; - strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name); - - if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0 - && (ifr.ifr_flags & IFF_LOOPBACK) == 0 - && ioctl (s, SIOCGIFHWADDR, &ifr) == 0) + for (struct ifaddrs* i = addrs; i != nullptr; i = i->ifa_next) { - MACAddress ma ((const uint8*) ifr.ifr_hwaddr.sa_data); + struct ifreq ifr; + strcpy (ifr.ifr_name, i->ifa_name); + ifr.ifr_addr.sa_family = AF_INET; + + if (ioctl (s, SIOCGIFHWADDR, &ifr) == 0) + { + MACAddress ma ((const uint8*) ifr.ifr_hwaddr.sa_data); - if (! ma.isNull()) - result.addIfNotAlreadyThere (ma); + if (! ma.isNull()) + result.addIfNotAlreadyThere (ma); + } } + + freeifaddrs (addrs); } close (s); @@ -263,7 +263,7 @@ private: } } - String responseHeader (readResponse (socketHandle, timeOutTime)); + String responseHeader (readResponse (timeOutTime)); position = 0; if (responseHeader.isNotEmpty()) @@ -302,7 +302,7 @@ private: } //============================================================================== - String readResponse (const int socketHandle, const uint32 timeOutTime) + String readResponse (const uint32 timeOutTime) { int numConsecutiveLFs = 0; MemoryOutputStream buffer; @@ -338,7 +338,8 @@ private: dest << "\r\n" << key << ' ' << value; } - static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, const int port) + static void writeHost (MemoryOutputStream& dest, const bool isPost, + const String& path, const String& host, int /*port*/) { dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.0\r\nHost: " << host; } diff --git a/source/modules/juce_core/native/juce_posix_SharedCode.h b/source/modules/juce_core/native/juce_posix_SharedCode.h index 34f7e4e2d..757105b8a 100644 --- a/source/modules/juce_core/native/juce_posix_SharedCode.h +++ b/source/modules/juce_core/native/juce_posix_SharedCode.h @@ -142,7 +142,7 @@ void JUCE_CALLTYPE Thread::sleep (int millisecs) void JUCE_CALLTYPE Process::terminate() { - #if JUCE_ANDROID || JUCE_HAIKU + #if JUCE_ANDROID _exit (EXIT_FAILURE); #else std::_Exit (EXIT_FAILURE); @@ -203,10 +203,6 @@ namespace #define JUCE_STAT stat #endif - #if JUCE_HAIKU - #define statfs statvfs - #endif - bool juce_stat (const String& fileName, juce_statStruct& info) { return fileName.isNotEmpty() @@ -227,6 +223,12 @@ namespace return statfs (f.getFullPathName().toUTF8(), &result) == 0; } + #if (JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || JUCE_IOS + static int64 getCreationTime (const juce_statStruct& s) noexcept { return (int64) s.st_birthtime; } + #else + static int64 getCreationTime (const juce_statStruct& s) noexcept { return (int64) s.st_ctime; } + #endif + void updateStatInfoForFile (const String& path, bool* const isDir, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { @@ -236,9 +238,9 @@ namespace const bool statOk = juce_stat (path, info); if (isDir != nullptr) *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); - if (fileSize != nullptr) *fileSize = statOk ? info.st_size : 0; - if (modTime != nullptr) *modTime = Time (statOk ? (int64) info.st_mtime * 1000 : 0); - if (creationTime != nullptr) *creationTime = Time (statOk ? (int64) info.st_ctime * 1000 : 0); + if (fileSize != nullptr) *fileSize = statOk ? (int64) info.st_size : 0; + if (modTime != nullptr) *modTime = Time (statOk ? (int64) info.st_mtime * 1000 : 0); + if (creationTime != nullptr) *creationTime = Time (statOk ? getCreationTime (info) * 1000 : 0); } if (isReadOnly != nullptr) @@ -546,11 +548,7 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode) if (m != MAP_FAILED) { address = m; - #if JUCE_HAIKU - posix_madvise (m, (size_t) range.getLength(), POSIX_MADV_SEQUENTIAL); - #else - madvise (m, (size_t) range.getLength(), MADV_SEQUENTIAL); - #endif + madvise (m, (size_t) range.getLength(), MADV_SEQUENTIAL); } else { @@ -671,11 +669,8 @@ int File::getVolumeSerialNumber() const return result; } -#if JUCE_HAIKU - #undef statvfs -#endif - //============================================================================== +#if ! JUCE_IOS void juce_runSystemCommand (const String&); void juce_runSystemCommand (const String& command) { @@ -696,7 +691,7 @@ String juce_getOutputFromCommand (const String& command) tempFile.deleteFile(); return result; } - +#endif //============================================================================== #if JUCE_IOS @@ -1012,15 +1007,15 @@ public: int pipeHandles[2] = { 0 }; - Array argv; - for (int i = 0; i < arguments.size(); ++i) - if (arguments[i].isNotEmpty()) - argv.add (const_cast (arguments[i].toUTF8().getAddress())); - - argv.add (nullptr); - if (pipe (pipeHandles) == 0) { + Array argv; + for (int i = 0; i < arguments.size(); ++i) + if (arguments[i].isNotEmpty()) + argv.add (const_cast (arguments[i].toUTF8().getAddress())); + + argv.add (nullptr); + #if JUCE_USE_VFORK const pid_t result = vfork(); #else @@ -1050,6 +1045,7 @@ public: close (pipeHandles[1]); #endif + execvp (argv[0], argv.getRawDataPointer()); exit (-1); } @@ -1120,11 +1116,6 @@ public: return 0; } - int getPID() const noexcept - { - return childPID; - } - int childPID; private: diff --git a/source/modules/juce_core/native/juce_win32_Network.cpp b/source/modules/juce_core/native/juce_win32_Network.cpp index 1b354532a..f0b6061b4 100644 --- a/source/modules/juce_core/native/juce_win32_Network.cpp +++ b/source/modules/juce_core/native/juce_win32_Network.cpp @@ -45,13 +45,14 @@ public: address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { - createConnection (progressCallback, progressCallbackContext); - - if (! isError()) + for (int maxRedirects = 10; --maxRedirects >= 0;) { - if (responseHeaders != nullptr) + createConnection (progressCallback, progressCallbackContext); + + if (! isError()) { DWORD bufferSizeBytes = 4096; + StringPairArray headers (false); for (;;) { @@ -65,11 +66,10 @@ public: for (int i = 0; i < headersArray.size(); ++i) { const String& header = headersArray[i]; - const String key (header.upToFirstOccurrenceOf (": ", false, false)); + const String key (header.upToFirstOccurrenceOf (": ", false, false)); const String value (header.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue ((*responseHeaders) [key]); - - responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + const String previousValue (headers[key]); + headers.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); } break; @@ -77,14 +77,34 @@ public: if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break; + + bufferSizeBytes += 4096; } - } - DWORD status = 0; - DWORD statusSize = sizeof (status); + DWORD status = 0; + DWORD statusSize = sizeof (status); + + if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, 0)) + { + statusCode = (int) status; + + if (status == 301 || status == 302 || status == 303 || status == 307) + { + const String newLocation (headers["Location"]); + + if (newLocation.isNotEmpty() && newLocation != address) + { + address = newLocation; + continue; + } + } + } + + if (responseHeaders != nullptr) + responseHeaders->addArray (headers); + } - if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, 0)) - statusCode = (int) status; + break; } } diff --git a/source/modules/juce_core/native/juce_win32_Threads.cpp b/source/modules/juce_core/native/juce_win32_Threads.cpp index ca22ea07c..1421c109f 100644 --- a/source/modules/juce_core/native/juce_win32_Threads.cpp +++ b/source/modules/juce_core/native/juce_win32_Threads.cpp @@ -534,11 +534,6 @@ public: return (uint32) exitCode; } - int getPID() const noexcept - { - return 0; - } - bool ok; private: diff --git a/source/modules/juce_core/network/juce_Socket.cpp b/source/modules/juce_core/network/juce_Socket.cpp index 97d0e0fb6..50da39cbc 100644 --- a/source/modules/juce_core/network/juce_Socket.cpp +++ b/source/modules/juce_core/network/juce_Socket.cpp @@ -263,6 +263,12 @@ namespace SocketHelpers return true; } + + static void makeReusable (int handle) noexcept + { + const int reuse = 1; + setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse)); + } } //============================================================================== @@ -418,8 +424,9 @@ bool StreamingSocket::createListener (const int newPortNumber, const String& loc if (handle < 0) return false; - const int reuse = 1; - setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse)); + #if ! JUCE_WINDOWS // on windows, adding this option produces behaviour different to posix + SocketHelpers::makeReusable (handle); + #endif if (bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) < 0 || listen (handle, SOMAXCONN) < 0) @@ -470,6 +477,7 @@ DatagramSocket::DatagramSocket (const int localPortNumber, const bool canBroadca SocketHelpers::initSockets(); handle = (int) socket (AF_INET, SOCK_DGRAM, 0); + SocketHelpers::makeReusable (handle); bindToPort (localPortNumber); } diff --git a/source/modules/juce_core/network/juce_URL.cpp b/source/modules/juce_core/network/juce_URL.cpp index 8b3afbdfd..76d2b1e79 100644 --- a/source/modules/juce_core/network/juce_URL.cpp +++ b/source/modules/juce_core/network/juce_URL.cpp @@ -179,6 +179,11 @@ String URL::toString (const bool includeGetParameters) const return url; } +bool URL::isEmpty() const noexcept +{ + return url.isEmpty(); +} + bool URL::isWellFormed() const { //xxx TODO @@ -342,7 +347,6 @@ InputStream* URL::createInputStream (const bool usePostCommand, if (! headers.endsWithChar ('\n')) headers << "\r\n"; - #if ! JUCE_HAIKU ScopedPointer wi (new WebInputStream (toString (! usePostCommand), usePostCommand, headersAndPostData, progressCallback, progressCallbackContext, @@ -352,9 +356,6 @@ InputStream* URL::createInputStream (const bool usePostCommand, *statusCode = wi->statusCode; return wi->isError() ? nullptr : wi.release(); - #else - return nullptr; // TODO - #endif } //============================================================================== diff --git a/source/modules/juce_core/network/juce_URL.h b/source/modules/juce_core/network/juce_URL.h index 4f511d870..06d8fb732 100644 --- a/source/modules/juce_core/network/juce_URL.h +++ b/source/modules/juce_core/network/juce_URL.h @@ -76,6 +76,9 @@ public: */ String toString (bool includeGetParameters) const; + /** Returns true if the URL is an empty string. */ + bool isEmpty() const noexcept; + /** True if it seems to be valid. */ bool isWellFormed() const; diff --git a/source/modules/juce_core/streams/juce_MemoryOutputStream.cpp b/source/modules/juce_core/streams/juce_MemoryOutputStream.cpp index 835b2bd79..adbaed2f2 100644 --- a/source/modules/juce_core/streams/juce_MemoryOutputStream.cpp +++ b/source/modules/juce_core/streams/juce_MemoryOutputStream.cpp @@ -182,7 +182,7 @@ int64 MemoryOutputStream::writeFromInputStream (InputStream& source, int64 maxNu if (availableData > 0) { - if (maxNumBytesToWrite > availableData) + if (maxNumBytesToWrite > availableData || maxNumBytesToWrite < 0) maxNumBytesToWrite = availableData; if (blockToUse != nullptr) diff --git a/source/modules/juce_core/system/juce_PlatformDefs.h b/source/modules/juce_core/system/juce_PlatformDefs.h index d28452d56..ee6532d5c 100644 --- a/source/modules/juce_core/system/juce_PlatformDefs.h +++ b/source/modules/juce_core/system/juce_PlatformDefs.h @@ -94,20 +94,34 @@ #define JUCE_ANALYZER_NORETURN #endif +//============================================================================== +#if JUCE_MSVC && ! DOXYGEN + #define MACRO_WITH_FORCED_SEMICOLON(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + do { x } while (false) \ + __pragma(warning(pop)) +#else + /** This is the good old C++ trick for creating a macro that forces the user to put + a semicolon after it when they use it. + */ + #define MACRO_WITH_FORCED_SEMICOLON(x) do { x } while (false) +#endif + //============================================================================== #if JUCE_DEBUG || DOXYGEN /** Writes a string to the standard error stream. This is only compiled in a debug build. @see Logger::outputDebugString */ - #define DBG(dbgtext) { juce::String tempDbgBuf; tempDbgBuf << dbgtext; juce::Logger::outputDebugString (tempDbgBuf); } + #define DBG(dbgtext) MACRO_WITH_FORCED_SEMICOLON (juce::String tempDbgBuf; tempDbgBuf << dbgtext; juce::Logger::outputDebugString (tempDbgBuf);) //============================================================================== /** This will always cause an assertion failure. It is only compiled in a debug build, (unless JUCE_LOG_ASSERTIONS is enabled for your build). @see jassert */ - #define jassertfalse { juce_LogCurrentAssertion; if (juce::juce_isRunningUnderDebugger()) juce_breakDebugger; JUCE_ANALYZER_NORETURN } + #define jassertfalse MACRO_WITH_FORCED_SEMICOLON (juce_LogCurrentAssertion; if (juce::juce_isRunningUnderDebugger()) juce_breakDebugger; JUCE_ANALYZER_NORETURN) //============================================================================== /** Platform-independent assertion macro. @@ -117,19 +131,19 @@ correct behaviour of your program! @see jassertfalse */ - #define jassert(expression) { if (! (expression)) jassertfalse; } + #define jassert(expression) MACRO_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) #else //============================================================================== // If debugging is disabled, these dummy debug and assertion macros are used.. #define DBG(dbgtext) - #define jassertfalse { juce_LogCurrentAssertion } + #define jassertfalse MACRO_WITH_FORCED_SEMICOLON (juce_LogCurrentAssertion) #if JUCE_LOG_ASSERTIONS - #define jassert(expression) { if (! (expression)) jassertfalse; } + #define jassert(expression) MACRO_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) #else - #define jassert(a) {} + #define jassert(a) MACRO_WITH_FORCED_SEMICOLON ( ; ) #endif #endif @@ -139,7 +153,7 @@ namespace juce { template struct JuceStaticAssert; - template <> struct JuceStaticAssert { static void dummy() {} }; + template <> struct JuceStaticAssert { static void dummy() {} }; } #endif @@ -211,6 +225,26 @@ namespace juce #define JUCE_STRINGIFY(item) JUCE_STRINGIFY_MACRO_HELPER (item) +//============================================================================== +#if JUCE_MSVC && ! defined (DOXYGEN) + #define JUCE_WARNING_HELPER(file, line, mess) message(file "(" JUCE_STRINGIFY (line) ") : Warning: " #mess) + #define JUCE_COMPILER_WARNING(message) __pragma(JUCE_WARNING_HELPER (__FILE__, __LINE__, message)); +#else + #ifndef DOXYGEN + #define JUCE_WARNING_HELPER(mess) message(#mess) + #endif + + /** This macro allows you to emit a custom compiler warning message. + Very handy for marking bits of code as "to-do" items, or for shaming + code written by your co-workers in a way that's hard to ignore. + + GCC and Clang provide the \#warning directive, but MSVC doesn't, so this macro + is a cross-compiler way to get the same functionality as \#warning. + */ + #define JUCE_COMPILER_WARNING(message) _Pragma(JUCE_STRINGIFY (JUCE_WARNING_HELPER (message))); +#endif + + //============================================================================== #if JUCE_CATCH_UNHANDLED_EXCEPTIONS @@ -317,6 +351,10 @@ namespace juce #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_DELETED_FUNCTION) #define JUCE_DELETED_FUNCTION = delete #endif + + #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && ! defined (JUCE_COMPILER_SUPPORTS_LAMBDAS) + #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 + #endif #endif #if JUCE_CLANG && defined (__has_feature) @@ -336,6 +374,13 @@ namespace juce #define JUCE_DELETED_FUNCTION = delete #endif + #if __has_feature (cxx_lambdas) \ + && ((JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_8) \ + || (JUCE_IOS && defined (__IPHONE_7_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) \ + || ! (JUCE_MAC || JUCE_IOS)) + #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 + #endif + #ifndef JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 #endif @@ -352,6 +397,7 @@ namespace juce #if defined (_MSC_VER) && _MSC_VER >= 1700 #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 + #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 #endif #ifndef JUCE_DELETED_FUNCTION diff --git a/source/modules/juce_core/system/juce_StandardHeader.h b/source/modules/juce_core/system/juce_StandardHeader.h index e87e565df..fcfd4c22e 100644 --- a/source/modules/juce_core/system/juce_StandardHeader.h +++ b/source/modules/juce_core/system/juce_StandardHeader.h @@ -72,6 +72,7 @@ #include #include #include +#include #if JUCE_USE_MSVC_INTRINSICS #include diff --git a/source/modules/juce_core/system/juce_SystemStats.cpp b/source/modules/juce_core/system/juce_SystemStats.cpp index 6b5656900..365e94d9f 100644 --- a/source/modules/juce_core/system/juce_SystemStats.cpp +++ b/source/modules/juce_core/system/juce_SystemStats.cpp @@ -97,7 +97,7 @@ String SystemStats::getStackBacktrace() { String result; - #if JUCE_ANDROID || JUCE_MINGW || JUCE_HAIKU + #if JUCE_ANDROID || JUCE_MINGW jassertfalse; // sorry, not implemented yet! #elif JUCE_WINDOWS diff --git a/source/modules/juce_core/system/juce_TargetPlatform.h b/source/modules/juce_core/system/juce_TargetPlatform.h index 00c49bb89..63470b668 100644 --- a/source/modules/juce_core/system/juce_TargetPlatform.h +++ b/source/modules/juce_core/system/juce_TargetPlatform.h @@ -51,8 +51,6 @@ #define JUCE_ANDROID 1 #elif defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 -#elif defined (__HAIKU__) - #define JUCE_HAIKU 1 #elif defined (__APPLE_CPP__) || defined(__APPLE_CC__) #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) #define Component CarbonDummyCompName @@ -142,7 +140,7 @@ #endif //============================================================================== -#if JUCE_LINUX || JUCE_ANDROID || JUCE_HAIKU +#if JUCE_LINUX || JUCE_ANDROID #ifdef _DEBUG #define JUCE_DEBUG 1 @@ -157,7 +155,7 @@ #define JUCE_BIG_ENDIAN 1 #endif - #if defined (__LP64__) || defined (_LP64) + #if defined (__LP64__) || defined (_LP64) || defined (__arm64__) #define JUCE_64BIT 1 #else #define JUCE_32BIT 1 diff --git a/source/modules/juce_core/text/juce_String.h b/source/modules/juce_core/text/juce_String.h index 4873bd9dd..6a511070b 100644 --- a/source/modules/juce_core/text/juce_String.h +++ b/source/modules/juce_core/text/juce_String.h @@ -1204,16 +1204,16 @@ public: //============================================================================== #if JUCE_MAC || JUCE_IOS || DOXYGEN - /** MAC ONLY - Creates a String from an OSX CFString. */ + /** OSX ONLY - Creates a String from an OSX CFString. */ static String fromCFString (CFStringRef cfString); - /** MAC ONLY - Converts this string to a CFString. + /** OSX ONLY - Converts this string to a CFString. Remember that you must use CFRelease() to free the returned string when you're finished with it. */ CFStringRef toCFString() const; - /** MAC ONLY - Returns a copy of this string in which any decomposed unicode characters have + /** OSX ONLY - Returns a copy of this string in which any decomposed unicode characters have been converted to their precomposed equivalents. */ String convertToPrecomposedUnicode() const; #endif diff --git a/source/modules/juce_core/threads/juce_ChildProcess.cpp b/source/modules/juce_core/threads/juce_ChildProcess.cpp index e98483782..4566b13f1 100644 --- a/source/modules/juce_core/threads/juce_ChildProcess.cpp +++ b/source/modules/juce_core/threads/juce_ChildProcess.cpp @@ -81,11 +81,6 @@ String ChildProcess::readAllProcessOutput() return result.toString(); } -uint32 ChildProcess::getPID() const noexcept -{ - return activeProcess != nullptr ? activeProcess->getPID() : 0; -} - //============================================================================== #if JUCE_UNIT_TESTS diff --git a/source/modules/juce_core/threads/juce_ChildProcess.h b/source/modules/juce_core/threads/juce_ChildProcess.h index 7a1c88c3e..0adcb57b7 100644 --- a/source/modules/juce_core/threads/juce_ChildProcess.h +++ b/source/modules/juce_core/threads/juce_ChildProcess.h @@ -106,8 +106,6 @@ public: */ bool kill(); - uint32 getPID() const noexcept; - private: //============================================================================== class ActiveProcess; diff --git a/source/modules/juce_core/xml/juce_XmlElement.cpp b/source/modules/juce_core/xml/juce_XmlElement.cpp index 0a9f0543a..866d10ab3 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.cpp +++ b/source/modules/juce_core/xml/juce_XmlElement.cpp @@ -53,7 +53,7 @@ static void sanityCheckTagName (const String& tag) (void) tag; // the tag name mustn't be empty, or it'll look like a text element! - jassert (tag.containsNonWhitespaceChars()) + jassert (tag.containsNonWhitespaceChars()); // The tag can't contain spaces or other characters that would create invalid XML! jassert (! tag.containsAnyOf (" <>/&(){}")); diff --git a/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp b/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp index 7b1fa737a..ece2121f3 100644 --- a/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp +++ b/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp @@ -82,7 +82,7 @@ File PropertiesFile::Options::getDefaultFile() const if (folderName.isNotEmpty()) dir = dir.getChildFile (folderName); - #elif JUCE_LINUX || JUCE_ANDROID || JUCE_HAIKU + #elif JUCE_LINUX || JUCE_ANDROID const File dir (File (commonToAllUsers ? "/var" : "~") .getChildFile (folderName.isNotEmpty() ? folderName : ("." + applicationName))); diff --git a/source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp b/source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp index 3557b90d8..07948c69e 100644 --- a/source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp +++ b/source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp @@ -29,10 +29,6 @@ struct UndoManager::ActionSet time (Time::getCurrentTime()) {} - OwnedArray actions; - String name; - Time time; - bool perform() const { for (int i = 0; i < actions.size(); ++i) @@ -60,6 +56,10 @@ struct UndoManager::ActionSet return total; } + + OwnedArray actions; + String name; + Time time; }; //============================================================================== @@ -101,6 +101,19 @@ void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep, //============================================================================== bool UndoManager::perform (UndoableAction* const newAction, const String& actionName) +{ + if (perform (newAction)) + { + if (actionName.isNotEmpty()) + setCurrentTransactionName (actionName); + + return true; + } + + return false; +} + +bool UndoManager::perform (UndoableAction* const newAction) { if (newAction != nullptr) { @@ -113,9 +126,6 @@ bool UndoManager::perform (UndoableAction* const newAction, const String& action return false; } - if (actionName.isNotEmpty()) - currentTransactionName = actionName; - if (action->perform()) { ActionSet* actionSet = getCurrentSet(); @@ -134,7 +144,7 @@ bool UndoManager::perform (UndoableAction* const newAction, const String& action } else { - actionSet = new ActionSet (currentTransactionName); + actionSet = new ActionSet (newTransactionName); transactions.insert (nextIndex, actionSet); ++nextIndex; } @@ -174,23 +184,31 @@ void UndoManager::clearFutureTransactions() } } -void UndoManager::beginNewTransaction (const String& actionName) +void UndoManager::beginNewTransaction() noexcept +{ + beginNewTransaction (String()); +} + +void UndoManager::beginNewTransaction (const String& actionName) noexcept { newTransaction = true; - currentTransactionName = actionName; + newTransactionName = actionName; } -void UndoManager::setCurrentTransactionName (const String& newName) +void UndoManager::setCurrentTransactionName (const String& newName) noexcept { - currentTransactionName = newName; + if (newTransaction) + newTransactionName = newName; + else if (ActionSet* action = getCurrentSet()) + action->name = newName; } //============================================================================== UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; } UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; } -bool UndoManager::canUndo() const { return getCurrentSet() != nullptr; } -bool UndoManager::canRedo() const { return getNextSet() != nullptr; } +bool UndoManager::canUndo() const noexcept { return getCurrentSet() != nullptr; } +bool UndoManager::canRedo() const noexcept { return getNextSet() != nullptr; } bool UndoManager::undo() { @@ -267,7 +285,7 @@ bool UndoManager::undoCurrentTransactionOnly() return newTransaction ? false : undo(); } -void UndoManager::getActionsInCurrentTransaction (Array & actionsFound) const +void UndoManager::getActionsInCurrentTransaction (Array& actionsFound) const { if (! newTransaction) if (const ActionSet* const s = getCurrentSet()) diff --git a/source/modules/juce_data_structures/undomanager/juce_UndoManager.h b/source/modules/juce_data_structures/undomanager/juce_UndoManager.h index eee9cbcfa..a3b691b69 100644 --- a/source/modules/juce_data_structures/undomanager/juce_UndoManager.h +++ b/source/modules/juce_data_structures/undomanager/juce_UndoManager.h @@ -98,16 +98,32 @@ public: //============================================================================== /** Performs an action and adds it to the undo history list. - @param action the action to perform - this will be deleted by the UndoManager - when no longer needed + @param action the action to perform - this object will be deleted by + the UndoManager when no longer needed + @returns true if the command succeeds - see UndoableAction::perform + @see beginNewTransaction + */ + bool perform (UndoableAction* action); + + /** Performs an action and also gives it a name. + + @param action the action to perform - this object will be deleted by + the UndoManager when no longer needed @param actionName if this string is non-empty, the current transaction will be given this name; if it's empty, the current transaction name will be left unchanged. See setCurrentTransactionName() @returns true if the command succeeds - see UndoableAction::perform @see beginNewTransaction */ - bool perform (UndoableAction* action, - const String& actionName = String()); + bool perform (UndoableAction* action, const String& actionName); + + /** Starts a new group of actions that together will be treated as a single transaction. + + All actions that are passed to the perform() method between calls to this + method are grouped together and undone/redone together by a single call to + undo() or redo(). + */ + void beginNewTransaction() noexcept; /** Starts a new group of actions that together will be treated as a single transaction. @@ -118,7 +134,7 @@ public: @param actionName a description of the transaction that is about to be performed */ - void beginNewTransaction (const String& actionName = String()); + void beginNewTransaction (const String& actionName) noexcept; /** Changes the name stored for the current transaction. @@ -126,19 +142,15 @@ public: called, but this can be used to change that name without starting a new transaction. */ - void setCurrentTransactionName (const String& newName); + void setCurrentTransactionName (const String& newName) noexcept; //============================================================================== /** Returns true if there's at least one action in the list to undo. @see getUndoDescription, undo, canRedo */ - bool canUndo() const; - - /** Returns the description of the transaction that would be next to get undone. - - The description returned is the one that was passed into beginNewTransaction - before the set of actions was performed. + bool canUndo() const noexcept; + /** Returns the name of the transaction that will be rolled-back when undo() is called. @see undo */ String getUndoDescription() const; @@ -172,7 +184,7 @@ public: The first item in the list is the earliest action performed. */ - void getActionsInCurrentTransaction (Array & actionsFound) const; + void getActionsInCurrentTransaction (Array& actionsFound) const; /** Returns the number of UndoableAction objects that have been performed during the transaction that is currently open. @@ -194,12 +206,9 @@ public: /** Returns true if there's at least one action in the list to redo. @see getRedoDescription, redo, canUndo */ - bool canRedo() const; - - /** Returns the description of the transaction that would be next to get redone. - The description returned is the one that was passed into beginNewTransaction - before the set of actions was performed. + bool canRedo() const noexcept; + /** Returns the name of the transaction that will be redone when redo() is called. @see redo */ String getRedoDescription() const; @@ -216,7 +225,7 @@ private: struct ActionSet; friend struct ContainerDeletePolicy; OwnedArray transactions; - String currentTransactionName; + String newTransactionName; int totalUnitsStored, maxNumUnitsToKeep, minimumTransactionsToKeep, nextIndex; bool newTransaction, reentrancyCheck; ActionSet* getCurrentSet() const noexcept; diff --git a/source/modules/juce_data_structures/values/juce_ValueTree.h b/source/modules/juce_data_structures/values/juce_ValueTree.h index 1e37e0cfd..42dd77811 100644 --- a/source/modules/juce_data_structures/values/juce_ValueTree.h +++ b/source/modules/juce_data_structures/values/juce_ValueTree.h @@ -318,7 +318,10 @@ public: /** Creates an XmlElement that holds a complete image of this node and all its children. If this node is invalid, this may return nullptr. Otherwise, the XML that is produced can - be used to recreate a similar node by calling fromXml() + be used to recreate a similar node by calling fromXml(). + + The caller must delete the object that is returned. + @see fromXml */ XmlElement* createXml() const; diff --git a/source/modules/juce_events/messages/juce_MessageManager.cpp b/source/modules/juce_events/messages/juce_MessageManager.cpp index 1383f90f9..44c2adc80 100644 --- a/source/modules/juce_events/messages/juce_MessageManager.cpp +++ b/source/modules/juce_events/messages/juce_MessageManager.cpp @@ -130,6 +130,25 @@ void MessageManager::stopDispatchLoop() #endif +//============================================================================== +#if JUCE_COMPILER_SUPPORTS_LAMBDAS +struct AsyncFunction : private MessageManager::MessageBase +{ + AsyncFunction (std::function f) : fn (f) { post(); } + +private: + std::function fn; + void messageCallback() override { fn(); } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncFunction) +}; + +void MessageManager::callAsync (std::function f) +{ + new AsyncFunction (f); +} +#endif + //============================================================================== class AsyncFunctionCallback : public MessageManager::MessageBase { diff --git a/source/modules/juce_events/messages/juce_MessageManager.h b/source/modules/juce_events/messages/juce_MessageManager.h index 9266d0503..0901b29d3 100644 --- a/source/modules/juce_events/messages/juce_MessageManager.h +++ b/source/modules/juce_events/messages/juce_MessageManager.h @@ -91,6 +91,13 @@ public: #endif //============================================================================== + #if JUCE_COMPILER_SUPPORTS_LAMBDAS + /** Asynchronously invokes a function or C++11 lambda on the message thread. + Internally this uses the CallbackMessage class to invoke the callback. + */ + static void callAsync (std::function); + #endif + /** Calls a function using the message-thread. This can be used by any thread to cause this function to be called-back diff --git a/source/modules/juce_events/native/juce_linux_Messaging.cpp b/source/modules/juce_events/native/juce_linux_Messaging.cpp index b17aafcf4..9f17ab1c8 100644 --- a/source/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/source/modules/juce_events/native/juce_linux_Messaging.cpp @@ -360,7 +360,7 @@ bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* cons if (InternalMessageQueue* const queue = InternalMessageQueue::getInstanceWithoutCreating()) { - InternalMessageQueue::getInstanceWithoutCreating()->postMessage (message); + queue->postMessage (message); return true; } diff --git a/source/modules/juce_graphics/contexts/juce_GraphicsContext.h b/source/modules/juce_graphics/contexts/juce_GraphicsContext.h index d59bcd2ba..ed0bc404b 100644 --- a/source/modules/juce_graphics/contexts/juce_GraphicsContext.h +++ b/source/modules/juce_graphics/contexts/juce_GraphicsContext.h @@ -158,7 +158,7 @@ public: void drawText (const String& text, int x, int y, int width, int height, Justification justificationType, - bool useEllipsesIfTooBig) const; + bool useEllipsesIfTooBig = true) const; /** Draws a line of text within a specified rectangle. @@ -172,7 +172,7 @@ public: void drawText (const String& text, const Rectangle& area, Justification justificationType, - bool useEllipsesIfTooBig) const; + bool useEllipsesIfTooBig = true) const; /** Draws a line of text within a specified rectangle. @@ -186,7 +186,7 @@ public: void drawText (const String& text, const Rectangle& area, Justification justificationType, - bool useEllipsesIfTooBig) const; + bool useEllipsesIfTooBig = true) const; /** Tries to draw a text string inside a given space. diff --git a/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index be4329c79..d963ed640 100644 --- a/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -384,7 +384,7 @@ void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const Affin { // this doesn't work correctly yet - it could be improved to handle solid gradients, but // postscript can't do semi-transparent ones. - notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file + notPossibleInPostscriptAssert; // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file writeClip(); out << "gsave "; diff --git a/source/modules/juce_graphics/image_formats/juce_PNGLoader.cpp b/source/modules/juce_graphics/image_formats/juce_PNGLoader.cpp index 4ee36abaa..636b29386 100644 --- a/source/modules/juce_graphics/image_formats/juce_PNGLoader.cpp +++ b/source/modules/juce_graphics/image_formats/juce_PNGLoader.cpp @@ -488,12 +488,13 @@ bool PNGImageFormat::writeImageToStream (const Image& image, OutputStream& out) PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - HeapBlock rowData ((size_t) width * 4); + HeapBlock rowData ((size_t) width * 4); png_color_8 sig_bit; - sig_bit.red = 8; + sig_bit.red = 8; sig_bit.green = 8; - sig_bit.blue = 8; + sig_bit.blue = 8; + sig_bit.gray = 0; sig_bit.alpha = 8; png_set_sBIT (pngWriteStruct, pngInfoStruct, &sig_bit); diff --git a/source/modules/juce_graphics/image_formats/pnglib/pngread.c b/source/modules/juce_graphics/image_formats/pnglib/pngread.c index 85a7d6163..f7bde9620 100644 --- a/source/modules/juce_graphics/image_formats/pnglib/pngread.c +++ b/source/modules/juce_graphics/image_formats/pnglib/pngread.c @@ -1026,7 +1026,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, if ((transforms & PNG_TRANSFORM_SHIFT) && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) { - png_color_8p sig_bit; + png_color_8p sig_bit = 0; png_get_sBIT(png_ptr, info_ptr, &sig_bit); png_set_shift(png_ptr, sig_bit); diff --git a/source/modules/juce_graphics/images/juce_Image.cpp b/source/modules/juce_graphics/images/juce_Image.cpp index d9aa0c4e5..e7be9a3f4 100644 --- a/source/modules/juce_graphics/images/juce_Image.cpp +++ b/source/modules/juce_graphics/images/juce_Image.cpp @@ -197,11 +197,11 @@ Image Image::getClippedImage (const Rectangle& area) const //============================================================================== -Image::Image() +Image::Image() noexcept { } -Image::Image (ImagePixelData* const instance) +Image::Image (ImagePixelData* const instance) noexcept : image (instance) { } @@ -216,7 +216,7 @@ Image::Image (const PixelFormat format, int width, int height, bool clearImage, { } -Image::Image (const Image& other) +Image::Image (const Image& other) noexcept : image (other.image) { } diff --git a/source/modules/juce_graphics/images/juce_Image.h b/source/modules/juce_graphics/images/juce_Image.h index 3580f9f2a..0f67b4eef 100644 --- a/source/modules/juce_graphics/images/juce_Image.h +++ b/source/modules/juce_graphics/images/juce_Image.h @@ -67,7 +67,7 @@ public: //============================================================================== /** Creates a null image. */ - Image(); + Image() noexcept; /** Creates an image with a specified size and format. @@ -106,7 +106,7 @@ public: point to the same shared image data. To make sure that an Image object has its own unique, unshared internal data, call duplicateIfShared(). */ - Image (const Image&); + Image (const Image&) noexcept; /** Makes this image refer to the same underlying image as another object. @@ -408,7 +408,7 @@ public: ImagePixelData* getPixelData() const noexcept { return image; } /** @internal */ - explicit Image (ImagePixelData*); + explicit Image (ImagePixelData*) noexcept; private: //============================================================================== diff --git a/source/modules/juce_gui_basics/buttons/juce_Button.cpp b/source/modules/juce_gui_basics/buttons/juce_Button.cpp index be7e55c5a..10d6e03f6 100644 --- a/source/modules/juce_gui_basics/buttons/juce_Button.cpp +++ b/source/modules/juce_gui_basics/buttons/juce_Button.cpp @@ -125,11 +125,12 @@ void Button::setTooltip (const String& newTooltip) generateTooltip = false; } -String Button::getTooltip() +void Button::updateAutomaticTooltip (const ApplicationCommandInfo& info) { - if (generateTooltip && commandManagerToUse != nullptr && commandID != 0) + if (generateTooltip && commandManagerToUse != nullptr) { - String tt (commandManagerToUse->getDescriptionOfCommand (commandID)); + String tt (info.description.isNotEmpty() ? info.description + : info.shortName); Array keyPresses (commandManagerToUse->getKeyMappings()->getKeyPressesAssignedToCommand (commandID)); @@ -145,10 +146,8 @@ String Button::getTooltip() tt << key << ']'; } - return tt; + SettableTooltipClient::setTooltip (tt); } - - return SettableTooltipClient::getTooltip(); } void Button::setConnectedEdges (const int newFlags) @@ -542,6 +541,7 @@ void Button::applicationCommandListChangeCallback() if (commandManagerToUse->getTargetForCommand (commandID, info) != nullptr) { + updateAutomaticTooltip (info); setEnabled ((info.flags & ApplicationCommandInfo::isDisabled) == 0); setToggleState ((info.flags & ApplicationCommandInfo::isTicked) != 0, dontSendNotification); } diff --git a/source/modules/juce_gui_basics/buttons/juce_Button.h b/source/modules/juce_gui_basics/buttons/juce_Button.h index 2ed85c2ee..a58342d56 100644 --- a/source/modules/juce_gui_basics/buttons/juce_Button.h +++ b/source/modules/juce_gui_basics/buttons/juce_Button.h @@ -277,12 +277,6 @@ public: */ void setTooltip (const String& newTooltip) override; - /** Returns the tooltip set by setTooltip(), or the description corresponding to - the currently mapped command if one is enabled (see setCommandToTrigger). - */ - String getTooltip() override; - - //============================================================================== /** A combination of these flags are used by setConnectedEdges(). */ enum ConnectedEdgeFlags @@ -349,6 +343,9 @@ public: */ void setState (ButtonState newState); + /** Returns the button's current over/down/up state. */ + ButtonState getState() const noexcept { return buttonState; } + // This method's parameters have changed - see the new version. JUCE_DEPRECATED (void setToggleState (bool, bool)); @@ -495,6 +492,7 @@ private: void repeatTimerCallback(); bool keyStateChangedCallback(); void applicationCommandListChangeCallback(); + void updateAutomaticTooltip (const ApplicationCommandInfo&); ButtonState updateState(); ButtonState updateState (bool isOver, bool isDown); diff --git a/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp b/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp index 4f4ff46c6..82ec3a248 100644 --- a/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp +++ b/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp @@ -51,7 +51,20 @@ void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& n // the name isn't optional! jassert (newCommand.shortName.isNotEmpty()); - if (getCommandForID (newCommand.commandID) == 0) + if (ApplicationCommandInfo* command = getMutableCommandForID (newCommand.commandID)) + { + // Trying to re-register the same command ID with different parameters can often indicate a typo. + // This assertion is here because I've found it useful catching some mistakes, but it may also cause + // false alarms if you're deliberately updating some flags for a command. + jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName + && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName + && newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses + && (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)) + == (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))); + + *command = newCommand; + } + else { ApplicationCommandInfo* const newInfo = new ApplicationCommandInfo (newCommand); newInfo->flags &= ~ApplicationCommandInfo::isTicked; @@ -61,16 +74,6 @@ void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& n triggerAsyncUpdate(); } - else - { - // trying to re-register the same command ID with different parameters? - jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName - && (newCommand.description == getCommandForID (newCommand.commandID)->description || newCommand.description.isEmpty()) - && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName - && newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses - && (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)) - == (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))); - } } void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target) @@ -113,7 +116,7 @@ void ApplicationCommandManager::commandStatusChanged() } //============================================================================== -const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const CommandID commandID) const noexcept +ApplicationCommandInfo* ApplicationCommandManager::getMutableCommandForID (CommandID commandID) const noexcept { for (int i = commands.size(); --i >= 0;) if (commands.getUnchecked(i)->commandID == commandID) @@ -122,6 +125,11 @@ const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const return nullptr; } +const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const CommandID commandID) const noexcept +{ + return getMutableCommandForID (commandID); +} + String ApplicationCommandManager::getNameOfCommand (const CommandID commandID) const noexcept { if (const ApplicationCommandInfo* const ci = getCommandForID (commandID)) @@ -215,7 +223,10 @@ ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (const target = target->getTargetForCommand (commandID); if (target != nullptr) + { + upToDateInfo.commandID = commandID; target->getCommandInfo (commandID, upToDateInfo); + } return target; } @@ -223,7 +234,7 @@ ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (const //============================================================================== ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c) { - ApplicationCommandTarget* target = dynamic_cast (c); + ApplicationCommandTarget* target = dynamic_cast (c); if (target == nullptr && c != nullptr) target = c->findParentComponentOfClass(); @@ -263,7 +274,7 @@ ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget( // component that really should get the event. And if not, the event will // still be passed up to the top level window anyway, so let's send it to the // content comp. - if (ResizableWindow* const resizableWindow = dynamic_cast (c)) + if (ResizableWindow* const resizableWindow = dynamic_cast (c)) if (Component* const content = resizableWindow->getContentComponent()) c = content; diff --git a/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h b/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h index 1d6158ed8..5be135a7d 100644 --- a/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h +++ b/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h @@ -308,6 +308,7 @@ private: void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo&); void handleAsyncUpdate() override; 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 diff --git a/source/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp b/source/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp index b37a98126..bcfac4e80 100644 --- a/source/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp +++ b/source/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp @@ -252,7 +252,7 @@ bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion) XmlElement* KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const { - ScopedPointer defaultSet; + ScopedPointer defaultSet; if (saveDifferencesFromDefaultSet) { diff --git a/source/modules/juce_gui_basics/components/juce_Component.h b/source/modules/juce_gui_basics/components/juce_Component.h index 629eac749..edc1957a3 100644 --- a/source/modules/juce_gui_basics/components/juce_Component.h +++ b/source/modules/juce_gui_basics/components/juce_Component.h @@ -1747,11 +1747,14 @@ public: */ virtual void focusLost (FocusChangeType cause); - /** Called to indicate that one of this component's children has been focused or unfocused. + /** Called to indicate a change in whether or not this component is the parent of the + currently-focused component. - Essentially this means that the return value of a call to hasKeyboardFocus (true) has + Essentially this is called when the return value of a call to hasKeyboardFocus (true) has changed. It happens when focus moves from one of this component's children (at any depth) to a component that isn't contained in this one, (or vice-versa). + Note that this method does NOT get called to when focus simply moves from one of its + child components to another. @see focusGained, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus */ @@ -1786,9 +1789,7 @@ public: bool isMouseButtonDown() const; /** True if the mouse is over this component, or if it's being dragged in this component. - This is a handy equivalent to (isMouseOver() || isMouseButtonDown()). - @see isMouseOver, isMouseButtonDown, isMouseButtonDownAnywhere */ bool isMouseOverOrDragging() const; diff --git a/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index f5dbcd9ff..cfb6b7b5f 100644 --- a/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -85,31 +85,13 @@ public: newState.viewBoxW = vwh.x; newState.viewBoxH = vwh.y; - int placementFlags = 0; + const int placementFlags = parsePlacementFlags (xml->getStringAttribute ("preserveAspectRatio").trim()); - const String aspect (xml->getStringAttribute ("preserveAspectRatio")); - - if (aspect.containsIgnoreCase ("none")) - { - placementFlags = RectanglePlacement::stretchToFit; - } - else - { - if (aspect.containsIgnoreCase ("slice")) placementFlags |= RectanglePlacement::fillDestination; - - if (aspect.containsIgnoreCase ("xMin")) placementFlags |= RectanglePlacement::xLeft; - else if (aspect.containsIgnoreCase ("xMax")) placementFlags |= RectanglePlacement::xRight; - else placementFlags |= RectanglePlacement::xMid; - - if (aspect.containsIgnoreCase ("yMin")) placementFlags |= RectanglePlacement::yTop; - else if (aspect.containsIgnoreCase ("yMax")) placementFlags |= RectanglePlacement::yBottom; - else placementFlags |= RectanglePlacement::yMid; - } - - newState.transform = RectanglePlacement (placementFlags) - .getTransformToFit (Rectangle (viewboxXY.x, viewboxXY.y, vwh.x, vwh.y), - Rectangle (newState.width, newState.height)) - .followedBy (newState.transform); + if (placementFlags != 0) + newState.transform = RectanglePlacement (placementFlags) + .getTransformToFit (Rectangle (viewboxXY.x, viewboxXY.y, vwh.x, vwh.y), + Rectangle (newState.width, newState.height)) + .followedBy (newState.transform); } } else @@ -560,24 +542,12 @@ private: path.applyTransform (transform); dp->setPath (path); - Path::Iterator iter (path); - - bool containsClosedSubPath = false; - while (iter.next()) - { - if (iter.elementType == Path::Iterator::closePath) - { - containsClosedSubPath = true; - break; - } - } - dp->setFill (getPathFillType (path, getStyleAttribute (xml, "fill"), getStyleAttribute (xml, "fill-opacity"), getStyleAttribute (xml, "opacity"), - containsClosedSubPath ? Colours::black - : Colours::transparentBlack)); + pathContainsClosedSubPath (path) ? Colours::black + : Colours::transparentBlack)); const String strokeType (getStyleAttribute (xml, "stroke")); @@ -594,6 +564,15 @@ private: return dp; } + static bool pathContainsClosedSubPath (const Path& path) noexcept + { + for (Path::Iterator iter (path); iter.next();) + if (iter.elementType == Path::Iterator::closePath) + return true; + + return false; + } + struct SetGradientStopsOp { const SVGState* state; @@ -796,44 +775,41 @@ private: return parseColour (fill, i, defaultColour).withMultipliedAlpha (opacity); } - PathStrokeType getStrokeFor (const XmlPath& xml) const + static PathStrokeType::JointStyle getJointStyle (const String& join) noexcept { - const String strokeWidth (getStyleAttribute (xml, "stroke-width")); - const String cap (getStyleAttribute (xml, "stroke-linecap")); - const String join (getStyleAttribute (xml, "stroke-linejoin")); + if (join.equalsIgnoreCase ("round")) return PathStrokeType::curved; + if (join.equalsIgnoreCase ("bevel")) return PathStrokeType::beveled; - //const String mitreLimit (getStyleAttribute (xml, "stroke-miterlimit")); - //const String dashArray (getStyleAttribute (xml, "stroke-dasharray")); - //const String dashOffset (getStyleAttribute (xml, "stroke-dashoffset")); - - PathStrokeType::JointStyle joinStyle = PathStrokeType::mitered; - PathStrokeType::EndCapStyle capStyle = PathStrokeType::butt; + return PathStrokeType::mitered; + } - if (join.equalsIgnoreCase ("round")) - joinStyle = PathStrokeType::curved; - else if (join.equalsIgnoreCase ("bevel")) - joinStyle = PathStrokeType::beveled; + static PathStrokeType::EndCapStyle getEndCapStyle (const String& cap) noexcept + { + if (cap.equalsIgnoreCase ("round")) return PathStrokeType::rounded; + if (cap.equalsIgnoreCase ("square")) return PathStrokeType::square; - if (cap.equalsIgnoreCase ("round")) - capStyle = PathStrokeType::rounded; - else if (cap.equalsIgnoreCase ("square")) - capStyle = PathStrokeType::square; + return PathStrokeType::butt; + } - float ox = 0.0f, oy = 0.0f; - float x = getCoordLength (strokeWidth, viewBoxW), y = 0.0f; - transform.transformPoints (ox, oy, x, y); + float getStrokeWidth (const String& strokeWidth) const noexcept + { + return transform.getScaleFactor() * getCoordLength (strokeWidth, viewBoxW); + } - return PathStrokeType (strokeWidth.isNotEmpty() ? juce_hypot (x - ox, y - oy) : 1.0f, - joinStyle, capStyle); + PathStrokeType getStrokeFor (const XmlPath& xml) const + { + return PathStrokeType (getStrokeWidth (getStyleAttribute (xml, "stroke-width", "1")), + getJointStyle (getStyleAttribute (xml, "stroke-linejoin")), + getEndCapStyle (getStyleAttribute (xml, "stroke-linecap"))); } //============================================================================== Drawable* parseText (const XmlPath& xml) { - Array xCoords, yCoords, dxCoords, dyCoords; + Array xCoords, yCoords, dxCoords, dyCoords; - getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true); - getCoordList (yCoords, getInheritedAttribute (xml, "y"), true, false); + getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true); + getCoordList (yCoords, getInheritedAttribute (xml, "y"), true, false); getCoordList (dxCoords, getInheritedAttribute (xml, "dx"), true, true); getCoordList (dyCoords, getInheritedAttribute (xml, "dy"), true, false); @@ -898,7 +874,7 @@ private: return false; } - float getCoordLength (const String& s, const float sizeForProportions) const + float getCoordLength (const String& s, const float sizeForProportions) const noexcept { float n = s.getFloatValue(); const int len = s.length(); @@ -920,13 +896,12 @@ private: return n; } - float getCoordLength (const XmlPath& xml, const char* attName, const float sizeForProportions) const + float getCoordLength (const XmlPath& xml, const char* attName, const float sizeForProportions) const noexcept { return getCoordLength (xml->getStringAttribute (attName), sizeForProportions); } - void getCoordList (Array & coords, const String& list, - const bool allowUnits, const bool isX) const + void getCoordList (Array& coords, const String& list, bool allowUnits, const bool isX) const { String::CharPointerType text (list.getCharPointer()); float value; @@ -960,7 +935,7 @@ private: return source; } - String getStyleAttribute (const XmlPath& xml, const String& attributeName, + String getStyleAttribute (const XmlPath& xml, StringRef attributeName, const String& defaultValue = String()) const { if (xml->hasAttribute (attributeName)) @@ -1000,7 +975,7 @@ private: return defaultValue; } - String getInheritedAttribute (const XmlPath& xml, const String& attributeName) const + String getInheritedAttribute (const XmlPath& xml, StringRef attributeName) const { if (xml->hasAttribute (attributeName)) return xml->getStringAttribute (attributeName); @@ -1011,13 +986,30 @@ private: return String(); } + static int parsePlacementFlags (const String& align) noexcept + { + if (align.isEmpty()) + return 0; + + if (align.containsIgnoreCase ("none")) + return RectanglePlacement::stretchToFit; + + return (align.containsIgnoreCase ("slice") ? RectanglePlacement::fillDestination : 0) + | (align.containsIgnoreCase ("xMin") ? RectanglePlacement::xLeft + : (align.containsIgnoreCase ("xMax") ? RectanglePlacement::xRight + : RectanglePlacement::xMid)) + | (align.containsIgnoreCase ("yMin") ? RectanglePlacement::yTop + : (align.containsIgnoreCase ("yMax") ? RectanglePlacement::yBottom + : RectanglePlacement::yMid)); + } + //============================================================================== static bool isIdentifierChar (const juce_wchar c) { return CharacterFunctions::isLetter (c) || c == '-'; } - static String getAttributeFromStyleList (const String& list, const String& attributeName, const String& defaultValue) + static String getAttributeFromStyleList (const String& list, StringRef attributeName, const String& defaultValue) { int i = 0; @@ -1221,7 +1213,7 @@ private: const bool largeArc, const bool sweep, double& rx, double& ry, double& centreX, double& centreY, - double& startAngle, double& deltaAngle) + double& startAngle, double& deltaAngle) noexcept { const double midX = (x1 - x2) * 0.5; const double midY = (y1 - y2) * 0.5; diff --git a/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp b/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp index a22414bd2..7c26c99af 100644 --- a/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp +++ b/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp @@ -217,7 +217,7 @@ void ComponentAnimator::animateComponent (Component* const component, const double endSpeed) { // the speeds must be 0 or greater! - jassert (startSpeed >= 0 && endSpeed >= 0) + jassert (startSpeed >= 0 && endSpeed >= 0); if (component != nullptr) { diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 37dc12457..4135afe3e 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -111,6 +111,9 @@ LookAndFeel_V2::LookAndFeel_V2() TextPropertyComponent::textColourId, 0xff000000, TextPropertyComponent::outlineColourId, standardOutlineColour, + BooleanPropertyComponent::backgroundColourId, 0xffffffff, + BooleanPropertyComponent::outlineColourId, standardOutlineColour, + ListBox::backgroundColourId, 0xffffffff, ListBox::outlineColourId, standardOutlineColour, ListBox::textColourId, 0xff000000, @@ -1001,6 +1004,16 @@ void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, const Rectangle& area, } } +void LookAndFeel_V2::drawPopupMenuSectionHeader (Graphics& g, const Rectangle& area, const String& sectionName) +{ + g.setFont (getPopupMenuFont().boldened()); + g.setColour (findColour (PopupMenu::headerTextColourId)); + + g.drawFittedText (sectionName, + area.getX() + 12, area.getY(), area.getWidth() - 16, (int) (area.getHeight() * 0.8f), + Justification::bottomLeft, 1); +} + //============================================================================== int LookAndFeel_V2::getMenuWindowFlags() { diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h index f728df3be..2342b7cf1 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -137,6 +137,9 @@ public: const String& text, const String& shortcutKeyText, const Drawable* icon, const Colour* textColour) override; + void drawPopupMenuSectionHeader (Graphics&, const Rectangle& area, + const String& sectionName) override; + Font getPopupMenuFont() override; void drawPopupMenuUpDownArrow (Graphics&, int width, int height, bool isScrollUpArrow) override; diff --git a/source/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp b/source/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp index 5a22fc7a4..842fa5952 100644 --- a/source/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp +++ b/source/modules/juce_gui_basics/menus/juce_MenuBarComponent.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -MenuBarComponent::MenuBarComponent (MenuBarModel* model_) +MenuBarComponent::MenuBarComponent (MenuBarModel* m) : model (nullptr), itemUnderMouse (-1), currentPopupIndex (-1), @@ -32,7 +32,7 @@ MenuBarComponent::MenuBarComponent (MenuBarModel* model_) setWantsKeyboardFocus (false); setMouseClickGrabsKeyboardFocus (false); - setModel (model_); + setModel (m); } MenuBarComponent::~MenuBarComponent() @@ -284,22 +284,26 @@ void MenuBarComponent::mouseMove (const MouseEvent& e) bool MenuBarComponent::keyPressed (const KeyPress& key) { - bool used = false; const int numMenus = menuNames.size(); - const int currentIndex = jlimit (0, menuNames.size() - 1, currentPopupIndex); - if (key.isKeyCode (KeyPress::leftKey)) + if (numMenus > 0) { - showMenu ((currentIndex + numMenus - 1) % numMenus); - used = true; - } - else if (key.isKeyCode (KeyPress::rightKey)) - { - showMenu ((currentIndex + 1) % numMenus); - used = true; + const int currentIndex = jlimit (0, numMenus - 1, currentPopupIndex); + + if (key.isKeyCode (KeyPress::leftKey)) + { + showMenu ((currentIndex + numMenus - 1) % numMenus); + return true; + } + + if (key.isKeyCode (KeyPress::rightKey)) + { + showMenu ((currentIndex + 1) % numMenus); + return true; + } } - return used; + return false; } void MenuBarComponent::menuBarItemsChanged (MenuBarModel* /*menuBarModel*/) diff --git a/source/modules/juce_gui_basics/menus/juce_MenuBarComponent.h b/source/modules/juce_gui_basics/menus/juce_MenuBarComponent.h index b8fc2c315..badd05a5c 100644 --- a/source/modules/juce_gui_basics/menus/juce_MenuBarComponent.h +++ b/source/modules/juce_gui_basics/menus/juce_MenuBarComponent.h @@ -40,9 +40,9 @@ public: //============================================================================== /** Creates a menu bar. - @param model the model object to use to control this bar. You can - pass 0 into this if you like, and set the model later - using the setModel() method + @param model the model object to use to control this bar. You can + pass nullptr into this if you like, and set the model + later using the setModel() method */ MenuBarComponent (MenuBarModel* model); @@ -57,8 +57,7 @@ public: */ void setModel (MenuBarModel* newModel); - /** Returns the current menu bar model being used. - */ + /** Returns the current menu bar model being used. */ MenuBarModel* getModel() const noexcept; //============================================================================== diff --git a/source/modules/juce_gui_basics/menus/juce_MenuBarModel.h b/source/modules/juce_gui_basics/menus/juce_MenuBarModel.h index ceac89a3a..99b698511 100644 --- a/source/modules/juce_gui_basics/menus/juce_MenuBarModel.h +++ b/source/modules/juce_gui_basics/menus/juce_MenuBarModel.h @@ -80,8 +80,7 @@ public: virtual ~Listener() {} //============================================================================== - /** This callback is made when items are changed in the menu bar model. - */ + /** This callback is made when items are changed in the menu bar model. */ virtual void menuBarItemsChanged (MenuBarModel* menuBarModel) = 0; /** This callback is made when an application command is invoked that @@ -101,7 +100,6 @@ public: void addListener (Listener* listenerToAdd) noexcept; /** Removes a listener. - @see addListener */ void removeListener (Listener* listenerToRemove) noexcept; @@ -130,7 +128,7 @@ public: //============================================================================== #if JUCE_MAC || DOXYGEN - /** MAC ONLY - Sets the model that is currently being shown as the main + /** OSX ONLY - Sets the model that is currently being shown as the main menu bar at the top of the screen on the Mac. You can pass 0 to stop the current model being displayed. Be careful @@ -151,12 +149,12 @@ public: const PopupMenu* extraAppleMenuItems = nullptr, const String& recentItemsMenuName = String::empty); - /** MAC ONLY - Returns the menu model that is currently being shown as + /** OSX ONLY - Returns the menu model that is currently being shown as the main menu bar. */ static MenuBarModel* getMacMainMenu(); - /** MAC ONLY - Returns the menu that was last passed as the extraAppleMenuItems + /** OSX ONLY - Returns the menu that was last passed as the extraAppleMenuItems argument to setMacMainMenu(), or nullptr if none was specified. */ static const PopupMenu* getMacExtraAppleItemsMenu(); @@ -164,7 +162,7 @@ public: //============================================================================== /** @internal */ - void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) override; + void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) override; /** @internal */ void applicationCommandListChanged() override; /** @internal */ @@ -172,7 +170,7 @@ public: private: ApplicationCommandManager* manager; - ListenerList listeners; + ListenerList listeners; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuBarModel) }; diff --git a/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index 963b530f1..b2e64a315 100644 --- a/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -740,7 +740,7 @@ public: void ensureItemIsVisible (const int itemID, int wantedY) { - jassert (itemID != 0) + jassert (itemID != 0); for (int i = items.size(); --i >= 0;) { @@ -1216,12 +1216,7 @@ public: void paint (Graphics& g) override { - g.setFont (getLookAndFeel().getPopupMenuFont().boldened()); - g.setColour (findColour (PopupMenu::headerTextColourId)); - - g.drawFittedText (getName(), - 12, 0, getWidth() - 16, proportionOfHeight (0.8f), - Justification::bottomLeft, 1); + getLookAndFeel().drawPopupMenuSectionHeader (g, getLocalBounds(), getName()); } void getIdealSize (int& idealWidth, int& idealHeight) diff --git a/source/modules/juce_gui_basics/menus/juce_PopupMenu.h b/source/modules/juce_gui_basics/menus/juce_PopupMenu.h index 82545a382..97ac59651 100644 --- a/source/modules/juce_gui_basics/menus/juce_PopupMenu.h +++ b/source/modules/juce_gui_basics/menus/juce_PopupMenu.h @@ -562,6 +562,9 @@ public: const Drawable* icon, const Colour* textColour) = 0; + virtual void drawPopupMenuSectionHeader (Graphics&, const Rectangle& area, + const String& sectionName) = 0; + /** Returns the size and style of font to use in popup menus. */ virtual Font getPopupMenuFont() = 0; diff --git a/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp b/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp index e4ff98ddd..2e9c44b2c 100644 --- a/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp +++ b/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp @@ -69,6 +69,8 @@ public: if (current->isInterestedInDragSource (sourceDetails)) current->itemDragExit (sourceDetails); } + + owner.dragOperationEnded(); } void paint (Graphics& g) override @@ -164,14 +166,14 @@ public: if (sourceDetails.sourceComponent == nullptr) { - delete this; + deleteSelf(); } else if (! isMouseButtonDownAnywhere()) { if (mouseDragSource != nullptr) mouseDragSource->removeMouseListener (this); - delete this; + deleteSelf(); } } @@ -180,7 +182,7 @@ public: if (key == KeyPress::escapeKey) { dismissWithAnimation (true); - delete this; + deleteSelf(); return true; } @@ -215,13 +217,28 @@ private: return dynamic_cast (currentlyOverComp.get()); } + static Component* findDesktopComponentBelow (Point screenPos) + { + Desktop& desktop = Desktop::getInstance(); + + for (int i = desktop.getNumComponents(); --i >= 0;) + { + Component* c = desktop.getComponent(i); + + if (Component* hit = c->getComponentAt (c->getLocalPoint (nullptr, screenPos))) + return hit; + } + + return nullptr; + } + DragAndDropTarget* findTarget (Point screenPos, Point& relativePos, Component*& resultComponent) const { Component* hit = getParentComponent(); if (hit == nullptr) - hit = Desktop::getInstance().findComponentAt (screenPos); + hit = findDesktopComponentBelow (screenPos); else hit = hit->getComponentAt (hit->getLocalPoint (nullptr, screenPos)); @@ -296,12 +313,17 @@ private: && ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) { (new ExternalDragAndDropMessage (files, canMoveFiles))->post(); - delete this; + deleteSelf(); } } } } + void deleteSelf() + { + delete this; + } + void dismissWithAnimation (const bool shouldSnapBack) { setVisible (true); @@ -434,6 +456,8 @@ void DragAndDropContainer::startDragging (const var& sourceDescription, if (ComponentPeer* const peer = dragImageComponent->getPeer()) peer->performAnyPendingRepaintsNow(); #endif + + dragOperationStarted(); } } @@ -458,6 +482,9 @@ bool DragAndDropContainer::shouldDropFilesWhenDraggedExternally (const DragAndDr return false; } +void DragAndDropContainer::dragOperationStarted() {} +void DragAndDropContainer::dragOperationEnded() {} + //============================================================================== DragAndDropTarget::SourceDetails::SourceDetails (const var& desc, Component* comp, Point pos) noexcept : description (desc), diff --git a/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h b/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h index 984375379..072f35ee1 100644 --- a/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h +++ b/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h @@ -165,6 +165,12 @@ protected: virtual bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails, StringArray& files, bool& canMoveFiles); + /** Subclasses can override this to be told when a drag starts. */ + virtual void dragOperationStarted(); + + /** Subclasses can override this to be told when a drag finishes. */ + virtual void dragOperationEnded(); + private: //============================================================================== class DragImageComponent; diff --git a/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm index 3d973d3e4..b139ccffb 100644 --- a/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm @@ -24,6 +24,14 @@ class UIViewComponentPeer; +// The way rotation works changed in iOS8.. +static bool isUsingOldRotationMethod() noexcept +{ + static bool isPreV8 = ([[[UIDevice currentDevice] systemVersion] compare: @"8.0" + options: NSNumericSearch] == NSOrderedAscending); + return isPreV8; +} + namespace Orientations { static Desktop::DisplayOrientation convertToJuce (UIInterfaceOrientation orientation) @@ -42,12 +50,15 @@ namespace Orientations static CGAffineTransform getCGTransformFor (const Desktop::DisplayOrientation orientation) noexcept { - switch (orientation) + if (isUsingOldRotationMethod()) { - case Desktop::upsideDown: return CGAffineTransformMake (-1, 0, 0, -1, 0, 0); - case Desktop::rotatedClockwise: return CGAffineTransformMake (0, -1, 1, 0, 0, 0); - case Desktop::rotatedAntiClockwise: return CGAffineTransformMake (0, 1, -1, 0, 0, 0); - default: break; + switch (orientation) + { + case Desktop::upsideDown: return CGAffineTransformMake (-1, 0, 0, -1, 0, 0); + case Desktop::rotatedClockwise: return CGAffineTransformMake (0, -1, 1, 0, 0, 0); + case Desktop::rotatedAntiClockwise: return CGAffineTransformMake (0, 1, -1, 0, 0, 0); + default: break; + } } return CGAffineTransformIdentity; @@ -85,10 +96,10 @@ using namespace juce; - (void) drawRect: (CGRect) r; -- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event; -- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event; -- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event; -- (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event; +- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event; +- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event; +- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event; +- (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event; - (BOOL) becomeFirstResponder; - (BOOL) resignFirstResponder; @@ -106,6 +117,7 @@ using namespace juce; - (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation; - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation duration: (NSTimeInterval) duration; - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation; +- (void) viewWillTransitionToSize: (CGSize) size withTransitionCoordinator: (id) coordinator; - (void) viewDidLoad; - (void) viewWillAppear: (BOOL) animated; @@ -189,7 +201,7 @@ public: bool isSharedWindow, fullScreen, insideDrawRect; static ModifierKeys currentModifiers; - static int64 getMouseTime (UIEvent* e) + static int64 getMouseTime (UIEvent* e) noexcept { return (Time::currentTimeMillis() - Time::getMillisecondCounter()) + (int64) ([e timestamp] * 1000.0); @@ -197,26 +209,29 @@ public: static Rectangle rotatedScreenPosToReal (const Rectangle& r) { - const Rectangle screen (convertToRectInt ([UIScreen mainScreen].bounds)); - - switch ([[UIApplication sharedApplication] statusBarOrientation]) + if (isUsingOldRotationMethod()) { - case UIInterfaceOrientationPortrait: - return r; + const Rectangle screen (convertToRectInt ([UIScreen mainScreen].bounds)); + + switch ([[UIApplication sharedApplication] statusBarOrientation]) + { + case UIInterfaceOrientationPortrait: + return r; - case UIInterfaceOrientationPortraitUpsideDown: - return Rectangle (screen.getWidth() - r.getRight(), screen.getHeight() - r.getBottom(), - r.getWidth(), r.getHeight()); + case UIInterfaceOrientationPortraitUpsideDown: + return Rectangle (screen.getWidth() - r.getRight(), screen.getHeight() - r.getBottom(), + r.getWidth(), r.getHeight()); - case UIInterfaceOrientationLandscapeLeft: - return Rectangle (r.getY(), screen.getHeight() - r.getRight(), - r.getHeight(), r.getWidth()); + case UIInterfaceOrientationLandscapeLeft: + return Rectangle (r.getY(), screen.getHeight() - r.getRight(), + r.getHeight(), r.getWidth()); - case UIInterfaceOrientationLandscapeRight: - return Rectangle (screen.getWidth() - r.getBottom(), r.getX(), - r.getHeight(), r.getWidth()); + case UIInterfaceOrientationLandscapeRight: + return Rectangle (screen.getWidth() - r.getBottom(), r.getX(), + r.getHeight(), r.getWidth()); - default: jassertfalse; // unknown orientation! + default: jassertfalse; // unknown orientation! + } } return r; @@ -224,26 +239,29 @@ public: static Rectangle realScreenPosToRotated (const Rectangle& r) { - const Rectangle screen (convertToRectInt ([UIScreen mainScreen].bounds)); - - switch ([[UIApplication sharedApplication] statusBarOrientation]) + if (isUsingOldRotationMethod()) { - case UIInterfaceOrientationPortrait: - return r; + const Rectangle screen (convertToRectInt ([UIScreen mainScreen].bounds)); + + switch ([[UIApplication sharedApplication] statusBarOrientation]) + { + case UIInterfaceOrientationPortrait: + return r; - case UIInterfaceOrientationPortraitUpsideDown: - return Rectangle (screen.getWidth() - r.getRight(), screen.getHeight() - r.getBottom(), - r.getWidth(), r.getHeight()); + case UIInterfaceOrientationPortraitUpsideDown: + return Rectangle (screen.getWidth() - r.getRight(), screen.getHeight() - r.getBottom(), + r.getWidth(), r.getHeight()); - case UIInterfaceOrientationLandscapeLeft: - return Rectangle (screen.getHeight() - r.getBottom(), r.getX(), - r.getHeight(), r.getWidth()); + case UIInterfaceOrientationLandscapeLeft: + return Rectangle (screen.getHeight() - r.getBottom(), r.getX(), + r.getHeight(), r.getWidth()); - case UIInterfaceOrientationLandscapeRight: - return Rectangle (r.getY(), screen.getWidth() - r.getRight(), - r.getHeight(), r.getWidth()); + case UIInterfaceOrientationLandscapeRight: + return Rectangle (r.getY(), screen.getWidth() - r.getRight(), + r.getHeight(), r.getWidth()); - default: jassertfalse; // unknown orientation! + default: jassertfalse; // unknown orientation! + } } return r; @@ -273,6 +291,13 @@ private: }; }; +static void sendScreenBoundsUpdate (JuceUIViewController* c) +{ + JuceUIView* juceView = (JuceUIView*) [c view]; + jassert (juceView != nil && juceView->owner != nullptr); + juceView->owner->updateTransformAndScreenBounds(); +} + } // (juce namespace) //============================================================================== @@ -301,19 +326,23 @@ private: - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation { (void) fromInterfaceOrientation; + sendScreenBoundsUpdate (self); + [UIView setAnimationsEnabled: YES]; +} - JuceUIView* juceView = (JuceUIView*) [self view]; - jassert (juceView != nil && juceView->owner != nullptr); - juceView->owner->updateTransformAndScreenBounds(); +- (void) viewWillTransitionToSize: (CGSize) size withTransitionCoordinator: (id) coordinator +{ + [super viewWillTransitionToSize: size withTransitionCoordinator: coordinator]; + sendScreenBoundsUpdate (self); - [UIView setAnimationsEnabled: YES]; + // On some devices the screen-size isn't yet updated at this point, so also trigger another + // async update to double-check.. + MessageManager::callAsync ([=]() { sendScreenBoundsUpdate (self); }); } - (void) viewDidLoad { - JuceUIView* juceView = (JuceUIView*) [self view]; - jassert (juceView != nil && juceView->owner != nullptr); - juceView->owner->updateTransformAndScreenBounds(); + sendScreenBoundsUpdate (self); } - (void) viewWillAppear: (BOOL) animated diff --git a/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp b/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp index d50092c40..8ac190b53 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp @@ -65,6 +65,12 @@ static void addKDialogArgs (StringArray& args, String& separator, if (title.isNotEmpty()) args.add ("--title=" + title); + if (uint64 topWindowID = getTopWindowID()) + { + args.add ("--attach"); + args.add (String (topWindowID)); + } + if (selectMultipleFiles) { separator = "\n"; @@ -79,12 +85,6 @@ static void addKDialogArgs (StringArray& args, String& separator, else args.add ("--getopenfilename"); } - if (uint64 topWindowID = getTopWindowID()) - { - args.add ("--attach"); - args.add (String (topWindowID)); - } - File startPath; if (file.exists()) diff --git a/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index b9bd76bbd..8ae63987c 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -979,11 +979,13 @@ public: Point localToGlobal (Point relativePosition) override { + updateWindowBounds(); return relativePosition + bounds.getPosition().toFloat(); } Point globalToLocal (Point screenPosition) override { + updateWindowBounds(); return screenPosition - bounds.getPosition().toFloat(); } @@ -1087,9 +1089,7 @@ public: { for (int i = windowListSize; --i >= 0;) { - LinuxComponentPeer* const peer = LinuxComponentPeer::getPeerFor (windowList[i]); - - if (peer != 0) + if (LinuxComponentPeer* const peer = LinuxComponentPeer::getPeerFor (windowList[i])) { result = (peer == this); break; diff --git a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 47e838e08..3ec007ccd 100644 --- a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -177,6 +177,7 @@ static void setWindowZOrder (HWND hwnd, HWND insertAfter) //============================================================================== static void setDPIAwareness() { + #if ! JUCE_DISABLE_WIN32_DPI_AWARENESS if (JUCEApplicationBase::isStandaloneApp()) { if (setProcessDPIAwareness == nullptr) @@ -203,6 +204,7 @@ static void setDPIAwareness() } } } + #endif } static double getGlobalDPI() @@ -845,7 +847,7 @@ public: void toBehind (ComponentPeer* other) override { - if (HWNDComponentPeer* const otherPeer = dynamic_cast (other)) + if (HWNDComponentPeer* const otherPeer = dynamic_cast (other)) { setMinimised (false); @@ -960,7 +962,7 @@ public: static ModifierKeys modifiersAtLastCallback; //============================================================================== - class JuceDropTarget : public ComBaseClassHelper + class JuceDropTarget : public ComBaseClassHelper { public: JuceDropTarget (HWNDComponentPeer& p) : ownerInfo (new OwnerInfo (p)) {} @@ -1099,13 +1101,13 @@ public: if (SUCCEEDED (fileData.error)) { - const LPDROPFILES dropFiles = static_cast (fileData.data); + const LPDROPFILES dropFiles = static_cast (fileData.data); const void* const names = addBytesToPointer (dropFiles, sizeof (DROPFILES)); if (dropFiles->fWide) - ownerInfo->parseFileList (static_cast (names), fileData.dataSize); + ownerInfo->parseFileList (static_cast (names), fileData.dataSize); else - ownerInfo->parseFileList (static_cast (names), fileData.dataSize); + ownerInfo->parseFileList (static_cast (names), fileData.dataSize); } else { @@ -1300,7 +1302,7 @@ private: //============================================================================== static void* createWindowCallback (void* userData) { - static_cast (userData)->createWindow(); + static_cast (userData)->createWindow(); return nullptr; } @@ -1643,7 +1645,7 @@ private: handlePaint (*context); } - static_cast (offscreenImage.getPixelData()) + static_cast (offscreenImage.getPixelData()) ->blitToWindow (hwnd, dc, transparent, x, y, updateLayeredWindowAlpha); } @@ -2261,6 +2263,24 @@ private: } } + void handlePowerBroadcast (WPARAM wParam) + { + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) + { + switch (wParam) + { + case PBT_APMSUSPEND: app->suspended(); break; + + case PBT_APMQUERYSUSPENDFAILED: + case PBT_APMRESUMECRITICAL: + case PBT_APMRESUMESUSPEND: + case PBT_APMRESUMEAUTOMATIC: app->resumed(); break; + + default: break; + } + } + } + void handleLeftClickInNCArea (WPARAM wParam) { if (! sendInputAttemptWhenModalMessage()) @@ -2309,7 +2329,7 @@ private: { Desktop& desktop = Desktop::getInstance(); - const_cast (desktop.getDisplays()).refresh(); + const_cast (desktop.getDisplays()).refresh(); if (fullScreen && ! isMinimised()) { @@ -2550,6 +2570,10 @@ private: } return TRUE; + case WM_POWERBROADCAST: + handlePowerBroadcast (wParam); + break; + case WM_SYNCPAINT: return 0; @@ -2879,7 +2903,7 @@ private: void moveCandidateWindowToLeftAlignWithSelection (HIMC hImc, ComponentPeer& peer, TextInputTarget* target) const { - if (Component* const targetComp = dynamic_cast (target)) + if (Component* const targetComp = dynamic_cast (target)) { const Rectangle area (peer.getComponent().getLocalArea (targetComp, target->getCaretRectangle())); @@ -2981,7 +3005,7 @@ bool JUCE_CALLTYPE Process::isForegroundProcess() fg = GetAncestor (fg, GA_ROOT); for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - if (HWNDComponentPeer* const wp = dynamic_cast (ComponentPeer::getPeer (i))) + if (HWNDComponentPeer* const wp = dynamic_cast (ComponentPeer::getPeer (i))) if (wp->isInside (fg)) return true; @@ -3007,7 +3031,7 @@ static BOOL CALLBACK enumAlwaysOnTopWindows (HWND hwnd, LPARAM lParam) if (GetWindowInfo (hwnd, &info) && (info.dwExStyle & WS_EX_TOPMOST) != 0) { - *reinterpret_cast (lParam) = true; + *reinterpret_cast (lParam) = true; return FALSE; } } @@ -3212,7 +3236,7 @@ void SystemClipboard::copyTextToClipboard (const String& text) { if (HGLOBAL bufH = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, bytesNeeded + sizeof (WCHAR))) { - if (WCHAR* const data = static_cast (GlobalLock (bufH))) + if (WCHAR* const data = static_cast (GlobalLock (bufH))) { text.copyToUTF16 (data, bytesNeeded); GlobalUnlock (bufH); diff --git a/source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.cpp b/source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.cpp index 6f0d45880..bb85a1990 100644 --- a/source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.cpp +++ b/source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.cpp @@ -66,7 +66,7 @@ void BooleanPropertyComponent::paint (Graphics& g) { PropertyComponent::paint (g); - g.setColour (Colours::white); + g.setColour (findColour (backgroundColourId)); g.fillRect (button.getBounds()); g.setColour (findColour (ComboBox::outlineColourId)); diff --git a/source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.h b/source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.h index 958428faa..066c9526f 100644 --- a/source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.h +++ b/source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.h @@ -56,6 +56,10 @@ protected: public: /** Creates a button component. + Note that if you call this constructor then you must use the Value to interact with the + button state, and you can't override the class with your own setState or getState methods. + If you want to use getState and setState, call the other constructor instead. + @param valueToControl a Value object that this property should refer to. @param propertyName the property name to be passed to the PropertyComponent @param buttonText the text shown in the ToggleButton component @@ -74,6 +78,20 @@ public: /** Must return the current value of the property. */ virtual bool getState() const; + //============================================================================== + /** A set of colour IDs to use to change the colour of various aspects of the component. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x100e801, /**< The colour to fill the background of the button area. */ + outlineColourId = 0x100e803, /**< The colour to use to draw an outline around the text area. */ + }; + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h b/source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h index 2956ae66d..78887a328 100644 --- a/source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h +++ b/source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h @@ -50,15 +50,17 @@ class JUCE_API ChoicePropertyComponent : public PropertyComponent, { protected: /** Creates the component. - - Your subclass's constructor must add a list of options to the choices - member variable. + Your subclass's constructor must add a list of options to the choices member variable. */ ChoicePropertyComponent (const String& propertyName); public: /** Creates the component. + Note that if you call this constructor then you must use the Value to interact with the + index, and you can't override the class with your own setIndex or getIndex methods. + If you want to use those methods, call the other constructor instead. + @param valueToControl the value 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 diff --git a/source/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.h b/source/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.h index 99d85e3fe..832c399af 100644 --- a/source/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.h +++ b/source/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.h @@ -58,6 +58,10 @@ public: If you need to customise the slider in other ways, your constructor can access the slider member variable and change it directly. + + Note that if you call this constructor then you must use the Value to interact with + the value, and you can't override the class with your own setValue or getValue methods. + If you want to use those methods, call the other constructor instead. */ SliderPropertyComponent (const Value& valueToControl, const String& propertyName, diff --git a/source/modules/juce_gui_basics/widgets/juce_Label.cpp b/source/modules/juce_gui_basics/widgets/juce_Label.cpp index 778425e30..7ab3d5e23 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Label.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_Label.cpp @@ -460,5 +460,3 @@ void Label::textEditorFocusLost (TextEditor& ed) { textEditorTextChanged (ed); } - -void Label::Listener::editorShown (Label*, TextEditor&) {} diff --git a/source/modules/juce_gui_basics/widgets/juce_Label.h b/source/modules/juce_gui_basics/widgets/juce_Label.h index e47fb681e..825b0a03d 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Label.h +++ b/source/modules/juce_gui_basics/widgets/juce_Label.h @@ -184,7 +184,7 @@ public: virtual void labelTextChanged (Label* labelThatHasChanged) = 0; /** Called when a Label goes into editing mode and displays a TextEditor. */ - virtual void editorShown (Label*, TextEditor& textEditorShown); + virtual void editorShown (Label*, TextEditor&) {} }; /** Registers a listener that will be called when the label's text changes. */ diff --git a/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp b/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp index ed12f980a..47f7f4c65 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp @@ -367,6 +367,7 @@ ListBox::ListBox (const String& name, ListBoxModel* const m) outlineThickness (0), lastRowSelected (-1), multipleSelection (false), + alwaysFlipSelection (false), hasDoneInitialUpdate (false) { addAndMakeVisible (viewport = new ListViewport (*this)); @@ -391,11 +392,16 @@ void ListBox::setModel (ListBoxModel* const newModel) } } -void ListBox::setMultipleSelectionEnabled (bool b) +void ListBox::setMultipleSelectionEnabled (bool b) noexcept { multipleSelection = b; } +void ListBox::setClickingTogglesRowSelection (bool b) noexcept +{ + alwaysFlipSelection = b; +} + void ListBox::setMouseMoveSelectsRows (bool b) { if (b) @@ -470,9 +476,7 @@ void ListBox::updateContent() } //============================================================================== -void ListBox::selectRow (const int row, - bool dontScroll, - bool deselectOthersFirst) +void ListBox::selectRow (int row, bool dontScroll, bool deselectOthersFirst) { selectRowInternal (row, dontScroll, deselectOthersFirst, false); } @@ -589,7 +593,7 @@ void ListBox::selectRowsBasedOnModifierKeys (const int row, ModifierKeys mods, const bool isMouseUpEvent) { - if (multipleSelection && mods.isCommandDown()) + if (multipleSelection && (mods.isCommandDown() || alwaysFlipSelection)) { flipRowSelection (row); } @@ -883,7 +887,7 @@ void ListBox::repaintRow (const int rowNumber) noexcept Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) { Rectangle imageArea; - const int firstRow = getRowContainingPosition (0, 0); + const int firstRow = getRowContainingPosition (0, viewport->getY()); for (int i = getNumRowsOnScreen() + 2; --i >= 0;) { diff --git a/source/modules/juce_gui_basics/widgets/juce_ListBox.h b/source/modules/juce_gui_basics/widgets/juce_ListBox.h index b9fb24629..fadf24b71 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ListBox.h +++ b/source/modules/juce_gui_basics/widgets/juce_ListBox.h @@ -45,7 +45,10 @@ public: */ virtual int getNumRows() = 0; - /** This method must be implemented to draw a row of the list. */ + /** This method must be implemented to draw a row of the list. + Note that the rowNumber value may be greater than the number of rows in your + list, so be careful that you don't assume it's less than getNumRows(). + */ virtual void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, @@ -216,7 +219,16 @@ public: clicked and to get it to do the appropriate selection based on whether the ctrl/shift keys are held down. */ - void setMultipleSelectionEnabled (bool shouldBeEnabled); + void setMultipleSelectionEnabled (bool shouldBeEnabled) noexcept; + + /** If enabled, this makes the listbox flip the selection status of + each row that the user clicks, without affecting other selected rows. + + (This only has an effect if multiple selection is also enabled). + If not enabled, you can still get the same row-flipping behaviour by holding + down CMD or CTRL when clicking. + */ + void setClickingTogglesRowSelection (bool flipRowSelection) noexcept; /** Makes the list react to mouse moves by selecting the row that the mouse if over. @@ -557,7 +569,7 @@ private: int totalItems, rowHeight, minimumRowWidth; int outlineThickness; int lastRowSelected; - bool multipleSelection, hasDoneInitialUpdate; + bool multipleSelection, alwaysFlipSelection, hasDoneInitialUpdate; SparseSet selected; void selectRowInternal (int rowNumber, bool dontScrollToShowThisRow, diff --git a/source/modules/juce_gui_basics/widgets/juce_Slider.cpp b/source/modules/juce_gui_basics/widgets/juce_Slider.cpp index db981e329..8c76b900e 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -464,9 +464,9 @@ public: const bool stopAtEnd) { // make sure the values are sensible.. - jassert (rotaryStart >= 0 && rotaryEnd >= 0); - jassert (rotaryStart < float_Pi * 4.0f && rotaryEnd < float_Pi * 4.0f); - jassert (rotaryStart < rotaryEnd); + jassert (startAngleRadians >= 0 && endAngleRadians >= 0); + jassert (startAngleRadians < float_Pi * 4.0f && endAngleRadians < float_Pi * 4.0f); + jassert (startAngleRadians < endAngleRadians); rotaryStart = startAngleRadians; rotaryEnd = endAngleRadians; @@ -1006,9 +1006,9 @@ public: valueBox->hideEditor (false); const double value = (double) currentValue.getValue(); - const double delta = getMouseWheelDelta (value, (wheel.deltaX != 0 ? -wheel.deltaX : wheel.deltaY) - * (wheel.isReversed ? -1.0f : 1.0f)); - + const double delta = getMouseWheelDelta (value, (std::abs (wheel.deltaX) > std::abs (wheel.deltaY) + ? -wheel.deltaX : wheel.deltaY) + * (wheel.isReversed ? -1.0f : 1.0f)); if (delta != 0) { const double newValue = value + jmax (interval, std::abs (delta)) * (delta < 0 ? -1.0 : 1.0); @@ -1287,6 +1287,7 @@ public: { setAlwaysOnTop (true); setAllowedPlacement (owner.getLookAndFeel().getSliderPopupPlacement (s)); + setLookAndFeel (&s.getLookAndFeel()); } void paintContent (Graphics& g, int w, int h) diff --git a/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp b/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp index ce71febb4..cedec8db2 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp @@ -26,7 +26,7 @@ class TableListBox::RowComp : public Component, public TooltipClient { public: - RowComp (TableListBox& tlb) : owner (tlb), row (-1), isSelected (false) + RowComp (TableListBox& tlb) noexcept : owner (tlb), row (-1), isSelected (false) { } @@ -192,7 +192,7 @@ public: if (TableListBoxModel* m = owner.getModel()) return m->getCellTooltip (row, columnId); - return String::empty; + return String(); } Component* findChildComponentForColumn (const int columnId) const @@ -275,7 +275,7 @@ void TableListBox::setHeader (TableHeaderComponent* newHeader) { jassert (newHeader != nullptr); // you need to supply a real header for a table! - Rectangle newBounds (0, 0, 100, 28); + Rectangle newBounds (100, 28); if (header != nullptr) newBounds = header->getBounds(); @@ -287,7 +287,7 @@ void TableListBox::setHeader (TableHeaderComponent* newHeader) header->addListener (this); } -int TableListBox::getHeaderHeight() const +int TableListBox::getHeaderHeight() const noexcept { return header->getHeight(); } @@ -312,16 +312,11 @@ void TableListBox::autoSizeAllColumns() autoSizeColumn (header->getColumnIdOfIndex (i, true)); } -void TableListBox::setAutoSizeMenuOptionShown (const bool shouldBeShown) +void TableListBox::setAutoSizeMenuOptionShown (const bool shouldBeShown) noexcept { autoSizeOptionsShown = shouldBeShown; } -bool TableListBox::isAutoSizeMenuOptionShown() const -{ - return autoSizeOptionsShown; -} - Rectangle TableListBox::getCellPosition (const int columnId, const int rowNumber, const bool relativeToComponentTopLeft) const { @@ -337,7 +332,7 @@ Rectangle TableListBox::getCellPosition (const int columnId, const int rowN Component* TableListBox::getCellComponent (int columnId, int rowNumber) const { - if (RowComp* const rowComp = dynamic_cast (getComponentForRowNumber (rowNumber))) + if (RowComp* const rowComp = dynamic_cast (getComponentForRowNumber (rowNumber))) return rowComp->findChildComponentForColumn (columnId); return nullptr; @@ -370,12 +365,12 @@ void TableListBox::paintListBoxItem (int, Graphics&, int, int, bool) { } -Component* TableListBox::refreshComponentForRow (int rowNumber, bool isRowSelected_, Component* existingComponentToUpdate) +Component* TableListBox::refreshComponentForRow (int rowNumber, bool rowSelected, Component* existingComponentToUpdate) { if (existingComponentToUpdate == nullptr) existingComponentToUpdate = new RowComp (*this); - static_cast (existingComponentToUpdate)->update (rowNumber, isRowSelected_); + static_cast (existingComponentToUpdate)->update (rowNumber, rowSelected); return existingComponentToUpdate; } @@ -450,7 +445,7 @@ void TableListBox::updateColumnComponents() const const int firstRow = getRowContainingPosition (0, 0); for (int i = firstRow + getNumRowsOnScreen() + 2; --i >= firstRow;) - if (RowComp* const rowComp = dynamic_cast (getComponentForRowNumber (i))) + if (RowComp* const rowComp = dynamic_cast (getComponentForRowNumber (i))) rowComp->resized(); } @@ -465,7 +460,7 @@ void TableListBoxModel::deleteKeyPressed (int) {} void TableListBoxModel::returnKeyPressed (int) {} void TableListBoxModel::listWasScrolled() {} -String TableListBoxModel::getCellTooltip (int /*rowNumber*/, int /*columnId*/) { return String::empty; } +String TableListBoxModel::getCellTooltip (int /*rowNumber*/, int /*columnId*/) { return String(); } var TableListBoxModel::getDragSourceDescription (const SparseSet&) { return var(); } Component* TableListBoxModel::refreshComponentForCell (int, int, bool, Component* existingComponentToUpdate) diff --git a/source/modules/juce_gui_basics/widgets/juce_TableListBox.h b/source/modules/juce_gui_basics/widgets/juce_TableListBox.h index 19096aeff..d63112550 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TableListBox.h +++ b/source/modules/juce_gui_basics/widgets/juce_TableListBox.h @@ -57,7 +57,7 @@ public: The graphics context has its origin at the row's top-left, and your method should fill the area specified by the width and height parameters. */ - virtual void paintRowBackground (Graphics& g, + virtual void paintRowBackground (Graphics&, int rowNumber, int width, int height, bool rowIsSelected) = 0; @@ -66,8 +66,11 @@ public: The graphics context's origin will already be set to the top-left of the cell, whose size is specified by (width, height). + + Note that the rowNumber value may be greater than the number of rows in your + list, so be careful that you don't assume it's less than getNumRows(). */ - virtual void paintCell (Graphics& g, + virtual void paintCell (Graphics&, int rowNumber, int columnId, int width, int height, @@ -142,25 +145,21 @@ public: */ virtual int getColumnAutoSizeWidth (int columnId); - /** Returns a tooltip for a particular cell in the table. - */ + /** Returns a tooltip for a particular cell in the table. */ virtual String getCellTooltip (int rowNumber, int columnId); //============================================================================== /** Override this to be informed when rows are selected or deselected. - @see ListBox::selectedRowsChanged() */ virtual void selectedRowsChanged (int lastRowSelected); /** Override this to be informed when the delete key is pressed. - @see ListBox::deleteKeyPressed() */ virtual void deleteKeyPressed (int lastRowSelected); /** Override this to be informed when the return key is pressed. - @see ListBox::returnKeyPressed() */ virtual void returnKeyPressed (int lastRowSelected); @@ -210,29 +209,34 @@ public: /** Creates a TableListBox. The model pointer passed-in can be null, in which case you can set it later - with setModel(). + with setModel(). The TableListBox does not take ownership of the model - it's + the caller's responsibility to manage its lifetime and make sure it + doesn't get deleted while still being used. */ - TableListBox (const String& componentName = String::empty, - TableListBoxModel* model = 0); + TableListBox (const String& componentName = String(), + TableListBoxModel* model = nullptr); /** Destructor. */ ~TableListBox(); //============================================================================== /** Changes the TableListBoxModel that is being used for this table. + The TableListBox does not take ownership of the model - it's the caller's responsibility + to manage its lifetime and make sure it doesn't get deleted while still being used. */ void setModel (TableListBoxModel* newModel); /** Returns the model currently in use. */ - TableListBoxModel* getModel() const { return model; } + TableListBoxModel* getModel() const noexcept { return model; } //============================================================================== /** Returns the header component being used in this table. */ - TableHeaderComponent& getHeader() const { return *header; } + TableHeaderComponent& getHeader() const noexcept { return *header; } /** Sets the header component to use for the table. The table will take ownership of the component that you pass in, and will delete it when it's no longer needed. + The pointer passed in may not be null. */ void setHeader (TableHeaderComponent* newHeader); @@ -244,7 +248,7 @@ public: /** Returns the height of the table header. @see setHeaderHeight */ - int getHeaderHeight() const; + int getHeaderHeight() const noexcept; //============================================================================== /** Resizes a column to fit its contents. @@ -260,15 +264,14 @@ public: void autoSizeAllColumns(); /** Enables or disables the auto size options on the popup menu. - By default, these are enabled. */ - void setAutoSizeMenuOptionShown (bool shouldBeShown); + void setAutoSizeMenuOptionShown (bool shouldBeShown) noexcept; /** True if the auto-size options should be shown on the menu. - @see setAutoSizeMenuOptionsShown + @see setAutoSizeMenuOptionShown */ - bool isAutoSizeMenuOptionShown() const; + bool isAutoSizeMenuOptionShown() const noexcept { return autoSizeOptionsShown; } /** Returns the position of one of the cells in the table. diff --git a/source/modules/juce_gui_basics/widgets/juce_Toolbar.cpp b/source/modules/juce_gui_basics/widgets/juce_Toolbar.cpp index fc7b4a29f..9b2b7cbb9 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Toolbar.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_Toolbar.cpp @@ -591,8 +591,8 @@ void Toolbar::itemDragMove (const SourceDetails& dragSourceDetails) { const Rectangle previousPos (animator.getComponentDestination (prev)); - if (abs (dragObjectLeft - (vertical ? previousPos.getY() : previousPos.getX()) - < abs (dragObjectRight - (vertical ? current.getBottom() : current.getRight())))) + if (std::abs (dragObjectLeft - (vertical ? previousPos.getY() : previousPos.getX())) + < std::abs (dragObjectRight - (vertical ? current.getBottom() : current.getRight()))) { newIndex = getIndexOfChildComponent (prev); } @@ -602,8 +602,8 @@ void Toolbar::itemDragMove (const SourceDetails& dragSourceDetails) { const Rectangle nextPos (animator.getComponentDestination (next)); - if (abs (dragObjectLeft - (vertical ? current.getY() : current.getX()) - > abs (dragObjectRight - (vertical ? nextPos.getBottom() : nextPos.getRight())))) + if (std::abs (dragObjectLeft - (vertical ? current.getY() : current.getX())) + > std::abs (dragObjectRight - (vertical ? nextPos.getBottom() : nextPos.getRight()))) { newIndex = getIndexOfChildComponent (next) + 1; } diff --git a/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp index c7bb76e6e..9360000f4 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -1773,6 +1773,11 @@ TreeViewItem* TreeViewItem::getNextVisibleItem (const bool recurse) const noexce return nullptr; } +static String escapeSlashesInTreeViewItemName (const String& s) +{ + return s.replaceCharacter ('/', '\\'); +} + String TreeViewItem::getItemIdentifierString() const { String s; @@ -1780,12 +1785,12 @@ String TreeViewItem::getItemIdentifierString() const if (parentItem != nullptr) s = parentItem->getItemIdentifierString(); - return s + "/" + getUniqueName().replaceCharacter ('/', '\\'); + return s + "/" + escapeSlashesInTreeViewItemName (getUniqueName()); } TreeViewItem* TreeViewItem::findItemFromIdentifierString (const String& identifierString) { - const String thisId ("/" + getUniqueName()); + const String thisId ("/" + escapeSlashesInTreeViewItemName (getUniqueName())); if (thisId == identifierString) return this; diff --git a/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index 39cc28b8e..13f1311cb 100644 --- a/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -23,7 +23,7 @@ */ CallOutBox::CallOutBox (Component& c, const Rectangle& area, Component* const parent) - : arrowSize (16.0f), content (c) + : arrowSize (16.0f), content (c), dismissalMouseClicksAreAlwaysConsumed (false) { addAndMakeVisible (content); @@ -123,9 +123,8 @@ bool CallOutBox::hitTest (int x, int y) void CallOutBox::inputAttemptWhenModal() { - const Point mousePos (getMouseXYRelative() + getBounds().getPosition()); - - if (targetArea.contains (mousePos)) + if (dismissalMouseClicksAreAlwaysConsumed + || targetArea.contains (getMouseXYRelative() + getBounds().getPosition())) { // if you click on the area that originally popped-up the callout, you expect it // to get rid of the box, but deleting the box here allows the click to pass through and @@ -139,6 +138,11 @@ void CallOutBox::inputAttemptWhenModal() } } +void CallOutBox::setDismissalMouseClicksAreAlwaysConsumed (bool b) noexcept +{ + dismissalMouseClicksAreAlwaysConsumed = b; +} + enum { callOutBoxDismissCommandId = 0x4f83a04b }; void CallOutBox::handleCommandMessage (int commandId) diff --git a/source/modules/juce_gui_basics/windows/juce_CallOutBox.h b/source/modules/juce_gui_basics/windows/juce_CallOutBox.h index 8f574c1a7..5de355072 100644 --- a/source/modules/juce_gui_basics/windows/juce_CallOutBox.h +++ b/source/modules/juce_gui_basics/windows/juce_CallOutBox.h @@ -123,6 +123,16 @@ public: */ void dismiss(); + /** Determines whether the mouse events for clicks outside the calloutbox are + consumed, or allowed to arrive at the other component that they were aimed at. + + By default this is false, so that when you click on something outside the calloutbox, + that event will also be sent to the component that was clicked on. If you set it to + true, then the first click will always just dismiss the box and not be sent to + anything else. + */ + void setDismissalMouseClicksAreAlwaysConsumed (bool shouldAlwaysBeConsumed) noexcept; + //============================================================================== /** This abstract base class is implemented by LookAndFeel classes. */ struct JUCE_API LookAndFeelMethods @@ -161,6 +171,7 @@ private: Point targetPoint; Rectangle availableArea, targetArea; Image background; + bool dismissalMouseClicksAreAlwaysConsumed; void refreshPath(); diff --git a/source/modules/juce_gui_basics/windows/juce_DialogWindow.h b/source/modules/juce_gui_basics/windows/juce_DialogWindow.h index 49a49f3a0..b0cf1973e 100644 --- a/source/modules/juce_gui_basics/windows/juce_DialogWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_DialogWindow.h @@ -78,7 +78,7 @@ public: initialise its fields with the appropriate details, and then call its launchAsync() method to launch the dialog. */ - struct LaunchOptions + struct JUCE_API LaunchOptions { LaunchOptions() noexcept; diff --git a/source/modules/juce_gui_basics/windows/juce_TopLevelWindow.h b/source/modules/juce_gui_basics/windows/juce_TopLevelWindow.h index b9822301b..ce1b10427 100644 --- a/source/modules/juce_gui_basics/windows/juce_TopLevelWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_TopLevelWindow.h @@ -127,7 +127,7 @@ public: //============================================================================== /** @internal */ - virtual void addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo = nullptr) override; + void addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo = nullptr) override; protected: //============================================================================== diff --git a/source/modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp b/source/modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp index a52a20d78..397b9f989 100644 --- a/source/modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp +++ b/source/modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp @@ -102,9 +102,7 @@ public: lineLength = 0; lineLengthWithoutNewLines = 0; - String::CharPointerType t (line.getCharPointer()); - - for (;;) + for (String::CharPointerType t (line.getCharPointer());;) { const juce_wchar c = t.getAndAdvance(); @@ -242,16 +240,16 @@ CodeDocument::Position::Position() noexcept } CodeDocument::Position::Position (const CodeDocument& ownerDocument, - const int line_, const int indexInLine_) noexcept - : owner (const_cast (&ownerDocument)), - characterPos (0), line (line_), - indexInLine (indexInLine_), positionMaintained (false) + const int lineNum, const int index) noexcept + : owner (const_cast (&ownerDocument)), + characterPos (0), line (lineNum), + indexInLine (index), positionMaintained (false) { - setLineAndIndex (line_, indexInLine_); + setLineAndIndex (lineNum, index); } CodeDocument::Position::Position (const CodeDocument& ownerDocument, const int characterPos_) noexcept - : owner (const_cast (&ownerDocument)), + : owner (const_cast (&ownerDocument)), positionMaintained (false) { setPosition (characterPos_); @@ -858,7 +856,7 @@ void CodeDocument::insert (const String& text, const int insertPos, const bool u } maximumLineLength = -1; - Array newLines; + Array newLines; CodeDocumentLine::createLines (newLines, textInsideOriginalLine); jassert (newLines.size() > 0); diff --git a/source/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp b/source/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp index 00aa69cb7..b2f26731d 100644 --- a/source/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp +++ b/source/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp @@ -287,8 +287,8 @@ public: void paint (Graphics& g) override { - jassert (dynamic_cast (getParentComponent()) != nullptr); - const CodeEditorComponent& editor = *static_cast (getParentComponent()); + jassert (dynamic_cast (getParentComponent()) != nullptr); + const CodeEditorComponent& editor = *static_cast (getParentComponent()); g.fillAll (editor.findColour (CodeEditorComponent::backgroundColourId) .overlaidWith (editor.findColour (lineNumberBackgroundId))); @@ -1569,7 +1569,7 @@ void CodeEditorComponent::updateCachedIterators (int maxLineNum) CodeDocument::Iterator* t = new CodeDocument::Iterator (last); cachedIterators.add (t); - const int targetLine = last.getLine() + linesBetweenCachedSources; + const int targetLine = jmin (maxLineNum, last.getLine() + linesBetweenCachedSources); for (;;) { diff --git a/source/modules/juce_gui_extra/native/juce_mac_NSViewComponent.mm b/source/modules/juce_gui_extra/native/juce_mac_NSViewComponent.mm index b7e814d56..4fd30aa07 100644 --- a/source/modules/juce_gui_extra/native/juce_mac_NSViewComponent.mm +++ b/source/modules/juce_gui_extra/native/juce_mac_NSViewComponent.mm @@ -142,7 +142,6 @@ public: if (currentPeer != peer) { - removeFromParent(); currentPeer = peer; if (peer != nullptr) @@ -151,6 +150,10 @@ public: [peerView addSubview: view]; componentMovedOrResized (false, false); } + else + { + removeFromParent(); + } } [view setHidden: ! owner.isShowing()]; diff --git a/source/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp b/source/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp index fdc6be7b3..273d790d0 100644 --- a/source/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp +++ b/source/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp @@ -226,6 +226,9 @@ void WebBrowserComponent::goToURL (const String& url, blankPageShown = false; + if (browser->browser == nullptr) + checkWindowAssociation(); + browser->goToURL (url, headers, postData); } @@ -262,7 +265,10 @@ void WebBrowserComponent::refresh() void WebBrowserComponent::paint (Graphics& g) { if (browser->browser == nullptr) + { g.fillAll (Colours::white); + checkWindowAssociation(); + } } void WebBrowserComponent::checkWindowAssociation()