From 6c7b0777045118ea537bf8c1ee165c78c0135567 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 26 May 2015 09:40:52 +0200 Subject: [PATCH] Update to latest juce --- .../buffers/juce_AudioSampleBuffer.cpp | 7 +- .../buffers/juce_FloatVectorOperations.cpp | 142 ++- .../buffers/juce_FloatVectorOperations.h | 6 + .../juce_audio_basics/effects/juce_Reverb.h | 2 +- .../juce_audio_basics/juce_audio_basics.cpp | 3 + .../midi/juce_MidiKeyboardState.cpp | 2 +- .../synthesisers/juce_Synthesiser.cpp | 122 +- .../synthesisers/juce_Synthesiser.h | 43 + .../audio_io/juce_AudioDeviceManager.cpp | 5 + .../native/juce_linux_ALSA.cpp | 42 +- .../native/juce_linux_Midi.cpp | 14 +- .../native/juce_mac_CoreAudio.cpp | 6 + .../native/juce_win32_ASIO.cpp | 12 +- .../codecs/juce_LAMEEncoderAudioFormat.cpp | 4 +- .../codecs/juce_MP3AudioFormat.cpp | 5 +- .../codecs/juce_QuickTimeAudioFormat.cpp | 10 +- .../codecs/juce_WavAudioFormat.cpp | 12 +- .../oggvorbis/libvorbis-1.3.2/lib/floor1.c | 2 +- .../format/juce_AudioFormat.h | 2 +- .../format/juce_AudioFormatReaderSource.h | 4 +- .../juce_audio_formats/sampler/juce_Sampler.h | 2 +- .../juce_AudioUnitPluginFormat.mm | 80 +- .../format_types/juce_LADSPAPluginFormat.cpp | 2 +- .../format_types/juce_VST3Common.h | 4 +- .../format_types/juce_VSTPluginFormat.cpp | 145 ++- .../processors/juce_AudioPlayHead.h | 4 +- .../processors/juce_AudioProcessor.cpp | 8 +- .../processors/juce_AudioProcessor.h | 62 +- .../processors/juce_AudioProcessorGraph.h | 2 +- .../processors/juce_AudioProcessorParameter.h | 2 +- .../scanning/juce_PluginListComponent.cpp | 53 +- .../scanning/juce_PluginListComponent.h | 22 +- .../juce_core/containers/juce_AbstractFifo.h | 2 +- .../modules/juce_core/containers/juce_Array.h | 4 +- .../containers/juce_ReferenceCountedArray.h | 31 +- .../juce_core/files/juce_FileInputStream.cpp | 2 +- .../juce_core/files/juce_FileOutputStream.cpp | 8 +- .../juce_core/javascript/juce_JSON.cpp | 2 +- .../juce_core/javascript/juce_Javascript.cpp | 9 + source/modules/juce_core/juce_core.cpp | 5 + .../juce_core/maths/juce_MathsFunctions.h | 59 +- .../modules/juce_core/memory/juce_ByteOrder.h | 2 +- .../modules/juce_core/memory/juce_HeapBlock.h | 26 +- .../juce_core/memory/juce_MemoryBlock.cpp | 2 +- .../modules/juce_core/memory/juce_Singleton.h | 2 +- .../native/java/JuceAppActivity.java | 32 +- .../native/juce_linux_CommonFile.cpp | 4 +- .../juce_core/native/juce_linux_Files.cpp | 12 +- .../juce_core/native/juce_linux_Network.cpp | 16 +- .../native/juce_linux_SystemStats.cpp | 27 +- .../juce_core/native/juce_linux_Threads.cpp | 22 - .../juce_core/native/juce_mac_Files.mm | 13 +- .../juce_core/native/juce_mac_Network.mm | 12 +- .../juce_core/native/juce_mac_SystemStats.mm | 2 + .../juce_core/native/juce_osx_ObjCHelpers.h | 6 +- .../juce_core/native/juce_posix_SharedCode.h | 58 +- .../native/juce_win32_SystemStats.cpp | 12 +- .../juce_core/network/juce_IPAddress.cpp | 2 +- .../modules/juce_core/network/juce_Socket.cpp | 414 ++++--- .../modules/juce_core/network/juce_Socket.h | 118 +- .../streams/juce_MemoryOutputStream.h | 2 +- .../juce_core/system/juce_SystemStats.cpp | 9 +- .../juce_core/system/juce_SystemStats.h | 52 +- .../juce_core/text/juce_CharPointer_UTF8.h | 2 +- .../text/juce_CharacterFunctions.cpp | 16 +- .../juce_core/text/juce_Identifier.cpp | 15 +- source/modules/juce_core/text/juce_String.cpp | 11 +- source/modules/juce_core/text/juce_String.h | 4 +- .../modules/juce_core/threads/juce_Thread.h | 2 +- .../modules/juce_core/xml/juce_XmlElement.h | 2 +- .../zip/juce_GZIPCompressorOutputStream.h | 2 +- .../zip/juce_GZIPDecompressorInputStream.cpp | 35 +- .../zip/juce_GZIPDecompressorInputStream.h | 20 +- source/modules/juce_core/zip/juce_ZipFile.cpp | 4 +- source/modules/juce_core/zip/zlib/trees.c | 32 +- .../app_properties/juce_PropertiesFile.h | 2 +- .../undomanager/juce_UndoManager.cpp | 8 + .../undomanager/juce_UndoManager.h | 5 + .../values/juce_Value.cpp | 4 +- .../values/juce_ValueTree.cpp | 20 +- .../messages/juce_ApplicationBase.cpp | 2 +- .../messages/juce_ApplicationBase.h | 3 +- .../native/juce_ios_MessageManager.mm | 12 +- .../native/juce_linux_Messaging.cpp | 6 +- .../native/juce_mac_MessageManager.mm | 6 +- .../modules/juce_events/timers/juce_Timer.cpp | 82 +- .../modules/juce_events/timers/juce_Timer.h | 18 +- .../juce_graphics/colour/juce_Colour.cpp | 39 +- .../juce_graphics/colour/juce_Colour.h | 13 + .../juce_graphics/colour/juce_PixelFormats.h | 460 ++++--- .../contexts/juce_GraphicsContext.h | 16 +- ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 4 +- .../modules/juce_graphics/fonts/juce_Font.cpp | 4 + .../modules/juce_graphics/fonts/juce_Font.h | 12 + .../fonts/juce_GlyphArrangement.cpp | 5 +- .../fonts/juce_GlyphArrangement.h | 6 +- .../juce_graphics/fonts/juce_TextLayout.cpp | 5 + .../juce_graphics/geometry/juce_Line.h | 26 +- .../juce_graphics/geometry/juce_Path.cpp | 10 +- .../juce_graphics/geometry/juce_Path.h | 23 +- .../juce_graphics/geometry/juce_Point.h | 3 + .../juce_graphics/geometry/juce_Rectangle.h | 3 + .../geometry/juce_RectangleList.h | 14 + .../juce_graphics/images/juce_Image.cpp | 21 +- .../modules/juce_graphics/images/juce_Image.h | 5 + .../modules/juce_graphics/juce_graphics.cpp | 10 +- .../native/juce_RenderingHelpers.h | 11 +- .../native/juce_freetype_Fonts.cpp | 14 +- .../native/juce_mac_CoreGraphicsContext.mm | 4 +- .../application/juce_Application.h | 8 +- .../commands/juce_ApplicationCommandManager.h | 2 +- .../components/juce_Component.cpp | 18 +- .../components/juce_Component.h | 61 +- .../components/juce_ModalComponentManager.h | 6 +- .../juce_gui_basics/drawables/juce_Drawable.h | 7 +- .../drawables/juce_DrawableComposite.h | 8 +- .../drawables/juce_DrawablePath.cpp | 6 +- .../drawables/juce_DrawableShape.cpp | 6 +- .../drawables/juce_SVGParser.cpp | 13 +- .../filebrowser/juce_FileBrowserComponent.cpp | 5 +- .../filebrowser/juce_FileListComponent.h | 12 +- .../juce_gui_basics/juce_gui_basics.cpp | 5 + .../modules/juce_gui_basics/juce_gui_basics.h | 12 + .../juce_gui_basics/layout/juce_ScrollBar.cpp | 10 +- .../juce_gui_basics/layout/juce_ScrollBar.h | 1 + .../juce_gui_basics/layout/juce_Viewport.cpp | 32 +- .../juce_gui_basics/layout/juce_Viewport.h | 3 + .../lookandfeel/juce_LookAndFeel_V2.cpp | 80 +- .../lookandfeel/juce_LookAndFeel_V2.h | 3 +- .../misc/juce_DropShadower.cpp | 2 +- .../mouse/juce_MouseInactivityDetector.cpp | 11 +- .../mouse/juce_MouseInactivityDetector.h | 9 +- .../mouse/juce_MouseInputSource.cpp | 7 +- .../native/juce_android_Windowing.cpp | 18 +- .../native/juce_ios_Windowing.mm | 35 +- .../native/juce_linux_Clipboard.cpp | 8 +- .../native/juce_linux_Windowing.cpp | 1080 +++++++++++++---- .../native/juce_mac_NSViewComponentPeer.mm | 19 +- .../positioning/juce_RelativeCoordinate.h | 35 + .../positioning/juce_RelativeRectangle.cpp | 6 +- .../properties/juce_PropertyComponent.h | 2 - .../properties/juce_PropertyPanel.cpp | 146 +-- .../properties/juce_PropertyPanel.h | 20 +- .../juce_gui_basics/widgets/juce_ComboBox.h | 4 +- .../juce_gui_basics/widgets/juce_Label.cpp | 12 +- .../juce_gui_basics/widgets/juce_Label.h | 5 +- .../juce_gui_basics/widgets/juce_ListBox.cpp | 40 +- .../juce_gui_basics/widgets/juce_ListBox.h | 21 +- .../juce_gui_basics/widgets/juce_Slider.cpp | 113 +- .../juce_gui_basics/widgets/juce_Slider.h | 14 + .../widgets/juce_TableHeaderComponent.h | 7 +- .../widgets/juce_TableListBox.cpp | 13 +- .../juce_gui_basics/widgets/juce_TextEditor.h | 2 + .../juce_gui_basics/widgets/juce_Toolbar.cpp | 20 +- .../juce_gui_basics/widgets/juce_Toolbar.h | 6 +- .../juce_gui_basics/widgets/juce_TreeView.h | 2 +- .../windows/juce_AlertWindow.h | 10 +- .../windows/juce_ComponentPeer.cpp | 6 +- .../windows/juce_DocumentWindow.h | 2 +- .../windows/juce_ResizableWindow.cpp | 38 +- .../windows/juce_ResizableWindow.h | 18 +- .../windows/juce_TooltipWindow.cpp | 13 +- .../windows/juce_TooltipWindow.h | 12 +- .../windows/juce_TopLevelWindow.h | 2 +- .../code_editor/juce_CodeEditorComponent.cpp | 4 +- .../juce_gui_extra/misc/juce_ColourSelector.h | 2 +- .../misc/juce_KeyMappingEditorComponent.cpp | 4 +- .../misc/juce_KeyMappingEditorComponent.h | 2 - .../native/juce_linux_SystemTrayIcon.cpp | 4 +- .../native/juce_mac_SystemTrayIcon.cpp | 28 +- 170 files changed, 3289 insertions(+), 1630 deletions(-) diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp index 15b59dafc..8fa04655a 100644 --- a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp @@ -116,8 +116,11 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo, jassert (dataToReferTo != nullptr); jassert (newNumChannels >= 0 && newNumSamples >= 0); - allocatedBytes = 0; - allocatedData.free(); + if (allocatedBytes != 0) + { + allocatedBytes = 0; + allocatedData.free(); + } numChannels = newNumChannels; size = newNumSamples; diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 67a7297a5..eacf0f218 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -49,8 +49,13 @@ namespace FloatVectorHelpers { typedef float Type; typedef __m128 ParallelType; + typedef __m128 IntegerType; enum { numParallel = 4 }; + // Integer and parallel types are the same for SSE. On neon they have different types + static forcedinline IntegerType toint (ParallelType v) noexcept { return v; } + static forcedinline ParallelType toflt (IntegerType v) noexcept { return v; } + static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_ps (&v); } static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_ps (v); } static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_ps (v); } @@ -63,6 +68,11 @@ namespace FloatVectorHelpers static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_ps (a, b); } static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_ps (a, b); } + static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return _mm_and_ps (a, b); } + static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return _mm_andnot_ps (a, b); } + static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return _mm_or_ps (a, b); } + static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return _mm_xor_ps (a, b); } + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } }; @@ -71,8 +81,13 @@ namespace FloatVectorHelpers { typedef double Type; typedef __m128d ParallelType; + typedef __m128d IntegerType; enum { numParallel = 2 }; + // Integer and parallel types are the same for SSE. On neon they have different types + static forcedinline IntegerType toint (ParallelType v) noexcept { return v; } + static forcedinline ParallelType toflt (IntegerType v) noexcept { return v; } + static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_pd (&v); } static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_pd (v); } static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_pd (v); } @@ -85,10 +100,17 @@ namespace FloatVectorHelpers static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_pd (a, b); } static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_pd (a, b); } + static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return _mm_and_pd (a, b); } + static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return _mm_andnot_pd (a, b); } + static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return _mm_or_pd (a, b); } + static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return _mm_xor_pd (a, b); } + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1]); } static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); } }; + + #define JUCE_BEGIN_VEC_OP \ typedef FloatVectorHelpers::ModeType::Mode Mode; \ if (FloatVectorHelpers::isSSE2Available()) \ @@ -126,23 +148,62 @@ namespace FloatVectorHelpers #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ + if (FloatVectorHelpers::isAligned (dest)) \ { \ - 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); \ + if (FloatVectorHelpers::isAligned (src1)) \ + { \ + if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ + else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadU, Mode::storeA, locals, increment) \ + } \ + else \ + { \ + if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ + else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeA, locals, increment) \ + } \ + } \ + else \ + { \ + if (FloatVectorHelpers::isAligned (src1)) \ + { \ + if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadA, Mode::storeU, locals, increment) \ + else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ + } \ + else \ + { \ + if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadA, Mode::storeU, locals, increment) \ + else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + } \ } \ JUCE_FINISH_VEC_OP (normalOp) #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ + if (FloatVectorHelpers::isAligned (dest)) \ { \ - 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; \ - Mode::ParallelType (&loadDst) (const Mode::Type* v) = FloatVectorHelpers::isAligned (dest) ? Mode::loadA : Mode::loadU; \ - void (&storeDst) (Mode::Type* dest, Mode::ParallelType a) = FloatVectorHelpers::isAligned (dest) ? Mode::storeA : Mode::storeU; \ - JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, loadSrc1, loadSrc2, loadDst, storeDst, locals, increment); \ + if (FloatVectorHelpers::isAligned (src1)) \ + { \ + if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ + else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ + } \ + else \ + { \ + if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ + else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ + } \ + } \ + else \ + { \ + if (FloatVectorHelpers::isAligned (src1)) \ + { \ + if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ + else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + } \ + else \ + { \ + if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ + else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + } \ } \ JUCE_FINISH_VEC_OP (normalOp) @@ -154,8 +215,12 @@ namespace FloatVectorHelpers { typedef float Type; typedef float32x4_t ParallelType; + typedef uint32x4_t IntegerType; enum { numParallel = 4 }; + static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; } + static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; } + static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); } static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); } static forcedinline ParallelType loadU (const Type* v) noexcept { return vld1q_f32 (v); } @@ -168,6 +233,11 @@ namespace FloatVectorHelpers static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return vmaxq_f32 (a, b); } static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return vminq_f32 (a, b); } + static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return toflt (vandq_u32 (toint (a), toint (b))); } + static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return toflt (vbicq_u32 (toint (a), toint (b))); } + static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return toflt (vorrq_u32 (toint (a), toint (b))); } + static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return toflt (veorq_u32 (toint (a), toint (b))); } + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } }; @@ -176,8 +246,12 @@ namespace FloatVectorHelpers { typedef double Type; typedef double ParallelType; + typedef uint64 IntegerType; enum { numParallel = 1 }; + static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; } + static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; } + static forcedinline ParallelType load1 (Type v) noexcept { return v; } static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; } static forcedinline ParallelType loadU (const Type* v) noexcept { return *v; } @@ -190,6 +264,11 @@ namespace FloatVectorHelpers static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return jmax (a, b); } static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return jmin (a, b); } + static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) & toint (b)); } + static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return toflt ((~toint (a)) & toint (b)); } + static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) | toint (b)); } + static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) ^ toint (b)); } + static forcedinline Type max (ParallelType a) noexcept { return a; } static forcedinline Type min (ParallelType a) noexcept { return a; } }; @@ -428,7 +507,7 @@ void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept #if JUCE_USE_VDSP_FRAMEWORK vDSP_vclr (dest, 1, (size_t) num); #else - zeromem (dest, num * sizeof (float)); + zeromem (dest, (size_t) num * sizeof (float)); #endif } @@ -437,7 +516,7 @@ void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept #if JUCE_USE_VDSP_FRAMEWORK vDSP_vclrD (dest, 1, (size_t) num); #else - zeromem (dest, num * sizeof (double)); + zeromem (dest, (size_t) num * sizeof (double)); #endif } @@ -495,8 +574,12 @@ void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsadd (dest, 1, &amount, dest, 1, (vDSP_Length) num); + #else JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, const Mode::ParallelType amountToAdd = Mode::load1 (amount);) + #endif } void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept @@ -601,9 +684,13 @@ void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsma (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); + #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif } void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept @@ -723,6 +810,33 @@ void FloatVectorOperations::negate (double* dest, const double* src, int num) no #endif } +void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); + #else + union {float f; uint32 i;} signMask; + signMask.i = 0x7fffffffUL; + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabsf (src[i]), Mode::bit_and (s, mask), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mask = Mode::load1 (signMask.f);) + #endif +} + +void FloatVectorOperations::abs (double* dest, const double* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); + #else + union {double d; uint64 i;} signMask; + signMask.i = 0x7fffffffffffffffULL; + + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabs (src[i]), Mode::bit_and (s, mask), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mask = Mode::load1 (signMask.d);) + #endif +} + void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept { #if JUCE_USE_ARM_NEON @@ -966,6 +1080,12 @@ public: FloatVectorOperations::subtract (data1, data2, num); u.expect (areAllValuesEqual (data1, num, (ValueType) 512)); + FloatVectorOperations::abs (data1, data2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 256)); + + FloatVectorOperations::abs (data2, data1, num); + u.expect (areAllValuesEqual (data2, num, (ValueType) 256)); + fillRandomly (random, int1, num); doConversionTest (u, data1, data2, int1, num); diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h index b7dc972da..114d51662 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -137,6 +137,12 @@ public: /** Copies a source vector to a destination, negating each value. */ static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; + /** Copies a source vector to a destination, taking the absolute of each value. */ + static void JUCE_CALLTYPE abs (float* dest, const float* src, int numValues) noexcept; + + /** Copies a source vector to a destination, taking the absolute of each value. */ + static void JUCE_CALLTYPE abs (double* dest, const double* src, int numValues) noexcept; + /** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; diff --git a/source/modules/juce_audio_basics/effects/juce_Reverb.h b/source/modules/juce_audio_basics/effects/juce_Reverb.h index c0b7596ec..6af4acc2a 100644 --- a/source/modules/juce_audio_basics/effects/juce_Reverb.h +++ b/source/modules/juce_audio_basics/effects/juce_Reverb.h @@ -333,7 +333,7 @@ private: if (countdown <= 0) currentValue = target; else - step = (target - currentValue) / countdown; + step = (target - currentValue) / (float) countdown; } } diff --git a/source/modules/juce_audio_basics/juce_audio_basics.cpp b/source/modules/juce_audio_basics/juce_audio_basics.cpp index a2ce4047d..76763056a 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/source/modules/juce_audio_basics/juce_audio_basics.cpp @@ -66,6 +66,9 @@ #if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) #define JUCE_USE_ARM_NEON 1 +#endif + +#if JUCE_USE_ARM_NEON #include #endif diff --git a/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp b/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp index a40262b37..6c4e794fa 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp @@ -145,7 +145,7 @@ void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, const bool injectIndirectEvents) { MidiBuffer::Iterator i (buffer); - MidiMessage message (0xf4, 0.0); + MidiMessage message; int time; const ScopedLock sl (lock); diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 45f2d6663..2fa2c540e 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -63,6 +63,7 @@ void SynthesiserVoice::clearCurrentNote() } void SynthesiserVoice::aftertouchChanged (int) {} +void SynthesiserVoice::channelPressureChanged (int) {} bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept { @@ -73,6 +74,7 @@ bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const no Synthesiser::Synthesiser() : sampleRate (0), lastNoteOnCounter (0), + minimumSubBlockSize (32), shouldStealNotes (true) { for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i) @@ -131,6 +133,12 @@ void Synthesiser::setNoteStealingEnabled (const bool shouldSteal) shouldStealNotes = shouldSteal; } +void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples) noexcept +{ + jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1 + minimumSubBlockSize = numSamples; +} + //============================================================================== void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) { @@ -153,30 +161,45 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu // must set the sample rate before using this! jassert (sampleRate != 0); - const ScopedLock sl (lock); - MidiBuffer::Iterator midiIterator (midiData); midiIterator.setNextSamplePosition (startSample); - MidiMessage m (0xf4, 0.0); + + int midiEventPos; + MidiMessage m; + + const ScopedLock sl (lock); while (numSamples > 0) { - int midiEventPos; - const bool useEvent = midiIterator.getNextEvent (m, midiEventPos) - && midiEventPos < startSample + numSamples; + if (! midiIterator.getNextEvent (m, midiEventPos)) + { + renderVoices (outputBuffer, startSample, numSamples); + return; + } - const int numThisTime = useEvent ? midiEventPos - startSample - : numSamples; + const int samplesToNextMidiMessage = midiEventPos - startSample; - if (numThisTime > 0) - renderVoices (outputBuffer, startSample, numThisTime); + if (samplesToNextMidiMessage >= numSamples) + { + renderVoices (outputBuffer, startSample, numSamples); + handleMidiEvent (m); + break; + } - if (useEvent) + if (samplesToNextMidiMessage < minimumSubBlockSize) + { handleMidiEvent (m); + continue; + } - startSample += numThisTime; - numSamples -= numThisTime; + renderVoices (outputBuffer, startSample, samplesToNextMidiMessage); + handleMidiEvent (m); + startSample += samplesToNextMidiMessage; + numSamples -= samplesToNextMidiMessage; } + + while (midiIterator.getNextEvent (m, midiEventPos)) + handleMidiEvent (m); } void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples) @@ -187,33 +210,41 @@ void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int void Synthesiser::handleMidiEvent (const MidiMessage& m) { + const int channel = m.getChannel(); + if (m.isNoteOn()) { - noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity()); + noteOn (channel, m.getNoteNumber(), m.getFloatVelocity()); } else if (m.isNoteOff()) { - noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true); + noteOff (channel, m.getNoteNumber(), m.getFloatVelocity(), true); } else if (m.isAllNotesOff() || m.isAllSoundOff()) { - allNotesOff (m.getChannel(), true); + allNotesOff (channel, true); } else if (m.isPitchWheel()) { - const int channel = m.getChannel(); const int wheelPos = m.getPitchWheelValue(); lastPitchWheelValues [channel - 1] = wheelPos; - handlePitchWheel (channel, wheelPos); } else if (m.isAftertouch()) { - handleAftertouch (m.getChannel(), m.getNoteNumber(), m.getAfterTouchValue()); + handleAftertouch (channel, m.getNoteNumber(), m.getAfterTouchValue()); + } + else if (m.isChannelPressure()) + { + handleChannelPressure (channel, m.getChannelPressureValue()); } else if (m.isController()) { - handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue()); + handleController (channel, m.getControllerNumber(), m.getControllerValue()); + } + else if (m.isProgramChange()) + { + handleProgramChange (channel, m.getProgramChangeNumber()); } } @@ -375,6 +406,19 @@ void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aft } } +void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue) +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) + voice->channelPressureChanged (channelPressureValue); + } +} + void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) { jassert (midiChannel > 0 && midiChannel <= 16); @@ -423,6 +467,12 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) jassert (midiChannel > 0 && midiChannel <= 16); } +void Synthesiser::handleProgramChange (int midiChannel, int programNumber) +{ + (void) midiChannel; (void) programNumber; + jassert (midiChannel > 0 && midiChannel <= 16); +} + //============================================================================== SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, int midiChannel, int midiNoteNumber, @@ -481,10 +531,10 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, } } - const int stealableVoiceRange = usableVoices.size() - 6; + const int numUsableVoices = usableVoices.size(); // The oldest note that's playing with the target pitch playing is ideal.. - for (int i = 0; i < stealableVoiceRange; ++i) + for (int i = 0; i < numUsableVoices; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); @@ -492,8 +542,27 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, return voice; } - // ..otherwise, look for the oldest note that isn't the top or bottom note.. - for (int i = 0; i < stealableVoiceRange; ++i) + // Oldest voice that has been released (no finger on it and not held by sustain pedal) + for (int i = 0; i < numUsableVoices; ++i) + { + SynthesiserVoice* const voice = usableVoices.getUnchecked (i); + + if (voice != bottom && voice != top && ! voice->isKeyDown() && ! voice->isSostenutoPedalDown()) + return voice; + } + + // Oldest voice that doesn't have a finger on it: + for (int i = 0; i < numUsableVoices; ++i) + { + SynthesiserVoice* const voice = usableVoices.getUnchecked (i); + + if (voice != bottom && voice != top && ! voice->isKeyDown()) + return voice; + } + + // At this point, all notes have fingers on them, so look for the oldest note + // that isn't the top or bottom note.. + for (int i = 0; i < numUsableVoices; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); @@ -501,6 +570,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, return voice; } - // ..otherwise, there's only one or two voices to choose from - we'll return the oldest one.. - return usableVoices.getFirst(); + // ..otherwise, there's only one or two voices to choose from - prefer to steal the highest one: + jassert (top != nullptr || bottom != nullptr); + return (top == nullptr ? bottom : top); } diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index 66f09609b..206ce2613 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -161,6 +161,11 @@ public: */ virtual void aftertouchChanged (int newAftertouchValue); + /** Called to let the voice know that the channel pressure has changed. + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void channelPressureChanged (int newChannelPressureValue); + //============================================================================== /** Renders the next block of data for this voice. @@ -440,6 +445,20 @@ public: */ virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue); + /** Sends a channel pressure message. + + This will send a channel pressure message to any voices that are playing sounds on + the given midi channel. + + This method will be called automatically according to the midi data passed into + renderNextBlock(), but may be called explicitly too. + + @param midiChannel the midi channel, from 1 to 16 inclusive + @param channelPressureValue the pressure value, between 0 and 127, as returned + by MidiMessage::getChannelPressureValue() + */ + virtual void handleChannelPressure (int midiChannel, int channelPressureValue); + /** Handles a sustain pedal event. */ virtual void handleSustainPedal (int midiChannel, bool isDown); @@ -449,6 +468,13 @@ public: /** Can be overridden to handle soft pedal events. */ virtual void handleSoftPedal (int midiChannel, bool isDown); + /** Can be overridden to handle an incoming program change message. + The base class implementation of this has no effect, but you may want to make your + own synth react to program changes. + */ + virtual void handleProgramChange (int midiChannel, + int programNumber); + //============================================================================== /** Tells the synthesiser what the sample rate is for the audio it's being used to render. @@ -479,6 +505,22 @@ public: */ double getSampleRate() const noexcept { return sampleRate; } + /** Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering. + + When rendering, the audio blocks that are passed into renderNextBlock() will be split up + into smaller blocks that lie between all the incoming midi messages, and it is these smaller + sub-blocks that are rendered with multiple calls to renderVoices(). + + Obviously in a pathological case where there are midi messages on every sample, then + renderVoices() could be called once per sample and lead to poor performance, so this + setting allows you to set a lower limit on the block size. + + The default setting is 32, which means that midi messages are accurate to about < 1ms + accuracy, which is probably fine for most purposes, but you may want to increase or + decrease this value for your synth. + */ + void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; + protected: //============================================================================== /** This is used to control access to the rendering callback and the note trigger methods. */ @@ -537,6 +579,7 @@ private: //============================================================================== double sampleRate; uint32 lastNoteOnCounter; + int minimumSubBlockSize; bool shouldStealNotes; BigInteger sustainPedalsDown; diff --git a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index e0b66146b..f129775aa 100644 --- a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -551,6 +551,11 @@ double AudioDeviceManager::chooseBestSampleRate (double rate) const const Array rates (currentAudioDevice->getAvailableSampleRates()); + if (rate > 0 && rates.contains (rate)) + return rate; + + rate = currentAudioDevice->getCurrentSampleRate(); + if (rate > 0 && rates.contains (rate)) return rate; 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 edbe361e0..cd05d827d 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -57,7 +57,7 @@ static void getDeviceSampleRates (snd_pcm_t* handle, Array& rates) for (int i = 0; ratesToTry[i] != 0; ++i) { if (snd_pcm_hw_params_any (handle, hwParams) >= 0 - && snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0) + && snd_pcm_hw_params_test_rate (handle, hwParams, (unsigned int) ratesToTry[i], 0) == 0) { rates.addIfNotAlreadyThere ((double) ratesToTry[i]); } @@ -257,10 +257,10 @@ public: int dir = 0; unsigned int periods = 4; - snd_pcm_uframes_t samplesPerPeriod = bufferSize; + snd_pcm_uframes_t samplesPerPeriod = (snd_pcm_uframes_t) bufferSize; if (JUCE_ALSA_FAILED (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) - || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, numChannels)) + || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, (unsigned int ) numChannels)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params (handle, hwParams))) @@ -274,7 +274,7 @@ public: || JUCE_ALSA_FAILED (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir))) latency = 0; else - latency = frames * (periods - 1); // (this is the method JACK uses to guess the latency..) + latency = (int) frames * ((int) periods - 1); // (this is the method JACK uses to guess the latency..) JUCE_ALSA_LOG ("frames: " << (int) frames << ", periods: " << (int) periods << ", samplesPerPeriod: " << (int) samplesPerPeriod); @@ -316,22 +316,22 @@ public: if (isInterleaved) { - scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); + scratch.ensureSize ((size_t) ((int) sizeof (float) * numSamples * numChannelsRunning), false); for (int i = 0; i < numChannelsRunning; ++i) converter->convertSamples (scratch.getData(), i, data[i], 0, numSamples); - numDone = snd_pcm_writei (handle, scratch.getData(), numSamples); + numDone = snd_pcm_writei (handle, scratch.getData(), (snd_pcm_uframes_t) numSamples); } else { for (int i = 0; i < numChannelsRunning; ++i) converter->convertSamples (data[i], data[i], numSamples); - numDone = snd_pcm_writen (handle, (void**) data, numSamples); + numDone = snd_pcm_writen (handle, (void**) data, (snd_pcm_uframes_t) numSamples); } - if (numDone < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, numDone, 1 /* silent */))) + if (numDone < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) numDone, 1 /* silent */))) return false; if (numDone < numSamples) @@ -347,12 +347,12 @@ public: if (isInterleaved) { - scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); + scratch.ensureSize ((size_t) ((int) sizeof (float) * numSamples * numChannelsRunning), false); scratch.fillWith (0); // (not clearing this data causes warnings in valgrind) - snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), numSamples); + snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), (snd_pcm_uframes_t) numSamples); - if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, num, 1 /* silent */))) + if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) return false; if (num < numSamples) @@ -363,9 +363,9 @@ public: } else { - snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, numSamples); + snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, (snd_pcm_uframes_t) numSamples); - if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, num, 1 /* silent */))) + if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) return false; if (num < numSamples) @@ -503,7 +503,7 @@ public: } } - ensureMinimumNumBitsSet (outputChannels, minChansOut); + ensureMinimumNumBitsSet (outputChannels, (int) minChansOut); outputChannelBuffer.setSize (jmax ((int) minChansOut, outputChannels.getHighestBit()) + 1, bufferSize); outputChannelBuffer.clear(); @@ -557,7 +557,7 @@ public: return; } - ensureMinimumNumBitsSet (currentInputChans, minChansIn); + ensureMinimumNumBitsSet (currentInputChans, (int) minChansIn); if (! inputDevice->setParameters ((unsigned int) sampleRate, jlimit ((int) minChansIn, (int) maxChansIn, currentInputChans.getHighestBit() + 1), @@ -656,7 +656,7 @@ public: snd_pcm_sframes_t avail = snd_pcm_avail_update (inputDevice->handle); if (avail < 0) - JUCE_ALSA_FAILED (snd_pcm_recover (inputDevice->handle, avail, 0)); + JUCE_ALSA_FAILED (snd_pcm_recover (inputDevice->handle, (int) avail, 0)); } audioIoInProgress = true; @@ -688,7 +688,7 @@ public: else { for (int i = 0; i < outputChannelDataForCallback.size(); ++i) - zeromem (outputChannelDataForCallback[i], sizeof (float) * bufferSize); + zeromem (outputChannelDataForCallback[i], sizeof (float) * (size_t) bufferSize); } } @@ -702,7 +702,7 @@ public: snd_pcm_sframes_t avail = snd_pcm_avail_update (outputDevice->handle); if (avail < 0) - JUCE_ALSA_FAILED (snd_pcm_recover (outputDevice->handle, avail, 0)); + JUCE_ALSA_FAILED (snd_pcm_recover (outputDevice->handle, (int) avail, 0)); audioIoInProgress = true; @@ -1092,9 +1092,9 @@ private: if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0) break; - snd_pcm_info_set_device (pcmInfo, device); + snd_pcm_info_set_device (pcmInfo, (unsigned int) device); - for (int subDevice = 0, nbSubDevice = 1; subDevice < nbSubDevice; ++subDevice) + for (unsigned int subDevice = 0, nbSubDevice = 1; subDevice < nbSubDevice; ++subDevice) { snd_pcm_info_set_subdevice (pcmInfo, subDevice); snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_CAPTURE); @@ -1118,7 +1118,7 @@ private: } else { - id << "hw:" << cardId << "," << device << "," << subDevice; + id << "hw:" << cardId << "," << device << "," << (int) subDevice; name << cardName << ", " << snd_pcm_info_get_name (pcmInfo) << " {" << snd_pcm_info_get_subdevice_name (pcmInfo) << "}"; } diff --git a/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp b/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp index 43cb43dc0..87aba6d69 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp @@ -133,14 +133,14 @@ private: if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) { const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); - HeapBlock pfd (numPfds); - snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); + HeapBlock pfd ((size_t) numPfds); + snd_seq_poll_descriptors (seqHandle, pfd, (unsigned int) numPfds, POLLIN); HeapBlock buffer (maxEventSize); while (! threadShouldExit()) { - if (poll (pfd, numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call + if (poll (pfd, (nfds_t) numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call { if (threadShouldExit()) break; @@ -154,14 +154,14 @@ private: if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) { // xxx what about SYSEXes that are too big for the buffer? - const int numBytes = snd_midi_event_decode (midiParser, buffer, + const long numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent); snd_midi_event_reset_decode (midiParser); if (numBytes > 0) { - const MidiMessage message ((const uint8*) buffer, numBytes, + const MidiMessage message ((const uint8*) buffer, (int) numBytes, Time::getMillisecondCounter() * 0.001); client.handleIncomingMidiMessage (message, inputEvent->dest.port); @@ -410,7 +410,7 @@ public: maxEventSize (16 * 1024) { jassert (port.isValid() && midiOutput != nullptr); - snd_midi_event_new (maxEventSize, &midiParser); + snd_midi_event_new ((size_t) maxEventSize, &midiParser); } ~MidiOutputDevice() @@ -425,7 +425,7 @@ public: { maxEventSize = message.getRawDataSize(); snd_midi_event_free (midiParser); - snd_midi_event_new (maxEventSize, &midiParser); + snd_midi_event_new ((size_t) maxEventSize, &midiParser); } snd_seq_event_t event; diff --git a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index 0061b0fb4..00ac3f64c 100644 --- a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -1067,6 +1067,12 @@ public: jassert (! isOpen()); jassert (! device->isOpen()); devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs)); + + if (currentSampleRate == 0) + currentSampleRate = device->getCurrentSampleRate(); + + if (currentBufferSize == 0) + currentBufferSize = device->getCurrentBufferSizeSamples(); } Array getDevices() const diff --git a/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index 44bab4273..99e3cfbde 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -1416,13 +1416,15 @@ private: } }; + template <> + struct ASIOCallbackFunctions + { + static void setCallbacksForDevice (ASIOCallbacks&, ASIOAudioIODevice*) noexcept {} + }; + void setCallbackFunctions() noexcept { - /**/ if (currentASIODev[0] == this) ASIOCallbackFunctions<0>::setCallbacks (callbacks); - else if (currentASIODev[1] == this) ASIOCallbackFunctions<1>::setCallbacks (callbacks); - else if (currentASIODev[2] == this) ASIOCallbackFunctions<2>::setCallbacks (callbacks); - else if (currentASIODev[3] == this) ASIOCallbackFunctions<3>::setCallbacks (callbacks); - else jassertfalse; + ASIOCallbackFunctions<0>::setCallbacksForDevice (callbacks, this); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) diff --git a/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp index 8a5263cb1..105e0a6cf 100644 --- a/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp @@ -30,9 +30,9 @@ public: Writer (OutputStream* destStream, const String& formatName, const File& appFile, int vbr, int cbr, double sampleRate, unsigned int numberOfChannels, - unsigned int bitsPerSample, const StringPairArray& metadata) + int bitsPerSample, const StringPairArray& metadata) : AudioFormatWriter (destStream, formatName, sampleRate, - numberOfChannels, bitsPerSample), + numberOfChannels, (unsigned int) bitsPerSample), vbrLevel (vbr), cbrBitrate (cbr), tempWav (".wav") { diff --git a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index 6551f1f53..524ecf351 100644 --- a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -426,9 +426,8 @@ struct VBRTagData if (flags & 4) { - if (toc != nullptr) - for (int i = 0; i < 100; ++i) - toc[i] = data[i]; + for (int i = 0; i < 100; ++i) + toc[i] = data[i]; data += 100; } diff --git a/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp index 17a06da61..59c2253c6 100644 --- a/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp @@ -194,7 +194,7 @@ public: bufferList->mNumberBuffers = 1; bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame; - bufferList->mBuffers[0].mDataByteSize = jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16); + bufferList->mBuffers[0].mDataByteSize = jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * (int) inputStreamDesc.mBytesPerFrame) + 16); dataBuffer.malloc (bufferList->mBuffers[0].mDataByteSize); bufferList->mBuffers[0].mData = dataBuffer; @@ -262,10 +262,10 @@ public: } int framesToDo = jmin (numSamples, (int) (bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame)); - bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * framesToDo; + bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * (UInt32) framesToDo; UInt32 outFlags = 0; - UInt32 actualNumFrames = framesToDo; + UInt32 actualNumFrames = (UInt32) framesToDo; OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumFrames, bufferList, &outFlags); if (err != noErr) { @@ -274,7 +274,7 @@ public: } lastSampleRead = startSampleInFile + actualNumFrames; - const int samplesReceived = actualNumFrames; + const int samplesReceived = (int) actualNumFrames; for (int j = numDestChannels; --j >= 0;) { @@ -298,7 +298,7 @@ public: { for (int j = numDestChannels; --j >= 0;) if (destSamples[j] != nullptr) - zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * numSamples); + zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); break; } diff --git a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 1929e4cbf..fd86ad3d3 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -609,16 +609,18 @@ namespace WavFileHelpers { static MemoryBlock createFrom (const StringPairArray& values) { - const String s = values[WavAudioFormat::tracktionLoopInfo]; - MemoryBlock data; + MemoryOutputStream out; + const String s (values[WavAudioFormat::tracktionLoopInfo]); if (s.isNotEmpty()) { - MemoryOutputStream os (data, false); - os.writeString (s); + out.writeString (s); + + if ((out.getDataSize() & 1) != 0) + out.writeByte (0); } - return data; + return out.getMemoryBlock(); } }; diff --git a/source/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c b/source/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c index 8ea197896..644b852c8 100644 --- a/source/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c +++ b/source/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c @@ -850,7 +850,7 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, /* generate the partition's first stage cascade value */ if(csubbits){ - int maxval[8]; + int maxval[8] = { 0 }; for(k=0;kclass_subbook[classx][k]; if(booknum<0){ diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormat.h b/source/modules/juce_audio_formats/format/juce_AudioFormat.h index 196d4366c..39bcbae14 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormat.h +++ b/source/modules/juce_audio_formats/format/juce_AudioFormat.h @@ -119,7 +119,7 @@ public: should then be deleted by the caller. If the stream can't be created for some reason (e.g. the parameters passed in - here aren't suitable), this will return 0. + here aren't suitable), this will return nullptr. @param streamToWriteTo the stream that the data will go to - this will be deleted by the AudioFormatWriter object when it's no longer diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h b/source/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h index d7ff5f784..1569ede86 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h @@ -58,10 +58,10 @@ public: @see isLooping */ - void setLooping (bool shouldLoop); + void setLooping (bool shouldLoop) override; /** Returns whether loop-mode is turned on or not. */ - bool isLooping() const { return looping; } + bool isLooping() const override { return looping; } /** Returns the reader that's being used. */ AudioFormatReader* getAudioFormatReader() const noexcept { return reader; } diff --git a/source/modules/juce_audio_formats/sampler/juce_Sampler.h b/source/modules/juce_audio_formats/sampler/juce_Sampler.h index f801bf232..cf1c5e2fc 100644 --- a/source/modules/juce_audio_formats/sampler/juce_Sampler.h +++ b/source/modules/juce_audio_formats/sampler/juce_Sampler.h @@ -126,7 +126,7 @@ public: void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; void stopNote (float velocity, bool allowTailOff) override; - void pitchWheelMoved (int newValue); + void pitchWheelMoved (int newValue) override; void controllerMoved (int controllerNumber, int newValue) override; void renderNextBlock (AudioSampleBuffer&, int startSample, int numSamples) 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 2877509e8..dc39bd1bc 100644 --- a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -184,7 +184,7 @@ namespace AudioUnitFormatHelpers const char* const utf8 = fileOrIdentifier.toUTF8(); if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory())) + (CFIndex) strlen (utf8), file.isDirectory())) { CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); @@ -345,8 +345,8 @@ public: refreshParameterList(); updateNumChannels(); producesMidiMessages = canProduceMidiOutput(); - setPlayConfigDetails (numInputBusChannels * numInputBusses, - numOutputBusChannels * numOutputBusses, + setPlayConfigDetails ((int) (numInputBusChannels * numInputBusses), + (int) (numOutputBusChannels * numOutputBusses), rate, blockSize); setLatencySamples (0); @@ -420,7 +420,7 @@ public: UInt32 sampleRateSize = sizeof (sampleRateIn); const Float64 sr = newSampleRate; - for (int i = 0; i < numInputBusses; ++i) + for (AudioUnitElement i = 0; i < numInputBusses; ++i) { AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sampleRateIn, &sampleRateSize); @@ -428,7 +428,7 @@ public: AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sr, sizeof (sr)); } - for (int i = 0; i < numOutputBusses; ++i) + for (AudioUnitElement i = 0; i < numOutputBusses; ++i) { AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, i, &sampleRateOut, &sampleRateSize); @@ -440,9 +440,9 @@ public: AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &frameSize, sizeof (frameSize)); - setPlayConfigDetails (numInputBusChannels * numInputBusses, - numOutputBusChannels * numOutputBusses, - newSampleRate, estimatedSamplesPerBlock); + setPlayConfigDetails ((int) (numInputBusChannels * numInputBusses), + (int) (numOutputBusChannels * numOutputBusses), + (double) newSampleRate, estimatedSamplesPerBlock); Float64 latencySecs = 0.0; UInt32 latencySize = sizeof (latencySecs); @@ -463,13 +463,13 @@ public: stream.mBitsPerChannel = 32; stream.mChannelsPerFrame = numInputBusChannels; - for (int i = 0; i < numInputBusses; ++i) + for (AudioUnitElement i = 0; i < numInputBusses; ++i) AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, &stream, sizeof (stream)); stream.mChannelsPerFrame = numOutputBusChannels; - for (int i = 0; i < numOutputBusses; ++i) + for (AudioUnitElement i = 0; i < numOutputBusses; ++i) AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, i, &stream, sizeof (stream)); } @@ -518,8 +518,8 @@ public: void resetBusses() { - for (int i = 0; i < numInputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, i); - for (int i = 0; i < numOutputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, i); + for (AudioUnitElement i = 0; i < numInputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, i); + for (AudioUnitElement i = 0; i < numOutputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, i); } void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override @@ -530,17 +530,17 @@ public: { timeStamp.mHostTime = AudioGetCurrentHostTime(); - for (int i = 0; i < numOutputBusses; ++i) + for (AudioUnitElement i = 0; i < numOutputBusses; ++i) { if (AudioBufferList* const abl = getAudioBufferListForBus(i)) { abl->mNumberBuffers = numOutputBusChannels; - for (int j = 0; j < numOutputBusChannels; ++j) + for (AudioUnitElement j = 0; j < numOutputBusChannels; ++j) { abl->mBuffers[j].mNumberChannels = 1; - abl->mBuffers[j].mDataByteSize = sizeof (float) * numSamples; - abl->mBuffers[j].mData = buffer.getWritePointer (i * numOutputBusChannels + j); + abl->mBuffers[j].mDataByteSize = sizeof (float) * (size_t) numSamples; + abl->mBuffers[j].mData = buffer.getWritePointer ((int) (i * numOutputBusChannels + j)); } } } @@ -557,19 +557,19 @@ public: if (midiEventSize <= 3) MusicDeviceMIDIEvent (audioUnit, midiEventData[0], midiEventData[1], midiEventData[2], - midiEventPosition); + (UInt32) midiEventPosition); else - MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize); + MusicDeviceSysEx (audioUnit, midiEventData, (UInt32) midiEventSize); } midiMessages.clear(); } - for (int i = 0; i < numOutputBusses; ++i) + for (AudioUnitElement i = 0; i < numOutputBusses; ++i) { AudioUnitRenderActionFlags flags = 0; - AudioUnitRender (audioUnit, &flags, &timeStamp, i, numSamples, getAudioBufferListForBus (i)); + AudioUnitRender (audioUnit, &flags, &timeStamp, i, (UInt32) numSamples, getAudioBufferListForBus (i)); } timeStamp.mSampleTime += numSamples; @@ -796,7 +796,7 @@ public: CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); - destData.setSize (bytesWritten); + destData.setSize ((size_t) bytesWritten); destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); CFRelease (data); @@ -911,7 +911,7 @@ private: HeapBlock outputBufferList; AudioTimeStamp timeStamp; AudioSampleBuffer* currentBuffer; - int numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; + AudioUnitElement numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; AudioUnit audioUnit; AUEventListenerRef eventListenerRef; @@ -941,7 +941,7 @@ private: info.inputProcRefCon = this; info.inputProc = renderGetInputCallback; - for (int i = 0; i < numInputBusses; ++i) + for (AudioUnitElement i = 0; i < numInputBusses; ++i) AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, i, &info, sizeof (info)); } @@ -1028,11 +1028,11 @@ private: break; case kAudioUnitEvent_BeginParameterChangeGesture: - beginParameterChangeGesture (event.mArgument.mParameter.mParameterID); + beginParameterChangeGesture ((int) event.mArgument.mParameter.mParameterID); break; case kAudioUnitEvent_EndParameterChangeGesture: - endParameterChangeGesture (event.mArgument.mParameter.mParameterID); + endParameterChangeGesture ((int) event.mArgument.mParameter.mParameterID); break; default: @@ -1062,7 +1062,7 @@ private: for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i) { - const int bufferChannel = inBusNumber * numInputBusChannels + i; + const int bufferChannel = (int) (inBusNumber * numInputBusChannels + i); if (bufferChannel < currentBuffer->getNumChannels()) { @@ -1131,16 +1131,16 @@ private: if (ph != nullptr && ph->getCurrentPosition (result)) { - setIfNotNull (outTimeSig_Numerator, result.timeSigNumerator); - setIfNotNull (outTimeSig_Denominator, result.timeSigDenominator); - setIfNotNull (outDeltaSampleOffsetToNextBeat, 0); //xxx + setIfNotNull (outTimeSig_Numerator, (UInt32) result.timeSigNumerator); + setIfNotNull (outTimeSig_Denominator, (UInt32) result.timeSigDenominator); + setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); //xxx setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong } else { - setIfNotNull (outDeltaSampleOffsetToNextBeat, 0); - setIfNotNull (outTimeSig_Numerator, 4); - setIfNotNull (outTimeSig_Denominator, 4); + setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); + setIfNotNull (outTimeSig_Numerator, (UInt32) 4); + setIfNotNull (outTimeSig_Denominator, (UInt32) 4); setIfNotNull (outCurrentMeasureDownBeat, 0); } @@ -1226,12 +1226,12 @@ private: return offsetof (AudioBufferList, mBuffers) + (sizeof (AudioBuffer) * numOutputBusChannels); } - AudioBufferList* getAudioBufferListForBus (int busIndex) const noexcept + AudioBufferList* getAudioBufferListForBus (AudioUnitElement busIndex) const noexcept { return addBytesToPointer (outputBufferList.getData(), getAudioBufferSizeInBytes() * busIndex); } - int getElementCount (AudioUnitScope scope) const noexcept + AudioUnitElement getElementCount (AudioUnitScope scope) const noexcept { UInt32 count; UInt32 countSize = sizeof (count); @@ -1240,7 +1240,7 @@ private: || countSize == 0) count = 1; - return (int) count; + return count; } void updateNumChannels() @@ -1266,17 +1266,17 @@ private: const int outChannels = (int) supportedChannels[i].outChannels; if (inChannels < 0) - maximumNumIns = jmin (maximumNumIns, inChannels); + maximumNumIns = jmin (maximumNumIns, inChannels); else explicitNumIns = jmax (explicitNumIns, inChannels); if (outChannels < 0) - maximumNumOuts = jmin (maximumNumOuts, outChannels); + maximumNumOuts = jmin (maximumNumOuts, outChannels); else explicitNumOuts = jmax (explicitNumOuts, outChannels); } - if ((maximumNumIns == -1 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, as long as they match) + if ((maximumNumIns == -1 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, as long as they match) || (maximumNumIns == -2 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, even if they don't match) || (maximumNumIns == -1 && maximumNumOuts == -2)) { @@ -1284,8 +1284,8 @@ private: } else { - numInputBusChannels = explicitNumIns; - numOutputBusChannels = explicitNumOuts; + numInputBusChannels = (AudioUnitElement) explicitNumIns; + numOutputBusChannels = (AudioUnitElement) explicitNumOuts; if (maximumNumIns == -1 || (maximumNumIns < 0 && explicitNumIns <= -maximumNumIns)) numInputBusChannels = 2; 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 1bb2413ec..8479bc3b3 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 +#include "ladspa.h" namespace juce { diff --git a/source/modules/juce_audio_processors/format_types/juce_VST3Common.h b/source/modules/juce_audio_processors/format_types/juce_VST3Common.h index d7e66cdc8..7875daaac 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/source/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -250,7 +250,7 @@ public: break; case Steinberg::Vst::Event::kDataEvent: - result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, e.data.size), + result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, (int) e.data.size), e.sampleOffset); break; @@ -300,7 +300,7 @@ public: { e.type = Steinberg::Vst::Event::kDataEvent; e.data.bytes = msg.getSysExData(); - e.data.size = msg.getSysExDataSize(); + e.data.size = (uint32) msg.getSysExDataSize(); e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx; } else if (msg.isAftertouch()) diff --git a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 38590613e..497f54305 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -488,7 +488,7 @@ public: const char* const utf8 = file.getFullPathName().toRawUTF8(); if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory())) + (CFIndex) strlen (utf8), file.isDirectory())) { bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); @@ -864,7 +864,7 @@ public: wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; #if JUCE_MAC && JUCE_SUPPORT_CARBON - usesCocoaNSView = (dispatch (effCanDo, 0, 0, (void*) "hasCockosViewAsConfig", 0) & 0xffff0000) == 0xbeef0000; + usesCocoaNSView = (dispatch (effCanDo, 0, 0, (void*) "hasCockosViewAsConfig", 0) & (int) 0xffff0000) == 0xbeef0000; #endif setLatencySamples (effect->initialDelay); @@ -987,27 +987,28 @@ public: if (AudioPlayHead* const playHead = getPlayHead()) { AudioPlayHead::CurrentPositionInfo position; - playHead->getCurrentPosition (position); - - vstHostTime.samplePos = (double) position.timeInSamples; - vstHostTime.tempo = position.bpm; - vstHostTime.timeSigNumerator = position.timeSigNumerator; - vstHostTime.timeSigDenominator = position.timeSigDenominator; - vstHostTime.ppqPos = position.ppqPosition; - vstHostTime.barStartPos = position.ppqPositionOfLastBarStart; - vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid; - - VstInt32 newTransportFlags = 0; - if (position.isPlaying) newTransportFlags |= kVstTransportPlaying; - if (position.isRecording) newTransportFlags |= kVstTransportRecording; - - if (newTransportFlags != (vstHostTime.flags & (kVstTransportPlaying | kVstTransportRecording))) - vstHostTime.flags = (vstHostTime.flags & ~(kVstTransportPlaying | kVstTransportRecording)) | newTransportFlags | kVstTransportChanged; - else - vstHostTime.flags &= ~kVstTransportChanged; - - switch (position.frameRate) + if (playHead->getCurrentPosition (position)) { + + vstHostTime.samplePos = (double) position.timeInSamples; + vstHostTime.tempo = position.bpm; + vstHostTime.timeSigNumerator = position.timeSigNumerator; + vstHostTime.timeSigDenominator = position.timeSigDenominator; + vstHostTime.ppqPos = position.ppqPosition; + vstHostTime.barStartPos = position.ppqPositionOfLastBarStart; + vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid; + + VstInt32 newTransportFlags = 0; + if (position.isPlaying) newTransportFlags |= kVstTransportPlaying; + if (position.isRecording) newTransportFlags |= kVstTransportRecording; + + if (newTransportFlags != (vstHostTime.flags & (kVstTransportPlaying | kVstTransportRecording))) + vstHostTime.flags = (vstHostTime.flags & ~(kVstTransportPlaying | kVstTransportRecording)) | newTransportFlags | kVstTransportChanged; + else + vstHostTime.flags &= ~kVstTransportChanged; + + switch (position.frameRate) + { case AudioPlayHead::fps24: setHostTimeFrameRate (0, 24.0, position.timeInSeconds); break; case AudioPlayHead::fps25: setHostTimeFrameRate (1, 25.0, position.timeInSeconds); break; case AudioPlayHead::fps2997: setHostTimeFrameRate (2, 29.97, position.timeInSeconds); break; @@ -1015,17 +1016,18 @@ public: case AudioPlayHead::fps2997drop: setHostTimeFrameRate (4, 29.97, position.timeInSeconds); break; case AudioPlayHead::fps30drop: setHostTimeFrameRate (5, 29.97, position.timeInSeconds); break; default: break; - } + } - if (position.isLooping) - { - vstHostTime.cycleStartPos = position.ppqLoopStart; - vstHostTime.cycleEndPos = position.ppqLoopEnd; - vstHostTime.flags |= (kVstCyclePosValid | kVstTransportCycleActive); - } - else - { - vstHostTime.flags &= ~(kVstCyclePosValid | kVstTransportCycleActive); + if (position.isLooping) + { + vstHostTime.cycleStartPos = position.ppqLoopStart; + vstHostTime.cycleEndPos = position.ppqLoopEnd; + vstHostTime.flags |= (kVstCyclePosValid | kVstTransportCycleActive); + } + else + { + vstHostTime.flags &= ~(kVstCyclePosValid | kVstTransportCycleActive); + } } } @@ -1228,8 +1230,8 @@ public: void getStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, true); } void getCurrentProgramStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, false); } - void setStateInformation (const void* data, int size) override { loadFromFXBFile (data, size); } - void setCurrentProgramStateInformation (const void* data, int size) override { loadFromFXBFile (data, size); } + void setStateInformation (const void* data, int size) override { loadFromFXBFile (data, (size_t) size); } + void setCurrentProgramStateInformation (const void* data, int size) override { loadFromFXBFile (data, (size_t) size); } //============================================================================== void timerCallback() override @@ -1282,7 +1284,12 @@ public: case audioMasterSizeWindow: if (AudioProcessorEditor* ed = getActiveEditor()) - ed->setSize (index, (int) value); + { + #if JUCE_LINUX + const MessageManagerLock mmLock; + #endif + ed->setSize (index, (int) value); + } return 1; @@ -1356,6 +1363,7 @@ public: "receiveVstEvents", "receiveVstMidiEvent", "supportShell", + "sizeWindow", "shellCategory" }; for (int i = 0; i < numElementsInArray (canDos); ++i) @@ -1449,7 +1457,7 @@ public: { const int oldProg = getCurrentProgram(); const int numParams = fxbSwap (((const fxProgram*) (set->programs))->numParams); - const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + const int progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float); for (int i = 0; i < fxbSwap (set->numPrograms); ++i) { @@ -1496,7 +1504,7 @@ public: // non-preset chunk const fxChunkSet* const cset = (const fxChunkSet*) data; - if (fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) + if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (size_t) dataSize) return false; setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false); @@ -1506,7 +1514,7 @@ public: // preset chunk const fxProgramSet* const cset = (const fxProgramSet*) data; - if (fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) + if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (size_t) dataSize) return false; setChunkData (cset->chunk, fxbSwap (cset->chunkSize), true); @@ -1571,8 +1579,8 @@ public: { if (isFXB) { - const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms); + const int progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float); + const size_t len = (sizeof (fxSet) - sizeof (fxProgram)) + (size_t) (progLen * jmax (1, numPrograms)); dest.setSize (len, true); fxSet* const set = (fxSet*) dest.getData(); @@ -1584,11 +1592,13 @@ public: set->fxVersion = fxbSwap (getVersionNumber()); set->numPrograms = fxbSwap (numPrograms); - const int oldProgram = getCurrentProgram(); MemoryBlock oldSettings; createTempParameterStore (oldSettings); - setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); + const int oldProgram = getCurrentProgram(); + + if (oldProgram >= 0) + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); for (int i = 0; i < numPrograms; ++i) { @@ -1599,14 +1609,14 @@ public: } } - setCurrentProgram (oldProgram); + if (oldProgram >= 0) + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); } else { - const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - dest.setSize (totalLen, true); - + dest.setSize (sizeof (fxProgram) + (size_t) ((numParams - 1) * (int) sizeof (float)), true); setParamsInProgramBlock ((fxProgram*) dest.getData()); } } @@ -1621,7 +1631,7 @@ public: if (usesChunks()) { void* data = nullptr; - const VstIntPtr bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); + const size_t bytes = (size_t) dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); if (data != nullptr && bytes <= maxSizeMB * 1024 * 1024) { @@ -1783,7 +1793,7 @@ private: //============================================================================== void createTempParameterStore (MemoryBlock& dest) { - dest.setSize (64 + 4 * getNumParameters()); + dest.setSize (64 + 4 * (size_t) getNumParameters()); dest.fillWith (0); getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63); @@ -1821,21 +1831,21 @@ private: String s; if (v == 0 || (int) v == -1) - v = getVersionNumber(); + v = (unsigned int) getVersionNumber(); if (v != 0) { int versionBits[32]; int n = 0; - for (int vv = v; vv != 0; vv /= 10) + for (unsigned int vv = v; vv != 0; vv /= 10) versionBits [n++] = vv % 10; if (n > 4) // if the number ends up silly, it's probably encoded as hex instead of decimal.. { n = 0; - for (int vv = v; vv != 0; vv >>= 8) + for (unsigned int vv = v; vv != 0; vv >>= 8) versionBits [n++] = vv & 255; } @@ -1968,9 +1978,13 @@ public: #elif JUCE_LINUX if (pluginWindow != 0) { - XResizeWindow (display, pluginWindow, getWidth(), getHeight()); - XMoveWindow (display, pluginWindow, pos.getX(), pos.getY()); + XMoveResizeWindow (display, pluginWindow, + pos.getX(), pos.getY(), + (unsigned int) getWidth(), + (unsigned int) getHeight()); + XMapRaised (display, pluginWindow); + XFlush (display); } #endif @@ -2081,6 +2095,16 @@ public: plugin.dispatch (effEditIdle, 0, 0, 0, 0); reentrant = false; } + + #if JUCE_LINUX + if (pluginWindow == 0) + { + updatePluginWindowHandle(); + + if (pluginWindow != 0) + componentMovedOrResized (true, true); + } + #endif } } @@ -2266,11 +2290,7 @@ private: } #elif JUCE_LINUX - pluginWindow = getChildWindow ((Window) getWindowHandle()); - - if (pluginWindow != 0) - pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, - XInternAtom (display, "_XEventProc", False)); + updatePluginWindowHandle(); int w = 250, h = 150; @@ -2498,6 +2518,15 @@ private: sendEventToChild (ev); } } + + void updatePluginWindowHandle() + { + pluginWindow = getChildWindow ((Window) getWindowHandle()); + + if (pluginWindow != 0) + pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, + XInternAtom (display, "_XEventProc", False)); + } #endif //============================================================================== diff --git a/source/modules/juce_audio_processors/processors/juce_AudioPlayHead.h b/source/modules/juce_audio_processors/processors/juce_AudioPlayHead.h index e3247b52b..e83a76a93 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioPlayHead.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioPlayHead.h @@ -128,7 +128,9 @@ public: //============================================================================== /** Fills-in the given structure with details about the transport's - position at the start of the current processing block. + position at the start of the current processing block. If this method returns + false then the current play head position is not available and the given + structure will be undefined. You can ONLY call this from your processBlock() method! Calling it at other times will produce undefined behaviour, as the host may not have any context diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 0dffb423e..2df116350 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -50,7 +50,7 @@ AudioProcessor::~AudioProcessor() jassert (activeEditor == nullptr); #endif - #if JUCE_DEBUG + #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING // This will fail if you've called beginParameterChangeGesture() for one // or more parameters without having made a corresponding call to endParameterChangeGesture... jassert (changingParams.countNumberOfSetBits() == 0); @@ -144,7 +144,7 @@ void AudioProcessor::beginParameterChangeGesture (int parameterIndex) { if (isPositiveAndBelow (parameterIndex, getNumParameters())) { - #if JUCE_DEBUG + #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING // This means you've called beginParameterChangeGesture twice in succession without a matching // call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. jassert (! changingParams [parameterIndex]); @@ -165,9 +165,9 @@ void AudioProcessor::endParameterChangeGesture (int parameterIndex) { if (isPositiveAndBelow (parameterIndex, getNumParameters())) { - #if JUCE_DEBUG + #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING // This means you've called endParameterChangeGesture without having previously called - // endParameterChangeGesture. That might be fine in most hosts, but better to keep the + // beginParameterChangeGesture. That might be fine in most hosts, but better to keep the // calls matched correctly. jassert (changingParams [parameterIndex]); changingParams.clearBit (parameterIndex); diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h index 0846e3379..4b6ea8582 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -384,10 +384,17 @@ public: //============================================================================== /** This must return the correct value immediately after the object has been created, and mustn't change the number of parameters later. + + NOTE! This method will eventually be deprecated! It's recommended that you use the + AudioProcessorParameter class instead to manage your parameters. */ virtual int getNumParameters(); - /** Returns the name of a particular parameter. */ + /** Returns the name of a particular parameter. + + NOTE! This method will eventually be deprecated! It's recommended that you use the + AudioProcessorParameter class instead to manage your parameters. + */ virtual const String getParameterName (int parameterIndex); /** Called by the host to find out the value of one of the filter's parameters. @@ -397,27 +404,39 @@ public: 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. + + NOTE! This method will eventually be deprecated! It's recommended that you use the + AudioProcessorParameter class instead to manage your parameters. */ virtual float getParameter (int parameterIndex); - /** Returns the value of a parameter as a text string. */ - 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 will look better in constrained spaces (e.g. the displays on hardware controller devices or mixing desks) then you should implement this method. If you don't override it, the default implementation will call getParameterText(int), and truncate the result. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::getName() instead. */ virtual String getParameterName (int parameterIndex, int maximumStringLength); + /** Returns the value of a parameter as a text string. + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::getText() instead. + */ + virtual const String getParameterText (int parameterIndex); + /** Returns the value of a parameter as a text string with a preferred maximum length. If you want to provide customised short versions of your parameter values that will look better in constrained spaces (e.g. the displays on hardware controller devices or mixing desks) then you should implement this method. If you don't override it, the default implementation will call getParameterText(int), and truncate the result. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::getText() instead. */ virtual String getParameterText (int parameterIndex, int maximumStringLength); @@ -426,10 +445,16 @@ public: 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. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::getNumSteps() instead. */ virtual int getParameterNumSteps (int parameterIndex); /** Returns the default number of steps for a parameter. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::getNumSteps() instead. @see getParameterNumSteps */ static int getDefaultNumParameterSteps() noexcept; @@ -437,16 +462,25 @@ public: /** 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. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::getDefaultValue() instead. */ virtual float getParameterDefaultValue (int parameterIndex); /** Some plugin types may be able to return a label string for a parameter's units. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::getLabel() instead. */ virtual String getParameterLabel (int index) const; /** This can be overridden to tell the host that particular parameters operate in the reverse direction. (Not all plugin formats or hosts will actually use this information). + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::isOrientationInverted() instead. */ virtual bool isParameterOrientationInverted (int index) const; @@ -462,6 +496,9 @@ public: won't be able to automate your parameters properly. The value passed will be between 0 and 1.0. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::setValue() instead. */ virtual void setParameter (int parameterIndex, float newValue); @@ -474,11 +511,17 @@ public: Note that to make sure the host correctly handles automation, you should call the beginParameterChangeGesture() and endParameterChangeGesture() methods to tell the host when the user has started and stopped changing the parameter. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::setValueNotifyingHost() instead. */ void setParameterNotifyingHost (int parameterIndex, float newValue); /** Returns true if the host can automate this parameter. By default, this returns true for all parameters. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::isAutomatable() instead. */ virtual bool isParameterAutomatable (int parameterIndex) const; @@ -486,6 +529,9 @@ public: 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. + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::isMetaParameter() instead. */ virtual bool isMetaParameter (int parameterIndex) const; @@ -496,6 +542,9 @@ public: it may use this information to help it record automation. If you call this, it must be matched by a later call to endParameterChangeGesture(). + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::beginChangeGesture() instead. */ void beginParameterChangeGesture (int parameterIndex); @@ -505,6 +554,9 @@ public: it may use this information to help it record automation. A call to this method must follow a call to beginParameterChangeGesture(). + + NOTE! This method will eventually be deprecated! It's recommended that you use + AudioProcessorParameter::endChangeGesture() instead. */ void endParameterChangeGesture (int parameterIndex); @@ -693,7 +745,7 @@ private: OwnedArray managedParameters; AudioProcessorParameter* getParamChecked (int) const noexcept; - #if JUCE_DEBUG + #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING BigInteger changingParams; #endif diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h index 6be1848f4..f6d1a6301 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h @@ -308,7 +308,7 @@ public: void fillInPluginDescription (PluginDescription&) const override; void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override; void releaseResources() override; - void processBlock (AudioSampleBuffer&, MidiBuffer&); + void processBlock (AudioSampleBuffer&, MidiBuffer&) override; const String getInputChannelName (int channelIndex) const override; const String getOutputChannelName (int channelIndex) const override; diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h index 8b4d2cf32..d9d0e6bcd 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h @@ -68,7 +68,7 @@ public: /** 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 + a parameter. This method will call the setValue() 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 diff --git a/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index a0c2388cf..42c94a38a 100644 --- a/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -85,7 +85,7 @@ public: void deleteKeyPressed (int) override { - owner.removeSelected(); + owner.removeSelectedPlugins(); } void sortOrderChanged (int newSortColumnId, bool isForwards) override @@ -102,14 +102,6 @@ public: } } - static void removePluginItem (KnownPluginList& list, int index) - { - if (index < list.getNumTypes()) - list.removeType (index); - else - list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); - } - static String getPluginDescription (const PluginDescription& desc) { StringArray items; @@ -179,6 +171,12 @@ void PluginListComponent::setOptionsButtonText (const String& newText) resized(); } +void PluginListComponent::setScanDialogText (const String& title, const String& content) +{ + dialogTitle = title; + dialogText = content; +} + void PluginListComponent::setNumberOfThreadsForScanning (int num) { numThreads = num; @@ -207,13 +205,24 @@ void PluginListComponent::updateList() table.repaint(); } -void PluginListComponent::removeSelected() +void PluginListComponent::removeSelectedPlugins() { const SparseSet selected (table.getSelectedRows()); for (int i = table.getNumRows(); --i >= 0;) if (selected.contains (i)) - TableModel::removePluginItem (list, i); + removePluginItem (i); +} + +void PluginListComponent::setTableModel (TableListBoxModel* model) +{ + table.setModel (nullptr); + tableModel = model; + table.setModel (tableModel); + + table.getHeader().reSortTable(); + table.updateContent(); + table.repaint(); } bool PluginListComponent::canShowSelectedFolder() const @@ -238,6 +247,14 @@ void PluginListComponent::removeMissingPlugins() list.removeType (i); } +void PluginListComponent::removePluginItem (int index) +{ + if (index < list.getNumTypes()) + list.removeType (index); + else + list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); +} + void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList) { if (pluginList != nullptr) @@ -250,7 +267,7 @@ void PluginListComponent::optionsMenuCallback (int result) { case 0: break; case 1: list.clear(); break; - case 2: removeSelected(); break; + case 2: removeSelectedPlugins(); break; case 3: showSelectedFolder(); break; case 4: removeMissingPlugins(); break; @@ -293,7 +310,7 @@ bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) void PluginListComponent::filesDropped (const StringArray& files, int, int) { - OwnedArray typesFound; + OwnedArray typesFound; list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); } @@ -313,11 +330,11 @@ void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPl class PluginListComponent::Scanner : private Timer { public: - Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, int threads) + Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, + int threads, const String& title, const String& text) : owner (plc), formatToScan (format), propertiesToUse (properties), pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon), - progressWindow (TRANS("Scanning for plug-ins..."), - TRANS("Searching for all possible plug-in files..."), AlertWindow::NoIcon), + progressWindow (title, text, AlertWindow::NoIcon), progress (0.0), numThreads (threads), finished (false) { FileSearchPath path (formatToScan.getDefaultLocationsToSearch()); @@ -528,7 +545,9 @@ private: void PluginListComponent::scanFor (AudioPluginFormat& format) { - currentScanner = new Scanner (*this, format, propertiesToUse, numThreads); + currentScanner = new Scanner (*this, format, propertiesToUse, numThreads, + dialogTitle.isNotEmpty() ? dialogTitle : TRANS("Scanning for plug-ins..."), + dialogText.isNotEmpty() ? dialogText : TRANS("Searching for all possible plug-in files...")); } bool PluginListComponent::isScanning() const noexcept diff --git a/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.h b/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.h index 97751a802..e0b2a6de7 100644 --- a/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.h +++ b/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.h @@ -55,6 +55,10 @@ public: /** Changes the text in the panel's options button. */ void setOptionsButtonText (const String& newText); + /** Changes the text in the progress dialog box that is shown when scanning. */ + void setScanDialogText (const String& textForProgressWindowTitle, + const String& textForProgressWindowDescription); + /** Sets how many threads to simultaneously scan for plugins. If this is 0, then all scanning happens on the message thread (this is the default) */ @@ -72,6 +76,17 @@ public: /** Returns true if there's currently a scan in progress. */ bool isScanning() const noexcept; + /** Removes the plugins currently selected in the table. */ + void removeSelectedPlugins(); + + /** Sets a custom table model to be used. + This will take ownership of the model and delete it when no longer needed. + */ + void setTableModel (TableListBoxModel* model); + + /** Returns the table used to display the plugin list. */ + TableListBox& getTableListBox() noexcept { return table; } + private: //============================================================================== AudioPluginFormatManager& formatManager; @@ -80,12 +95,11 @@ private: TableListBox table; TextButton optionsButton; PropertiesFile* propertiesToUse; + String dialogTitle, dialogText; int numThreads; class TableModel; - friend class TableModel; - friend struct ContainerDeletePolicy; - ScopedPointer tableModel; + ScopedPointer tableModel; class Scanner; friend class Scanner; @@ -98,8 +112,8 @@ private: void updateList(); void showSelectedFolder(); bool canShowSelectedFolder() const; - void removeSelected(); void removeMissingPlugins(); + void removePluginItem (int index); void resized() override; bool isInterestedInFileDrag (const StringArray&) override; diff --git a/source/modules/juce_core/containers/juce_AbstractFifo.h b/source/modules/juce_core/containers/juce_AbstractFifo.h index 02feac74e..5b81a21eb 100644 --- a/source/modules/juce_core/containers/juce_AbstractFifo.h +++ b/source/modules/juce_core/containers/juce_AbstractFifo.h @@ -69,7 +69,7 @@ void readFromFifo (int* someData, int numItems) { int start1, size1, start2, size2; - abstractFifo.prepareToRead (numSamples, start1, size1, start2, size2); + abstractFifo.prepareToRead (numItems, start1, size1, start2, size2); if (size1 > 0) copySomeData (someData, myBuffer + start1, size1); diff --git a/source/modules/juce_core/containers/juce_Array.h b/source/modules/juce_core/containers/juce_Array.h index b729c99d2..53584f419 100644 --- a/source/modules/juce_core/containers/juce_Array.h +++ b/source/modules/juce_core/containers/juce_Array.h @@ -832,8 +832,8 @@ public: /** Removes an item from the array. - This will remove the first occurrence of the given element from the array. - If the item isn't found, no action is taken. + This will remove all occurrences of the given element from the array. + If no such items are found, no action is taken. @param valueToRemove the object to try to remove @see remove, removeRange diff --git a/source/modules/juce_core/containers/juce_ReferenceCountedArray.h b/source/modules/juce_core/containers/juce_ReferenceCountedArray.h index 6b02c3e3e..1930074b7 100644 --- a/source/modules/juce_core/containers/juce_ReferenceCountedArray.h +++ b/source/modules/juce_core/containers/juce_ReferenceCountedArray.h @@ -119,26 +119,30 @@ public: */ ~ReferenceCountedArray() { - clear(); + releaseAllObjects(); } //============================================================================== /** Removes all objects from the array. - - Any objects in the array that are not referenced from elsewhere will be deleted. + Any objects in the array that whose reference counts drop to zero will be deleted. */ void clear() { const ScopedLockType lock (getLock()); - - while (numUsed > 0) - if (ObjectClass* o = data.elements [--numUsed]) - releaseObject (o); - - jassert (numUsed == 0); + releaseAllObjects(); data.setAllocatedSize (0); } + /** Removes all objects from the array without freeing the array's allocated storage. + Any objects in the array that whose reference counts drop to zero will be deleted. + @see clear + */ + void clearQuick() + { + const ScopedLockType lock (getLock()); + releaseAllObjects(); + } + /** Returns the current number of objects in the array. */ inline int size() const noexcept { @@ -886,6 +890,15 @@ private: ArrayAllocationBase data; int numUsed; + void releaseAllObjects() + { + while (numUsed > 0) + if (ObjectClass* o = data.elements [--numUsed]) + releaseObject (o); + + jassert (numUsed == 0); + } + static void releaseObject (ObjectClass* o) { if (o->decReferenceCountWithoutDeleting()) diff --git a/source/modules/juce_core/files/juce_FileInputStream.cpp b/source/modules/juce_core/files/juce_FileInputStream.cpp index 801ccfaaf..58f53b84d 100644 --- a/source/modules/juce_core/files/juce_FileInputStream.cpp +++ b/source/modules/juce_core/files/juce_FileInputStream.cpp @@ -57,7 +57,7 @@ int FileInputStream::read (void* buffer, int bytesToRead) jassert (buffer != nullptr && bytesToRead >= 0); const size_t num = readInternal (buffer, (size_t) bytesToRead); - currentPosition += num; + currentPosition += (int64) num; return (int) num; } diff --git a/source/modules/juce_core/files/juce_FileOutputStream.cpp b/source/modules/juce_core/files/juce_FileOutputStream.cpp index 961a8d19e..3ffc3bd83 100644 --- a/source/modules/juce_core/files/juce_FileOutputStream.cpp +++ b/source/modules/juce_core/files/juce_FileOutputStream.cpp @@ -90,7 +90,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) { memcpy (buffer + bytesInBuffer, src, numBytes); bytesInBuffer += numBytes; - currentPosition += numBytes; + currentPosition += (int64) numBytes; } else { @@ -101,7 +101,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) { memcpy (buffer + bytesInBuffer, src, numBytes); bytesInBuffer += numBytes; - currentPosition += numBytes; + currentPosition += (int64) numBytes; } else { @@ -110,7 +110,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) if (bytesWritten < 0) return false; - currentPosition += bytesWritten; + currentPosition += (int64) bytesWritten; return bytesWritten == (ssize_t) numBytes; } } @@ -126,7 +126,7 @@ bool FileOutputStream::writeRepeatedByte (uint8 byte, size_t numBytes) { memset (buffer + bytesInBuffer, byte, numBytes); bytesInBuffer += numBytes; - currentPosition += numBytes; + currentPosition += (int64) numBytes; return true; } diff --git a/source/modules/juce_core/javascript/juce_JSON.cpp b/source/modules/juce_core/javascript/juce_JSON.cpp index 5a0f7f7c0..fe48da0e0 100644 --- a/source/modules/juce_core/javascript/juce_JSON.cpp +++ b/source/modules/juce_core/javascript/juce_JSON.cpp @@ -579,7 +579,7 @@ public: case 1: return r.nextInt(); case 2: return r.nextInt64(); case 3: return r.nextBool(); - case 4: return r.nextDouble(); + case 4: return String (r.nextDouble(), 8).getDoubleValue(); case 5: return createRandomWideCharString (r); case 6: diff --git a/source/modules/juce_core/javascript/juce_Javascript.cpp b/source/modules/juce_core/javascript/juce_Javascript.cpp index c2e6e9649..1d7fcf45d 100644 --- a/source/modules/juce_core/javascript/juce_Javascript.cpp +++ b/source/modules/juce_core/javascript/juce_Javascript.cpp @@ -149,6 +149,10 @@ struct JavascriptEngine::RootObject : public DynamicObject if (const var* prop = getPropertyPointer (p, functionName)) return *prop; } + + // if there's a class with an overridden DynamicObject::hasMethod, this avoids an error + if (o->hasMethod (functionName)) + return var(); } if (targetObject.isString()) @@ -699,6 +703,11 @@ struct JavascriptEngine::RootObject : public DynamicObject if (FunctionObject* fo = dynamic_cast (function.getObject())) return fo->invoke (s, args); + if (DotOperator* dot = dynamic_cast (object.get())) + if (DynamicObject* o = thisObject.getDynamicObject()) + if (o->hasMethod (dot->child)) // allow an overridden DynamicObject::invokeMethod to accept a method call. + return o->invokeMethod (dot->child, args); + location.throwError ("This expression is not a function!"); return var(); } diff --git a/source/modules/juce_core/juce_core.cpp b/source/modules/juce_core/juce_core.cpp index de5c8e562..4c7266ac5 100644 --- a/source/modules/juce_core/juce_core.cpp +++ b/source/modules/juce_core/juce_core.cpp @@ -107,6 +107,11 @@ #include #endif +//============================================================================== +#ifndef JUCE_STANDALONE_APPLICATION + JUCE_COMPILER_WARNING ("Please re-save your Introjucer project with the latest Introjucer version to avoid this warning") + #define JUCE_STANDALONE_APPLICATION 0 +#endif //============================================================================== namespace juce diff --git a/source/modules/juce_core/maths/juce_MathsFunctions.h b/source/modules/juce_core/maths/juce_MathsFunctions.h index 9e0781e11..7a71ad8d0 100644 --- a/source/modules/juce_core/maths/juce_MathsFunctions.h +++ b/source/modules/juce_core/maths/juce_MathsFunctions.h @@ -274,6 +274,19 @@ inline void swapVariables (Type& variable1, Type& variable2) std::swap (variable1, variable2); } +/** Handy function for avoiding unused variables warning. */ +template +void ignoreUnused (const Type1&) noexcept {} + +template +void ignoreUnused (const Type1&, const Type2&) noexcept {} + +template +void ignoreUnused (const Type1&, const Type2&, const Type3&) noexcept {} + +template +void ignoreUnused (const Type1&, const Type2&, const Type3&, const Type4&) noexcept {} + /** Handy function for getting the number of elements in a simple const C array. E.g. @code @@ -299,11 +312,23 @@ template inline Type juce_hypot (Type a, Type b) noexcept { #if JUCE_MSVC - return static_cast (_hypot (a, b)); + return static_cast (_hypot (a, b)); + #else + return static_cast (hypot (a, b)); + #endif +} + +#ifndef DOXYGEN +template <> +inline float juce_hypot (float a, float b) noexcept +{ + #if JUCE_MSVC + return (_hypotf (a, b)); #else - return static_cast (hypot (a, b)); + return (hypotf (a, b)); #endif } +#endif /** 64-bit abs function. */ inline int64 abs64 (const int64 n) noexcept @@ -333,13 +358,27 @@ const float float_Pi = 3.14159265358979323846f; /** The isfinite() method seems to vary between platforms, so this is a platform-independent function for it. */ -template -inline bool juce_isfinite (FloatingPointType value) +template +inline bool juce_isfinite (NumericType) noexcept { - #if JUCE_WINDOWS - return _finite (value); - #elif JUCE_ANDROID - return isfinite (value); + return true; // Integer types are always finite +} + +template <> +inline bool juce_isfinite (float value) noexcept +{ + #if JUCE_MSVC + return _finite (value) != 0; + #else + return std::isfinite (value); + #endif +} + +template <> +inline bool juce_isfinite (double value) noexcept +{ + #if JUCE_MSVC + return _finite (value) != 0; #else return std::isfinite (value); #endif @@ -495,12 +534,12 @@ NumericType square (NumericType n) noexcept } //============================================================================== -#if (JUCE_INTEL && JUCE_32BIT) || defined (DOXYGEN) +#if JUCE_INTEL || defined (DOXYGEN) /** This macro can be applied to a float variable to check whether it contains a denormalised value, and to normalise it if necessary. On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. */ - #define JUCE_UNDENORMALISE(x) x += 1.0f; x -= 1.0f; + #define JUCE_UNDENORMALISE(x) { (x) += 0.1f; (x) -= 0.1f; } #else #define JUCE_UNDENORMALISE(x) #endif diff --git a/source/modules/juce_core/memory/juce_ByteOrder.h b/source/modules/juce_core/memory/juce_ByteOrder.h index fcd56bce2..cd17927b7 100644 --- a/source/modules/juce_core/memory/juce_ByteOrder.h +++ b/source/modules/juce_core/memory/juce_ByteOrder.h @@ -153,7 +153,7 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept #elif JUCE_USE_MSVC_INTRINSICS return _byteswap_uint64 (value); #else - return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); + return (((uint64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); #endif } diff --git a/source/modules/juce_core/memory/juce_HeapBlock.h b/source/modules/juce_core/memory/juce_HeapBlock.h index 3731f0b30..402b6173a 100644 --- a/source/modules/juce_core/memory/juce_HeapBlock.h +++ b/source/modules/juce_core/memory/juce_HeapBlock.h @@ -36,7 +36,7 @@ namespace HeapBlockHelper struct ThrowOnFail { static void check (void*) {} }; template<> - struct ThrowOnFail { static void check (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; + struct ThrowOnFail { static void check (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; } #endif @@ -67,7 +67,7 @@ namespace HeapBlockHelper ..you could just write this: @code - HeapBlock temp (1024); + HeapBlock temp (1024); memcpy (temp, xyz, 1024 * sizeof (int)); temp.calloc (2048); temp[0] = 1234; @@ -109,7 +109,7 @@ public: other constructor that takes an InitialisationState parameter. */ explicit HeapBlock (const size_t numElements) - : data (static_cast (std::malloc (numElements * sizeof (ElementType)))) + : data (static_cast (std::malloc (numElements * sizeof (ElementType)))) { throwOnAllocationFailure(); } @@ -120,7 +120,7 @@ public: or left uninitialised. */ HeapBlock (const size_t numElements, const bool initialiseToZero) - : data (static_cast (initialiseToZero + : data (static_cast (initialiseToZero ? std::calloc (numElements, sizeof (ElementType)) : std::malloc (numElements * sizeof (ElementType)))) { @@ -166,13 +166,13 @@ public: This may be a null pointer if the data hasn't yet been allocated, or if it has been freed by calling the free() method. */ - inline operator void*() const noexcept { return static_cast (data); } + inline operator void*() const noexcept { return static_cast (data); } /** Returns a void pointer to the allocated data. This may be a null pointer if the data hasn't yet been allocated, or if it has been freed by calling the free() method. */ - inline operator const void*() const noexcept { return static_cast (data); } + inline operator const void*() const noexcept { return static_cast (data); } /** Lets you use indirect calls to the first element in the array. Obviously this will cause problems if the array hasn't been initialised, because it'll @@ -220,7 +220,7 @@ public: void malloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { std::free (data); - data = static_cast (std::malloc (newNumElements * elementSize)); + data = static_cast (std::malloc (newNumElements * elementSize)); throwOnAllocationFailure(); } @@ -230,7 +230,7 @@ public: void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { std::free (data); - data = static_cast (std::calloc (newNumElements, elementSize)); + data = static_cast (std::calloc (newNumElements, elementSize)); throwOnAllocationFailure(); } @@ -241,7 +241,7 @@ public: void allocate (const size_t newNumElements, bool initialiseToZero) { std::free (data); - data = static_cast (initialiseToZero + data = static_cast (initialiseToZero ? std::calloc (newNumElements, sizeof (ElementType)) : std::malloc (newNumElements * sizeof (ElementType))); throwOnAllocationFailure(); @@ -254,15 +254,15 @@ public: */ void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { - data = static_cast (data == nullptr ? std::malloc (newNumElements * elementSize) - : std::realloc (data, newNumElements * elementSize)); + data = static_cast (data == nullptr ? std::malloc (newNumElements * elementSize) + : std::realloc (data, newNumElements * elementSize)); throwOnAllocationFailure(); } /** Frees any currently-allocated data. This will free the data and reset this object to be a null pointer. */ - void free() + void free() noexcept { std::free (data); data = nullptr; @@ -272,7 +272,7 @@ public: The two objects simply exchange their data pointers. */ template - void swapWith (HeapBlock & other) noexcept + void swapWith (HeapBlock& other) noexcept { std::swap (data, other.data); } diff --git a/source/modules/juce_core/memory/juce_MemoryBlock.cpp b/source/modules/juce_core/memory/juce_MemoryBlock.cpp index 6097c7e7e..b258c3129 100644 --- a/source/modules/juce_core/memory/juce_MemoryBlock.cpp +++ b/source/modules/juce_core/memory/juce_MemoryBlock.cpp @@ -259,7 +259,7 @@ void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcep if ((size_t) offset + num > size) { - const size_t newNum = size - (size_t) offset; + const size_t newNum = (size_t) size - (size_t) offset; zeromem (d + newNum, num - newNum); num = newNum; } diff --git a/source/modules/juce_core/memory/juce_Singleton.h b/source/modules/juce_core/memory/juce_Singleton.h index 00ea0afe0..ce1406e97 100644 --- a/source/modules/juce_core/memory/juce_Singleton.h +++ b/source/modules/juce_core/memory/juce_Singleton.h @@ -44,7 +44,7 @@ destructor, in case it is deleted by other means than deleteInstance() Clients can then call the static method MyClass::getInstance() to get a pointer - to the singleton, or MyClass::getInstanceWithoutCreating() which will return 0 if + to the singleton, or MyClass::getInstanceWithoutCreating() which will return nullptr if no instance currently exists. e.g. @code diff --git a/source/modules/juce_core/native/java/JuceAppActivity.java b/source/modules/juce_core/native/java/JuceAppActivity.java index 5c079df6c..c936ccdf7 100644 --- a/source/modules/juce_core/native/java/JuceAppActivity.java +++ b/source/modules/juce_core/native/java/JuceAppActivity.java @@ -331,15 +331,26 @@ public class JuceAppActivity extends Activity setFocusableInTouchMode (true); setOnFocusChangeListener (this); requestFocus(); + + // swap red and blue colours to match internal opengl texture format + ColorMatrix colorMatrix = new ColorMatrix(); + + float[] colorTransform = { 0, 0, 1.0f, 0, 0, + 0, 1.0f, 0, 0, 0, + 1.0f, 0, 0, 0, 0, + 0, 0, 0, 1.0f, 0 }; + + colorMatrix.set (colorTransform); + paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix)); } //============================================================================== - private native void handlePaint (long host, Canvas canvas); + private native void handlePaint (long host, Canvas canvas, Paint paint); @Override public void onDraw (Canvas canvas) { - handlePaint (host, canvas); + handlePaint (host, canvas, paint); } @Override @@ -350,6 +361,7 @@ public class JuceAppActivity extends Activity private boolean opaque; private long host; + private Paint paint = new Paint(); //============================================================================== private native void handleMouseDown (long host, int index, float x, float y, long time); @@ -448,6 +460,22 @@ public class JuceAppActivity extends Activity return true; } + @Override + public boolean onKeyMultiple (int keyCode, int count, KeyEvent event) + { + if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE) + return super.onKeyMultiple (keyCode, count, event); + + if (event.getCharacters() != null) + { + int utf8Char = event.getCharacters().codePointAt (0); + handleKeyDown (host, utf8Char, utf8Char); + return true; + } + + return false; + } + // this is here to make keyboard entry work on a Galaxy Tab2 10.1 @Override public InputConnection onCreateInputConnection (EditorInfo outAttrs) diff --git a/source/modules/juce_core/native/juce_linux_CommonFile.cpp b/source/modules/juce_core/native/juce_linux_CommonFile.cpp index 2ce98b3bd..acdb48a41 100644 --- a/source/modules/juce_core/native/juce_linux_CommonFile.cpp +++ b/source/modules/juce_core/native/juce_linux_CommonFile.cpp @@ -138,8 +138,8 @@ private: JUCE_DECLARE_NON_COPYABLE (Pimpl) }; -DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) - : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCardStr) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCardStr)) { } diff --git a/source/modules/juce_core/native/juce_linux_Files.cpp b/source/modules/juce_core/native/juce_linux_Files.cpp index 088877dbb..a0941abae 100644 --- a/source/modules/juce_core/native/juce_linux_Files.cpp +++ b/source/modules/juce_core/native/juce_linux_Files.cpp @@ -150,7 +150,10 @@ File File::getSpecialLocation (const SpecialLocationType type) case currentExecutableFile: case currentApplicationFile: + #if ! JUCE_STANDALONE_APPLICATION return juce_getExecutableFile(); + #endif + // deliberate fall-through if this is not a shared-library case hostApplicationPath: { @@ -216,7 +219,7 @@ bool Process::openDocument (const String& fileName, const String& parameters) cmdString = cmdLines.joinIntoString (" || "); } - const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 }; + const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr }; #if JUCE_USE_VFORK const int cpid = vfork(); @@ -226,11 +229,12 @@ bool Process::openDocument (const String& fileName, const String& parameters) if (cpid == 0) { +#if ! JUCE_USE_VFORK setsid(); - +#endif // Child process - execve (argv[0], (char**) argv, environ); - exit (0); + if (execvp (argv[0], (char**) argv) < 0) + _exit (0); } return cpid >= 0; diff --git a/source/modules/juce_core/native/juce_linux_Network.cpp b/source/modules/juce_core/native/juce_linux_Network.cpp index d07766859..da0649fbd 100644 --- a/source/modules/juce_core/native/juce_linux_Network.cpp +++ b/source/modules/juce_core/native/juce_linux_Network.cpp @@ -127,7 +127,7 @@ public: if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) return 0; // (timeout) - const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); + const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, (size_t) bytesToRead, MSG_WAITALL)); if (bytesRead == 0) finished = true; @@ -182,7 +182,7 @@ private: } int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const int numRedirectsToFollow) + const int numRedirects) { closeSocket (false); @@ -193,7 +193,7 @@ private: else if (timeOutMs < 0) timeOutTime = 0xffffffff; else - timeOutTime += timeOutMs; + timeOutTime += (uint32) timeOutMs; String hostName, hostPath; int hostPort; @@ -279,7 +279,7 @@ private: String location (findHeaderItem (headerLines, "Location:")); - if (++levelsOfRedirection <= numRedirectsToFollow + if (++levelsOfRedirection <= numRedirects && status >= 300 && status < 400 && location.isNotEmpty() && location != address) { @@ -295,7 +295,7 @@ private: } address = location; - return createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); + return createConnection (progressCallback, progressCallbackContext, numRedirects); } return status; @@ -391,12 +391,12 @@ private: const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); - if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, numToSend, 0) != numToSend) + if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, (size_t) numToSend, 0) != numToSend) return false; - totalHeaderSent += numToSend; + totalHeaderSent += (size_t) numToSend; - if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, totalHeaderSent, requestHeader.getSize())) + if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, (int) totalHeaderSent, (int) requestHeader.getSize())) return false; } diff --git a/source/modules/juce_core/native/juce_linux_SystemStats.cpp b/source/modules/juce_core/native/juce_linux_SystemStats.cpp index 939edb47b..48e9313f5 100644 --- a/source/modules/juce_core/native/juce_linux_SystemStats.cpp +++ b/source/modules/juce_core/native/juce_linux_SystemStats.cpp @@ -55,10 +55,10 @@ bool SystemStats::isOperatingSystem64Bit() //============================================================================== namespace LinuxStatsHelpers { - String getCpuInfo (const char* const key) + String getConfigFileValue (const char* file, const char* const key) { StringArray lines; - File ("/proc/cpuinfo").readLines (lines); + File (file).readLines (lines); for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) if (lines[i].upToFirstOccurrenceOf (":", false, false).trim().equalsIgnoreCase (key)) @@ -66,6 +66,11 @@ namespace LinuxStatsHelpers return String(); } + + String getCpuInfo (const char* key) + { + return getConfigFileValue ("/proc/cpuinfo", key); + } } String SystemStats::getDeviceDescription() @@ -93,14 +98,14 @@ int SystemStats::getMemorySizeInMegabytes() struct sysinfo sysi; if (sysinfo (&sysi) == 0) - return sysi.totalram * sysi.mem_unit / (1024 * 1024); + return (int) (sysi.totalram * sysi.mem_unit / (1024 * 1024)); return 0; } int SystemStats::getPageSize() { - return sysconf (_SC_PAGESIZE); + return (int) sysconf (_SC_PAGESIZE); } //============================================================================== @@ -150,6 +155,8 @@ void CPUInformation::initialise() noexcept hasSSE2 = flags.contains ("sse2"); hasSSE3 = flags.contains ("sse3"); has3DNow = flags.contains ("3dnow"); + hasSSSE3 = flags.contains ("ssse3"); + hasAVX = flags.contains ("avx"); numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; } @@ -160,7 +167,7 @@ uint32 juce_millisecondsSinceStartup() noexcept timespec t; clock_gettime (CLOCK_MONOTONIC, &t); - return t.tv_sec * 1000 + t.tv_nsec / 1000000; + return (uint32) (t.tv_sec * 1000 + t.tv_nsec / 1000000); } int64 Time::getHighResolutionTicks() noexcept @@ -189,3 +196,13 @@ bool Time::setSystemTimeToThisTime() const return settimeofday (&t, 0) == 0; } + +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() +{ + #if JUCE_BSD + return false; + #else + return LinuxStatsHelpers::getConfigFileValue ("/proc/self/status", "TracerPid") + .getIntValue() > 0; + #endif +} diff --git a/source/modules/juce_core/native/juce_linux_Threads.cpp b/source/modules/juce_core/native/juce_linux_Threads.cpp index 659fbd997..f28eff692 100644 --- a/source/modules/juce_core/native/juce_linux_Threads.cpp +++ b/source/modules/juce_core/native/juce_linux_Threads.cpp @@ -52,28 +52,6 @@ JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior) pthread_setschedparam (pthread_self(), policy, ¶m); } -JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() -{ - #if JUCE_BSD - return false; - #else - static char testResult = 0; - - if (testResult == 0) - { - testResult = (char) ptrace (PT_TRACE_ME, 0, 0, 0); - - if (testResult >= 0) - { - ptrace (PT_DETACH, 0, (caddr_t) 1, 0); - testResult = 1; - } - } - - return testResult < 0; - #endif -} - JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() { return juce_isRunningUnderDebugger(); diff --git a/source/modules/juce_core/native/juce_mac_Files.mm b/source/modules/juce_core/native/juce_mac_Files.mm index d7bd74a25..fdebd6953 100644 --- a/source/modules/juce_core/native/juce_mac_Files.mm +++ b/source/modules/juce_core/native/juce_mac_Files.mm @@ -110,7 +110,7 @@ namespace FileHelpers static bool launchExecutable (const String& pathAndArguments) { - const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), 0 }; + const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), nullptr }; #if JUCE_USE_VFORK const int cpid = vfork(); @@ -121,16 +121,11 @@ namespace FileHelpers if (cpid == 0) { // Child process - if (execve (argv[0], (char**) argv, 0) < 0) - exit (0); - } - else - { - if (cpid < 0) - return false; + if (execvp (argv[0], (char**) argv) < 0) + _exit (0); } - return true; + return cpid >= 0; } } diff --git a/source/modules/juce_core/native/juce_mac_Network.mm b/source/modules/juce_core/native/juce_mac_Network.mm index a0ddef572..299bb1135 100644 --- a/source/modules/juce_core/native/juce_mac_Network.mm +++ b/source/modules/juce_core/native/juce_mac_Network.mm @@ -128,7 +128,8 @@ public: hasFailed (false), hasFinished (false), numRedirectsToFollow (maxRedirects), - numRedirects (0) + numRedirects (0), + latestTotalBytes (0) { static DelegateClass cls; delegate = [cls.createInstance() init]; @@ -152,7 +153,7 @@ public: while (isThreadRunning() && ! initialised) { if (callback != nullptr) - callback (context, -1, (int) [[request HTTPBody] length]); + callback (context, latestTotalBytes, (int) [[request HTTPBody] length]); Thread::sleep (1); } @@ -203,7 +204,6 @@ public: [data setLength: 0]; } - initialised = true; contentLength = [response expectedContentLength]; [headers release]; @@ -215,6 +215,8 @@ public: headers = [[httpResponse allHeaderFields] retain]; statusCode = (int) [httpResponse statusCode]; } + + initialised = true; } NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) @@ -245,8 +247,9 @@ public: initialised = true; } - void didSendBodyData (NSInteger /*totalBytesWritten*/, NSInteger /*totalBytesExpected*/) + void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) { + latestTotalBytes = static_cast (totalBytesWritten); } void finishedLoading() @@ -280,6 +283,7 @@ public: bool initialised, hasFailed, hasFinished; const int numRedirectsToFollow; int numRedirects; + int latestTotalBytes; private: //============================================================================== diff --git a/source/modules/juce_core/native/juce_mac_SystemStats.mm b/source/modules/juce_core/native/juce_mac_SystemStats.mm index 2d49ae698..f188a9f39 100644 --- a/source/modules/juce_core/native/juce_mac_SystemStats.mm +++ b/source/modules/juce_core/native/juce_mac_SystemStats.mm @@ -80,6 +80,8 @@ void CPUInformation::initialise() noexcept hasSSE2 = (d & (1u << 26)) != 0; has3DNow = (b & (1u << 31)) != 0; hasSSE3 = (c & (1u << 0)) != 0; + hasSSSE3 = (c & (1u << 9)) != 0; + hasAVX = (c & (1u << 28)) != 0; #endif #if JUCE_IOS || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) diff --git a/source/modules/juce_core/native/juce_osx_ObjCHelpers.h b/source/modules/juce_core/native/juce_osx_ObjCHelpers.h index d338afd20..678ced205 100644 --- a/source/modules/juce_core/native/juce_osx_ObjCHelpers.h +++ b/source/modules/juce_core/native/juce_osx_ObjCHelpers.h @@ -65,14 +65,16 @@ namespace static_cast (r.getWidth()), static_cast (r.getHeight())); } - #endif // These hacks are a workaround for newer Xcode builds which by default prevent calls to these objc functions.. typedef id (*MsgSendSuperFn) (struct objc_super*, SEL, ...); static inline MsgSendSuperFn getMsgSendSuperFn() noexcept { return (MsgSendSuperFn) (void*) objc_msgSendSuper; } + #if ! JUCE_PPC typedef double (*MsgSendFPRetFn) (id, SEL op, ...); static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; } + #endif + #endif } //============================================================================== @@ -147,11 +149,13 @@ struct ObjCClass jassert (b); (void) b; } + #if JUCE_MAC static id sendSuperclassMessage (id self, SEL selector) { objc_super s = { self, [SuperclassType class] }; return getMsgSendSuperFn() (&s, selector); } + #endif template static Type getIvar (id self, const char* name) diff --git a/source/modules/juce_core/native/juce_posix_SharedCode.h b/source/modules/juce_core/native/juce_posix_SharedCode.h index 089569744..17def6680 100644 --- a/source/modules/juce_core/native/juce_posix_SharedCode.h +++ b/source/modules/juce_core/native/juce_posix_SharedCode.h @@ -596,12 +596,38 @@ File juce_getExecutableFile() { Dl_info exeInfo; dladdr ((void*) juce_getExecutableFile, &exeInfo); - return CharPointer_UTF8 (exeInfo.dli_fname); + const CharPointer_UTF8 filename (exeInfo.dli_fname); + + // if the filename is absolute simply return it + if (File::isAbsolutePath (filename)) + return filename; + + // if the filename is relative construct from CWD + if (filename[0] == '.') + return File::getCurrentWorkingDirectory().getChildFile (filename).getFullPathName(); + + // filename is abstract, look up in PATH + if (const char* const envpath = ::getenv ("PATH")) + { + StringArray paths (StringArray::fromTokens (envpath, ":", "")); + + for (int i=paths.size(); --i>=0;) + { + const File filepath (File (paths[i]).getChildFile (filename)); + + if (filepath.existsAsFile()) + return filepath.getFullPathName(); + } + } + + // if we reach this, we failed to find ourselves... + jassertfalse; + return filename; } }; static String filename (DLAddrReader::getFilename()); - return File::getCurrentWorkingDirectory().getChildFile (filename); + return filename; #endif } @@ -961,22 +987,22 @@ void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMa if ((affinityMask & (1 << i)) != 0) CPU_SET (i, &affinity); - /* - N.B. If this line causes a compile error, then you've probably not got the latest - version of glibc installed. - - If you don't want to update your copy of glibc and don't care about cpu affinities, - then you can just disable all this stuff by setting the SUPPORT_AFFINITIES macro to 0. - */ + #if (! JUCE_LINUX) || ((__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2004) + pthread_setaffinity_np (pthread_self(), sizeof (cpu_set_t), &affinity); + #else + // NB: this call isn't really correct because it sets the affinity of the process, + // not the thread. But it's included here as a fallback for people who are using + // ridiculously old versions of glibc sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity); + #endif + sched_yield(); #else - /* affinities aren't supported because either the appropriate header files weren't found, - or the SUPPORT_AFFINITIES macro was turned off - */ + // affinities aren't supported because either the appropriate header files weren't found, + // or the SUPPORT_AFFINITIES macro was turned off jassertfalse; - (void) affinityMask; + ignoreUnused (affinityMask); #endif } @@ -1057,8 +1083,8 @@ public: close (pipeHandles[1]); #endif - execvp (argv[0], argv.getRawDataPointer()); - exit (-1); + if (execvp (argv[0], argv.getRawDataPointer()) < 0) + _exit (-1); } else { @@ -1285,7 +1311,7 @@ private: { struct timespec t; clock_gettime (CLOCK_MONOTONIC, &t); - time = 1000000000 * (int64) t.tv_sec + t.tv_nsec; + time = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec); } void wait() noexcept diff --git a/source/modules/juce_core/native/juce_win32_SystemStats.cpp b/source/modules/juce_core/native/juce_win32_SystemStats.cpp index d98e3237b..0943f205d 100644 --- a/source/modules/juce_core/native/juce_win32_SystemStats.cpp +++ b/source/modules/juce_core/native/juce_win32_SystemStats.cpp @@ -105,6 +105,8 @@ void CPUInformation::initialise() noexcept hasSSE = (info[3] & (1 << 25)) != 0; hasSSE2 = (info[3] & (1 << 26)) != 0; hasSSE3 = (info[2] & (1 << 0)) != 0; + hasAVX = (info[2] & (1 << 28)) != 0; + hasSSSE3 = (info[2] & (1 << 9)) != 0; has3DNow = (info[1] & (1 << 31)) != 0; SYSTEM_INFO systemInfo; @@ -131,7 +133,12 @@ static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) zerostruct (info); info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); - if (target >= SystemStats::WinVista) + if (target >= SystemStats::Windows10) + { + info.dwMajorVersion = 10; + info.dwMinorVersion = 0; + } + else if (target >= SystemStats::WinVista) { info.dwMajorVersion = 6; @@ -166,7 +173,7 @@ static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { const SystemStats::OperatingSystemType types[] - = { Windows8_1, Windows8_0, Windows7, WinVista, WinXP, Win2000 }; + = { Windows10, Windows8_1, Windows8_0, Windows7, WinVista, WinXP, Win2000 }; for (int i = 0; i < numElementsInArray (types); ++i) if (isWindowsVersionOrLater (types[i])) @@ -182,6 +189,7 @@ String SystemStats::getOperatingSystemName() switch (getOperatingSystemType()) { + case Windows10: name = "Windows 10"; break; case Windows8_1: name = "Windows 8.1"; break; case Windows8_0: name = "Windows 8.0"; break; case Windows7: name = "Windows 7"; break; diff --git a/source/modules/juce_core/network/juce_IPAddress.cpp b/source/modules/juce_core/network/juce_IPAddress.cpp index 6c4d08771..5560a49b0 100644 --- a/source/modules/juce_core/network/juce_IPAddress.cpp +++ b/source/modules/juce_core/network/juce_IPAddress.cpp @@ -126,7 +126,7 @@ static void findIPAddresses (int sock, Array& result) cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; } #else - for (size_t i = 0; i < cfg.ifc_len / sizeof (struct ifreq); ++i) + for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i) { const ifreq& item = cfg.ifc_req[i]; diff --git a/source/modules/juce_core/network/juce_Socket.cpp b/source/modules/juce_core/network/juce_Socket.cpp index 291b580a7..10de2f7f2 100644 --- a/source/modules/juce_core/network/juce_Socket.cpp +++ b/source/modules/juce_core/network/juce_Socket.cpp @@ -75,9 +75,59 @@ namespace SocketHelpers : (setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (one)) == 0)); } - static bool bindSocketToPort (const SocketHandle handle, const int port) noexcept + static void closeSocket (volatile int& handle, CriticalSection& readLock, + const bool isListener, int portNumber, bool& connected) noexcept { - if (handle <= 0 || port <= 0) + const SocketHandle h = handle; + handle = -1; + + #if JUCE_WINDOWS + ignoreUnused (portNumber, isListener, readLock); + + if (h != SOCKET_ERROR || connected) + closesocket (h); + + // make sure any read process finishes before we delete the socket + CriticalSection::ScopedLockType lock(readLock); + connected = false; + #else + if (connected) + { + connected = false; + + if (isListener) + { + // need to do this to interrupt the accept() function.. + StreamingSocket temp; + temp.connect (IPAddress::local().toString(), portNumber, 1000); + } + } + + if (h != -1) + { + // unblock any pending read requests + ::shutdown (h, SHUT_RDWR); + { + // see man-page of recv on linux about a race condition where the + // shutdown command is lost if the receiving thread does not have + // a chance to process before close is called. On Mac OS X shutdown + // does not unblock a select call, so using a lock here will dead-lock + // both threads. + #if JUCE_LINUX + CriticalSection::ScopedLockType lock (readLock); + ::close (h); + #else + ::close (h); + CriticalSection::ScopedLockType lock (readLock); + #endif + } + } + #endif + } + + static bool bindSocket (const SocketHandle handle, const int port, const String& address) noexcept + { + if (handle <= 0 || port < 0) return false; struct sockaddr_in servTmpAddr; @@ -86,30 +136,64 @@ namespace SocketHelpers servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); servTmpAddr.sin_port = htons ((uint16) port); + if (address.isNotEmpty()) + servTmpAddr.sin_addr.s_addr = ::inet_addr (address.toUTF8()); + return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0; } + static int getBoundPort (const SocketHandle handle) noexcept + { + if (handle <= 0) + return -1; + + struct sockaddr_in sin_addr; + socklen_t len = sizeof (sin_addr); + + if (getsockname (handle, (struct sockaddr*) &sin_addr, &len) == 0) + return ntohs (sin_addr.sin_port); + + return -1; + } + static int readSocket (const SocketHandle handle, void* const destBuffer, const int maxBytesToRead, bool volatile& connected, - const bool blockUntilSpecifiedAmountHasArrived) noexcept + const bool blockUntilSpecifiedAmountHasArrived, + CriticalSection& readLock, + String* senderIP = nullptr, + int* senderPort = nullptr) noexcept { int bytesRead = 0; while (bytesRead < maxBytesToRead) { - int bytesThisTime; + long bytesThisTime = -1; + char* const buffer = static_cast (destBuffer) + bytesRead; + const juce_socklen_t numToRead = (juce_socklen_t) (maxBytesToRead - bytesRead); - #if JUCE_WINDOWS - bytesThisTime = recv (handle, static_cast (destBuffer) + bytesRead, maxBytesToRead - bytesRead, 0); - #else - while ((bytesThisTime = (int) ::read (handle, addBytesToPointer (destBuffer, bytesRead), - (size_t) (maxBytesToRead - bytesRead))) < 0 - && errno == EINTR - && connected) { + // avoid race-condition + CriticalSection::ScopedTryLockType lock (readLock); + + if (lock.isLocked()) + { + if (senderIP == nullptr || senderPort == nullptr) + { + bytesThisTime = ::recv (handle, buffer, numToRead, 0); + } + else + { + sockaddr_in client; + socklen_t clientLen = sizeof (sockaddr); + + bytesThisTime = ::recvfrom (handle, buffer, numToRead, 0, (sockaddr*) &client, &clientLen); + + *senderIP = String::fromUTF8 (inet_ntoa (client.sin_addr), 16); + *senderPort = ntohs (client.sin_port); + } + } } - #endif if (bytesThisTime <= 0 || ! connected) { @@ -125,11 +209,20 @@ namespace SocketHelpers break; } - return bytesRead; + return (int) bytesRead; } - static int waitForReadiness (const SocketHandle handle, const bool forReading, const int timeoutMsecs) noexcept + static int waitForReadiness (const volatile int& handle, CriticalSection& readLock, + const bool forReading, const int timeoutMsecs) noexcept { + // avoid race-condition + CriticalSection::ScopedTryLockType lock (readLock); + + if (! lock.isLocked()) + return -1; + + int h = handle; + struct timeval timeout; struct timeval* timeoutp; @@ -146,20 +239,20 @@ namespace SocketHelpers fd_set rset, wset; FD_ZERO (&rset); - FD_SET (handle, &rset); + FD_SET (h, &rset); FD_ZERO (&wset); - FD_SET (handle, &wset); + FD_SET (h, &wset); fd_set* const prset = forReading ? &rset : nullptr; fd_set* const pwset = forReading ? nullptr : &wset; #if JUCE_WINDOWS - if (select ((int) handle + 1, prset, pwset, 0, timeoutp) < 0) + if (select ((int) h + 1, prset, pwset, 0, timeoutp) < 0) return -1; #else { int result; - while ((result = select (handle + 1, prset, pwset, 0, timeoutp)) < 0 + while ((result = select (h + 1, prset, pwset, 0, timeoutp)) < 0 && errno == EINTR) { } @@ -169,16 +262,20 @@ namespace SocketHelpers } #endif + // we are closing + if (handle < 0) + return -1; + { int opt; juce_socklen_t len = sizeof (opt); - if (getsockopt (handle, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 + if (getsockopt (h, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 || opt != 0) return -1; } - return FD_ISSET (handle, forReading ? &rset : &wset) ? 1 : 0; + return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0; } static bool setSocketBlockingState (const SocketHandle handle, const bool shouldBlock) noexcept @@ -201,12 +298,7 @@ namespace SocketHelpers #endif } - static bool connectSocket (int volatile& handle, - const bool isDatagram, - struct addrinfo** const serverAddress, - const String& hostName, - const int portNumber, - const int timeOutMillisecs) noexcept + static addrinfo* getAddressInfo (const bool isDatagram, const String& hostName, int portNumber) { struct addrinfo hints; zerostruct (hints); @@ -216,8 +308,22 @@ namespace SocketHelpers hints.ai_flags = AI_NUMERICSERV; struct addrinfo* info = nullptr; - if (getaddrinfo (hostName.toUTF8(), String (portNumber).toUTF8(), &hints, &info) != 0 - || info == nullptr) + if (getaddrinfo (hostName.toUTF8(), String (portNumber).toUTF8(), &hints, &info) == 0 + && info != nullptr) + return info; + + return nullptr; + } + + static bool connectSocket (int volatile& handle, + CriticalSection& readLock, + const String& hostName, + const int portNumber, + const int timeOutMillisecs) noexcept + { + struct addrinfo* info = getAddressInfo (false, hostName, portNumber); + + if (info == nullptr) return false; if (handle < 0) @@ -229,19 +335,12 @@ namespace SocketHelpers return false; } - if (isDatagram) - { - if (*serverAddress != nullptr) - freeaddrinfo (*serverAddress); - - *serverAddress = info; - return true; - } - setSocketBlockingState (handle, false); const int result = ::connect (handle, info->ai_addr, (socklen_t) info->ai_addrlen); freeaddrinfo (info); + bool retval = (result >= 0); + if (result < 0) { #if JUCE_WINDOWS @@ -250,18 +349,17 @@ namespace SocketHelpers if (errno == EINPROGRESS) #endif { - if (waitForReadiness (handle, false, timeOutMillisecs) != 1) - { - setSocketBlockingState (handle, true); - return false; - } + if (waitForReadiness (handle, readLock, false, timeOutMillisecs) == 1) + retval = true; } } setSocketBlockingState (handle, true); - resetSocketOptions (handle, false, false); - return true; + if (retval) + resetSocketOptions (handle, false, false); + + return retval; } static void makeReusable (int handle) noexcept @@ -269,6 +367,23 @@ namespace SocketHelpers const int reuse = 1; setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse)); } + + static bool multicast (int handle, const String& multicastIPAddress, + const String& interfaceIPAddress, bool join) noexcept + { + struct ip_mreq mreq; + + zerostruct (mreq); + mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toUTF8()); + mreq.imr_interface.s_addr = INADDR_ANY; + + if (interfaceIPAddress.isNotEmpty()) + mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toUTF8()); + + int joinCmd = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + + return setsockopt (handle, IPPROTO_IP, joinCmd, (const char*) &mreq, sizeof (mreq)) == 0; + } } //============================================================================== @@ -298,11 +413,10 @@ StreamingSocket::~StreamingSocket() } //============================================================================== -int StreamingSocket::read (void* destBuffer, const int maxBytesToRead, - const bool blockUntilSpecifiedAmountHasArrived) +int StreamingSocket::read (void* destBuffer, const int maxBytesToRead, bool shouldBlock) { return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, - connected, blockUntilSpecifiedAmountHasArrived) + connected, shouldBlock, readLock) : -1; } @@ -311,32 +425,31 @@ int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite) if (isListener || ! connected) return -1; - #if JUCE_WINDOWS - return send (handle, (const char*) sourceBuffer, numBytesToWrite, 0); - #else - int result; - - while ((result = (int) ::write (handle, sourceBuffer, (size_t) numBytesToWrite)) < 0 - && errno == EINTR) - { - } - - return result; - #endif + return (int) ::send (handle, (const char*) sourceBuffer, (juce_socklen_t) numBytesToWrite, 0); } //============================================================================== int StreamingSocket::waitUntilReady (const bool readyForReading, const int timeoutMsecs) const { - return connected ? SocketHelpers::waitForReadiness (handle, readyForReading, timeoutMsecs) + return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs) : -1; } //============================================================================== bool StreamingSocket::bindToPort (const int port) { - return SocketHelpers::bindSocketToPort (handle, port); + return bindToPort (port, String()); +} + +bool StreamingSocket::bindToPort (const int port, const String& addr) +{ + return SocketHelpers::bindSocket (handle, port, addr); +} + +int StreamingSocket::getBoundPort() const noexcept +{ + return SocketHelpers::getBoundPort (handle); } bool StreamingSocket::connect (const String& remoteHostName, @@ -356,7 +469,7 @@ bool StreamingSocket::connect (const String& remoteHostName, portNumber = remotePortNumber; isListener = false; - connected = SocketHelpers::connectSocket (handle, false, nullptr, remoteHostName, + connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName, remotePortNumber, timeOutMillisecs); if (! (connected && SocketHelpers::resetSocketOptions (handle, false, false))) @@ -370,30 +483,7 @@ bool StreamingSocket::connect (const String& remoteHostName, void StreamingSocket::close() { - #if JUCE_WINDOWS - if (handle != SOCKET_ERROR || connected) - closesocket (handle); - - connected = false; - #else - if (connected) - { - connected = false; - - if (isListener) - { - // need to do this to interrupt the accept() function.. - StreamingSocket temp; - temp.connect (IPAddress::local().toString(), portNumber, 1000); - } - } - - if (handle != -1) - { - ::shutdown (handle, SHUT_RDWR); - ::close (handle); - } - #endif + SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected); hostName.clear(); portNumber = 0; @@ -470,133 +560,109 @@ bool StreamingSocket::isLocal() const noexcept //============================================================================== //============================================================================== -DatagramSocket::DatagramSocket (const int localPortNumber, const bool canBroadcast) - : portNumber (0), - handle (-1), - connected (true), - allowBroadcast (canBroadcast), - serverAddress (nullptr) +DatagramSocket::DatagramSocket (const bool canBroadcast) + : handle (-1), + isBound (false), + lastServerPort (-1), + lastServerAddress (nullptr) { SocketHelpers::initSockets(); handle = (int) socket (AF_INET, SOCK_DGRAM, 0); + SocketHelpers::resetSocketOptions (handle, true, canBroadcast); SocketHelpers::makeReusable (handle); - bindToPort (localPortNumber); -} - -DatagramSocket::DatagramSocket (const String& host, const int portNum, - const int h, const int localPortNumber) - : hostName (host), - portNumber (portNum), - handle (h), - connected (true), - allowBroadcast (false), - serverAddress (nullptr) -{ - SocketHelpers::initSockets(); - - SocketHelpers::resetSocketOptions (h, true, allowBroadcast); - bindToPort (localPortNumber); } DatagramSocket::~DatagramSocket() { - close(); + if (lastServerAddress != nullptr) + freeaddrinfo (static_cast (lastServerAddress)); - if (serverAddress != nullptr) - freeaddrinfo (static_cast (serverAddress)); -} - -void DatagramSocket::close() -{ - #if JUCE_WINDOWS - closesocket (handle); - connected = false; - #else - connected = false; - ::close (handle); - #endif - - hostName.clear(); - portNumber = 0; - handle = -1; + bool connected = false; + SocketHelpers::closeSocket (handle, readLock, false, 0, connected); } bool DatagramSocket::bindToPort (const int port) { - return SocketHelpers::bindSocketToPort (handle, port); + return bindToPort (port, String()); } -bool DatagramSocket::connect (const String& remoteHostName, - const int remotePortNumber, - const int timeOutMillisecs) +bool DatagramSocket::bindToPort (const int port, const String& addr) { - if (connected) - close(); - - hostName = remoteHostName; - portNumber = remotePortNumber; - - connected = SocketHelpers::connectSocket (handle, true, (struct addrinfo**) &serverAddress, - remoteHostName, remotePortNumber, - timeOutMillisecs); - - if (! (connected && SocketHelpers::resetSocketOptions (handle, true, allowBroadcast))) + if (SocketHelpers::bindSocket (handle, port, addr)) { - close(); - return false; + isBound = true; + lastBindAddress = addr; + + return true; } - return true; + return false; } -DatagramSocket* DatagramSocket::waitForNextConnection() const +int DatagramSocket::getBoundPort() const noexcept { - while (waitUntilReady (true, -1) == 1) - { - struct sockaddr_storage address; - juce_socklen_t len = sizeof (address); - char buf[1]; - - if (recvfrom (handle, buf, 0, 0, (struct sockaddr*) &address, &len) > 0) - return new DatagramSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), - ntohs (((struct sockaddr_in*) &address)->sin_port), - -1, -1); - } - - return nullptr; + return isBound ? SocketHelpers::getBoundPort (handle) : -1; } //============================================================================== int DatagramSocket::waitUntilReady (const bool readyForReading, const int timeoutMsecs) const { - return connected ? SocketHelpers::waitForReadiness (handle, readyForReading, timeoutMsecs) - : -1; + return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs); } -int DatagramSocket::read (void* destBuffer, const int maxBytesToRead, const bool blockUntilSpecifiedAmountHasArrived) +int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock) { - return connected ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, - connected, blockUntilSpecifiedAmountHasArrived) - : -1; + bool connected = true; + return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, + connected, shouldBlock, readLock) : -1; } -int DatagramSocket::write (const void* sourceBuffer, const int numBytesToWrite) +int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock, String& senderIPAddress, int& senderPort) { - // You need to call connect() first to set the server address.. - jassert (serverAddress != nullptr && connected); + bool connected = true; + return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, + shouldBlock, readLock, &senderIPAddress, &senderPort) : -1; +} - return connected ? (int) sendto (handle, (const char*) sourceBuffer, - (size_t) numBytesToWrite, 0, - static_cast (serverAddress)->ai_addr, - (juce_socklen_t) static_cast (serverAddress)->ai_addrlen) - : -1; +int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, + const void* sourceBuffer, int numBytesToWrite) +{ + struct addrinfo*& info = reinterpret_cast (lastServerAddress); + + // getaddrinfo can be quite slow so cache the result of the address lookup + if (info == nullptr || remoteHostname != lastServerHost || remotePortNumber != lastServerPort) + { + if (info != nullptr) + freeaddrinfo (info); + + if ((info = SocketHelpers::getAddressInfo (true, remoteHostname, remotePortNumber)) == nullptr) + return -1; + + lastServerHost = remoteHostname; + lastServerPort = remotePortNumber; + } + + return (int) ::sendto (handle, (const char*) sourceBuffer, + (juce_socklen_t) numBytesToWrite, 0, + info->ai_addr, (socklen_t) info->ai_addrlen); } -bool DatagramSocket::isLocal() const noexcept +bool DatagramSocket::joinMulticast (const String& multicastIPAddress) { - return hostName == "127.0.0.1"; + if (! isBound) + return false; + + return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, true); +} + +bool DatagramSocket::leaveMulticast (const String& multicastIPAddress) +{ + if (! isBound) + return false; + + return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, false); } #if JUCE_MSVC diff --git a/source/modules/juce_core/network/juce_Socket.h b/source/modules/juce_core/network/juce_Socket.h index 73795c956..d35847dd6 100644 --- a/source/modules/juce_core/network/juce_Socket.h +++ b/source/modules/juce_core/network/juce_Socket.h @@ -65,6 +65,25 @@ public: */ bool bindToPort (int localPortNumber); + /** Binds the socket to the specified local port and local address. + + If localAddress is not an empty string then the socket will be bound to localAddress + as well. This is useful if you would like to bind your socket to a specific network + adapter. Note that localAddress must be an IP address assigned to one of your + network address otherwise this function will fail. + @returns true on success; false may indicate that another socket is already bound + on the same port + @see bindToPort(int localPortNumber), IPAddress::findAllAddresses + */ + bool bindToPort (int localPortNumber, const String& localAddress); + + /** Returns the local port number to which this socket is currently bound. + + This is useful if you need to know to which port the OS has actually bound your + socket when calling the constructor or bindToPort with zero as the + localPortNumber argument. Returns -1 if the function fails. */ + int getBoundPort() const noexcept; + /** Tries to connect the socket to hostname:port. If timeOutMillisecs is 0, then this method will block until the operating system @@ -164,6 +183,7 @@ private: String hostName; int volatile portNumber, handle; bool connected, isListener; + mutable CriticalSection readLock; StreamingSocket (const String& hostname, int portNumber, int handle); @@ -185,22 +205,16 @@ class JUCE_API DatagramSocket public: //============================================================================== /** - Creates an (uninitialised) datagram socket. + Creates a datagram socket. - The localPortNumber is the port on which to bind this socket. If this value is 0, - the port number is assigned by the operating system. - - To use the socket for sending, call the connect() method. This will not immediately - make a connection, but will save the destination you've provided. After this, you can - call read() or write(). + You first need to bind this socket to a port with bindToPort if you intend to read + from this socket. If enableBroadcasting is true, the socket will be allowed to send broadcast messages (may require extra privileges on linux) - - To wait for other sockets to connect to this one, call waitForNextConnection(). */ - DatagramSocket (int localPortNumber, - bool enableBroadcasting = false); + DatagramSocket (bool enableBroadcasting = false); + /** Destructor. */ ~DatagramSocket(); @@ -208,37 +222,33 @@ public: //============================================================================== /** Binds the socket to the specified local port. + The localPortNumber is the port on which to bind this socket. If this value is 0, + the port number is assigned by the operating system. + @returns true on success; false may indicate that another socket is already bound on the same port */ bool bindToPort (int localPortNumber); - /** Tries to connect the socket to hostname:port. - - If timeOutMillisecs is 0, then this method will block until the operating system - rejects the connection (which could take a long time). + /** Binds the socket to the specified local port and local address. - @returns true if it succeeds. - @see isConnected + If localAddress is not an empty string then the socket will be bound to localAddress + as well. This is useful if you would like to bind your socket to a specific network + adapter. Note that localAddress must be an IP address assigned to one of your + network address otherwise this function will fail. + @returns true on success; false may indicate that another socket is already bound + on the same port + @see bindToPort(int localPortNumber), IPAddress::findAllAddresses */ - bool connect (const String& remoteHostname, - int remotePortNumber, - int timeOutMillisecs = 3000); + bool bindToPort (int localPortNumber, const String& localAddress); - /** True if the socket is currently connected. */ - bool isConnected() const noexcept { return connected; } + /** Returns the local port number to which this socket is currently bound. - /** Closes the connection. */ - void close(); - - /** Returns the name of the currently connected host. */ - const String& getHostName() const noexcept { return hostName; } - - /** Returns the port number that's currently open. */ - int getPort() const noexcept { return portNumber; } + This is useful if you need to know to which port the OS has actually bound your + socket when bindToPort was called with zero. - /** True if the socket is connected to this machine rather than over the network. */ - bool isLocal() const noexcept; + Returns -1 if the socket didn't bind to any port yet or an error occured. */ + int getBoundPort() const noexcept; /** Returns the OS's socket handle that's currently open. */ int getRawSocketHandle() const noexcept { return handle; } @@ -271,6 +281,21 @@ public: int read (void* destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived); + /** Reads bytes from the socket and return the IP address of the sender. + + If blockUntilSpecifiedAmountHasArrived is true, the method will block until + maxBytesToRead bytes have been read, (or until an error occurs). If this + flag is false, the method will return as much data as is currently available + without blocking. + + @returns the number of bytes read, or -1 if there was an error. On a successful + result, the senderIPAddress value will be set to the IP of the sender. + @see waitUntilReady + */ + int read (void* destBuffer, int maxBytesToRead, + bool blockUntilSpecifiedAmountHasArrived, + String& senderIPAddress, int& senderPortNumber); + /** Writes bytes to the socket from a buffer. Note that this method will block unless you have checked the socket is ready @@ -278,25 +303,30 @@ public: @returns the number of bytes written, or -1 if there was an error. */ - int write (const void* sourceBuffer, int numBytesToWrite); + int write (const String& remoteHostname, int remotePortNumber, + const void* sourceBuffer, int numBytesToWrite); //============================================================================== - /** This waits for incoming data to be sent, and returns a socket that can be used - to read it. + /** Join a multicast group + + @returns true if it succeeds. + */ + bool joinMulticast (const String& multicastIPAddress); - The object that gets returned is owned by the caller, and can't be used for - sending, but can be used to read the data. + /** Leave a multicast group + + @returns true if it succeeds. */ - DatagramSocket* waitForNextConnection() const; + bool leaveMulticast (const String& multicastIPAddress); private: //============================================================================== - String hostName; - int volatile portNumber, handle; - bool connected, allowBroadcast; - void* serverAddress; - - DatagramSocket (const String& hostname, int portNumber, int handle, int localPortNumber); + int handle; + bool isBound; + String lastBindAddress, lastServerHost; + int lastServerPort; + void* lastServerAddress; + mutable CriticalSection readLock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DatagramSocket) }; diff --git a/source/modules/juce_core/streams/juce_MemoryOutputStream.h b/source/modules/juce_core/streams/juce_MemoryOutputStream.h index 2e764effe..33cbb2e8c 100644 --- a/source/modules/juce_core/streams/juce_MemoryOutputStream.h +++ b/source/modules/juce_core/streams/juce_MemoryOutputStream.h @@ -111,7 +111,7 @@ public: capacity off the block, so that its length matches the amount of actual data that has been written so far. */ - void flush(); + void flush() override; bool write (const void*, size_t) override; int64 getPosition() override { return (int64) position; } diff --git a/source/modules/juce_core/system/juce_SystemStats.cpp b/source/modules/juce_core/system/juce_SystemStats.cpp index 365e94d9f..540cc889e 100644 --- a/source/modules/juce_core/system/juce_SystemStats.cpp +++ b/source/modules/juce_core/system/juce_SystemStats.cpp @@ -67,7 +67,8 @@ struct CPUInformation { CPUInformation() noexcept : numCpus (0), hasMMX (false), hasSSE (false), - hasSSE2 (false), hasSSE3 (false), has3DNow (false) + hasSSE2 (false), hasSSE3 (false), has3DNow (false), + hasSSSE3 (false), hasAVX (false) { initialise(); } @@ -75,7 +76,7 @@ struct CPUInformation void initialise() noexcept; int numCpus; - bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow; + bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow, hasSSSE3, hasAVX; }; static const CPUInformation& getCPUInformation() noexcept @@ -86,10 +87,12 @@ static const CPUInformation& getCPUInformation() noexcept int SystemStats::getNumCpus() noexcept { return getCPUInformation().numCpus; } bool SystemStats::hasMMX() noexcept { return getCPUInformation().hasMMX; } +bool SystemStats::has3DNow() noexcept { return getCPUInformation().has3DNow; } bool SystemStats::hasSSE() noexcept { return getCPUInformation().hasSSE; } bool SystemStats::hasSSE2() noexcept { return getCPUInformation().hasSSE2; } bool SystemStats::hasSSE3() noexcept { return getCPUInformation().hasSSE3; } -bool SystemStats::has3DNow() noexcept { return getCPUInformation().has3DNow; } +bool SystemStats::hasSSSE3() noexcept { return getCPUInformation().hasSSSE3; } +bool SystemStats::hasAVX() noexcept { return getCPUInformation().hasAVX; } //============================================================================== diff --git a/source/modules/juce_core/system/juce_SystemStats.h b/source/modules/juce_core/system/juce_SystemStats.h index 023dcc0a4..665d86fbb 100644 --- a/source/modules/juce_core/system/juce_SystemStats.h +++ b/source/modules/juce_core/system/juce_SystemStats.h @@ -47,29 +47,31 @@ public: /** The set of possible results of the getOperatingSystemType() method. */ enum OperatingSystemType { - UnknownOS = 0, - - Linux = 0x2000, - Android = 0x3000, - iOS = 0x8000, - - MacOSX_10_4 = 0x1004, - MacOSX_10_5 = 0x1005, - MacOSX_10_6 = 0x1006, - MacOSX_10_7 = 0x1007, - MacOSX_10_8 = 0x1008, - MacOSX_10_9 = 0x1009, - MacOSX_10_10 = 0x100a, - - Win2000 = 0x4105, - WinXP = 0x4106, - WinVista = 0x4107, - Windows7 = 0x4108, - Windows8_0 = 0x4109, - Windows8_1 = 0x410a, - - Windows = 0x4000, /**< To test whether any version of Windows is running, - you can use the expression ((getOperatingSystemType() & Windows) != 0). */ + UnknownOS = 0, + + MacOSX = 0x0100, /**< To test whether any version of OSX is running, + you can use the expression ((getOperatingSystemType() & MacOSX) != 0). */ + Windows = 0x0200, /**< To test whether any version of Windows is running, + you can use the expression ((getOperatingSystemType() & Windows) != 0). */ + Linux = 0x0400, + Android = 0x0800, + iOS = 0x1000, + + MacOSX_10_4 = MacOSX | 4, + MacOSX_10_5 = MacOSX | 5, + MacOSX_10_6 = MacOSX | 6, + MacOSX_10_7 = MacOSX | 7, + MacOSX_10_8 = MacOSX | 8, + MacOSX_10_9 = MacOSX | 9, + MacOSX_10_10 = MacOSX | 10, + + Win2000 = Windows | 1, + WinXP = Windows | 2, + WinVista = Windows | 3, + Windows7 = Windows | 4, + Windows8_0 = Windows | 5, + Windows8_1 = Windows | 6, + Windows10 = Windows | 7 }; /** Returns the type of operating system we're running on. @@ -150,10 +152,12 @@ public: static String getCpuVendor(); static bool hasMMX() noexcept; /**< Returns true if Intel MMX instructions are available. */ + static bool has3DNow() noexcept; /**< Returns true if AMD 3DNOW instructions are available. */ static bool hasSSE() noexcept; /**< Returns true if Intel SSE instructions are available. */ static bool hasSSE2() noexcept; /**< Returns true if Intel SSE2 instructions are available. */ static bool hasSSE3() noexcept; /**< Returns true if Intel SSE2 instructions are available. */ - static bool has3DNow() noexcept; /**< Returns true if AMD 3DNOW instructions are available. */ + static bool hasSSSE3() noexcept; /**< Returns true if Intel SSSE3 instructions are available. */ + static bool hasAVX() noexcept; /**< Returns true if Intel AVX instructions are available. */ //============================================================================== /** Finds out how much RAM is in the machine. diff --git a/source/modules/juce_core/text/juce_CharPointer_UTF8.h b/source/modules/juce_core/text/juce_CharPointer_UTF8.h index f6e156e14..6cc5ebf4f 100644 --- a/source/modules/juce_core/text/juce_CharPointer_UTF8.h +++ b/source/modules/juce_core/text/juce_CharPointer_UTF8.h @@ -511,7 +511,7 @@ public: if (byte < 0) { - uint8 bit = 0x40; + int bit = 0x40; int numExtraValues = 0; while ((byte & bit) != 0) diff --git a/source/modules/juce_core/text/juce_CharacterFunctions.cpp b/source/modules/juce_core/text/juce_CharacterFunctions.cpp index 11eaa188b..b33940594 100644 --- a/source/modules/juce_core/text/juce_CharacterFunctions.cpp +++ b/source/modules/juce_core/text/juce_CharacterFunctions.cpp @@ -34,18 +34,18 @@ juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) noexcept { - return towupper ((wchar_t) character); + return (juce_wchar) towupper ((wint_t) character); } juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) noexcept { - return towlower ((wchar_t) character); + return (juce_wchar) towlower ((wint_t) character); } bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept { #if JUCE_WINDOWS - return iswupper ((wchar_t) character) != 0; + return iswupper ((wint_t) character) != 0; #else return toLowerCase (character) != character; #endif @@ -54,7 +54,7 @@ bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept bool CharacterFunctions::isLowerCase (const juce_wchar character) noexcept { #if JUCE_WINDOWS - return iswlower ((wchar_t) character) != 0; + return iswlower ((wint_t) character) != 0; #else return toUpperCase (character) != character; #endif @@ -72,7 +72,7 @@ bool CharacterFunctions::isWhitespace (const char character) noexcept bool CharacterFunctions::isWhitespace (const juce_wchar character) noexcept { - return iswspace ((wchar_t) character) != 0; + return iswspace ((wint_t) character) != 0; } bool CharacterFunctions::isDigit (const char character) noexcept @@ -82,7 +82,7 @@ bool CharacterFunctions::isDigit (const char character) noexcept bool CharacterFunctions::isDigit (const juce_wchar character) noexcept { - return iswdigit ((wchar_t) character) != 0; + return iswdigit ((wint_t) character) != 0; } bool CharacterFunctions::isLetter (const char character) noexcept @@ -93,7 +93,7 @@ bool CharacterFunctions::isLetter (const char character) noexcept bool CharacterFunctions::isLetter (const juce_wchar character) noexcept { - return iswalpha ((wchar_t) character) != 0; + return iswalpha ((wint_t) character) != 0; } bool CharacterFunctions::isLetterOrDigit (const char character) noexcept @@ -105,7 +105,7 @@ bool CharacterFunctions::isLetterOrDigit (const char character) noexcept bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) noexcept { - return iswalnum ((wchar_t) character) != 0; + return iswalnum ((wint_t) character) != 0; } int CharacterFunctions::getHexDigitValue (const juce_wchar digit) noexcept diff --git a/source/modules/juce_core/text/juce_Identifier.cpp b/source/modules/juce_core/text/juce_Identifier.cpp index bac4dd58a..4d9d3ea55 100644 --- a/source/modules/juce_core/text/juce_Identifier.cpp +++ b/source/modules/juce_core/text/juce_Identifier.cpp @@ -40,25 +40,22 @@ Identifier& Identifier::operator= (const Identifier other) noexcept Identifier::Identifier (const String& nm) : name (StringPool::getGlobalPool().getPooledString (nm)) { - /* An Identifier string must be suitable for use as a script variable or XML - attribute, so it can only contain this limited set of characters.. */ - jassert (isValidIdentifier (toString())); + // An Identifier cannot be created from an empty string! + jassert (nm.isNotEmpty()); } Identifier::Identifier (const char* nm) : name (StringPool::getGlobalPool().getPooledString (nm)) { - /* An Identifier string must be suitable for use as a script variable or XML - attribute, so it can only contain this limited set of characters.. */ - jassert (isValidIdentifier (toString())); + // An Identifier cannot be created from an empty string! + jassert (nm != nullptr && nm[0] != 0); } Identifier::Identifier (String::CharPointerType start, String::CharPointerType end) : name (StringPool::getGlobalPool().getPooledString (start, end)) { - /* An Identifier string must be suitable for use as a script variable or XML - attribute, so it can only contain this limited set of characters.. */ - jassert (isValidIdentifier (toString())); + // An Identifier cannot be created from an empty string! + jassert (start < end); } Identifier Identifier::null; diff --git a/source/modules/juce_core/text/juce_String.cpp b/source/modules/juce_core/text/juce_String.cpp index 3dbc0c08e..f91adf509 100644 --- a/source/modules/juce_core/text/juce_String.cpp +++ b/source/modules/juce_core/text/juce_String.cpp @@ -1617,10 +1617,10 @@ String String::upToLastOccurrenceOf (StringRef sub, bool String::isQuotedString() const { - const String trimmed (trimStart()); + const juce_wchar trimmedStart = trimStart()[0]; - return trimmed[0] == '"' - || trimmed[0] == '\''; + return trimmedStart == '"' + || trimmedStart == '\''; } String String::unquoted() const @@ -2522,14 +2522,13 @@ public: beginTest ("var"); var v1 = 0; - var v2 = 0.1; - var v3 = "0.1"; + var v2 = 0.16; + var v3 = "0.16"; var v4 = (int64) 0; var v5 = 0.0; expect (! v2.equals (v1)); expect (! v1.equals (v2)); expect (v2.equals (v3)); - expect (v3.equals (v2)); expect (! v3.equals (v1)); expect (! v1.equals (v3)); expect (v1.equals (v4)); diff --git a/source/modules/juce_core/text/juce_String.h b/source/modules/juce_core/text/juce_String.h index 0260acff1..8fb209adb 100644 --- a/source/modules/juce_core/text/juce_String.h +++ b/source/modules/juce_core/text/juce_String.h @@ -299,13 +299,13 @@ public: Note that there's also an isNotEmpty() method to help write readable code. @see containsNonWhitespaceChars() */ - inline bool isEmpty() const noexcept { return text[0] == 0; } + inline bool isEmpty() const noexcept { return text.isEmpty(); } /** Returns true if the string contains at least one character. Note that there's also an isEmpty() method to help write readable code. @see containsNonWhitespaceChars() */ - inline bool isNotEmpty() const noexcept { return text[0] != 0; } + inline bool isNotEmpty() const noexcept { return ! text.isEmpty(); } /** Resets this string to be empty. */ void clear() noexcept; diff --git a/source/modules/juce_core/threads/juce_Thread.h b/source/modules/juce_core/threads/juce_Thread.h index aef96b7dc..74b699680 100644 --- a/source/modules/juce_core/threads/juce_Thread.h +++ b/source/modules/juce_core/threads/juce_Thread.h @@ -235,7 +235,7 @@ public: /** Finds the thread object that is currently running. Note that the main UI thread (or other non-Juce threads) don't have a Thread - object associated with them, so this will return 0. + object associated with them, so this will return nullptr. */ static Thread* JUCE_CALLTYPE getCurrentThread(); diff --git a/source/modules/juce_core/xml/juce_XmlElement.h b/source/modules/juce_core/xml/juce_XmlElement.h index 2f03e93aa..f755fb16d 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.h +++ b/source/modules/juce_core/xml/juce_XmlElement.h @@ -352,7 +352,7 @@ public: /** Returns the value of a named attribute as floating-point. - This will try to find the attribute and convert it to an integer (using + This will try to find the attribute and convert it to a double (using the String::getDoubleValue() method). @param attributeName the name of the attribute to look up diff --git a/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h b/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h index 3309486a0..d45d5ae7d 100644 --- a/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h +++ b/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h @@ -72,7 +72,7 @@ public: the stream is closed - this means that no more data can be written to it, and any subsequent attempts to call write() will cause an assertion. */ - void flush(); + void flush() override; int64 getPosition() override; bool setPosition (int64) override; diff --git a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp index 71e085072..e2f660850 100644 --- a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp +++ b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp @@ -90,7 +90,7 @@ namespace zlibNamespace class GZIPDecompressorInputStream::GZIPDecompressHelper { public: - GZIPDecompressHelper (const bool dontWrap) + GZIPDecompressHelper (Format f) : finished (true), needsDictionary (false), error (true), @@ -100,7 +100,7 @@ public: { using namespace zlibNamespace; zerostruct (stream); - streamIsValid = (inflateInit2 (&stream, dontWrap ? -MAX_WBITS : MAX_WBITS) == Z_OK); + streamIsValid = (inflateInit2 (&stream, getBitsForFormat (f)) == Z_OK); finished = error = ! streamIsValid; } @@ -157,6 +157,19 @@ public: return 0; } + static int getBitsForFormat (Format f) noexcept + { + switch (f) + { + case zlibFormat: return MAX_WBITS; + case deflateFormat: return -MAX_WBITS; + case gzipFormat: return MAX_WBITS | 16; + default: jassertfalse; break; + } + + return MAX_WBITS; + } + bool finished, needsDictionary, error, streamIsValid; enum { gzipDecompBufferSize = 32768 }; @@ -170,32 +183,30 @@ private: }; //============================================================================== -GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream* const source, - const bool deleteSourceWhenDestroyed, - const bool noWrap_, - const int64 uncompressedStreamLength_) +GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream* source, bool deleteSourceWhenDestroyed, + Format f, int64 uncompressedLength) : sourceStream (source, deleteSourceWhenDestroyed), - uncompressedStreamLength (uncompressedStreamLength_), - noWrap (noWrap_), + uncompressedStreamLength (uncompressedLength), + format (f), isEof (false), activeBufferSize (0), originalSourcePos (source->getPosition()), currentPos (0), buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), - helper (new GZIPDecompressHelper (noWrap_)) + helper (new GZIPDecompressHelper (f)) { } GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream& source) : sourceStream (&source, false), uncompressedStreamLength (-1), - noWrap (false), + format (zlibFormat), isEof (false), activeBufferSize (0), originalSourcePos (source.getPosition()), currentPos (0), buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), - helper (new GZIPDecompressHelper (false)) + helper (new GZIPDecompressHelper (zlibFormat)) { } @@ -278,7 +289,7 @@ bool GZIPDecompressorInputStream::setPosition (int64 newPos) isEof = false; activeBufferSize = 0; currentPos = 0; - helper = new GZIPDecompressHelper (noWrap); + helper = new GZIPDecompressHelper (format); sourceStream->setPosition (originalSourcePos); } diff --git a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h index 78a0b77b7..614a87a56 100644 --- a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h +++ b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h @@ -43,21 +43,28 @@ class JUCE_API GZIPDecompressorInputStream : public InputStream { public: + enum Format + { + zlibFormat = 0, + deflateFormat, + gzipFormat + }; + //============================================================================== /** Creates a decompressor stream. @param sourceStream the stream to read from @param deleteSourceWhenDestroyed whether or not to delete the source stream when this object is destroyed - @param noWrap this is used internally by the ZipFile class - and should be ignored by user applications + @param sourceFormat can be used to select which of the supported + formats the data is expected to be in @param uncompressedStreamLength if the creator knows the length that the uncompressed stream will be, then it can supply this value, which will be returned by getTotalLength() */ GZIPDecompressorInputStream (InputStream* sourceStream, bool deleteSourceWhenDestroyed, - bool noWrap = false, + Format sourceFormat = zlibFormat, int64 uncompressedStreamLength = -1); /** Creates a decompressor stream. @@ -81,7 +88,7 @@ private: //============================================================================== OptionalScopedPointer sourceStream; const int64 uncompressedStreamLength; - const bool noWrap; + const Format format; bool isEof; int activeBufferSize; int64 originalSourcePos, currentPos; @@ -91,6 +98,11 @@ private: friend struct ContainerDeletePolicy; ScopedPointer helper; + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // The arguments to this method have changed! Please pass a Format enum instead of the old dontWrap bool. + GZIPDecompressorInputStream (InputStream*, bool, bool, int64 x = -1); + #endif + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPDecompressorInputStream) }; diff --git a/source/modules/juce_core/zip/juce_ZipFile.cpp b/source/modules/juce_core/zip/juce_ZipFile.cpp index a76d8b890..33f164ac4 100644 --- a/source/modules/juce_core/zip/juce_ZipFile.cpp +++ b/source/modules/juce_core/zip/juce_ZipFile.cpp @@ -298,7 +298,9 @@ InputStream* ZipFile::createStreamForEntry (const int index) if (zei->compressed) { - stream = new GZIPDecompressorInputStream (stream, true, true, (int64) zei->entry.uncompressedSize); + stream = new GZIPDecompressorInputStream (stream, true, + GZIPDecompressorInputStream::deflateFormat, + (int64) zei->entry.uncompressedSize); // (much faster to unzip in big blocks using a buffer..) stream = new BufferedInputStream (stream, 32768, true); diff --git a/source/modules/juce_core/zip/zlib/trees.c b/source/modules/juce_core/zip/zlib/trees.c index 36a124d7b..463bfc26e 100644 --- a/source/modules/juce_core/zip/zlib/trees.c +++ b/source/modules/juce_core/zip/zlib/trees.c @@ -571,7 +571,7 @@ local void gen_codes (ct_data *tree, /* the tree to decorate */ ushf *bl_count) /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ - ush code = 0; /* running code value */ + ush code_ = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ @@ -579,12 +579,12 @@ local void gen_codes (ct_data *tree, /* the tree to decorate */ * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits-1]) << 1; + next_code[bits] = code_ = (code_ + bl_count[bits-1]) << 1; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ - Assert (code + bl_count[MAX_BITS]-1 == (1<last_lit != 0) do { @@ -1066,21 +1066,21 @@ local void compress_block (deflate_state *s, Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code+LITERALS+1, ltree); /* send the length code */ - extra = extra_lbits[code]; + code_ = _length_code[lc]; + send_code(s, code_+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code_]; if (extra != 0) { - lc -= base_length[code]; + lc -= base_length[code_]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - Assert (code < D_CODES, "bad d_code"); + code_ = d_code(dist); + Assert (code_ < D_CODES, "bad d_code"); - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; + send_code(s, code_, dtree); /* send the distance code */ + extra = extra_dbits[code_]; if (extra != 0) { - dist -= base_dist[code]; + dist -= base_dist[code_]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ @@ -1120,12 +1120,12 @@ local void set_data_type (deflate_state *s) * method would use a table) * IN assertion: 1 <= len <= 15 */ -local unsigned bi_reverse (unsigned code, int len) +local unsigned bi_reverse (unsigned code_, int len) { register unsigned res = 0; do { - res |= code & 1; - code >>= 1, res <<= 1; + res |= code_ & 1; + code_ >>= 1, res <<= 1; } while (--len > 0); return res >> 1; } diff --git a/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h b/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h index f3d9551b1..a8198e699 100644 --- a/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h +++ b/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h @@ -223,7 +223,7 @@ public: protected: /** @internal */ - virtual void propertyChanged(); + void propertyChanged() override; private: //============================================================================== diff --git a/source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp b/source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp index 07948c69e..c1c174116 100644 --- a/source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp +++ b/source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp @@ -203,6 +203,14 @@ void UndoManager::setCurrentTransactionName (const String& newName) noexcept action->name = newName; } +String UndoManager::getCurrentTransactionName() const noexcept +{ + if (ActionSet* action = getCurrentSet()) + return action->name; + + return newTransactionName; +} + //============================================================================== UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; } UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; } diff --git a/source/modules/juce_data_structures/undomanager/juce_UndoManager.h b/source/modules/juce_data_structures/undomanager/juce_UndoManager.h index a3b691b69..ad22272bf 100644 --- a/source/modules/juce_data_structures/undomanager/juce_UndoManager.h +++ b/source/modules/juce_data_structures/undomanager/juce_UndoManager.h @@ -144,6 +144,11 @@ public: */ void setCurrentTransactionName (const String& newName) noexcept; + /** Returns the name of the current transaction. + @see setCurrentTransactionName + */ + String getCurrentTransactionName() const noexcept; + //============================================================================== /** Returns true if there's at least one action in the list to undo. @see getUndoDescription, undo, canRedo diff --git a/source/modules/juce_data_structures/values/juce_Value.cpp b/source/modules/juce_data_structures/values/juce_Value.cpp index b0bb40507..9fd05f40b 100644 --- a/source/modules/juce_data_structures/values/juce_Value.cpp +++ b/source/modules/juce_data_structures/values/juce_Value.cpp @@ -72,12 +72,12 @@ public: { } - var getValue() const + var getValue() const override { return value; } - void setValue (const var& newValue) + void setValue (const var& newValue) override { if (! newValue.equalsWithSameType (value)) { diff --git a/source/modules/juce_data_structures/values/juce_ValueTree.cpp b/source/modules/juce_data_structures/values/juce_ValueTree.cpp index 667d30d09..43cfc564c 100644 --- a/source/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/source/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -814,8 +814,8 @@ public: tree.removeListener (this); } - var getValue() const { return tree [property]; } - void setValue (const var& newValue) { tree.setProperty (property, newValue, undoManager); } + var getValue() const override { return tree [property]; } + void setValue (const var& newValue) override { tree.setProperty (property, newValue, undoManager); } private: ValueTree tree; @@ -1003,9 +1003,16 @@ ValueTree ValueTree::readFromStream (InputStream& input) for (int i = 0; i < numProps; ++i) { const String name (input.readString()); - jassert (name.isNotEmpty()); - const var value (var::readFromStream (input)); - v.object->properties.set (name, value); + + if (name.isNotEmpty()) + { + const var value (var::readFromStream (input)); + v.object->properties.set (name, value); + } + else + { + jassertfalse; // trying to read corrupted data! + } } const int numChildren = input.readCompressedInt(); @@ -1015,6 +1022,9 @@ ValueTree ValueTree::readFromStream (InputStream& input) { ValueTree child (readFromStream (input)); + if (! child.isValid()) + return v; + v.object->children.add (child.object); child.object->parent = v.object; } diff --git a/source/modules/juce_events/messages/juce_ApplicationBase.cpp b/source/modules/juce_events/messages/juce_ApplicationBase.cpp index dafc27e66..8bbda9baf 100644 --- a/source/modules/juce_events/messages/juce_ApplicationBase.cpp +++ b/source/modules/juce_events/messages/juce_ApplicationBase.cpp @@ -232,7 +232,7 @@ int JUCEApplicationBase::main() jassert (app != nullptr); if (! app->initialiseApp()) - return app->getApplicationReturnValue(); + return app->shutdownApp(); JUCE_TRY { diff --git a/source/modules/juce_events/messages/juce_ApplicationBase.h b/source/modules/juce_events/messages/juce_ApplicationBase.h index 073e5d6c7..733501980 100644 --- a/source/modules/juce_events/messages/juce_ApplicationBase.h +++ b/source/modules/juce_events/messages/juce_ApplicationBase.h @@ -259,6 +259,7 @@ public: static CreateInstanceFunction createInstance; virtual bool initialiseApp(); + int shutdownApp(); static void JUCE_CALLTYPE sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); bool sendCommandLineToPreexistingInstance(); #endif @@ -274,8 +275,6 @@ private: friend struct ContainerDeletePolicy; ScopedPointer multipleInstanceHandler; - int shutdownApp(); - JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase) }; diff --git a/source/modules/juce_events/native/juce_ios_MessageManager.mm b/source/modules/juce_events/native/juce_ios_MessageManager.mm index bdfed6599..1c53b49bf 100644 --- a/source/modules/juce_events/native/juce_ios_MessageManager.mm +++ b/source/modules/juce_events/native/juce_ios_MessageManager.mm @@ -25,7 +25,15 @@ void MessageManager::runDispatchLoop() { jassert (isThisTheMessageThread()); // must only be called by the message thread - runDispatchLoopUntil (-1); + + while (! quitMessagePosted) + { + JUCE_AUTORELEASEPOOL + { + [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode + beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]]; + } + } } void MessageManager::stopDispatchLoop() @@ -34,6 +42,7 @@ void MessageManager::stopDispatchLoop() exit (0); // iOS apps get no mercy.. } +#if JUCE_MODAL_LOOPS_PERMITTED bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { JUCE_AUTORELEASEPOOL @@ -59,6 +68,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) return ! quitMessagePosted; } } +#endif //============================================================================== static ScopedPointer messageQueue; diff --git a/source/modules/juce_events/native/juce_linux_Messaging.cpp b/source/modules/juce_events/native/juce_linux_Messaging.cpp index 8134b26e5..6e17abb59 100644 --- a/source/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/source/modules/juce_events/native/juce_linux_Messaging.cpp @@ -74,7 +74,7 @@ public: ScopedUnlock ul (lock); const unsigned char x = 0xff; - size_t bytesWritten = write (fd[0], &x, 1); + ssize_t bytesWritten = write (fd[0], &x, 1); (void) bytesWritten; } } @@ -186,7 +186,7 @@ private: const ScopedUnlock ul (lock); unsigned char x; - size_t numBytes = read (fd[1], &x, 1); + ssize_t numBytes = read (fd[1], &x, 1); (void) numBytes; } @@ -235,6 +235,8 @@ namespace LinuxErrorHandling int errorHandler (Display* display, XErrorEvent* event) { + (void) display; (void) event; + #if JUCE_DEBUG_XERRORS char errorStr[64] = { 0 }; char requestStr[64] = { 0 }; diff --git a/source/modules/juce_events/native/juce_mac_MessageManager.mm b/source/modules/juce_events/native/juce_mac_MessageManager.mm index 0090d0bd4..928abfd4d 100644 --- a/source/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/source/modules/juce_events/native/juce_mac_MessageManager.mm @@ -261,11 +261,13 @@ static void shutdownNSApp() void MessageManager::stopDispatchLoop() { + #if JUCE_PROJUCER_LIVE_BUILD quitMessagePosted = true; + #else - #if ! JUCE_PROJUCER_LIVE_BUILD if (isThisTheMessageThread()) { + quitMessagePosted = true; shutdownNSApp(); } else @@ -273,7 +275,7 @@ void MessageManager::stopDispatchLoop() struct QuitCallback : public CallbackMessage { QuitCallback() {} - void messageCallback() override { shutdownNSApp(); } + void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); } }; (new QuitCallback())->post(); diff --git a/source/modules/juce_events/timers/juce_Timer.cpp b/source/modules/juce_events/timers/juce_Timer.cpp index dfde957a7..25037dfc8 100644 --- a/source/modules/juce_events/timers/juce_Timer.cpp +++ b/source/modules/juce_events/timers/juce_Timer.cpp @@ -112,10 +112,10 @@ public: { const LockType::ScopedLockType sl (lock); - while (firstTimer != nullptr && firstTimer->countdownMs <= 0) + while (firstTimer != nullptr && firstTimer->timerCountdownMs <= 0) { Timer* const t = firstTimer; - t->countdownMs = t->periodMs; + t->timerCountdownMs = t->timerPeriodMs; removeTimer (t); addTimer (t); @@ -169,11 +169,11 @@ public: { if (instance != nullptr) { - tim->countdownMs = newCounter; - tim->periodMs = newCounter; + tim->timerCountdownMs = newCounter; + tim->timerPeriodMs = newCounter; - if ((tim->next != nullptr && tim->next->countdownMs < tim->countdownMs) - || (tim->previous != nullptr && tim->previous->countdownMs > tim->countdownMs)) + if ((tim->nextTimer != nullptr && tim->nextTimer->timerCountdownMs < tim->timerCountdownMs) + || (tim->previousTimer != nullptr && tim->previousTimer->timerCountdownMs > tim->timerCountdownMs)) { instance->removeTimer (tim); instance->addTimer (tim); @@ -210,28 +210,28 @@ private: Timer* i = firstTimer; - if (i == nullptr || i->countdownMs > t->countdownMs) + if (i == nullptr || i->timerCountdownMs > t->timerCountdownMs) { - t->next = firstTimer; + t->nextTimer = firstTimer; firstTimer = t; } else { - while (i->next != nullptr && i->next->countdownMs <= t->countdownMs) - i = i->next; + while (i->nextTimer != nullptr && i->nextTimer->timerCountdownMs <= t->timerCountdownMs) + i = i->nextTimer; jassert (i != nullptr); - t->next = i->next; - t->previous = i; - i->next = t; + t->nextTimer = i->nextTimer; + t->previousTimer = i; + i->nextTimer = t; } - if (t->next != nullptr) - t->next->previous = t; + if (t->nextTimer != nullptr) + t->nextTimer->previousTimer = t; - jassert ((t->next == nullptr || t->next->countdownMs >= t->countdownMs) - && (t->previous == nullptr || t->previous->countdownMs <= t->countdownMs)); + jassert ((t->nextTimer == nullptr || t->nextTimer->timerCountdownMs >= t->timerCountdownMs) + && (t->previousTimer == nullptr || t->previousTimer->timerCountdownMs <= t->timerCountdownMs)); notify(); } @@ -244,32 +244,32 @@ private: jassert (timerExists (t)); #endif - if (t->previous != nullptr) + if (t->previousTimer != nullptr) { jassert (firstTimer != t); - t->previous->next = t->next; + t->previousTimer->nextTimer = t->nextTimer; } else { jassert (firstTimer == t); - firstTimer = t->next; + firstTimer = t->nextTimer; } - if (t->next != nullptr) - t->next->previous = t->previous; + if (t->nextTimer != nullptr) + t->nextTimer->previousTimer = t->previousTimer; - t->next = nullptr; - t->previous = nullptr; + t->nextTimer = nullptr; + t->previousTimer = nullptr; } int getTimeUntilFirstTimer (const int numMillisecsElapsed) const { const LockType::ScopedLockType sl (lock); - for (Timer* t = firstTimer; t != nullptr; t = t->next) - t->countdownMs -= numMillisecsElapsed; + for (Timer* t = firstTimer; t != nullptr; t = t->nextTimer) + t->timerCountdownMs -= numMillisecsElapsed; - return firstTimer != nullptr ? firstTimer->countdownMs : 1000; + return firstTimer != nullptr ? firstTimer->timerCountdownMs : 1000; } void handleAsyncUpdate() override @@ -280,7 +280,7 @@ private: #if JUCE_DEBUG bool timerExists (Timer* const t) const noexcept { - for (Timer* tt = firstTimer; tt != nullptr; tt = tt->next) + for (Timer* tt = firstTimer; tt != nullptr; tt = tt->nextTimer) if (tt == t) return true; @@ -296,18 +296,18 @@ Timer::TimerThread::LockType Timer::TimerThread::lock; //============================================================================== Timer::Timer() noexcept - : countdownMs (0), - periodMs (0), - previous (nullptr), - next (nullptr) + : timerCountdownMs (0), + timerPeriodMs (0), + previousTimer (nullptr), + nextTimer (nullptr) { } Timer::Timer (const Timer&) noexcept - : countdownMs (0), - periodMs (0), - previous (nullptr), - next (nullptr) + : timerCountdownMs (0), + timerPeriodMs (0), + previousTimer (nullptr), + nextTimer (nullptr) { } @@ -320,10 +320,10 @@ void Timer::startTimer (const int interval) noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - if (periodMs == 0) + if (timerPeriodMs == 0) { - countdownMs = interval; - periodMs = jmax (1, interval); + timerCountdownMs = interval; + timerPeriodMs = jmax (1, interval); TimerThread::add (this); } else @@ -344,10 +344,10 @@ void Timer::stopTimer() noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - if (periodMs > 0) + if (timerPeriodMs > 0) { TimerThread::remove (this); - periodMs = 0; + timerPeriodMs = 0; } } diff --git a/source/modules/juce_events/timers/juce_Timer.h b/source/modules/juce_events/timers/juce_Timer.h index 57b0ce821..859cfc644 100644 --- a/source/modules/juce_events/timers/juce_Timer.h +++ b/source/modules/juce_events/timers/juce_Timer.h @@ -54,7 +54,6 @@ class JUCE_API Timer protected: //============================================================================== /** Creates a Timer. - When created, the timer is stopped, so use startTimer() to get it going. */ Timer() noexcept; @@ -64,7 +63,7 @@ protected: Note that this timer won't be started, even if the one you're copying is running. */ - Timer (const Timer& other) noexcept; + Timer (const Timer&) noexcept; public: //============================================================================== @@ -86,8 +85,8 @@ public: time between calling this method and the next timer callback will not be less than the interval length passed in. - @param intervalInMilliseconds the interval to use (any values less than 1 will be - rounded up to 1) + @param intervalInMilliseconds the interval to use (any value less + than 1 will be rounded up to 1) */ void startTimer (int intervalInMilliseconds) noexcept; @@ -108,12 +107,12 @@ public: //============================================================================== /** Returns true if the timer is currently running. */ - bool isTimerRunning() const noexcept { return periodMs > 0; } + bool isTimerRunning() const noexcept { return timerPeriodMs > 0; } /** Returns the timer's interval. @returns the timer's interval in milliseconds if it's running, or 0 if it's not. */ - int getTimerInterval() const noexcept { return periodMs; } + int getTimerInterval() const noexcept { return timerPeriodMs; } //============================================================================== @@ -125,11 +124,10 @@ public: private: class TimerThread; friend class TimerThread; - int countdownMs, periodMs; - Timer* previous; - Timer* next; + int timerCountdownMs, timerPeriodMs; // NB: these member variable names are a little verbose + Timer* previousTimer, *nextTimer; // to reduce risk of name-clashes with user subclasses - Timer& operator= (const Timer&); + Timer& operator= (const Timer&) JUCE_DELETED_FUNCTION; }; #endif // JUCE_TIMER_H_INCLUDED diff --git a/source/modules/juce_graphics/colour/juce_Colour.cpp b/source/modules/juce_graphics/colour/juce_Colour.cpp index aaf946865..efbffe14b 100644 --- a/source/modules/juce_graphics/colour/juce_Colour.cpp +++ b/source/modules/juce_graphics/colour/juce_Colour.cpp @@ -136,7 +136,7 @@ namespace ColourHelpers //============================================================================== Colour::Colour() noexcept - : argb (0) + : argb (0, 0, 0, 0) { } @@ -151,11 +151,12 @@ Colour& Colour::operator= (const Colour& other) noexcept return *this; } -bool Colour::operator== (const Colour& other) const noexcept { return argb.getARGB() == other.argb.getARGB(); } -bool Colour::operator!= (const Colour& other) const noexcept { return argb.getARGB() != other.argb.getARGB(); } +bool Colour::operator== (const Colour& other) const noexcept { return argb.getNativeARGB() == other.argb.getNativeARGB(); } +bool Colour::operator!= (const Colour& other) const noexcept { return argb.getNativeARGB() != other.argb.getNativeARGB(); } //============================================================================== -Colour::Colour (const uint32 col) noexcept : argb (col) +Colour::Colour (const uint32 col) noexcept + : argb ((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff) { } @@ -206,10 +207,26 @@ Colour::Colour (const float hue, const float saturation, const float brightness, { } +Colour::Colour (PixelARGB argb_) noexcept + : argb (argb_) +{ +} + +Colour::Colour (PixelRGB rgb) noexcept + : argb (Colour (rgb.getInARGBMaskOrder()).argb) +{ +} + +Colour::Colour (PixelAlpha alpha) noexcept + : argb (Colour (alpha.getInARGBMaskOrder()).argb) +{ +} + Colour::~Colour() noexcept { } + //============================================================================== const PixelARGB Colour::getPixelARGB() const noexcept { @@ -220,7 +237,7 @@ const PixelARGB Colour::getPixelARGB() const noexcept uint32 Colour::getARGB() const noexcept { - return argb.getARGB(); + return argb.getInARGBMaskOrder(); } //============================================================================== @@ -238,7 +255,7 @@ Colour Colour::withAlpha (const uint8 newAlpha) const noexcept { PixelARGB newCol (argb); newCol.setAlpha (newAlpha); - return Colour (newCol.getARGB()); + return Colour (newCol); } Colour Colour::withAlpha (const float newAlpha) const noexcept @@ -247,7 +264,7 @@ Colour Colour::withAlpha (const float newAlpha) const noexcept PixelARGB newCol (argb); newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha)); - return Colour (newCol.getARGB()); + return Colour (newCol); } Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept @@ -256,7 +273,7 @@ Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept PixelARGB newCol (argb); newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha()))); - return Colour (newCol.getARGB()); + return Colour (newCol); } //============================================================================== @@ -294,7 +311,7 @@ Colour Colour::interpolatedWith (Colour other, float proportionOfOther) const no c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f)); c1.unpremultiply(); - return Colour (c1.getARGB()); + return Colour (c1); } //============================================================================== @@ -428,7 +445,7 @@ Colour Colour::contrasting (Colour colour1, //============================================================================== String Colour::toString() const { - return String::toHexString ((int) argb.getARGB()); + return String::toHexString ((int) argb.getInARGBMaskOrder()); } Colour Colour::fromString (StringRef encodedColourString) @@ -438,7 +455,7 @@ Colour Colour::fromString (StringRef encodedColourString) String Colour::toDisplayString (const bool includeAlphaValue) const { - return String::toHexString ((int) (argb.getARGB() & (includeAlphaValue ? 0xffffffff : 0xffffff))) + return String::toHexString ((int) (argb.getInARGBMaskOrder() & (includeAlphaValue ? 0xffffffff : 0xffffff))) .paddedLeft ('0', includeAlphaValue ? 8 : 6) .toUpperCase(); } diff --git a/source/modules/juce_graphics/colour/juce_Colour.h b/source/modules/juce_graphics/colour/juce_Colour.h index 4d845048f..3b074fab2 100644 --- a/source/modules/juce_graphics/colour/juce_Colour.h +++ b/source/modules/juce_graphics/colour/juce_Colour.h @@ -115,6 +115,19 @@ public: float brightness, float alpha) noexcept; + /** Creates a colour using a PixelARGB object. This function assumes that the argb pixel is + not premultiplied. + */ + Colour (PixelARGB argb) noexcept; + + /** Creates a colour using a PixelRGB object. + */ + Colour (PixelRGB rgb) noexcept; + + /** Creates a colour using a PixelAlpha object. + */ + Colour (PixelAlpha alpha) noexcept; + /** Creates a colour using floating point hue, saturation and brightness values, and an 8-bit alpha. The floating point values must be between 0.0 and 1.0. diff --git a/source/modules/juce_graphics/colour/juce_PixelFormats.h b/source/modules/juce_graphics/colour/juce_PixelFormats.h index 2715cb2ed..beb9c6011 100644 --- a/source/modules/juce_graphics/colour/juce_PixelFormats.h +++ b/source/modules/juce_graphics/colour/juce_PixelFormats.h @@ -46,7 +46,7 @@ inline uint32 clampPixelComponents (uint32 x) noexcept //============================================================================== /** - Represents a 32-bit ARGB pixel with premultiplied alpha, and can perform compositing + Represents a 32-bit INTERNAL pixel with premultiplied alpha, and can perform compositing operations with it. This is used internally by the imaging classes. @@ -60,13 +60,6 @@ public: PixelARGB() noexcept {} ~PixelARGB() noexcept {} - /** Creates a pixel from a 32-bit argb value. - */ - PixelARGB (const uint32 argbValue) noexcept - : argb (argbValue) - { - } - PixelARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept { components.b = b; @@ -75,30 +68,81 @@ public: components.a = a; } - forcedinline uint32 getARGB() const noexcept { return argb; } - forcedinline uint32 getUnpremultipliedARGB() const noexcept { PixelARGB p (argb); p.unpremultiply(); return p.getARGB(); } + //============================================================================== + /** Returns a uint32 which represents the pixel in a platform dependent format. */ + forcedinline uint32 getNativeARGB() const noexcept { return internal; } + + /** Returns a uint32 which will be in argb order as if constructed with the following mask operation + ((alpha << 24) | (red << 16) | (green << 8) | blue). */ + forcedinline uint32 getInARGBMaskOrder() const noexcept + { + #if JUCE_ANDROID + return (uint32) ((components.a << 24) | (components.r << 16) | (components.g << 8) | (components.b << 0)); + #else + return getNativeARGB(); + #endif + } + + /** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, + if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ + inline uint32 getInARGBMemoryOrder() const noexcept + { + #if JUCE_BIG_ENDIAN + return getInARGBMaskOrder(); + #else + return (uint32) ((components.b << 24) | (components.g << 16) | (components.r << 8) | components.a); + #endif + } + + /** Return channels with an even index and insert zero bytes between them. This is useful for blending + operations. The exact channels which are returned is platform dependent. */ + forcedinline uint32 getEvenBytes() const noexcept { return 0x00ff00ff & internal; } - forcedinline uint32 getRB() const noexcept { return 0x00ff00ff & argb; } - forcedinline uint32 getAG() const noexcept { return 0x00ff00ff & (argb >> 8); } + /** Return channels with an odd index and insert zero bytes between them. This is useful for blending + operations. The exact channels which are returned is platform dependent. */ + forcedinline uint32 getOddBytes() const noexcept { return 0x00ff00ff & (internal >> 8); } - forcedinline uint8 getAlpha() const noexcept { return components.a; } - forcedinline uint8 getRed() const noexcept { return components.r; } - forcedinline uint8 getGreen() const noexcept { return components.g; } - forcedinline uint8 getBlue() const noexcept { return components.b; } + //============================================================================== + forcedinline uint8 getAlpha() const noexcept { return components.a; } + forcedinline uint8 getRed() const noexcept { return components.r; } + forcedinline uint8 getGreen() const noexcept { return components.g; } + forcedinline uint8 getBlue() const noexcept { return components.b; } #if JUCE_GCC && ! JUCE_CLANG // NB these are here as a workaround because GCC refuses to bind to packed values. - forcedinline uint8& getAlpha() noexcept { return comps [indexA]; } - forcedinline uint8& getRed() noexcept { return comps [indexR]; } - forcedinline uint8& getGreen() noexcept { return comps [indexG]; } - forcedinline uint8& getBlue() noexcept { return comps [indexB]; } + forcedinline uint8& getAlpha() noexcept { return comps [indexA]; } + forcedinline uint8& getRed() noexcept { return comps [indexR]; } + forcedinline uint8& getGreen() noexcept { return comps [indexG]; } + forcedinline uint8& getBlue() noexcept { return comps [indexB]; } #else - forcedinline uint8& getAlpha() noexcept { return components.a; } - forcedinline uint8& getRed() noexcept { return components.r; } - forcedinline uint8& getGreen() noexcept { return components.g; } - forcedinline uint8& getBlue() noexcept { return components.b; } + forcedinline uint8& getAlpha() noexcept { return components.a; } + forcedinline uint8& getRed() noexcept { return components.r; } + forcedinline uint8& getGreen() noexcept { return components.g; } + forcedinline uint8& getBlue() noexcept { return components.b; } #endif + //============================================================================== + /** Copies another pixel colour over this one. + + This doesn't blend it - this colour is simply replaced by the other one. + */ + template + forcedinline void set (const Pixel& src) noexcept + { + internal = src.getNativeARGB(); + } + + //============================================================================== + /** Sets the pixel's colour from individual components. */ + void setARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept + { + components.b = b; + components.g = g; + components.r = r; + components.a = a; + } + + //============================================================================== /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends @@ -107,10 +151,15 @@ public: template forcedinline void blend (const Pixel& src) noexcept { - const uint32 alpha = 0x100 - src.getAlpha(); - uint32 rb = src.getRB() + maskPixelComponents (getRB() * alpha); - uint32 ag = src.getAG() + maskPixelComponents (getAG() * alpha); - argb = clampPixelComponents (rb) + (clampPixelComponents (ag) << 8); + uint32 rb = src.getEvenBytes(); + uint32 ag = src.getOddBytes(); + + const uint32 alpha = 0x100 - (ag >> 16); + + rb += maskPixelComponents (getEvenBytes() * alpha); + ag += maskPixelComponents (getOddBytes() * alpha); + + internal = clampPixelComponents (rb) | (clampPixelComponents (ag) << 8); } /** Blends another pixel onto this one. @@ -129,14 +178,15 @@ public: template forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept { - uint32 ag = maskPixelComponents (extraAlpha * src.getAG()); + uint32 rb = maskPixelComponents (extraAlpha * src.getEvenBytes()); + uint32 ag = maskPixelComponents (extraAlpha * src.getOddBytes()); + const uint32 alpha = 0x100 - (ag >> 16); - ag += maskPixelComponents (getAG() * alpha); - uint32 rb = maskPixelComponents (extraAlpha * src.getRB()) - + maskPixelComponents (getRB() * alpha); + rb += maskPixelComponents (getEvenBytes() * alpha); + ag += maskPixelComponents (getOddBytes() * alpha); - argb = clampPixelComponents(rb) + (clampPixelComponents (ag) << 8); + internal = clampPixelComponents (rb) | (clampPixelComponents (ag) << 8); } /** Blends another pixel with this one, creating a colour that is somewhere @@ -145,29 +195,20 @@ public: template forcedinline void tween (const Pixel& src, const uint32 amount) noexcept { - uint32 drb = getRB(); - drb += (((src.getRB() - drb) * amount) >> 8); - drb &= 0x00ff00ff; - - uint32 dag = getAG(); - dag += (((src.getAG() - dag) * amount) >> 8); - dag &= 0x00ff00ff; - dag <<= 8; + uint32 dEvenBytes = getEvenBytes(); + dEvenBytes += (((src.getEvenBytes() - dEvenBytes) * amount) >> 8); + dEvenBytes &= 0x00ff00ff; - dag |= drb; - argb = dag; - } + uint32 dOddBytes = getOddBytes(); + dOddBytes += (((src.getOddBytes() - dOddBytes) * amount) >> 8); + dOddBytes &= 0x00ff00ff; + dOddBytes <<= 8; - /** Copies another pixel colour over this one. - - This doesn't blend it - this colour is simply replaced by the other one. - */ - template - forcedinline void set (const Pixel& src) noexcept - { - argb = src.getARGB(); + dOddBytes |= dEvenBytes; + internal = dOddBytes; } + //============================================================================== /** Replaces the colour's alpha value with another one. */ forcedinline void setAlpha (const uint8 newAlpha) noexcept { @@ -177,10 +218,12 @@ public: /** Multiplies the colour's alpha value with another one. */ forcedinline void multiplyAlpha (int multiplier) noexcept { + // increment alpha by 1, so that if multiplier == 255 (full alpha), + // this function will not change the values. ++multiplier; - argb = ((((uint32) multiplier) * getAG()) & 0xff00ff00) - | (((((uint32) multiplier) * getRB()) >> 8) & 0x00ff00ff); + internal = ((((uint32) multiplier) * getOddBytes()) & 0xff00ff00) + | (((((uint32) multiplier) * getEvenBytes()) >> 8) & 0x00ff00ff); } forcedinline void multiplyAlpha (const float multiplier) noexcept @@ -188,14 +231,8 @@ public: multiplyAlpha ((int) (multiplier * 255.0f)); } - /** Sets the pixel's colour from individual components. */ - void setARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept - { - components.b = b; - components.g = g; - components.r = r; - components.a = a; - } + + inline PixelARGB getUnpremultiplied() const noexcept { PixelARGB p (internal); p.unpremultiply(); return p; } /** Premultiplies the pixel's RGB values by its alpha. */ forcedinline void premultiply() noexcept @@ -234,9 +271,9 @@ public: } else { - components.b = (uint8) jmin ((uint32) 0xff, (components.b * 0xff) / alpha); - components.g = (uint8) jmin ((uint32) 0xff, (components.g * 0xff) / alpha); - components.r = (uint8) jmin ((uint32) 0xff, (components.r * 0xff) / alpha); + components.b = (uint8) jmin ((uint32) 0xffu, (components.b * 0xffu) / alpha); + components.g = (uint8) jmin ((uint32) 0xffu, (components.g * 0xffu) / alpha); + components.r = (uint8) jmin ((uint32) 0xffu, (components.r * 0xffu) / alpha); } } } @@ -257,41 +294,53 @@ public: } } - /** Returns a uint32 which when written to memory, will be in the order r, g, b, a. */ - inline uint32 getInRGBAMemoryOrder() const noexcept - { - #if JUCE_BIG_ENDIAN - return (((uint32) components.r) << 24) | (((uint32) components.g) << 16) | (((uint32) components.b) << 8) | components.a; - #else - return (((uint32) components.a) << 24) | (((uint32) components.b) << 16) | (((uint32) components.g) << 8) | components.r; - #endif - } - //============================================================================== /** The indexes of the different components in the byte layout of this type of colour. */ + #if JUCE_ANDROID + #if JUCE_BIG_ENDIAN + enum { indexA = 0, indexR = 3, indexG = 2, indexB = 1 }; + #else + enum { indexA = 3, indexR = 0, indexG = 1, indexB = 2 }; + #endif + #else #if JUCE_BIG_ENDIAN enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 }; #else enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 }; #endif + #endif private: + //============================================================================== + PixelARGB (const uint32 internalValue) noexcept + : internal (internalValue) + { + } + //============================================================================== struct Components { + #if JUCE_ANDROID + #if JUCE_BIG_ENDIAN + uint8 a, b, g, r; + #else + uint8 r, g, b, a; + #endif + #else #if JUCE_BIG_ENDIAN uint8 a, r, g, b; #else uint8 b, g, r, a; #endif + #endif } JUCE_PACKED; union { - uint32 argb; + uint32 internal; Components components; #if JUCE_GCC - uint8 comps[4]; + uint8 comps[4]; // helper struct needed because gcc does not allow references to packed union members #endif }; } @@ -316,23 +365,64 @@ public: PixelRGB() noexcept {} ~PixelRGB() noexcept {} - /** Creates a pixel from a 32-bit argb value. + //============================================================================== + /** Returns a uint32 which represents the pixel in a platform dependent format which is compatible + with the native format of a PixelARGB. - (The argb format is that used by PixelARGB) - */ - PixelRGB (const uint32 argb) noexcept + @see PixelARGB::getNativeARGB */ + forcedinline uint32 getNativeARGB() const noexcept + { + #if JUCE_ANDROID + return (uint32) ((0xff << 24) | r | (g << 8) | (b << 16)); + #else + return (uint32) ((0xff << 24) | b | (g << 8) | (r << 16)); + #endif + } + + /** Returns a uint32 which will be in argb order as if constructed with the following mask operation + ((alpha << 24) | (red << 16) | (green << 8) | blue). */ + forcedinline uint32 getInARGBMaskOrder() const noexcept + { + #if JUCE_ANDROID + return (uint32) ((0xff << 24) | (r << 16) | (g << 8) | (b << 0)); + #else + return getNativeARGB(); + #endif + } + + /** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, + if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ + inline uint32 getInARGBMemoryOrder() const noexcept { - r = (uint8) (argb >> 16); - g = (uint8) (argb >> 8); - b = (uint8) (argb); + #if JUCE_BIG_ENDIAN + return getInARGBMaskOrder(); + #else + return (uint32) ((b << 24) | (g << 16) | (r << 8) | 0xff); + #endif } - forcedinline uint32 getARGB() const noexcept { return 0xff000000 | b | (((uint32) g) << 8) | (((uint32) r) << 16); } - forcedinline uint32 getUnpremultipliedARGB() const noexcept { return getARGB(); } + /** Return channels with an even index and insert zero bytes between them. This is useful for blending + operations. The exact channels which are returned is platform dependent but compatible with the + return value of getEvenBytes of the PixelARGB class. - forcedinline uint32 getRB() const noexcept { return b | (uint32) (r << 16); } - forcedinline uint32 getAG() const noexcept { return (uint32) (0xff0000 | g); } + @see PixelARGB::getEvenBytes */ + forcedinline uint32 getEvenBytes() const noexcept + { + #if JUCE_ANDROID + return (uint32) (r | (b << 16)); + #else + return (uint32) (b | (r << 16)); + #endif + } + /** Return channels with an odd index and insert zero bytes between them. This is useful for blending + operations. The exact channels which are returned is platform dependent but compatible with the + return value of getOddBytes of the PixelARGB class. + + @see PixelARGB::getOddBytes */ + forcedinline uint32 getOddBytes() const noexcept { return (uint32)0xff0000 | g; } + + //============================================================================== forcedinline uint8 getAlpha() const noexcept { return 0xff; } forcedinline uint8 getRed() const noexcept { return r; } forcedinline uint8 getGreen() const noexcept { return g; } @@ -342,6 +432,30 @@ public: forcedinline uint8& getGreen() noexcept { return g; } forcedinline uint8& getBlue() noexcept { return b; } + //============================================================================== + /** Copies another pixel colour over this one. + + This doesn't blend it - this colour is simply replaced by the other one. + Because PixelRGB has no alpha channel, any alpha value in the source pixel + is thrown away. + */ + template + forcedinline void set (const Pixel& src) noexcept + { + b = src.getBlue(); + g = src.getGreen(); + r = src.getRed(); + } + + /** Sets the pixel's colour from individual components. */ + void setARGB (const uint8, const uint8 red, const uint8 green, const uint8 blue) noexcept + { + r = red; + g = green; + b = blue; + } + + //============================================================================== /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends @@ -350,14 +464,22 @@ public: template forcedinline void blend (const Pixel& src) noexcept { - const uint32 alpha = 0x100 - src.getAlpha(); + const uint32 alpha = (uint32) (0x100 - src.getAlpha()); + + // getEvenBytes returns 0x00rr00bb on non-android + uint32 rb = clampPixelComponents (src.getEvenBytes() + maskPixelComponents (getEvenBytes() * alpha)); + // getOddBytes returns 0x00aa00gg on non-android + uint32 ag = clampPixelComponents (src.getOddBytes() + ((g * alpha) >> 8)); - uint32 rb = clampPixelComponents (src.getRB() + maskPixelComponents (getRB() * alpha)); - uint32 ag = src.getAG() + (g * alpha >> 8); + g = (uint8) (ag & 0xff); + #if JUCE_ANDROID + b = (uint8) (rb >> 16); + r = (uint8) (rb & 0xff); + #else r = (uint8) (rb >> 16); - g = (uint8) clampPixelComponents (ag); - b = (uint8) rb; + b = (uint8) (rb & 0xff); + #endif } forcedinline void blend (const PixelRGB src) noexcept @@ -373,16 +495,23 @@ public: template forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept { - uint32 ag = maskPixelComponents (extraAlpha * src.getAG()); + uint32 ag = maskPixelComponents (extraAlpha * src.getOddBytes()); + uint32 rb = maskPixelComponents (extraAlpha * src.getEvenBytes()); + const uint32 alpha = 0x100 - (ag >> 16); - ag += g * alpha >> 8; - uint32 rb = clampPixelComponents (maskPixelComponents (extraAlpha * src.getRB()) - + maskPixelComponents (getRB() * alpha)); + ag = clampPixelComponents (ag + (g * alpha >> 8)); + rb = clampPixelComponents (rb + maskPixelComponents (getEvenBytes() * alpha)); + + g = (uint8) (ag & 0xff); - b = (uint8) rb; - g = (uint8) clampPixelComponents (ag); + #if JUCE_ANDROID + b = (uint8) (rb >> 16); + r = (uint8) (rb & 0xff); + #else r = (uint8) (rb >> 16); + b = (uint8) (rb & 0xff); + #endif } /** Blends another pixel with this one, creating a colour that is somewhere @@ -391,31 +520,24 @@ public: template forcedinline void tween (const Pixel& src, const uint32 amount) noexcept { - uint32 drb = getRB(); - drb += (((src.getRB() - drb) * amount) >> 8); + uint32 dEvenBytes = getEvenBytes(); + dEvenBytes += (((src.getEvenBytes() - dEvenBytes) * amount) >> 8); - uint32 dag = getAG(); - dag += (((src.getAG() - dag) * amount) >> 8); + uint32 dOddBytes = getOddBytes(); + dOddBytes += (((src.getOddBytes() - dOddBytes) * amount) >> 8); - b = (uint8) drb; - g = (uint8) dag; - r = (uint8) (drb >> 16); - } + g = (uint8) (dOddBytes & 0xff); // dOddBytes = 0x00aa00gg - /** Copies another pixel colour over this one. - - This doesn't blend it - this colour is simply replaced by the other one. - Because PixelRGB has no alpha channel, any alpha value in the source pixel - is thrown away. - */ - template - forcedinline void set (const Pixel& src) noexcept - { - b = src.getBlue(); - g = src.getGreen(); - r = src.getRed(); + #if JUCE_ANDROID + r = (uint8) (dEvenBytes & 0xff); // dEvenBytes = 0x00bb00rr + b = (uint8) (dEvenBytes >> 16); + #else + b = (uint8) (dEvenBytes & 0xff); // dEvenBytes = 0x00rr00bb + r = (uint8) (dEvenBytes >> 16); + #endif } + //============================================================================== /** This method is included for compatibility with the PixelARGB class. */ forcedinline void setAlpha (const uint8) noexcept {} @@ -425,14 +547,6 @@ public: /** Multiplies the colour's alpha value with another one. */ forcedinline void multiplyAlpha (float) noexcept {} - /** Sets the pixel's colour from individual components. */ - void setARGB (const uint8, const uint8 red, const uint8 green, const uint8 blue) noexcept - { - r = red; - g = green; - b = blue; - } - /** Premultiplies the pixel's RGB values by its alpha. */ forcedinline void premultiply() noexcept {} @@ -454,6 +568,20 @@ public: private: //============================================================================== + PixelRGB (const uint32 internal) noexcept + { + #if JUCE_ANDROID + b = (uint8) (internal >> 16); + g = (uint8) (internal >> 8); + r = (uint8) (internal); + #else + r = (uint8) (internal >> 16); + g = (uint8) (internal >> 8); + b = (uint8) (internal); + #endif + } + + //============================================================================== #if JUCE_MAC uint8 r, g, b; #else @@ -486,21 +614,36 @@ public: PixelAlpha() noexcept {} ~PixelAlpha() noexcept {} - /** Creates a pixel from a 32-bit argb value. + //============================================================================== + /** Returns a uint32 which represents the pixel in a platform dependent format which is compatible + with the native format of a PixelARGB. - (The argb format is that used by PixelARGB) - */ - PixelAlpha (const uint32 argb) noexcept - { - a = (uint8) (argb >> 24); - } + @see PixelARGB::getNativeARGB */ + forcedinline uint32 getNativeARGB() const noexcept { return (uint32) ((a << 24) | (a << 16) | (a << 8) | a); } - forcedinline uint32 getARGB() const noexcept { return (((uint32) a) << 24) | (((uint32) a) << 16) | (((uint32) a) << 8) | a; } - forcedinline uint32 getUnpremultipliedARGB() const noexcept { return (((uint32) a) << 24) | 0xffffff; } + /** Returns a uint32 which will be in argb order as if constructed with the following mask operation + ((alpha << 24) | (red << 16) | (green << 8) | blue). */ + forcedinline uint32 getInARGBMaskOrder() const noexcept { return getNativeARGB(); } - forcedinline uint32 getRB() const noexcept { return (((uint32) a) << 16) | a; } - forcedinline uint32 getAG() const noexcept { return (((uint32) a) << 16) | a; } + /** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, + if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ + inline uint32 getInARGBMemoryOrder() const noexcept { return getNativeARGB(); } + /** Return channels with an even index and insert zero bytes between them. This is useful for blending + operations. The exact channels which are returned is platform dependent but compatible with the + return value of getEvenBytes of the PixelARGB class. + + @see PixelARGB::getEvenBytes */ + forcedinline uint32 getEvenBytes() const noexcept { return (uint32) ((a << 16) | a); } + + /** Return channels with an odd index and insert zero bytes between them. This is useful for blending + operations. The exact channels which are returned is platform dependent but compatible with the + return value of getOddBytes of the PixelARGB class. + + @see PixelARGB::getOddBytes */ + forcedinline uint32 getOddBytes() const noexcept { return (uint32) ((a << 16) | a); } + + //============================================================================== forcedinline uint8 getAlpha() const noexcept { return a; } forcedinline uint8& getAlpha() noexcept { return a; } @@ -508,6 +651,24 @@ public: forcedinline uint8 getGreen() const noexcept { return 0; } forcedinline uint8 getBlue() const noexcept { return 0; } + //============================================================================== + /** Copies another pixel colour over this one. + + This doesn't blend it - this colour is simply replaced by the other one. + */ + template + forcedinline void set (const Pixel& src) noexcept + { + a = src.getAlpha(); + } + + /** Sets the pixel's colour from individual components. */ + forcedinline void setARGB (const uint8 a_, const uint8 /*r*/, const uint8 /*g*/, const uint8 /*b*/) noexcept + { + a = a_; + } + + //============================================================================== /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends @@ -542,16 +703,7 @@ public: a += ((src.getAlpha() - a) * amount) >> 8; } - /** Copies another pixel colour over this one. - - This doesn't blend it - this colour is simply replaced by the other one. - */ - template - forcedinline void set (const Pixel& src) noexcept - { - a = src.getAlpha(); - } - + //============================================================================== /** Replaces the colour's alpha value with another one. */ forcedinline void setAlpha (const uint8 newAlpha) noexcept { @@ -570,12 +722,6 @@ public: a = (uint8) (a * multiplier); } - /** Sets the pixel's colour from individual components. */ - forcedinline void setARGB (const uint8 a_, const uint8 /*r*/, const uint8 /*g*/, const uint8 /*b*/) noexcept - { - a = a_; - } - /** Premultiplies the pixel's RGB values by its alpha. */ forcedinline void premultiply() noexcept {} @@ -589,6 +735,12 @@ public: enum { indexA = 0 }; private: + //============================================================================== + PixelAlpha (const uint32 internal) noexcept + { + a = (uint8) (internal >> 24); + } + //============================================================================== uint8 a; } diff --git a/source/modules/juce_graphics/contexts/juce_GraphicsContext.h b/source/modules/juce_graphics/contexts/juce_GraphicsContext.h index ed0bc404b..ff9446626 100644 --- a/source/modules/juce_graphics/contexts/juce_GraphicsContext.h +++ b/source/modules/juce_graphics/contexts/juce_GraphicsContext.h @@ -203,7 +203,7 @@ public: The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you - can set this value to 1.0f. + can set this value to 1.0f. Pass 0 if you want it to use a default value. @see GlyphArrangement::addFittedText */ @@ -211,7 +211,7 @@ public: int x, int y, int width, int height, Justification justificationFlags, int maximumNumberOfLines, - float minimumHorizontalScale = 0.7f) const; + float minimumHorizontalScale = 0.0f) const; /** Tries to draw a text string inside a given space. @@ -228,7 +228,7 @@ public: The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you - can set this value to 1.0f. + can set this value to 1.0f. Pass 0 if you want it to use a default value. @see GlyphArrangement::addFittedText */ @@ -236,7 +236,7 @@ public: const Rectangle& area, Justification justificationFlags, int maximumNumberOfLines, - float minimumHorizontalScale = 0.7f) const; + float minimumHorizontalScale = 0.0f) const; //============================================================================== /** Fills the context's entire clip region with the current colour or brush. @@ -374,21 +374,29 @@ public: //============================================================================== /** Draws a line between two points. The line is 1 pixel wide and drawn with the current colour or brush. + TIP: If you're trying to draw horizontal or vertical lines, don't use this - + it's better to use fillRect() instead unless you really need an angled line. */ void drawLine (float startX, float startY, float endX, float endY) const; /** Draws a line between two points with a given thickness. + TIP: If you're trying to draw horizontal or vertical lines, don't use this - + it's better to use fillRect() instead unless you really need an angled line. @see Path::addLineSegment */ void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const; /** Draws a line between two points. The line is 1 pixel wide and drawn with the current colour or brush. + TIP: If you're trying to draw horizontal or vertical lines, don't use this - + it's better to use fillRect() instead unless you really need an angled line. */ void drawLine (const Line& line) const; /** Draws a line between two points with a given thickness. @see Path::addLineSegment + TIP: If you're trying to draw horizontal or vertical lines, don't use this - + it's better to use fillRect() instead unless you really need an angled line. */ void drawLine (const Line& line, float lineThickness) const; diff --git a/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index d963ed640..c159b4a06 100644 --- a/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -433,11 +433,11 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, { PixelARGB p (*(const PixelARGB*) pixelData); p.unpremultiply(); - pixel = Colours::white.overlaidWith (Colour (p.getARGB())); + pixel = Colours::white.overlaidWith (Colour (p)); } else if (im.isRGB()) { - pixel = Colour (((const PixelRGB*) pixelData)->getARGB()); + pixel = Colour (*((const PixelRGB*) pixelData)); } else { diff --git a/source/modules/juce_graphics/fonts/juce_Font.cpp b/source/modules/juce_graphics/fonts/juce_Font.cpp index f0668d1d7..d1c3d5c56 100644 --- a/source/modules/juce_graphics/fonts/juce_Font.cpp +++ b/source/modules/juce_graphics/fonts/juce_Font.cpp @@ -30,6 +30,7 @@ namespace FontValues } const float defaultFontHeight = 14.0f; + float minimumHorizontalScale = 0.7f; String fallbackFont; String fallbackFontStyle; } @@ -37,6 +38,9 @@ namespace FontValues typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&); GetTypefaceForFont juce_getTypefaceForFont = nullptr; +float Font::getDefaultMinimumHorizontalScaleFactor() noexcept { return FontValues::minimumHorizontalScale; } +void Font::setDefaultMinimumHorizontalScaleFactor (float newValue) noexcept { FontValues::minimumHorizontalScale = newValue; } + //============================================================================== class TypefaceCache : private DeletedAtShutdown { diff --git a/source/modules/juce_graphics/fonts/juce_Font.h b/source/modules/juce_graphics/fonts/juce_Font.h index 9888b2788..3da239d73 100644 --- a/source/modules/juce_graphics/fonts/juce_Font.h +++ b/source/modules/juce_graphics/fonts/juce_Font.h @@ -319,6 +319,18 @@ public: */ void setHorizontalScale (float scaleFactor); + /** Returns the minimum horizontal scale to which fonts may be squashed when trying to + create a layout. + @see setDefaultMinimumHorizontalScaleFactor + */ + static float getDefaultMinimumHorizontalScaleFactor() noexcept; + + /** Sets the minimum horizontal scale to which fonts may be squashed when trying to + create a text layout. + @see getDefaultMinimumHorizontalScaleFactor + */ + static void setDefaultMinimumHorizontalScaleFactor (float newMinimumScaleFactor) noexcept; + /** Returns the font's kerning. This is the extra space added between adjacent characters, as a proportion diff --git a/source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp b/source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp index 7b0de6ef5..895166859 100644 --- a/source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp +++ b/source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp @@ -366,8 +366,11 @@ void GlyphArrangement::addFittedText (const Font& f, const float width, const float height, Justification layout, int maximumLines, - const float minimumHorizontalScale) + float minimumHorizontalScale) { + if (minimumHorizontalScale == 0.0f) + minimumHorizontalScale = Font::getDefaultMinimumHorizontalScaleFactor(); + // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0 jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f); diff --git a/source/modules/juce_graphics/fonts/juce_GlyphArrangement.h b/source/modules/juce_graphics/fonts/juce_GlyphArrangement.h index c771bffe6..1749728cd 100644 --- a/source/modules/juce_graphics/fonts/juce_GlyphArrangement.h +++ b/source/modules/juce_graphics/fonts/juce_GlyphArrangement.h @@ -208,6 +208,10 @@ public: A Justification parameter lets you specify how the text is laid out within the rectangle, both horizontally and vertically. + The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally + to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you + can set this value to 1.0f. Pass 0 if you want it to use the default value. + @see Graphics::drawFittedText */ void addFittedText (const Font& font, @@ -215,7 +219,7 @@ public: float x, float y, float width, float height, Justification layout, int maximumLinesToUse, - float minimumHorizontalScale = 0.7f); + float minimumHorizontalScale = 0.0f); /** Appends another glyph arrangement to this one. */ void addGlyphArrangement (const GlyphArrangement&); diff --git a/source/modules/juce_graphics/fonts/juce_TextLayout.cpp b/source/modules/juce_graphics/fonts/juce_TextLayout.cpp index 1989bc7b7..f5073680c 100644 --- a/source/modules/juce_graphics/fonts/juce_TextLayout.cpp +++ b/source/modules/juce_graphics/fonts/juce_TextLayout.cpp @@ -635,4 +635,9 @@ void TextLayout::recalculateSize (const AttributedString& text) width = bounds.getWidth(); height = bounds.getHeight(); } + else + { + width = 0; + height = 0; + } } diff --git a/source/modules/juce_graphics/geometry/juce_Line.h b/source/modules/juce_graphics/geometry/juce_Line.h index 95665c523..b13ad0d18 100644 --- a/source/modules/juce_graphics/geometry/juce_Line.h +++ b/source/modules/juce_graphics/geometry/juce_Line.h @@ -224,8 +224,8 @@ public: if (length <= 0) return start; - return Point (start.x + static_cast ((delta.x * distanceFromStart - delta.y * perpendicularDistance) / length), - start.y + static_cast ((delta.y * distanceFromStart + delta.x * perpendicularDistance) / length)); + return Point (start.x + static_cast ((delta.x * distanceFromStart - delta.y * perpendicularDistance) / length), + start.y + static_cast ((delta.y * distanceFromStart + delta.x * perpendicularDistance) / length)); } /** Returns the location of the point which is a given distance along this line @@ -267,7 +267,7 @@ public: if (prop >= 0 && prop <= 1.0) { - pointOnLine = start + delta * static_cast (prop); + pointOnLine = start + delta * static_cast (prop); return targetPoint.getDistanceFrom (pointOnLine); } } @@ -301,9 +301,9 @@ public: const double length = delta.x * delta.x + delta.y * delta.y; return length <= 0 ? 0 - : jlimit (ValueType(), static_cast (1), - static_cast ((((point.x - start.x) * delta.x - + (point.y - start.y) * delta.y) / length))); + : jlimit (ValueType(), static_cast (1), + static_cast ((((point.x - start.x) * delta.x + + (point.y - start.y) * delta.y) / length))); } /** Finds the point on this line which is nearest to a given point. @@ -375,40 +375,40 @@ private: { const ValueType along = (p1.y - p3.y) / d2.y; intersection = p1.withX (p3.x + along * d2.x); - return along >= 0 && along <= static_cast (1); + return along >= 0 && along <= static_cast (1); } else if (d2.y == 0 && d1.y != 0) { const ValueType along = (p3.y - p1.y) / d1.y; intersection = p3.withX (p1.x + along * d1.x); - return along >= 0 && along <= static_cast (1); + return along >= 0 && along <= static_cast (1); } else if (d1.x == 0 && d2.x != 0) { const ValueType along = (p1.x - p3.x) / d2.x; intersection = p1.withY (p3.y + along * d2.y); - return along >= 0 && along <= static_cast (1); + return along >= 0 && along <= static_cast (1); } else if (d2.x == 0 && d1.x != 0) { const ValueType along = (p3.x - p1.x) / d1.x; intersection = p3.withY (p1.y + along * d1.y); - return along >= 0 && along <= static_cast (1); + return along >= 0 && along <= static_cast (1); } } - intersection = (p2 + p3) / static_cast (2); + intersection = (p2 + p3) / static_cast (2); return false; } const ValueType along1 = ((p1.y - p3.y) * d2.x - (p1.x - p3.x) * d2.y) / divisor; intersection = p1 + d1 * along1; - if (along1 < 0 || along1 > static_cast (1)) + if (along1 < 0 || along1 > static_cast (1)) return false; const ValueType along2 = ((p1.y - p3.y) * d1.x - (p1.x - p3.x) * d1.y) / divisor; - return along2 >= 0 && along2 <= static_cast (1); + return along2 >= 0 && along2 <= static_cast (1); } }; diff --git a/source/modules/juce_graphics/geometry/juce_Path.cpp b/source/modules/juce_graphics/geometry/juce_Path.cpp index 204181a61..0f204e68d 100644 --- a/source/modules/juce_graphics/geometry/juce_Path.cpp +++ b/source/modules/juce_graphics/geometry/juce_Path.cpp @@ -1567,17 +1567,17 @@ void Path::restoreFromString (StringRef stringVersion) } //============================================================================== -Path::Iterator::Iterator (const Path& path_) - : path (path_), - index (0) +Path::Iterator::Iterator (const Path& p) noexcept + : x1 (0), y1 (0), x2 (0), y2 (0), x3 (0), y3 (0), + path (p), index (0) { } -Path::Iterator::~Iterator() +Path::Iterator::~Iterator() noexcept { } -bool Path::Iterator::next() +bool Path::Iterator::next() noexcept { const float* const elements = path.data.elements; diff --git a/source/modules/juce_graphics/geometry/juce_Path.h b/source/modules/juce_graphics/geometry/juce_Path.h index 3dad5da1f..d2aeacae9 100644 --- a/source/modules/juce_graphics/geometry/juce_Path.h +++ b/source/modules/juce_graphics/geometry/juce_Path.h @@ -499,21 +499,20 @@ public: /** Adds a "pie-chart" shape to the path. - The shape is added as a new sub-path. (Any currently open paths will be - left open). + The shape is added as a new sub-path. (Any currently open paths will be left open). Note that when specifying the start and end angles, the curve will be drawn either clockwise or anti-clockwise according to whether the end angle is greater than the start. This means that sometimes you may need to use values greater than 2*Pi for the end angle. - @param area the outer rectangle in which the elliptical outline fits - @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the - top-centre of the ellipse) - @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the - top-centre of the ellipse) + @param segmentBounds the outer rectangle in which the elliptical outline fits + @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the + top-centre of the ellipse) + @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the + top-centre of the ellipse) @param innerCircleProportionalSize if this is > 0, then the pie will be drawn as a curved band around a hollow - ellipse at its centre, where this value indicates the inner ellipse's size with - respect to the outer one. + ellipse at its centre, where this value indicates the inner ellipse's size with + respect to the outer one. @see addArc */ void addPieSegment (Rectangle segmentBounds, @@ -718,8 +717,8 @@ public: { public: //============================================================================== - Iterator (const Path& path); - ~Iterator(); + Iterator (const Path& path) noexcept; + ~Iterator() noexcept; //============================================================================== /** Moves onto the next element in the path. @@ -728,7 +727,7 @@ public: the elementType variable will be set to the type of the current element, and some of the x and y variables will be filled in with values. */ - bool next(); + bool next() noexcept; //============================================================================== enum PathElementType diff --git a/source/modules/juce_graphics/geometry/juce_Point.h b/source/modules/juce_graphics/geometry/juce_Point.h index 7cc5d4990..c62f7c4dd 100644 --- a/source/modules/juce_graphics/geometry/juce_Point.h +++ b/source/modules/juce_graphics/geometry/juce_Point.h @@ -58,6 +58,9 @@ public: /** Returns true if the point is (0, 0). */ bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); } + /** Returns true if the coordinates are finite values. */ + inline bool isFinite() const noexcept { return juce_isfinite(x) && juce_isfinite(y); } + /** Returns the point's x coordinate. */ inline ValueType getX() const noexcept { return x; } diff --git a/source/modules/juce_graphics/geometry/juce_Rectangle.h b/source/modules/juce_graphics/geometry/juce_Rectangle.h index 6191d1aa7..49f2d3007 100644 --- a/source/modules/juce_graphics/geometry/juce_Rectangle.h +++ b/source/modules/juce_graphics/geometry/juce_Rectangle.h @@ -100,6 +100,9 @@ public: /** Returns true if the rectangle's width or height are zero or less */ bool isEmpty() const noexcept { return w <= ValueType() || h <= ValueType(); } + /** Returns true if the rectangle's values are all finite numbers, i.e. not NaN or infinity. */ + inline bool isFinite() const noexcept { return pos.isFinite() && juce_isfinite(w) && juce_isfinite(h); } + /** Returns the x coordinate of the rectangle's left-hand-side. */ inline ValueType getX() const noexcept { return pos.x; } diff --git a/source/modules/juce_graphics/geometry/juce_RectangleList.h b/source/modules/juce_graphics/geometry/juce_RectangleList.h index f1b81a74a..254124283 100644 --- a/source/modules/juce_graphics/geometry/juce_RectangleList.h +++ b/source/modules/juce_graphics/geometry/juce_RectangleList.h @@ -101,9 +101,14 @@ public: The rectangle being added will first be clipped to remove any parts of it that overlap existing rectangles in the list, and adjacent rectangles will be merged into it. + + The rectangle can have any size and may be empty, but if it's floating point + then it's expected to not contain any INF values. */ void add (const RectangleType& rect) { + jassert (rect.isFinite()); // You must provide a valid rectangle to this method! + if (! rect.isEmpty()) { if (rects.size() == 0) @@ -168,9 +173,14 @@ public: This simply adds the rectangle to the end, it doesn't merge it or remove any overlapping bits. + + The rectangle can have any size and may be empty, but if it's floating point + then it's expected to not contain any INF values. */ void addWithoutMerging (const RectangleType& rect) { + jassert (rect.isFinite()); // You must provide a valid rectangle to this method! + if (! rect.isEmpty()) rects.add (rect); } @@ -300,6 +310,8 @@ public: */ bool clipTo (const RectangleType& rect) { + jassert (rect.isFinite()); // You must provide a valid rectangle to this method! + bool notEmpty = false; if (rect.isEmpty()) @@ -367,6 +379,8 @@ public: */ bool getIntersectionWith (const RectangleType& rect, RectangleList& destRegion) const { + jassert (rect.isFinite()); // You must provide a valid rectangle to this method! + destRegion.clear(); if (! rect.isEmpty()) diff --git a/source/modules/juce_graphics/images/juce_Image.cpp b/source/modules/juce_graphics/images/juce_Image.cpp index e7be9a3f4..7a0ddbe2e 100644 --- a/source/modules/juce_graphics/images/juce_Image.cpp +++ b/source/modules/juce_graphics/images/juce_Image.cpp @@ -39,6 +39,11 @@ void ImagePixelData::sendDataChangeMessage() listeners.call (&Listener::imageDataChanged, this); } +int ImagePixelData::getSharedCount() const noexcept +{ + return getReferenceCount(); +} + //============================================================================== ImageType::ImageType() {} ImageType::~ImageType() {} @@ -177,9 +182,13 @@ public: return newImage.getPixelData(); } - ImageType* createType() const override { return image->createType(); } + ImageType* createType() const override { return image->createType(); } + + /* as we always hold a reference to image, don't double count */ + int getSharedCount() const noexcept override { return getReferenceCount() + image->getSharedCount() - 1; } private: + friend class Image; const ImagePixelData::Ptr image; const Rectangle area; @@ -246,7 +255,7 @@ Image::~Image() const Image Image::null; -int Image::getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getReferenceCount(); } +int Image::getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getSharedCount(); } int Image::getWidth() const noexcept { return image == nullptr ? 0 : image->width; } int Image::getHeight() const noexcept { return image == nullptr ? 0 : image->height; } Rectangle Image::getBounds() const noexcept { return image == nullptr ? Rectangle() : Rectangle (image->width, image->height); } @@ -263,7 +272,7 @@ LowLevelGraphicsContext* Image::createLowLevelContext() const void Image::duplicateIfShared() { - if (image != nullptr && image->getReferenceCount() > 1) + if (getReferenceCount() > 1) image = image->clone(); } @@ -398,9 +407,9 @@ Colour Image::BitmapData::getPixelColour (const int x, const int y) const noexce switch (pixelFormat) { - case Image::ARGB: return Colour (((const PixelARGB*) pixel)->getUnpremultipliedARGB()); - case Image::RGB: return Colour (((const PixelRGB*) pixel)->getUnpremultipliedARGB()); - case Image::SingleChannel: return Colour (((const PixelAlpha*) pixel)->getUnpremultipliedARGB()); + case Image::ARGB: return Colour ( ((const PixelARGB*) pixel)->getUnpremultiplied()); + case Image::RGB: return Colour (*((const PixelRGB*) pixel)); + case Image::SingleChannel: return Colour (*((const PixelAlpha*) pixel)); default: jassertfalse; break; } diff --git a/source/modules/juce_graphics/images/juce_Image.h b/source/modules/juce_graphics/images/juce_Image.h index 0f67b4eef..0e624ebcc 100644 --- a/source/modules/juce_graphics/images/juce_Image.h +++ b/source/modules/juce_graphics/images/juce_Image.h @@ -443,6 +443,11 @@ public: virtual ImageType* createType() const = 0; /** Initialises a BitmapData object. */ virtual void initialiseBitmapData (Image::BitmapData&, int x, int y, Image::BitmapData::ReadWriteMode) = 0; + /** Returns the number of Image objects which are currently referring to the same internal + shared image data. This is different to the reference count as an instance of ImagePixelData + can internally depend on another ImagePixelData via it's member variables. */ + virtual int getSharedCount() const noexcept; + /** The pixel format of the image data. */ const Image::PixelFormat pixelFormat; diff --git a/source/modules/juce_graphics/juce_graphics.cpp b/source/modules/juce_graphics/juce_graphics.cpp index 627d5654a..a03c4640d 100644 --- a/source/modules/juce_graphics/juce_graphics.cpp +++ b/source/modules/juce_graphics/juce_graphics.cpp @@ -73,17 +73,17 @@ #ifndef JUCE_USE_FREETYPE #define JUCE_USE_FREETYPE 1 #endif +#endif - #if ! JUCE_USE_FREETYPE_AMALGAMATED +#if JUCE_USE_FREETYPE + #if JUCE_USE_FREETYPE_AMALGAMATED + #include "native/freetype/FreeTypeAmalgam.h" + #else #include #include FT_FREETYPE_H #endif #endif -#if JUCE_USE_FREETYPE && JUCE_USE_FREETYPE_AMALGAMATED - #include "native/freetype/FreeTypeAmalgam.h" -#endif - #undef SIZEOF #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER diff --git a/source/modules/juce_graphics/native/juce_RenderingHelpers.h b/source/modules/juce_graphics/native/juce_RenderingHelpers.h index ad08e9f93..53c2f10a7 100644 --- a/source/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/source/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -433,12 +433,12 @@ namespace GradientPixelIterators if (vertical) { scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.y - p1.y)); - start = roundToInt (p1.y * scale); + start = roundToInt (p1.y * (float) scale); } else if (horizontal) { scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.x - p1.x)); - start = roundToInt (p1.x * scale); + start = roundToInt (p1.x * (float) scale); } else { @@ -534,8 +534,9 @@ namespace GradientPixelIterators forcedinline void setY (const int y) noexcept { - lineYM01 = inverseTransform.mat01 * y + inverseTransform.mat02 - gx1; - lineYM11 = inverseTransform.mat11 * y + inverseTransform.mat12 - gy1; + const float floatY = (float) y; + lineYM01 = inverseTransform.mat01 * floatY + inverseTransform.mat02 - gx1; + lineYM11 = inverseTransform.mat11 * floatY + inverseTransform.mat12 - gy1; } inline PixelARGB getPixel (const int px) const noexcept @@ -1301,7 +1302,7 @@ namespace EdgeTableFillers sx += pixelOffset; sy += pixelOffset; float x1 = sx, y1 = sy; - sx += numPixels; + sx += (float) numPixels; inverseTransform.transformPoints (x1, y1, sx, sy); xBresenham.set ((int) (x1 * 256.0f), (int) (sx * 256.0f), numPixels, pixelOffsetInt); diff --git a/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp b/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp index d124eafd9..6114456e9 100644 --- a/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp +++ b/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp @@ -244,7 +244,7 @@ private: if (face.face != 0) { if (faceIndex == 0) - numFaces = face.face->num_faces; + numFaces = (int) face.face->num_faces; if ((face.face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) faces.add (new KnownTypeface (file, faceIndex, face)); @@ -307,9 +307,9 @@ public: faceWrapper->face->style_name); } - void initialiseCharacteristics (const String& name, const String& style) + void initialiseCharacteristics (const String& fontName, const String& fontStyle) { - setCharacteristics (name, style, + setCharacteristics (fontName, fontStyle, faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender), L' '); } @@ -319,7 +319,7 @@ public: if (faceWrapper != nullptr) { FT_Face face = faceWrapper->face; - const unsigned int glyphIndex = FT_Get_Char_Index (face, character); + const unsigned int glyphIndex = FT_Get_Char_Index (face, (FT_ULong) character); if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING) == 0 && face->glyph->format == ft_glyph_format_outline) @@ -332,7 +332,7 @@ public: addGlyph (character, destShape, face->glyph->metrics.horiAdvance * scale); if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) - addKerning (face, character, glyphIndex); + addKerning (face, (uint32) character, glyphIndex); return true; } @@ -437,7 +437,7 @@ private: const float height = (float) (face->ascender - face->descender); uint32 rightGlyphIndex; - uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex); + FT_ULong rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex); while (rightGlyphIndex != 0) { @@ -445,7 +445,7 @@ private: if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0 && kerning.x != 0) - addKerningPair (character, rightCharCode, kerning.x / height); + addKerningPair ((juce_wchar) character, (juce_wchar) rightCharCode, kerning.x / height); rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex); } diff --git a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index ac7f27a04..9858afd7f 100644 --- a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -405,7 +405,9 @@ void CoreGraphicsContext::fillCGRect (const CGRect& cgRect, const bool replaceEx { if (replaceExistingContents) { - #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 + #if JUCE_IOS + CGContextSetBlendMode (context, kCGBlendModeCopy); + #elif MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 CGContextClearRect (context, cgRect); #else #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 diff --git a/source/modules/juce_gui_basics/application/juce_Application.h b/source/modules/juce_gui_basics/application/juce_Application.h index 140cf9df2..1647ce63b 100644 --- a/source/modules/juce_gui_basics/application/juce_Application.h +++ b/source/modules/juce_gui_basics/application/juce_Application.h @@ -172,13 +172,13 @@ public: //============================================================================== /** @internal */ - ApplicationCommandTarget* getNextCommandTarget(); + ApplicationCommandTarget* getNextCommandTarget() override; /** @internal */ - void getCommandInfo (CommandID, ApplicationCommandInfo&); + void getCommandInfo (CommandID, ApplicationCommandInfo&) override; /** @internal */ - void getAllCommands (Array&); + void getAllCommands (Array&) override; /** @internal */ - bool perform (const InvocationInfo&); + bool perform (const InvocationInfo&) override; private: bool initialiseApp() override; diff --git a/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h b/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h index 5be135a7d..d040dc9a0 100644 --- a/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h +++ b/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h @@ -154,7 +154,7 @@ public: This will search the list of registered commands for one with the given command ID number, and return its associated info. If no matching command is found, this - will return 0. + will return nullptr. */ const ApplicationCommandInfo* getCommandForID (CommandID commandID) const noexcept; diff --git a/source/modules/juce_gui_basics/components/juce_Component.cpp b/source/modules/juce_gui_basics/components/juce_Component.cpp index d1a93182d..e468f6235 100644 --- a/source/modules/juce_gui_basics/components/juce_Component.cpp +++ b/source/modules/juce_gui_basics/components/juce_Component.cpp @@ -154,7 +154,7 @@ private: bool shouldBailOut() const noexcept { - return checker.shouldBailOut() || safePointer == 0; + return checker.shouldBailOut() || safePointer == nullptr; } private: @@ -200,7 +200,7 @@ struct ScalingHelpers // For these, we need to avoid getSmallestIntegerContainer being used, which causes // judder when moving windows - static Rectangle unscaledScreenPosToScaled (float scale, const Rectangle& pos) noexcept + static Rectangle unscaledScreenPosToScaled (float scale, Rectangle pos) noexcept { return scale != 1.0f ? Rectangle (roundToInt (pos.getX() / scale), roundToInt (pos.getY() / scale), @@ -208,7 +208,7 @@ struct ScalingHelpers roundToInt (pos.getHeight() / scale)) : pos; } - static Rectangle scaledScreenPosToUnscaled (float scale, Rectangle& pos) noexcept + static Rectangle scaledScreenPosToUnscaled (float scale, Rectangle pos) noexcept { return scale != 1.0f ? Rectangle (roundToInt (pos.getX() * scale), roundToInt (pos.getY() * scale), @@ -399,7 +399,7 @@ struct Component::ComponentHelpers return convertFromDistantParentSpace (topLevelComp, *target, p); } - static bool clipObscuredRegions (const Component& comp, Graphics& g, const Rectangle& clipRect, Point delta) + static bool clipObscuredRegions (const Component& comp, Graphics& g, const Rectangle clipRect, Point delta) { bool nothingChanged = true; @@ -1881,15 +1881,15 @@ void Component::repaintParent() parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, getLocalBounds())); } -void Component::internalRepaint (const Rectangle& area) +void Component::internalRepaint (Rectangle area) { - const Rectangle r (area.getIntersection (getLocalBounds())); + area = area.getIntersection (getLocalBounds()); - if (! r.isEmpty()) - internalRepaintUnchecked (r, false); + if (! area.isEmpty()) + internalRepaintUnchecked (area, false); } -void Component::internalRepaintUnchecked (const Rectangle& area, const bool isEntireComponent) +void Component::internalRepaintUnchecked (Rectangle area, const bool isEntireComponent) { if (flags.visibleFlag) { diff --git a/source/modules/juce_gui_basics/components/juce_Component.h b/source/modules/juce_gui_basics/components/juce_Component.h index 8004fd633..142a62f4f 100644 --- a/source/modules/juce_gui_basics/components/juce_Component.h +++ b/source/modules/juce_gui_basics/components/juce_Component.h @@ -480,42 +480,10 @@ public: component's bounds when the source values change. See RelativeRectangle::applyToComponent() for more details. - When using relative expressions, the following symbols are available: - - "left", "right", "top", "bottom" refer to the position of those edges in this component, so - e.g. for a component whose width is always 100, you might set the right edge to the "left + 100". - - "[id].left", "[id].right", "[id].top", "[id].bottom", "[id].width", "[id].height", where [id] is - the identifier of one of this component's siblings. A component's identifier is set with - Component::setComponentID(). So for example if you want your component to always be 50 pixels to the - right of the one called "xyz", you could set your left edge to be "xyz.right + 50". - - Instead of an [id], you can use the name "parent" to refer to this component's parent. Like - any other component, these values are relative to their component's parent, so "parent.right" won't be - very useful for positioning a component because it refers to a position with the parent's parent.. but - "parent.width" can be used for setting positions relative to the parent's size. E.g. to make a 10x10 - component which remains 1 pixel away from its parent's bottom-right, you could use - "right - 10, bottom - 10, parent.width - 1, parent.height - 1". - - The name of one of the parent component's markers can also be used as a symbol. For markers to be - used, the parent component must implement its Component::getMarkers() method, and return at least one - valid MarkerList. So if you want your component's top edge to be 10 pixels below the - marker called "foobar", you'd set it to "foobar + 10". - - See the Expression class for details about the operators that are supported, but for example - if you wanted to make your component remain centred within its parent with a size of 100, 100, - you could express it as: - @code myComp.setBounds (RelativeBounds ("parent.width / 2 - 50, parent.height / 2 - 50, left + 100, top + 100")); - @endcode - ..or an alternative way to achieve the same thing: - @code myComp.setBounds (RelativeBounds ("right - 100, bottom - 100, parent.width / 2 + 50, parent.height / 2 + 50")); - @endcode - - Or if you wanted a 100x100 component whose top edge is lined up to a marker called "topMarker" and - which is positioned 50 pixels to the right of another component called "otherComp", you could write: - @code myComp.setBounds (RelativeBounds ("otherComp.right + 50, topMarker, left + 100, top + 100")); - @endcode - - Be careful not to make your coordinate expressions recursive, though, or exceptions and assertions will - be thrown! - - @see setBounds, RelativeRectangle::applyToComponent(), Expression + For the syntax of the expressions that are allowed in the string, see the notes + for the RelativeCoordinate class. + + @see RelativeCoordinate, setBounds, RelativeRectangle::applyToComponent(), Expression */ void setBounds (const RelativeRectangle& newBounds); @@ -796,7 +764,7 @@ public: /** Searches the parent components for a component of a specified class. For example findParentComponentOfClass \() would return the first parent - component that can be dynamically cast to a MyComp, or will return 0 if none + component that can be dynamically cast to a MyComp, or will return nullptr if none of the parents are suitable. */ template @@ -1013,17 +981,12 @@ public: /** Makes the component use an internal buffer to optimise its redrawing. Setting this flag to true will cause the component to allocate an - internal buffer into which it paints itself, so that when asked to - redraw itself, it can use this buffer rather than actually calling the - paint() method. - - The buffer is kept until the repaint() method is called directly on - this component (or until it is resized), when the image is invalidated - and then redrawn the next time the component is painted. + internal buffer into which it paints itself and all its child components, so that + when asked to redraw itself, it can use this buffer rather than actually calling + the paint() method. - Note that only the drawing that happens within the component's paint() - method is drawn into the buffer, it's child components are not buffered, and - nor is the paintOverChildren() method. + Parts of the buffer are invalidated when repaint() is called on this component + or its children. The buffer is then repainted at the next paint() callback. @see repaint, paint, createComponentSnapshot */ @@ -2336,8 +2299,8 @@ private: void internalModifierKeysChanged(); void internalChildrenChanged(); void internalHierarchyChanged(); - void internalRepaint (const Rectangle&); - void internalRepaintUnchecked (const Rectangle&, bool); + void internalRepaint (Rectangle); + void internalRepaintUnchecked (Rectangle, bool); Component* removeChildComponent (int index, bool sendParentEvents, bool sendChildEvents); void reorderChildInternal (int sourceIndex, int destIndex); void paintComponentAndChildren (Graphics&); diff --git a/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h b/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h index 1b927e121..1eac49938 100644 --- a/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h +++ b/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h @@ -72,6 +72,9 @@ public: virtual void modalStateFinished (int returnValue) = 0; }; + //============================================================================== + juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager) + //============================================================================== /** Returns the number of components currently being shown modally. @see getModalComponent @@ -119,9 +122,6 @@ public: int runEventLoopForCurrentComponent(); #endif - //============================================================================== - juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager) - protected: /** Creates a ModalComponentManager. You shouldn't ever call the constructor - it's a singleton, so use ModalComponentManager::getInstance() diff --git a/source/modules/juce_gui_basics/drawables/juce_Drawable.h b/source/modules/juce_gui_basics/drawables/juce_Drawable.h index b8956d5a0..1338d7e92 100644 --- a/source/modules/juce_gui_basics/drawables/juce_Drawable.h +++ b/source/modules/juce_gui_basics/drawables/juce_Drawable.h @@ -227,14 +227,15 @@ protected: owner (c) {} - bool registerCoordinates() { return owner.registerCoordinates (*this); } - void applyToComponentBounds() + bool registerCoordinates() override { return owner.registerCoordinates (*this); } + + void applyToComponentBounds() override { ComponentScope scope (getComponent()); owner.recalculateCoordinates (&scope); } - void applyNewBounds (const Rectangle&) + void applyNewBounds (const Rectangle&) override { jassertfalse; // drawables can't be resized directly! } diff --git a/source/modules/juce_gui_basics/drawables/juce_DrawableComposite.h b/source/modules/juce_gui_basics/drawables/juce_DrawableComposite.h index 1de9111c4..7629ef895 100644 --- a/source/modules/juce_gui_basics/drawables/juce_DrawableComposite.h +++ b/source/modules/juce_gui_basics/drawables/juce_DrawableComposite.h @@ -92,15 +92,15 @@ public: //============================================================================== /** @internal */ - Drawable* createCopy() const; + Drawable* createCopy() const override; /** @internal */ - void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder); + void refreshFromValueTree (const ValueTree&, ComponentBuilder&); /** @internal */ - ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const; + ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const override; /** @internal */ static const Identifier valueTreeType; /** @internal */ - Rectangle getDrawableBounds() const; + Rectangle getDrawableBounds() const override; /** @internal */ void childBoundsChanged (Component*) override; /** @internal */ diff --git a/source/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp b/source/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp index 6dc46ba64..0b437a4b6 100644 --- a/source/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp @@ -83,7 +83,7 @@ public: { } - bool registerCoordinates() + bool registerCoordinates() override { bool ok = true; @@ -104,7 +104,7 @@ public: return ok; } - void applyToComponentBounds() + void applyToComponentBounds() override { jassert (owner.relativePath != nullptr); @@ -112,7 +112,7 @@ public: owner.applyRelativePath (*owner.relativePath, &scope); } - void applyNewBounds (const Rectangle&) + void applyNewBounds (const Rectangle&) override { jassertfalse; // drawables can't be resized directly! } diff --git a/source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp b/source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp index 5813493c8..907aafac7 100644 --- a/source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp @@ -53,14 +53,14 @@ public: { } - bool registerCoordinates() + bool registerCoordinates() override { bool ok = addPoint (fill.gradientPoint1); ok = addPoint (fill.gradientPoint2) && ok; return addPoint (fill.gradientPoint3) && ok; } - void applyToComponentBounds() + void applyToComponentBounds() override { ComponentScope scope (owner); if (isMainFill ? owner.mainFill.recalculateCoords (&scope) @@ -68,7 +68,7 @@ public: owner.repaint(); } - void applyNewBounds (const Rectangle&) + void applyNewBounds (const Rectangle&) override { jassertfalse; // drawables can't be resized directly! } diff --git a/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index 5e236f03c..d5a926a91 100644 --- a/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -54,7 +54,7 @@ public: DrawableComposite* const drawable = new DrawableComposite(); - setDrawableID (*drawable, xml); + setCommonAttributes (*drawable, xml); SVGState newState (*this); @@ -345,11 +345,14 @@ private: AffineTransform transform; String cssStyleText; - static void setDrawableID (Drawable& d, const XmlPath& xml) + static void setCommonAttributes (Drawable& d, const XmlPath& xml) { String compID (xml->getStringAttribute ("id")); d.setName (compID); d.setComponentID (compID); + + if (xml->getStringAttribute ("display") == "none") + d.setVisible (false); } //============================================================================== @@ -391,7 +394,7 @@ private: { DrawableComposite* const drawable = new DrawableComposite(); - setDrawableID (*drawable, xml); + setCommonAttributes (*drawable, xml); if (xml->hasAttribute ("transform")) { @@ -536,7 +539,7 @@ private: } DrawablePath* dp = new DrawablePath(); - setDrawableID (*dp, xml); + setCommonAttributes (*dp, xml); dp->setFill (Colours::transparentBlack); path.applyTransform (transform); @@ -825,7 +828,7 @@ private: const String anchorStr = getStyleAttribute(xml, "text-anchor"); DrawableComposite* dc = new DrawableComposite(); - setDrawableID (*dc, xml); + setCommonAttributes (*dc, xml); forEachXmlChildElement (*xml, e) { diff --git a/source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp b/source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp index 4a2f02387..8f4154907 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp +++ b/source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp @@ -240,6 +240,9 @@ void FileBrowserComponent::setRoot (const File& newRootDirectory) currentRoot = newRootDirectory; fileList->setDirectory (currentRoot, true, true); + if (FileTreeComponent* tree = dynamic_cast (fileListComponent.get())) + tree->refresh(); + String currentRootName (currentRoot.getFullPathName()); if (currentRootName.isEmpty()) currentRootName = File::separatorString; @@ -555,7 +558,7 @@ void FileBrowserComponent::getDefaultRoots (StringArray& rootNames, StringArray& rootPaths.add (String::empty); rootNames.add (String::empty); - Array volumes; + Array volumes; File vol ("/Volumes"); vol.findChildFiles (volumes, File::findDirectories, false); diff --git a/source/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h b/source/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h index 7f074d2c6..3e8cd907b 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h +++ b/source/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h @@ -56,23 +56,23 @@ public: /** Returns the number of files the user has got selected. @see getSelectedFile */ - int getNumSelectedFiles() const; + int getNumSelectedFiles() const override; /** Returns one of the files that the user has currently selected. The index should be in the range 0 to (getNumSelectedFiles() - 1). @see getNumSelectedFiles */ - File getSelectedFile (int index = 0) const; + File getSelectedFile (int index = 0) const override; /** Deselects any files that are currently selected. */ - void deselectAllFiles(); + void deselectAllFiles() override; /** Scrolls to the top of the list. */ - void scrollToTop(); + void scrollToTop() override; /** If the specified file is in the list, it will become the only selected item (and if the file isn't in the list, all other items will be deselected). */ - void setSelectedFile (const File&); + void setSelectedFile (const File&) override; private: //============================================================================== @@ -84,7 +84,7 @@ private: int getNumRows() override; void paintListBoxItem (int, Graphics&, int, int, bool) override; - Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate) override; + Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component*) override; void selectedRowsChanged (int lastRowSelected) override; void deleteKeyPressed (int currentSelectedRow) override; void returnKeyPressed (int currentSelectedRow) override; diff --git a/source/modules/juce_gui_basics/juce_gui_basics.cpp b/source/modules/juce_gui_basics/juce_gui_basics.cpp index d7f6feda3..4771ebd6d 100644 --- a/source/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/source/modules/juce_gui_basics/juce_gui_basics.cpp @@ -105,6 +105,11 @@ #include #include + #if JUCE_USE_XRANDR + /* If you're trying to use Xrandr, you'll need to install the "libxrandr-dev" package.. */ + #include + #endif + #if JUCE_USE_XINERAMA /* If you're trying to use Xinerama, you'll need to install the "libxinerama-dev" package.. */ #include diff --git a/source/modules/juce_gui_basics/juce_gui_basics.h b/source/modules/juce_gui_basics/juce_gui_basics.h index dedd3a01a..c5e3567e2 100644 --- a/source/modules/juce_gui_basics/juce_gui_basics.h +++ b/source/modules/juce_gui_basics/juce_gui_basics.h @@ -38,8 +38,20 @@ #define JUCE_ENABLE_REPAINT_DEBUGGING 0 #endif +/** JUCE_USE_XRANDR: Enables Xrandr multi-monitor support (Linux only). + Unless you specifically want to disable this, it's best to leave this option turned on. + Note that your users do not need to have Xrandr installed for your JUCE app to run, as + the availability of Xrandr is queried during runtime. +*/ +#ifndef JUCE_USE_XRANDR + #define JUCE_USE_XRANDR 1 +#endif + /** JUCE_USE_XINERAMA: Enables Xinerama multi-monitor support (Linux only). Unless you specifically want to disable this, it's best to leave this option turned on. + This will be used as a fallback if JUCE_USE_XRANDR not set or libxrandr cannot be found. + Note that your users do not need to have Xrandr installed for your JUCE app to run, as + the availability of Xinerama is queried during runtime. */ #ifndef JUCE_USE_XINERAMA #define JUCE_USE_XINERAMA 1 diff --git a/source/modules/juce_gui_basics/layout/juce_ScrollBar.cpp b/source/modules/juce_gui_basics/layout/juce_ScrollBar.cpp index 6184306ae..83524c90b 100644 --- a/source/modules/juce_gui_basics/layout/juce_ScrollBar.cpp +++ b/source/modules/juce_gui_basics/layout/juce_ScrollBar.cpp @@ -60,6 +60,7 @@ ScrollBar::ScrollBar (const bool vertical_) thumbAreaSize (0), thumbStart (0), thumbSize (0), + minimumScrollBarThumbSize (0), initialDelayInMillisecs (100), repeatDelayInMillisecs (50), minimumDelayInMillisecs (10), @@ -67,6 +68,8 @@ ScrollBar::ScrollBar (const bool vertical_) isDraggingThumb (false), autohides (true) { + minimumScrollBarThumbSize = getLookAndFeel().getMinimumScrollbarThumbSize (*this); + setRepaintsOnMouseActivity (true); setFocusContainer (true); } @@ -190,10 +193,8 @@ void ScrollBar::updateThumbPosition() int newThumbSize = roundToInt (totalRange.getLength() > 0 ? (visibleRange.getLength() * thumbAreaSize) / totalRange.getLength() : thumbAreaSize); - LookAndFeel& lf = getLookAndFeel(); - - if (newThumbSize < lf.getMinimumScrollbarThumbSize (*this)) - newThumbSize = jmin (lf.getMinimumScrollbarThumbSize (*this), thumbAreaSize - 1); + if (newThumbSize < minimumScrollBarThumbSize) + newThumbSize = jmin (minimumScrollBarThumbSize, thumbAreaSize - 1); if (newThumbSize > thumbAreaSize) newThumbSize = thumbAreaSize; @@ -280,6 +281,7 @@ void ScrollBar::resized() const int length = vertical ? getHeight() : getWidth(); LookAndFeel& lf = getLookAndFeel(); + minimumScrollBarThumbSize = lf.getMinimumScrollbarThumbSize (*this); const bool buttonsVisible = lf.areScrollbarButtonsVisible(); int buttonSize = 0; diff --git a/source/modules/juce_gui_basics/layout/juce_ScrollBar.h b/source/modules/juce_gui_basics/layout/juce_ScrollBar.h index 2731af030..0bb1ddf79 100644 --- a/source/modules/juce_gui_basics/layout/juce_ScrollBar.h +++ b/source/modules/juce_gui_basics/layout/juce_ScrollBar.h @@ -382,6 +382,7 @@ private: Range totalRange, visibleRange; double singleStepSize, dragStartRange; int thumbAreaStart, thumbAreaSize, thumbStart, thumbSize; + int minimumScrollBarThumbSize; int dragStartMousePos, lastMousePos; int initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs; bool vertical, isDraggingThumb, autohides; diff --git a/source/modules/juce_gui_basics/layout/juce_Viewport.cpp b/source/modules/juce_gui_basics/layout/juce_Viewport.cpp index 80266cea6..21360a041 100644 --- a/source/modules/juce_gui_basics/layout/juce_Viewport.cpp +++ b/source/modules/juce_gui_basics/layout/juce_Viewport.cpp @@ -24,6 +24,7 @@ Viewport::Viewport (const String& name) : Component (name), + customScrollBarThickness(false), scrollBarThickness (0), singleStepX (16), singleStepY (16), @@ -39,6 +40,8 @@ Viewport::Viewport (const String& name) addAndMakeVisible (contentHolder); contentHolder.setInterceptsMouseClicks (false, true); + scrollBarThickness = getLookAndFeel().getDefaultScrollbarWidth(); + addChildComponent (verticalScrollBar); addChildComponent (horizontalScrollBar); @@ -174,6 +177,12 @@ void Viewport::componentMovedOrResized (Component&, bool, bool) updateVisibleArea(); } +void Viewport::lookAndFeelChanged() +{ + if (! customScrollBarThickness) + scrollBarThickness = getLookAndFeel().getDefaultScrollbarWidth(); +} + void Viewport::resized() { updateVisibleArea(); @@ -314,17 +323,32 @@ void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded, void Viewport::setScrollBarThickness (const int thickness) { - if (scrollBarThickness != thickness) + int newThickness; + + // To stay compatible with the previous code: use the + // default thickness if thickness parameter is zero + // or negative + if (thickness <= 0) + { + customScrollBarThickness = false; + newThickness = getLookAndFeel().getDefaultScrollbarWidth(); + } + else + { + customScrollBarThickness = true; + newThickness = thickness; + } + + if (scrollBarThickness != newThickness) { - scrollBarThickness = thickness; + scrollBarThickness = newThickness; updateVisibleArea(); } } int Viewport::getScrollBarThickness() const { - return scrollBarThickness > 0 ? scrollBarThickness - : getLookAndFeel().getDefaultScrollbarWidth(); + return scrollBarThickness; } void Viewport::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart) diff --git a/source/modules/juce_gui_basics/layout/juce_Viewport.h b/source/modules/juce_gui_basics/layout/juce_Viewport.h index c42751529..78aa4fa01 100644 --- a/source/modules/juce_gui_basics/layout/juce_Viewport.h +++ b/source/modules/juce_gui_basics/layout/juce_Viewport.h @@ -253,6 +253,8 @@ public: /** @internal */ void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override; /** @internal */ + void lookAndFeelChanged() override; + /** @internal */ bool useMouseWheelMoveIfNeeded (const MouseEvent&, const MouseWheelDetails&); /** @internal */ static bool respondsToKey (const KeyPress&); @@ -261,6 +263,7 @@ private: //============================================================================== WeakReference contentComp; Rectangle lastVisibleArea; + bool customScrollBarThickness; int scrollBarThickness; int singleStepX, singleStepY; bool showHScrollbar, showVScrollbar, deleteContent; 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 56aa11ae1..2d894e567 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -1493,12 +1493,86 @@ int LookAndFeel_V2::getSliderPopupPlacement (Slider&) } //============================================================================== -void LookAndFeel_V2::getTooltipSize (const String& tipText, int& width, int& height) +Slider::SliderLayout LookAndFeel_V2::getSliderLayout (Slider& slider) +{ + // 1. compute the actually visible textBox size from the slider textBox size and some additional constraints + + int minXSpace = 0; + int minYSpace = 0; + + Slider::TextEntryBoxPosition textBoxPos = slider.getTextBoxPosition(); + + if (textBoxPos == Slider::TextBoxLeft || textBoxPos == Slider::TextBoxRight) + minXSpace = 30; + else + minYSpace = 15; + + Rectangle localBounds = slider.getLocalBounds(); + + const int textBoxWidth = jmax (0, jmin (slider.getTextBoxWidth(), localBounds.getWidth() - minXSpace)); + const int textBoxHeight = jmax (0, jmin (slider.getTextBoxHeight(), localBounds.getHeight() - minYSpace)); + + Slider::SliderLayout layout; + + // 2. set the textBox bounds + + if (textBoxPos != Slider::NoTextBox) + { + if (slider.isBar()) + { + layout.textBoxBounds = localBounds; + } + else + { + layout.textBoxBounds.setWidth (textBoxWidth); + layout.textBoxBounds.setHeight (textBoxHeight); + + if (textBoxPos == Slider::TextBoxLeft) layout.textBoxBounds.setX (0); + else if (textBoxPos == Slider::TextBoxRight) layout.textBoxBounds.setX (localBounds.getWidth() - textBoxWidth); + else /* above or below -> centre horizontally */ layout.textBoxBounds.setX ((localBounds.getWidth() - textBoxWidth) / 2); + + if (textBoxPos == Slider::TextBoxAbove) layout.textBoxBounds.setY (0); + else if (textBoxPos == Slider::TextBoxBelow) layout.textBoxBounds.setY (localBounds.getHeight() - textBoxHeight); + else /* left or right -> centre vertically */ layout.textBoxBounds.setY ((localBounds.getHeight() - textBoxHeight) / 2); + } + } + + // 3. set the slider bounds + + layout.sliderBounds = localBounds; + + if (slider.isBar()) + { + layout.sliderBounds.reduce (1, 1); // bar border + } + else + { + if (textBoxPos == Slider::TextBoxLeft) layout.sliderBounds.removeFromLeft (textBoxWidth); + else if (textBoxPos == Slider::TextBoxRight) layout.sliderBounds.removeFromRight (textBoxWidth); + else if (textBoxPos == Slider::TextBoxAbove) layout.sliderBounds.removeFromTop (textBoxHeight); + else if (textBoxPos == Slider::TextBoxBelow) layout.sliderBounds.removeFromBottom (textBoxHeight); + + const int thumbIndent = getSliderThumbRadius (slider); + + if (slider.isHorizontal()) layout.sliderBounds.reduce (thumbIndent, 0); + else if (slider.isVertical()) layout.sliderBounds.reduce (0, thumbIndent); + } + + return layout; +} + +//============================================================================== +Rectangle LookAndFeel_V2::getTooltipBounds (const String& tipText, Point screenPos, Rectangle parentArea) { const TextLayout tl (LookAndFeelHelpers::layoutTooltipText (tipText, Colours::black)); - width = (int) (tl.getWidth() + 14.0f); - height = (int) (tl.getHeight() + 6.0f); + const int w = (int) (tl.getWidth() + 14.0f); + const int h = (int) (tl.getHeight() + 6.0f); + + return Rectangle (screenPos.x > parentArea.getCentreX() ? screenPos.x - (w + 12) : screenPos.x + 24, + screenPos.y > parentArea.getCentreY() ? screenPos.y - (h + 6) : screenPos.y + 6, + w, h) + .constrainedWithin (parentArea); } void LookAndFeel_V2::drawTooltip (Graphics& g, const String& text, int width, int height) 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 2342b7cf1..40ccad202 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -193,9 +193,10 @@ public: ImageEffectFilter* getSliderEffect (Slider&) override; Font getSliderPopupFont (Slider&) override; int getSliderPopupPlacement (Slider&) override; + Slider::SliderLayout getSliderLayout (Slider&) override; //============================================================================== - void getTooltipSize (const String& tipText, int& width, int& height) override; + Rectangle getTooltipBounds (const String& tipText, Point screenPos, Rectangle parentArea) override; void drawTooltip (Graphics&, const String& text, int width, int height) override; //============================================================================== diff --git a/source/modules/juce_gui_basics/misc/juce_DropShadower.cpp b/source/modules/juce_gui_basics/misc/juce_DropShadower.cpp index c6d0d77fd..18b706d96 100644 --- a/source/modules/juce_gui_basics/misc/juce_DropShadower.cpp +++ b/source/modules/juce_gui_basics/misc/juce_DropShadower.cpp @@ -31,7 +31,7 @@ public: setVisible (true); setInterceptsMouseClicks (false, false); - if (comp->isOnDesktop()) + if (! comp->isOnDesktop()) { setSize (1, 1); // to keep the OS happy by not having zero-size windows addToDesktop (ComponentPeer::windowIgnoresMouseClicks diff --git a/source/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.cpp b/source/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.cpp index b450181a8..3cc5763c2 100644 --- a/source/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.cpp +++ b/source/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.cpp @@ -23,7 +23,7 @@ */ MouseInactivityDetector::MouseInactivityDetector (Component& c) - : targetComp (c), delayMs (1500), isActive (true) + : targetComp (c), delayMs (1500), toleranceDistance (15), isActive (true) { targetComp.addMouseListener (this, true); } @@ -33,11 +33,8 @@ MouseInactivityDetector::~MouseInactivityDetector() targetComp.removeMouseListener (this); } -void MouseInactivityDetector::setDelay (int newDelayMilliseconds) -{ - delayMs = newDelayMilliseconds; -} - +void MouseInactivityDetector::setDelay (int newDelay) noexcept { delayMs = newDelay; } +void MouseInactivityDetector::setMouseMoveTolerance (int newDistance) noexcept { toleranceDistance = newDistance; } void MouseInactivityDetector::addListener (Listener* l) { listenerList.add (l); } void MouseInactivityDetector::removeListener (Listener* l) { listenerList.remove (l); } @@ -51,7 +48,7 @@ void MouseInactivityDetector::wakeUp (const MouseEvent& e, bool alwaysWake) { const Point newPos (e.getEventRelativeTo (&targetComp).getPosition()); - if ((! isActive) && (alwaysWake || e.source.isTouch() || newPos.getDistanceFrom (lastMousePos) > 15)) + if ((! isActive) && (alwaysWake || e.source.isTouch() || newPos.getDistanceFrom (lastMousePos) > toleranceDistance)) setActive (true); if (lastMousePos != newPos) diff --git a/source/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.h b/source/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.h index a2f6533d0..7c293285e 100644 --- a/source/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.h +++ b/source/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.h @@ -54,7 +54,12 @@ public: /** Sets the time for which the mouse must be still before the callback is triggered. */ - void setDelay (int newDelayMilliseconds); + void setDelay (int newDelayMilliseconds) noexcept; + + /** Sets the number of pixels by which the cursor is allowed to drift before it is + considered to be actively moved. + */ + void setMouseMoveTolerance (int pixelsNeededToTrigger) noexcept; //============================================================================== /** Classes should implement this to receive callbacks from a MouseInactivityDetector @@ -84,7 +89,7 @@ private: Component& targetComp; ListenerList listenerList; Point lastMousePos; - int delayMs; + int delayMs, toleranceDistance; bool isActive; void timerCallback() override; diff --git a/source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp b/source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp index 8bed5afa8..7df09b4b8 100644 --- a/source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp +++ b/source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp @@ -351,13 +351,10 @@ public: int getNumberOfMultipleClicks() const noexcept { - int numClicks = 0; + int numClicks = 1; - if (mouseDowns[0].time != Time()) + if (! hasMouseMovedSignificantlySincePressed()) { - if (! mouseMovedSignificantlySincePressed) - ++numClicks; - for (int i = 1; i < numElementsInArray (mouseDowns); ++i) { if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2))) diff --git a/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp index 6946c68ea..0a7df049b 100644 --- a/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp @@ -41,9 +41,15 @@ JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, launchApp, void, (JNIEnv* en initialiseJuce_GUI(); - JUCEApplicationBase* app = JUCEApplicationBase::createInstance(); - if (! app->initialiseApp()) - exit (app->getApplicationReturnValue()); + if (JUCEApplicationBase* app = JUCEApplicationBase::createInstance()) + { + if (! app->initialiseApp()) + exit (app->shutdownApp()); + } + else + { + jassertfalse; // you must supply an application object for an android app! + } jassert (MessageManager::getInstance()->isThisTheMessageThread()); } @@ -407,7 +413,7 @@ public: } //============================================================================== - void handlePaintCallback (JNIEnv* env, jobject canvas) + void handlePaintCallback (JNIEnv* env, jobject canvas, jobject paint) { jobject rect = env->CallObjectMethod (canvas, CanvasMinimal.getClipBounds); const int left = env->GetIntField (rect, RectClass.left); @@ -443,7 +449,7 @@ public: env->CallVoidMethod (canvas, CanvasMinimal.drawBitmap, (jintArray) buffer.get(), 0, clip.getWidth(), (jfloat) clip.getX(), (jfloat) clip.getY(), - clip.getWidth(), clip.getHeight(), true, (jobject) 0); + clip.getWidth(), clip.getHeight(), true, paint); } } @@ -569,7 +575,7 @@ int64 AndroidComponentPeer::touchesDown = 0; peer->juceMethodInvocation; \ } -JUCE_VIEW_CALLBACK (void, handlePaint, (JNIEnv* env, jobject view, jlong host, jobject canvas), handlePaintCallback (env, canvas)) +JUCE_VIEW_CALLBACK (void, handlePaint, (JNIEnv* env, jobject view, jlong host, jobject canvas, jobject paint), handlePaintCallback (env, canvas, paint)) JUCE_VIEW_CALLBACK (void, handleMouseDown, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseDownCallback (i, Point ((float) x, (float) y), (int64) time)) JUCE_VIEW_CALLBACK (void, handleMouseDrag, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseDragCallback (i, Point ((float) x, (float) y), (int64) time)) JUCE_VIEW_CALLBACK (void, handleMouseUp, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseUpCallback (i, Point ((float) x, (float) y), (int64) time)) diff --git a/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm b/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm index 6df6db061..531d3787f 100644 --- a/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm +++ b/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm @@ -43,44 +43,51 @@ extern bool isIOSAppActive; - (void) applicationDidFinishLaunching: (UIApplication*) application { - (void) application; + ignoreUnused (application); initialiseJuce_GUI(); - JUCEApplicationBase* app = JUCEApplicationBase::createInstance(); - - if (! app->initialiseApp()) - exit (0); + if (JUCEApplicationBase* app = JUCEApplicationBase::createInstance()) + { + if (! app->initialiseApp()) + exit (app->shutdownApp()); + } + else + { + jassertfalse; // you must supply an application object for an iOS app! + } } - (void) applicationWillTerminate: (UIApplication*) application { - (void) application; + ignoreUnused (application); JUCEApplicationBase::appWillTerminateByForce(); } - (void) applicationDidEnterBackground: (UIApplication*) application { - (void) application; + ignoreUnused (application); + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) app->suspended(); } - (void) applicationWillEnterForeground: (UIApplication*) application { - (void) application; + ignoreUnused (application); + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) app->resumed(); } - (void) applicationDidBecomeActive: (UIApplication*) application { - (void) application; + ignoreUnused (application); isIOSAppActive = true; } - (void) applicationWillResignActive: (UIApplication*) application { - (void) application; + ignoreUnused (application); isIOSAppActive = false; } @@ -207,7 +214,7 @@ void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType JUCE_AUTORELEASEPOOL { iOSMessageBox mb (title, message, @"OK", nil, nil, nullptr, false); - (void) mb.getResult(); + ignoreUnused (mb.getResult()); } } #endif @@ -294,10 +301,10 @@ void SystemClipboard::copyTextToClipboard (const String& text) String SystemClipboard::getTextFromClipboard() { - NSString* text = [[UIPasteboard generalPasteboard] valueForPasteboardType: @"public.text"]; + if (NSString* text = [[UIPasteboard generalPasteboard] valueForPasteboardType: @"public.text"]) + return nsStringToJuce (text); - return text == nil ? String::empty - : nsStringToJuce (text); + return String(); } //============================================================================== diff --git a/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp b/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp index 3b77c5349..f43d5568f 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp @@ -22,8 +22,8 @@ ============================================================================== */ -extern Display* display; -extern Window juce_messageWindowHandle; +extern ::Display* display; +extern ::Window juce_messageWindowHandle; namespace ClipboardHelpers { @@ -68,7 +68,7 @@ namespace ClipboardHelpers (unsigned char**) &clipData) == Success) { if (actualType == atom_UTF8_STRING && actualFormat == 8) - returnData = String::fromUTF8 (clipData, numItems); + returnData = String::fromUTF8 (clipData, (int) numItems); else if (actualType == XA_STRING && actualFormat == 8) returnData = String (clipData, numItems); @@ -184,7 +184,7 @@ namespace ClipboardHelpers XChangeProperty (evt.display, evt.requestor, evt.property, evt.target, propertyFormat /* 8 or 32 */, PropModeReplace, - reinterpret_cast (data.getData()), numDataItems); + reinterpret_cast (data.getData()), (int) numDataItems); reply.property = evt.property; // " == success" } } 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 79152b356..ff1993cba 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -extern Display* display; +extern ::Display* display; extern XContext windowHandleXContext; typedef void (*WindowMessageReceiveCallback) (XEvent&); extern WindowMessageReceiveCallback dispatchWindowMessage; @@ -189,7 +189,7 @@ bool KeyPress::isKeyCurrentlyDown (const int keyCode) ScopedXLock xlock; - const int keycode = XKeysymToKeycode (display, keysym); + const int keycode = XKeysymToKeycode (display, (KeySym) keysym); const int keybyte = keycode >> 3; const int keybit = (1 << (keycode & 7)); @@ -235,7 +235,7 @@ namespace XSHMHelpers 24, ZPixmap, 0, &segmentInfo, 50, 50); if ((segmentInfo.shmid = shmget (IPC_PRIVATE, - xImage->bytes_per_line * xImage->height, + (size_t) (xImage->bytes_per_line * xImage->height), IPC_CREAT | 0777)) >= 0) { segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, 0, 0); @@ -505,7 +505,7 @@ class XBitmapImage : public ImagePixelData { public: XBitmapImage (const Image::PixelFormat format, const int w, const int h, - const bool clearImage, const int imageDepth_, Visual* visual) + const bool clearImage, const unsigned int imageDepth_, Visual* visual) : ImagePixelData (format, w, h), imageDepth (imageDepth_), gc (None) @@ -528,12 +528,13 @@ public: segmentInfo.shmaddr = (char *) -1; segmentInfo.readOnly = False; - xImage = XShmCreateImage (display, visual, imageDepth, ZPixmap, 0, &segmentInfo, w, h); + xImage = XShmCreateImage (display, visual, imageDepth, ZPixmap, 0, + &segmentInfo, (unsigned int) w, (unsigned int) h); if (xImage != nullptr) { if ((segmentInfo.shmid = shmget (IPC_PRIVATE, - xImage->bytes_per_line * xImage->height, + (size_t) (xImage->bytes_per_line * xImage->height), IPC_CREAT | 0777)) >= 0) { if (segmentInfo.shmid != -1) @@ -561,10 +562,10 @@ public: } } - if (! usingXShm) + if (! isUsingXShm()) #endif { - imageDataAllocated.allocate (lineStride * h, format == Image::ARGB && clearImage); + imageDataAllocated.allocate ((size_t) (lineStride * h), format == Image::ARGB && clearImage); imageData = imageDataAllocated; xImage = (XImage*) ::calloc (1, sizeof (XImage)); @@ -587,15 +588,15 @@ public: if (imageDepth == 16) { - const int pixelStride = 2; - const int lineStride = ((w * pixelStride + 3) & ~3); + const int pixStride = 2; + const int stride = ((w * pixStride + 3) & ~3); - imageData16Bit.malloc (lineStride * h); + imageData16Bit.malloc ((size_t) (stride * h)); xImage->data = imageData16Bit; xImage->bitmap_pad = 16; - xImage->depth = pixelStride * 8; - xImage->bytes_per_line = lineStride; - xImage->bits_per_pixel = pixelStride * 8; + xImage->depth = pixStride * 8; + xImage->bytes_per_line = stride; + xImage->bits_per_pixel = pixStride * 8; xImage->red_mask = visual->red_mask; xImage->green_mask = visual->green_mask; xImage->blue_mask = visual->blue_mask; @@ -614,7 +615,7 @@ public: XFreeGC (display, gc); #if JUCE_USE_XSHM - if (usingXShm) + if (isUsingXShm()) { XShmDetach (display, &segmentInfo); @@ -657,7 +658,7 @@ public: ImageType* createType() const override { return new NativeImageType(); } - void blitToWindow (Window window, int dx, int dy, int dw, int dh, int sx, int sy) + void blitToWindow (Window window, int dx, int dy, unsigned int dw, unsigned int dh, int sx, int sy) { ScopedXLock xlock; @@ -678,23 +679,23 @@ public: if (imageDepth == 16) { - const uint32 rMask = xImage->red_mask; - const uint32 gMask = xImage->green_mask; - const uint32 bMask = xImage->blue_mask; - const uint32 rShiftL = jmax (0, getShiftNeeded (rMask)); - const uint32 rShiftR = jmax (0, -getShiftNeeded (rMask)); - const uint32 gShiftL = jmax (0, getShiftNeeded (gMask)); - const uint32 gShiftR = jmax (0, -getShiftNeeded (gMask)); - const uint32 bShiftL = jmax (0, getShiftNeeded (bMask)); - const uint32 bShiftR = jmax (0, -getShiftNeeded (bMask)); + const uint32 rMask = (uint32) xImage->red_mask; + const uint32 gMask = (uint32) xImage->green_mask; + const uint32 bMask = (uint32) xImage->blue_mask; + const uint32 rShiftL = (uint32) jmax (0, getShiftNeeded (rMask)); + const uint32 rShiftR = (uint32) jmax (0, -getShiftNeeded (rMask)); + const uint32 gShiftL = (uint32) jmax (0, getShiftNeeded (gMask)); + const uint32 gShiftR = (uint32) jmax (0, -getShiftNeeded (gMask)); + const uint32 bShiftL = (uint32) jmax (0, getShiftNeeded (bMask)); + const uint32 bShiftR = (uint32) jmax (0, -getShiftNeeded (bMask)); const Image::BitmapData srcData (Image (this), Image::BitmapData::readOnly); - for (int y = sy; y < sy + dh; ++y) + for (int y = sy; y < sy + (int)dh; ++y) { const uint8* p = srcData.getPixelPointer (sx, y); - for (int x = sx; x < sx + dw; ++x) + for (int x = sx; x < sx + (int)dw; ++x) { const PixelRGB* const pixel = (const PixelRGB*) p; p += srcData.pixelStride; @@ -709,17 +710,21 @@ public: // blit results to screen. #if JUCE_USE_XSHM - if (usingXShm) + if (isUsingXShm()) XShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True); else #endif XPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh); } + #if JUCE_USE_XSHM + bool isUsingXShm() const noexcept { return usingXShm; } + #endif + private: //============================================================================== XImage* xImage; - const int imageDepth; + const unsigned int imageDepth; HeapBlock imageDataAllocated; HeapBlock imageData16Bit; int pixelStride, lineStride; @@ -744,6 +749,674 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XBitmapImage) }; +//============================================================================== +#if JUCE_USE_XRANDR +template <> +struct ContainerDeletePolicy +{ + static void destroy (XRRScreenResources* object); +}; + +template <> +struct ContainerDeletePolicy +{ + static void destroy (XRROutputInfo* object); +}; + +template <> +struct ContainerDeletePolicy +{ + static void destroy (XRRCrtcInfo* object); +}; +#endif + +//============================================================================== +class DisplayGeometry +{ +private: + //============================================================================== + DisplayGeometry (::Display* dpy, double masterScale) + { + jassert (instance == nullptr); + instance = this; + + queryDisplayInfos (dpy, masterScale); + updatePositions(); + } + +public: + //============================================================================== + struct ExtendedInfo + { + // Unlike Desktop::Displays::Display, the following is in + // physical pixels, i.e. the area is not scaled + Rectangle totalBounds; + // Usable bounds is the usable area in local coordinates + // with respect to the above totalBounds + Rectangle usableBounds; + // top-left point of display in scaled coordinates. This + // is different from totalBounds.getTopLeft() / scale, + // because the neighbouring display may have a different + // scale factor + Point topLeftScaled; + double dpi, scale; + bool isMain; + }; + + Array infos; + + //============================================================================== + ExtendedInfo& findDisplayForRect (const Rectangle& bounds, bool isScaledBounds) + { + int maxArea = -1; + ExtendedInfo* retval = nullptr; + + for (int i = 0; i < infos.size(); ++i) + { + ExtendedInfo& dpy = infos.getReference (i); + + Rectangle displayBounds = dpy.totalBounds; + + if (isScaledBounds) + displayBounds = (displayBounds.withZeroOrigin() / dpy.scale) + dpy.topLeftScaled; + + displayBounds = displayBounds.getIntersection (bounds); + int area = displayBounds.getWidth() * displayBounds.getHeight(); + + if (area >= maxArea) + { + maxArea = area; + retval = &dpy; + } + } + + return *retval; + } + + ExtendedInfo& findDisplayForPoint (Point pt, bool isScaledPoint) + { + int minDistance = (int) ((((unsigned int)(-1)) >> 1) - 1); + ExtendedInfo* retval = nullptr; + + for (int i = 0; i < infos.size(); ++i) + { + ExtendedInfo& dpy = infos.getReference (i); + + Rectangle displayBounds = dpy.totalBounds; + + if (isScaledPoint) + displayBounds = (displayBounds.withZeroOrigin() / dpy.scale) + dpy.topLeftScaled; + + if (displayBounds.contains (pt)) + return dpy; + + int distance = displayBounds.getCentre().getDistanceFrom (pt); + if (distance <= minDistance) + { + minDistance = distance; + retval = &dpy; + } + } + + return *retval; + } + + //============================================================================== + static Rectangle physicalToScaled (const Rectangle& physicalBounds) + { + // first find with which display physicalBounds has the most overlap + ExtendedInfo& dpy = getInstance().findDisplayForRect (physicalBounds, false); + + // convert to local screen bounds + Rectangle retval = physicalBounds - dpy.totalBounds.getTopLeft(); + + // now we can safely scale the coordinates and convert to global again + return (retval / dpy.scale) + dpy.topLeftScaled; + } + + static Rectangle scaledToPhysical (const Rectangle& scaledBounds) + { + // first find with which display physicalBounds has the most overlap + ExtendedInfo& dpy = getInstance().findDisplayForRect (scaledBounds, true); + + // convert to local screen bounds + Rectangle retval = scaledBounds - dpy.topLeftScaled; + + // now we can safely scale the coordinates and convert to global again + return (retval * dpy.scale) + dpy.totalBounds.getTopLeft(); + } + + //============================================================================== + template + static Point physicalToScaled (const Point& physicalPoint) + { + ExtendedInfo& dpy = getInstance().findDisplayForPoint (physicalPoint.roundToInt(), false); + Point scaledTopLeft = + Point (dpy.topLeftScaled.getX(), dpy.topLeftScaled.getY()); + Point physicalTopLeft = + Point (dpy.totalBounds.getX(), dpy.totalBounds.getY()); + + return ((physicalPoint - physicalTopLeft) / dpy.scale) + scaledTopLeft; + } + + template + static Point scaledToPhysical (const Point& scaledPoint) + { + ExtendedInfo& dpy = getInstance().findDisplayForPoint (scaledPoint.roundToInt(), true); + Point scaledTopLeft = + Point (dpy.topLeftScaled.getX(), dpy.topLeftScaled.getY()); + Point physicalTopLeft = + Point (dpy.totalBounds.getX(), dpy.totalBounds.getY()); + + return ((scaledPoint - scaledTopLeft) * dpy.scale) + physicalTopLeft; + } + + //============================================================================== + static DisplayGeometry& getInstance() + { + jassert (instance != nullptr); + return *instance; + } + + static DisplayGeometry& getOrCreateInstance (::Display* dpy, double masterScale) + { + if (instance == nullptr) + new DisplayGeometry (dpy, masterScale); + + return getInstance(); + } + +private: + //============================================================================== + static DisplayGeometry* instance; + + //============================================================================== + #if JUCE_USE_XINERAMA + static Array XineramaQueryDisplays (::Display* dpy) + { + typedef Bool (*tXineramaIsActive) (::Display*); + typedef XineramaScreenInfo* (*tXineramaQueryScreens) (::Display*, int*); + + int major_opcode, first_event, first_error; + + if (XQueryExtension (dpy, "XINERAMA", &major_opcode, &first_event, &first_error)) + { + static void* libXinerama = nullptr; + static tXineramaIsActive isActiveFuncPtr = nullptr; + static tXineramaQueryScreens xineramaQueryScreens = nullptr; + + if (libXinerama == nullptr) + { + libXinerama = dlopen ("libXinerama.so", RTLD_GLOBAL | RTLD_NOW); + + if (libXinerama == nullptr) + libXinerama = dlopen ("libXinerama.so.1", RTLD_GLOBAL | RTLD_NOW); + + if (libXinerama != nullptr) + { + isActiveFuncPtr = (tXineramaIsActive) dlsym (libXinerama, "XineramaIsActive"); + xineramaQueryScreens = (tXineramaQueryScreens) dlsym (libXinerama, "XineramaQueryScreens"); + } + } + + if (isActiveFuncPtr != nullptr && xineramaQueryScreens != nullptr && isActiveFuncPtr (dpy) != 0) + { + int numScreens; + if (XineramaScreenInfo* xinfo = xineramaQueryScreens (dpy, &numScreens)) + { + Array infos (xinfo, numScreens); + XFree (xinfo); + + return infos; + } + } + } + + return Array(); + } + #endif + + //============================================================================== + #if JUCE_USE_XRANDR + friend class ContainerDeletePolicy; + friend class ContainerDeletePolicy; + friend class ContainerDeletePolicy; + + class XRandrWrapper + { + private: + XRandrWrapper() + : libXrandr (nullptr), + getScreenResourcesPtr (nullptr), + freeScreenResourcesPtr (nullptr), + getOutputInfoPtr (nullptr), + freeOutputInfoPtr (nullptr), + getCrtcInfoPtr (nullptr), + freeCrtcInfoPtr (nullptr), + getOutputPrimaryPtr (nullptr) + { + if (libXrandr == nullptr) + { + libXrandr = dlopen ("libXrandr.so", RTLD_GLOBAL | RTLD_NOW); + + if (libXrandr == nullptr) + libXrandr = dlopen ("libXinerama.so.2", RTLD_GLOBAL | RTLD_NOW); + + if (libXrandr != nullptr) + { + getScreenResourcesPtr = (tXRRGetScreenResources) dlsym (libXrandr, "XRRGetScreenResources"); + freeScreenResourcesPtr = (tXRRFreeScreenResources) dlsym (libXrandr, "XRRFreeScreenResources"); + getOutputInfoPtr = (tXRRGetOutputInfo) dlsym (libXrandr, "XRRGetOutputInfo"); + freeOutputInfoPtr = (tXRRFreeOutputInfo) dlsym (libXrandr, "XRRFreeOutputInfo"); + getCrtcInfoPtr = (tXRRGetCrtcInfo) dlsym (libXrandr, "XRRGetCrtcInfo"); + freeCrtcInfoPtr = (tXRRFreeCrtcInfo) dlsym (libXrandr, "XRRFreeCrtcInfo"); + getOutputPrimaryPtr = (tXRRGetOutputPrimary) dlsym (libXrandr, "XRRGetOutputPrimary"); + } + } + + instance = this; + } + + public: + //============================================================================== + static XRandrWrapper& getInstance() + { + if (instance == nullptr) + instance = new XRandrWrapper(); + + return *instance; + } + + //============================================================================== + XRRScreenResources* getScreenResources (::Display* dpy, ::Window window) + { + if (getScreenResourcesPtr != nullptr) + return getScreenResourcesPtr (dpy, window); + + return nullptr; + } + + XRROutputInfo* getOutputInfo (::Display* dpy, XRRScreenResources* resources, RROutput output) + { + if (getOutputInfoPtr != nullptr) + return getOutputInfoPtr (dpy, resources, output); + + return nullptr; + } + + XRRCrtcInfo* getCrtcInfo (::Display* dpy, XRRScreenResources* resources, RRCrtc crtc) + { + if (getCrtcInfoPtr != nullptr) + return getCrtcInfoPtr (dpy, resources, crtc); + + return nullptr; + } + + RROutput getOutputPrimary (::Display* dpy, ::Window window) + { + if (getOutputPrimaryPtr != nullptr) + return getOutputPrimaryPtr (dpy, window); + + return 0; + } + + private: + //============================================================================== + friend class ContainerDeletePolicy; + friend class ContainerDeletePolicy; + friend class ContainerDeletePolicy; + + void freeScreenResources (XRRScreenResources* ptr) + { + if (freeScreenResourcesPtr != nullptr) + freeScreenResourcesPtr (ptr); + } + + void freeOutputInfo (XRROutputInfo* ptr) + { + if (freeOutputInfoPtr != nullptr) + freeOutputInfoPtr (ptr); + } + + void freeCrtcInfo (XRRCrtcInfo* ptr) + { + if (freeCrtcInfoPtr != nullptr) + freeCrtcInfoPtr (ptr); + } + private: + static XRandrWrapper* instance; + + typedef XRRScreenResources* (*tXRRGetScreenResources) (::Display*, ::Window); + typedef void (*tXRRFreeScreenResources) (XRRScreenResources*); + typedef XRROutputInfo* (*tXRRGetOutputInfo) (::Display*, XRRScreenResources*, RROutput); + typedef void (*tXRRFreeOutputInfo) (XRROutputInfo*); + typedef XRRCrtcInfo* (*tXRRGetCrtcInfo) (::Display*, XRRScreenResources*, RRCrtc); + typedef void (*tXRRFreeCrtcInfo) (XRRCrtcInfo*); + typedef RROutput (*tXRRGetOutputPrimary) (::Display*, ::Window); + + void* libXrandr; + tXRRGetScreenResources getScreenResourcesPtr; + tXRRFreeScreenResources freeScreenResourcesPtr; + tXRRGetOutputInfo getOutputInfoPtr; + tXRRFreeOutputInfo freeOutputInfoPtr; + tXRRGetCrtcInfo getCrtcInfoPtr; + tXRRFreeCrtcInfo freeCrtcInfoPtr; + tXRRGetOutputPrimary getOutputPrimaryPtr; + }; + #endif + + + static double getDisplayDPI (int index) + { + double dpiX = (DisplayWidth (display, index) * 25.4) / DisplayWidthMM (display, index); + double dpiY = (DisplayHeight (display, index) * 25.4) / DisplayHeightMM (display, index); + return (dpiX + dpiY) / 2.0; + } + + static double getScaleForDisplay (const String& name, const ExtendedInfo& info) + { + if (! name.isEmpty()) + { + // Ubuntu and derived distributions now save a per-display scale factor as a configuration + // variable. This can be changed in the Monitor system settings panel. + ChildProcess dconf; + if (File ("/usr/bin/dconf").existsAsFile() && + dconf.start ("/usr/bin/dconf read /com/ubuntu/user-interface/scale-factor", ChildProcess::wantStdOut)) + { + if (dconf.waitForProcessToFinish (200)) + { + String jsonOutput = dconf.readAllProcessOutput().replaceCharacter ('\'', '"'); + + if (dconf.getExitCode() == 0 && jsonOutput.isNotEmpty()) + { + var jsonVar = JSON::parse (jsonOutput); + + if (DynamicObject* object = jsonVar.getDynamicObject()) + { + var scaleFactorVar = object->getProperty (name); + if (! scaleFactorVar.isVoid()) + { + double scaleFactor = ((double) scaleFactorVar) / 8.0; + + if (scaleFactor > 0.0) + return scaleFactor; + } + } + } + } + } + } + + { + // Other gnome based distros now use gsettings for a global scale factor + ChildProcess gsettings; + if (File ("/usr/bin/gsettings").existsAsFile() && + gsettings.start ("/usr/bin/gsettings get org.gnome.desktop.interface scaling-factor", ChildProcess::wantStdOut)) + { + if (gsettings.waitForProcessToFinish (200)) + { + StringArray gsettingsOutput = StringArray::fromTokens (gsettings.readAllProcessOutput(), true); + if (gsettingsOutput.size() >= 2 && gsettingsOutput[1].length() > 0) + { + double scaleFactor = gsettingsOutput[1].getDoubleValue(); + + if (scaleFactor > 0.0) + return scaleFactor; + } + } + } + } + + // If no scale factor is set by GNOME or Ubuntu then calculate from monitor dpi + // We use the same approach as chromium which simply divides the dpi by 96 + // and then rounds the result + return round (info.dpi / 150.0); + } + + //============================================================================== + void queryDisplayInfos (::Display* dpy, double masterScale) noexcept + { + ScopedXLock xlock; + + #if JUCE_USE_XRANDR + { + int major_opcode, first_event, first_error; + + if (XQueryExtension (dpy, "RANDR", &major_opcode, &first_event, &first_error)) + { + XRandrWrapper& xrandr = XRandrWrapper::getInstance(); + + ScopedPointer screens; + + const int numMonitors = ScreenCount (dpy); + RROutput mainDisplay = xrandr.getOutputPrimary (dpy, RootWindow (dpy, 0)); + + for (int i = 0; i < numMonitors; ++i) + { + if ((screens = xrandr.getScreenResources (dpy, RootWindow (dpy, i))).get()) + { + for (int j = 0; j < screens->noutput; ++j) + { + if (! screens->outputs[j]) + continue; + + // Xrandr on the raspberry pi fails to determine the main display (mainDisplay == 0)! + // Detect this edge case and make the first found display the main display + if (! mainDisplay) + mainDisplay = screens->outputs[j]; + + ScopedPointer output; + + if ((output = xrandr.getOutputInfo (dpy, screens.get(), screens->outputs[j])).get()) + { + if (! output->crtc) + continue; + + ScopedPointer crtc; + + if ((crtc = xrandr.getCrtcInfo (dpy, screens.get(), output->crtc)).get()) + { + ExtendedInfo e; + e.totalBounds = Rectangle (crtc->x, crtc->y, + (int) crtc->width, (int) crtc->height); + e.usableBounds = e.totalBounds.withZeroOrigin(); // Support for usable area is not implemented in JUCE yet + e.topLeftScaled = e.totalBounds.getTopLeft(); + e.isMain = (mainDisplay == screens->outputs[j]) && (i == 0); + e.dpi = getDisplayDPI (0); + + // The raspberry pi returns a zero sized display, so we need to guard for divide-by-zero + if (output->mm_width > 0 && output->mm_height > 0) + e.dpi = ((static_cast (crtc->width) * 25.4 * 0.5) / static_cast (output->mm_width)) + + ((static_cast (crtc->height) * 25.4 * 0.5) / static_cast (output->mm_height)); + + e.scale = masterScale * getScaleForDisplay (output->name, e); + + infos.add (e); + } + } + } + } + } + } + } + if (infos.size() == 0) + #endif + #if JUCE_USE_XINERAMA + { + Array screens = XineramaQueryDisplays (dpy); + int numMonitors = screens.size(); + + for (int index = 0; index < numMonitors; ++index) + { + for (int j = numMonitors; --j >= 0;) + { + if (screens[j].screen_number == index) + { + ExtendedInfo e; + e.totalBounds = Rectangle (screens[j].x_org, + screens[j].y_org, + screens[j].width, + screens[j].height); + e.usableBounds = e.totalBounds.withZeroOrigin(); // Support for usable area is not implemented in JUCE yet + e.topLeftScaled = e.totalBounds.getTopLeft(); // this will be overwritten by updatePositions later + e.isMain = (index == 0); + e.scale = masterScale; + e.dpi = getDisplayDPI (0); // (all screens share the same DPI) + + infos.add (e); + } + } + } + } + + if (infos.size() == 0) + #endif + { + Atom hints = Atoms::getIfExists ("_NET_WORKAREA"); + + if (hints != None) + { + const int numMonitors = ScreenCount (dpy); + + for (int i = 0; i < numMonitors; ++i) + { + GetXProperty prop (RootWindow (dpy, i), hints, 0, 4, false, XA_CARDINAL); + + if (prop.success && prop.actualType == XA_CARDINAL && prop.actualFormat == 32 && prop.numItems == 4) + { + const long* const position = (const long*) prop.data; + + ExtendedInfo e; + e.totalBounds = Rectangle ((int) position[0], (int) position[1], + (int) position[2], (int) position[3]); + e.usableBounds = e.totalBounds.withZeroOrigin(); // Support for usable area is not implemented in JUCE yet + e.topLeftScaled = e.totalBounds.getTopLeft(); // this will be overwritten by updatePositions later + e.isMain = (infos.size() == 0); + e.scale = masterScale; + e.dpi = getDisplayDPI (i); + + infos.add (e); + } + } + } + + if (infos.size() == 0) + { + ExtendedInfo e; + e.totalBounds = Rectangle (DisplayWidth (dpy, DefaultScreen (dpy)), + DisplayHeight (dpy, DefaultScreen (dpy))); + e.usableBounds = e.totalBounds; // Support for usable area is not implemented in JUCE yet + e.topLeftScaled = e.totalBounds.getTopLeft(); // this will be overwritten by updatePositions later + e.isMain = true; + e.scale = masterScale; + e.dpi = getDisplayDPI (0); + + infos.add (e); + } + } + } + + //============================================================================== + struct SortByCoordinate + { + bool sortByYCoordinate; + + SortByCoordinate (bool byYCoordinate) + : sortByYCoordinate (byYCoordinate) + { + } + + int compareElements (const ExtendedInfo* a, const ExtendedInfo* b) + { + int coordinateA, coordinateB; + + if (sortByYCoordinate) + { + coordinateA = a->totalBounds.getY(); + coordinateB = b->totalBounds.getY(); + } + else + { + coordinateA = a->totalBounds.getX(); + coordinateB = b->totalBounds.getX(); + } + + return coordinateA - coordinateB; + } + }; + + //============================================================================== + void updateScaledDisplayCoordinate(bool updateYCoordinates) + { + if (infos.size() < 2) + return; + + Array copy; + { + SortByCoordinate sorter (updateYCoordinates); + for (int i = 0; i < infos.size(); ++i) + copy.addSorted (sorter, &infos.getReference (i)); + } + + for (int i = 1; i < copy.size(); ++i) + { + ExtendedInfo& current = *copy[i]; + + // Is this screen's position aligned to any other previous display? + for (int j = i - 1; j >= 0; --j) + { + ExtendedInfo& other = *copy[j]; + int prevCoordinate = updateYCoordinates ? other.totalBounds.getBottom() : other.totalBounds.getRight(); + int curCoordinate = updateYCoordinates ? current.totalBounds.getY() : current.totalBounds.getX(); + if (prevCoordinate == curCoordinate) + { + // both displays are aligned! As "other" comes before "current" in the array, it must already + // have a valid topLeftScaled which we can use + Point topLeftScaled = other.topLeftScaled; + topLeftScaled += Point (other.totalBounds.getWidth(), other.totalBounds.getHeight()) / other.scale; + + if (updateYCoordinates) + current.topLeftScaled.setY (topLeftScaled.getY()); + else + current.topLeftScaled.setX (topLeftScaled.getX()); + + break; + } + } + } + } + + void updatePositions() + { + updateScaledDisplayCoordinate (false); + updateScaledDisplayCoordinate (true); + } +}; + +DisplayGeometry* DisplayGeometry::instance = nullptr; + +#if JUCE_USE_XRANDR +DisplayGeometry::XRandrWrapper* DisplayGeometry::XRandrWrapper::instance = nullptr; + +void ContainerDeletePolicy::destroy (XRRScreenResources* ptr) +{ + if (ptr != nullptr) + DisplayGeometry::XRandrWrapper::getInstance().freeScreenResources (ptr); +} + +void ContainerDeletePolicy::destroy (XRROutputInfo* ptr) +{ + if (ptr != nullptr) + DisplayGeometry::XRandrWrapper::getInstance().freeOutputInfo (ptr); +} + +void ContainerDeletePolicy::destroy (XRRCrtcInfo* ptr) +{ + if (ptr != nullptr) + DisplayGeometry::XRandrWrapper::getInstance().freeCrtcInfo (ptr); +} +#endif + //============================================================================== namespace PixmapHelpers { @@ -751,13 +1424,13 @@ namespace PixmapHelpers { ScopedXLock xlock; - const int width = image.getWidth(); - const int height = image.getHeight(); + const unsigned int width = (unsigned int) image.getWidth(); + const unsigned int height = (unsigned int) image.getHeight(); HeapBlock colour (width * height); int index = 0; - for (int y = 0; y < height; ++y) - for (int x = 0; x < width; ++x) + for (int y = 0; y < (int) height; ++y) + for (int x = 0; x < (int) width; ++x) colour[index++] = image.getPixelAt (x, y).getARGB(); XImage* ximage = XCreateImage (display, CopyFromParent, 24, ZPixmap, @@ -778,21 +1451,21 @@ namespace PixmapHelpers { ScopedXLock xlock; - const int width = image.getWidth(); - const int height = image.getHeight(); - const int stride = (width + 7) >> 3; + const unsigned int width = (unsigned int) image.getWidth(); + const unsigned int height = (unsigned int) image.getHeight(); + const unsigned int stride = (width + 7) >> 3; HeapBlock mask; mask.calloc (stride * height); const bool msbfirst = (BitmapBitOrder (display) == MSBFirst); - for (int y = 0; y < height; ++y) + for (unsigned int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) + for (unsigned int x = 0; x < width; ++x) { const char bit = (char) (1 << (msbfirst ? (7 - (x & 7)) : (x & 7))); - const int offset = y * stride + (x >> 3); + const unsigned int offset = y * stride + (x >> 3); - if (image.getPixelAt (x, y).getAlpha() >= 128) + if (image.getPixelAt ((int) x, (int) y).getAlpha() >= 128) mask[offset] |= bit; } } @@ -829,7 +1502,8 @@ public: windowH (0), parentWindow (0), fullScreen (false), mapped (false), visual (nullptr), depth (0), - isAlwaysOnTop (comp.isAlwaysOnTop()) + isAlwaysOnTop (comp.isAlwaysOnTop()), + currentScaleFactor (1.0) { // it's dangerous to create a window on a thread other than the message thread.. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); @@ -939,7 +1613,7 @@ public: clientMsg.format = 32; clientMsg.message_type = Atoms::get().windowState; clientMsg.data.l[0] = 0; // Remove - clientMsg.data.l[1] = fs; + clientMsg.data.l[1] = (long) fs; clientMsg.data.l[2] = 0; clientMsg.data.l[3] = 1; // Normal Source @@ -957,17 +1631,22 @@ public: bounds = newBounds.withSize (jmax (1, newBounds.getWidth()), jmax (1, newBounds.getHeight())); + currentScaleFactor = DisplayGeometry::getInstance().findDisplayForRect (bounds, true).scale; + + Rectangle physicalBounds = + DisplayGeometry::scaledToPhysical (bounds); + WeakReference deletionChecker (&component); ScopedXLock xlock; XSizeHints* const hints = XAllocSizeHints(); hints->flags = USSize | USPosition; - hints->x = bounds.getX(); - hints->y = bounds.getY(); - hints->width = bounds.getWidth(); - hints->height = bounds.getHeight(); + hints->x = physicalBounds.getX(); + hints->y = physicalBounds.getY(); + hints->width = physicalBounds.getWidth(); + hints->height = physicalBounds.getHeight(); - if ((getStyleFlags() & (windowHasTitleBar | windowIsResizable)) == windowHasTitleBar) + if ((getStyleFlags() & windowIsResizable) == 0) { hints->min_width = hints->max_width = hints->width; hints->min_height = hints->max_height = hints->height; @@ -978,10 +1657,10 @@ public: XFree (hints); XMoveResizeWindow (display, windowH, - bounds.getX() - windowBorder.getLeft(), - bounds.getY() - windowBorder.getTop(), - bounds.getWidth(), - bounds.getHeight()); + physicalBounds.getX() - windowBorder.getLeft(), + physicalBounds.getY() - windowBorder.getTop(), + (unsigned int) physicalBounds.getWidth(), + (unsigned int) physicalBounds.getHeight()); if (deletionChecker != nullptr) { @@ -1101,7 +1780,7 @@ public: if (XQueryTree (display, root, &root, &parent, &windowList, &windowListSize) != 0) { - for (int i = windowListSize; --i >= 0;) + for (int i = (int) windowListSize; --i >= 0;) { if (LinuxComponentPeer* const peer = LinuxComponentPeer::getPeerFor (windowList[i])) { @@ -1139,11 +1818,13 @@ public: ::Window root, child; int wx, wy; - unsigned int ww, wh, bw, depth; + unsigned int ww, wh, bw, bitDepth; ScopedXLock xlock; - return XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &depth) + localPos *= currentScaleFactor; + + return XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth) && XTranslateCoordinates (display, windowH, windowH, localPos.getX(), localPos.getY(), &wx, &wy, &child) && child == None; } @@ -1231,7 +1912,7 @@ public: && atts.map_state == IsViewable && ! isFocused()) { - XSetInputFocus (display, windowH, RevertToParent, getUserTime()); + XSetInputFocus (display, windowH, RevertToParent, (::Time) getUserTime()); isActiveApplication = true; } } @@ -1251,7 +1932,7 @@ public: void setIcon (const Image& newIcon) override { const int dataSize = newIcon.getWidth() * newIcon.getHeight() + 2; - HeapBlock data (dataSize); + HeapBlock data ((size_t) dataSize); int index = 0; data[index++] = (unsigned long) newIcon.getWidth(); @@ -1359,6 +2040,8 @@ public: void handleKeyPressEvent (XKeyEvent& keyEvent) { + const ModifierKeys oldMods (currentModifiers); + char utf8 [64] = { 0 }; juce_wchar unicodeChar = 0; int keyCode = 0; @@ -1367,7 +2050,7 @@ public: { ScopedXLock xlock; - updateKeyStates (keyEvent.keycode, true); + updateKeyStates ((int) keyEvent.keycode, true); String oldLocale (::setlocale (LC_ALL, 0)); ::setlocale (LC_ALL, ""); @@ -1380,12 +2063,11 @@ public: keyCode = (int) unicodeChar; if (keyCode < 0x20) - keyCode = XkbKeycodeToKeysym (display, keyEvent.keycode, 0, currentModifiers.isShiftDown() ? 1 : 0); + keyCode = (int) XkbKeycodeToKeysym (display, (::KeyCode) keyEvent.keycode, 0, currentModifiers.isShiftDown() ? 1 : 0); keyDownChange = (sym != NoSymbol) && ! updateKeyModifiersFromSym (sym, true); } - const ModifierKeys oldMods (currentModifiers); bool keyPressed = false; if ((sym & 0xff00) == 0xff00 || keyCode == XK_ISO_Left_Tab) @@ -1495,12 +2177,12 @@ public: { if (! isKeyReleasePartOfAutoRepeat (keyEvent)) { - updateKeyStates (keyEvent.keycode, false); + updateKeyStates ((int) keyEvent.keycode, false); KeySym sym; { ScopedXLock xlock; - sym = XkbKeycodeToKeysym (display, keyEvent.keycode, 0, 0); + sym = XkbKeycodeToKeysym (display, (::KeyCode) keyEvent.keycode, 0, 0); } const ModifierKeys oldMods (currentModifiers); @@ -1515,9 +2197,9 @@ public: } template - static Point getMousePos (const EventType& e) noexcept + Point getMousePos (const EventType& e) noexcept { - return Point ((float) e.x, (float) e.y); + return Point ((float) e.x, (float) e.y) / currentScaleFactor; } void handleWheelEvent (const XButtonPressedEvent& buttonPressEvent, const float amount) @@ -1540,7 +2222,7 @@ public: void handleButtonPressEvent (const XButtonPressedEvent& buttonPressEvent) { - updateKeyModifiers (buttonPressEvent.state); + updateKeyModifiers ((int) buttonPressEvent.state); switch (pointerMap [buttonPressEvent.button - Button1]) { @@ -1557,7 +2239,7 @@ public: void handleButtonReleaseEvent (const XButtonReleasedEvent& buttonRelEvent) { - updateKeyModifiers (buttonRelEvent.state); + updateKeyModifiers ((int) buttonRelEvent.state); if (parentWindow != 0) updateWindowBounds(); @@ -1580,7 +2262,7 @@ public: void handleMotionNotifyEvent (const XPointerMovedEvent& movedEvent) { - updateKeyModifiers (movedEvent.state); + updateKeyModifiers ((int) movedEvent.state); lastMousePos = Point (movedEvent.x_root, movedEvent.y_root); @@ -1599,7 +2281,7 @@ public: if (! currentModifiers.isAnyMouseButtonDown()) { - updateKeyModifiers (enterEvent.state); + updateKeyModifiers ((int) enterEvent.state); handleMouseEvent (0, getMousePos (enterEvent), currentModifiers, getEventTime (enterEvent)); } } @@ -1612,7 +2294,7 @@ public: if (((! currentModifiers.isAnyMouseButtonDown()) && leaveEvent.mode == NotifyNormal) || leaveEvent.mode == NotifyUngrab) { - updateKeyModifiers (leaveEvent.state); + updateKeyModifiers ((int) leaveEvent.state); handleMouseEvent (0, getMousePos (leaveEvent), currentModifiers, getEventTime (leaveEvent)); } } @@ -1645,8 +2327,10 @@ public: &child); } + // exposeEvent is in local window local coordinates so do not convert with + // physicalToScaled, but rather use currentScaleFactor repaint (Rectangle (exposeEvent.x, exposeEvent.y, - exposeEvent.width, exposeEvent.height)); + exposeEvent.width, exposeEvent.height) / currentScaleFactor); while (XEventsQueued (display, QueuedAfterFlush) > 0) { @@ -1657,7 +2341,7 @@ public: XNextEvent (display, &nextEvent); const XExposeEvent& nextExposeEvent = (const XExposeEvent&) nextEvent.xexpose; repaint (Rectangle (nextExposeEvent.x, nextExposeEvent.y, - nextExposeEvent.width, nextExposeEvent.height)); + nextExposeEvent.width, nextExposeEvent.height) / currentScaleFactor); } } @@ -1747,7 +2431,7 @@ public: && XGetWindowAttributes (display, clientMsg.window, &atts)) { if (atts.map_state == IsViewable) - XSetInputFocus (display, clientMsg.window, RevertToParent, clientMsg.data.l[1]); + XSetInputFocus (display, clientMsg.window, RevertToParent, (::Time) clientMsg.data.l[1]); } } } @@ -1818,6 +2502,12 @@ public: XDefineCursor (display, windowH, cursor); } + //============================================================================== + double getCurrentScale() noexcept + { + return currentScaleFactor; + } + //============================================================================== bool dontRepaint; @@ -1876,7 +2566,7 @@ private: if (! isTimerRunning()) startTimer (repaintTimerPeriod); - regionsNeedingRepaint.add (area); + regionsNeedingRepaint.add (area * peer.currentScaleFactor); } void performAnyPendingRepaintsNow() @@ -1906,7 +2596,7 @@ private: #endif (totalArea.getWidth() + 31) & ~31, (totalArea.getHeight() + 31) & ~31, - false, peer.depth, peer.visual)); + false, (unsigned int) peer.depth, peer.visual)); } startTimer (repaintTimerPeriod); @@ -1921,20 +2611,24 @@ private: { ScopedPointer context (peer.getComponent().getLookAndFeel() .createGraphicsContext (image, -totalArea.getPosition(), adjustedList)); + context->addTransform (AffineTransform::scale ((float) peer.currentScaleFactor)); peer.handlePaint (*context); } for (const Rectangle* i = originalRepaintRegion.begin(), * const e = originalRepaintRegion.end(); i != e; ++i) { + XBitmapImage* xbitmap = static_cast (image.getPixelData()); #if JUCE_USE_XSHM - if (XSHMHelpers::isShmAvailable()) + if (xbitmap->isUsingXShm()) ++shmPaintsPending; #endif - static_cast (image.getPixelData()) - ->blitToWindow (peer.windowH, - i->getX(), i->getY(), i->getWidth(), i->getHeight(), - i->getX() - totalArea.getX(), i->getY() - totalArea.getY()); + + xbitmap->blitToWindow (peer.windowH, + i->getX(), i->getY(), + (unsigned int) i->getWidth(), + (unsigned int) i->getHeight(), + i->getX() - totalArea.getX(), i->getY() - totalArea.getY()); } } @@ -1972,6 +2666,7 @@ private: int depth; BorderSize windowBorder; bool isAlwaysOnTop; + double currentScaleFactor; enum { KeyPressEventType = 2 }; struct MotifWmHints @@ -2184,8 +2879,9 @@ private: { Atom netHints [2]; - if ((styleFlags & windowIsTemporary) != 0 - || ((styleFlags & windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows())) + if (styleFlags & windowIsTemporary) + netHints [0] = Atoms::getIfExists ("_NET_WM_WINDOW_TYPE_TOOLTIP"); + else if ((styleFlags & windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows()) netHints [0] = Atoms::getIfExists ("_NET_WM_WINDOW_TYPE_COMBO"); else netHints [0] = Atoms::getIfExists ("_NET_WM_WINDOW_TYPE_NORMAL"); @@ -2215,6 +2911,8 @@ private: const int screen = DefaultScreen (display); Window root = RootWindow (display, screen); + parentWindow = parentToAddTo; + // Try to obtain a 32-bit visual or fallback to 24 or 16 visual = Visuals::findVisualFormat ((styleFlags & windowIsSemiTransparent) ? 32 : 24, depth); @@ -2233,7 +2931,7 @@ private: swa.border_pixel = 0; swa.background_pixmap = None; swa.colormap = colormap; - swa.override_redirect = (component.isAlwaysOnTop() && (styleFlags & windowIsTemporary) != 0) ? True : False; + swa.override_redirect = (styleFlags & windowIsTemporary) ? True : False; swa.event_mask = getAllEventsMask(); windowH = XCreateWindow (display, parentToAddTo != 0 ? parentToAddTo : root, @@ -2279,7 +2977,7 @@ private: const Atoms& atoms = Atoms::get(); // Associate the PID, allowing to be shut down when something goes wrong - unsigned long pid = getpid(); + unsigned long pid = (unsigned long) getpid(); xchangeProperty (windowH, atoms.pid, XA_CARDINAL, 32, &pid, 1); // Set window manager protocols @@ -2330,7 +3028,7 @@ private: static int64 getEventTime (::Time t) { static int64 eventTimeOffset = 0x12345678; - const int64 thisMessageTime = t; + const int64 thisMessageTime = (int64) t; if (eventTimeOffset == 0x12345678) eventTimeOffset = Time::currentTimeMillis() - thisMessageTime; @@ -2377,15 +3075,20 @@ private: { Window root, child; int wx = 0, wy = 0; - unsigned int ww = 0, wh = 0, bw, depth; + unsigned int ww = 0, wh = 0, bw, bitDepth; ScopedXLock xlock; - if (XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &depth)) + if (XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth)) if (! XTranslateCoordinates (display, windowH, root, 0, 0, &wx, &wy, &child)) wx = wy = 0; - bounds.setBounds (wx, wy, ww, wh); + Rectangle physicalBounds (wx, wy, (int) ww, (int) wh); + + currentScaleFactor = + DisplayGeometry::getInstance().findDisplayForRect (physicalBounds, false).scale; + + bounds = DisplayGeometry::physicalToScaled (physicalBounds); } } @@ -2445,7 +3148,7 @@ private: msg.display = display; msg.window = dragAndDropSourceWindow; msg.format = 32; - msg.data.l[0] = windowH; + msg.data.l[0] = (long) windowH; ScopedXLock xlock; XSendEvent (display, dragAndDropSourceWindow, False, 0, (XEvent*) &msg); @@ -2457,7 +3160,7 @@ private: msg.display = display; msg.window = targetWindow; msg.format = 32; - msg.data.l[0] = windowH; + msg.data.l[0] = (long) windowH; ScopedXLock xlock; return XSendEvent (display, targetWindow, False, 0, (XEvent*) &msg) != 0; @@ -2485,9 +3188,9 @@ private: const int numMimeTypes = dragState.getNumMimeTypes(); msg.data.l[1] = (dragState.xdndVersion << 24) | (numMimeTypes > 3); - msg.data.l[2] = numMimeTypes > 0 ? mimeTypes[0] : 0; - msg.data.l[3] = numMimeTypes > 1 ? mimeTypes[1] : 0; - msg.data.l[4] = numMimeTypes > 2 ? mimeTypes[2] : 0; + msg.data.l[2] = numMimeTypes > 0 ? (long) mimeTypes[0] : 0; + msg.data.l[3] = numMimeTypes > 1 ? (long) mimeTypes[1] : 0; + msg.data.l[4] = numMimeTypes > 2 ? (long) mimeTypes[2] : 0; sendExternalDragAndDropMessage (msg, targetWindow); } @@ -2499,15 +3202,16 @@ private: msg.message_type = Atoms::get().XdndPosition; - const Point mousePos (Desktop::getInstance().getMousePosition()); + Point mousePos (Desktop::getInstance().getMousePosition()); if (dragState.silentRect.contains (mousePos)) // we've been asked to keep silent return; + mousePos = DisplayGeometry::scaledToPhysical (mousePos); msg.data.l[1] = 0; msg.data.l[2] = (mousePos.x << 16) | mousePos.y; msg.data.l[3] = CurrentTime; - msg.data.l[4] = Atoms::get().XdndActionCopy; // this is all JUCE currently supports + msg.data.l[4] = (long) Atoms::get().XdndActionCopy; // this is all JUCE currently supports dragState.expectingStatus = sendExternalDragAndDropMessage (msg, targetWindow); } @@ -2519,7 +3223,7 @@ private: msg.message_type = Atoms::get().XdndStatus; msg.data.l[1] = (acceptDrop ? 1 : 0) | 2; // 2 indicates that we want to receive position messages - msg.data.l[4] = dropAction; + msg.data.l[4] = (long) dropAction; sendDragAndDropMessage (msg); } @@ -2568,7 +3272,7 @@ private: evt.xselectionrequest.property, targetType, 8, dragState.textOrFiles.toRawUTF8(), - dragState.textOrFiles.getNumBytesAsUTF8()); + (int) dragState.textOrFiles.getNumBytesAsUTF8()); } XSendEvent (display, evt.xselectionrequest.requestor, True, 0, &s); @@ -2587,10 +3291,10 @@ private: || (Atom) clientMsg.data.l[4] == Atoms::get().XdndActionPrivate)) { if ((clientMsg.data.l[1] & 2) == 0) // target requests silent rectangle - dragState.silentRect.setBounds (clientMsg.data.l[2] >> 16, - clientMsg.data.l[2] & 0xffff, - clientMsg.data.l[3] >> 16, - clientMsg.data.l[3] & 0xffff); + dragState.silentRect.setBounds ((int) clientMsg.data.l[2] >> 16, + (int) clientMsg.data.l[2] & 0xffff, + (int) clientMsg.data.l[3] >> 16, + (int) clientMsg.data.l[3] & 0xffff); dragState.canDrop = true; } @@ -2657,7 +3361,7 @@ private: if (dragAndDropSourceWindow == 0) return; - dragAndDropSourceWindow = clientMsg.data.l[0]; + dragAndDropSourceWindow = (::Window) clientMsg.data.l[0]; Point dropPos ((int) clientMsg.data.l[2] >> 16, (int) clientMsg.data.l[2] & 0xffff); @@ -2728,7 +3432,7 @@ private: return; } - dragAndDropSourceWindow = clientMsg.data.l[0]; + dragAndDropSourceWindow = (::Window) clientMsg.data.l[0]; if ((clientMsg.data.l[1] & 1) != 0) { @@ -2752,7 +3456,7 @@ private: { for (int i = 2; i < 5; ++i) if (clientMsg.data.l[i] != None) - srcMimeTypeAtomList.add (clientMsg.data.l[i]); + srcMimeTypeAtomList.add ((unsigned long) clientMsg.data.l[i]); if (srcMimeTypeAtomList.size() == 0) { @@ -2789,7 +3493,7 @@ private: if (! prop.success) break; - dropData.append (prop.data, prop.numItems * prop.actualFormat / 8); + dropData.append (prop.data, prop.numItems * (size_t) prop.actualFormat / 8); if (prop.bytesLeft <= 0) break; @@ -2829,7 +3533,7 @@ private: dragAndDropCurrentMimeType, Atoms::getCreating ("JXSelectionWindowProperty"), windowH, - clientMsg.data.l[2]); + (::Time) clientMsg.data.l[2]); } } @@ -3002,10 +3706,10 @@ ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept //============================================================================== -void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool /* allowMenusAndBars */) +void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool /* allowMenusAndBars */) { if (enableOrDisable) - kioskModeComponent->setBounds (getDisplays().getMainDisplay().totalArea); + comp->setBounds (getDisplays().getMainDisplay().totalArea); } //============================================================================== @@ -3015,118 +3719,23 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAtt } //============================================================================== -static double getDisplayDPI (int index) -{ - double dpiX = (DisplayWidth (display, index) * 25.4) / DisplayWidthMM (display, index); - double dpiY = (DisplayHeight (display, index) * 25.4) / DisplayHeightMM (display, index); - return (dpiX + dpiY) / 2.0; -} - void Desktop::Displays::findDisplays (float masterScale) { - if (display == 0) - return; - - ScopedXLock xlock; + DisplayGeometry& geometry = DisplayGeometry::getOrCreateInstance (display, masterScale); - #if JUCE_USE_XINERAMA - int major_opcode, first_event, first_error; - - if (XQueryExtension (display, "XINERAMA", &major_opcode, &first_event, &first_error)) + for (int i = 0; i < geometry.infos.size(); ++i) { - typedef Bool (*tXineramaIsActive) (::Display*); - typedef XineramaScreenInfo* (*tXineramaQueryScreens) (::Display*, int*); - - static tXineramaIsActive xineramaIsActive = nullptr; - static tXineramaQueryScreens xineramaQueryScreens = nullptr; - - if (xineramaIsActive == nullptr || xineramaQueryScreens == nullptr) - { - void* h = dlopen ("libXinerama.so", RTLD_GLOBAL | RTLD_NOW); + const DisplayGeometry::ExtendedInfo& info = geometry.infos.getReference (i); + Desktop::Displays::Display d; - if (h == nullptr) - h = dlopen ("libXinerama.so.1", RTLD_GLOBAL | RTLD_NOW); + d.isMain = info.isMain; + d.scale = masterScale * info.scale; + d.dpi = info.dpi; - if (h != nullptr) - { - xineramaIsActive = (tXineramaIsActive) dlsym (h, "XineramaIsActive"); - xineramaQueryScreens = (tXineramaQueryScreens) dlsym (h, "XineramaQueryScreens"); - } - } + d.totalArea = DisplayGeometry::physicalToScaled (info.totalBounds); + d.userArea = (info.usableBounds / d.scale) + info.topLeftScaled; - if (xineramaIsActive != nullptr - && xineramaQueryScreens != nullptr - && xineramaIsActive (display)) - { - int numMonitors = 0; - - if (XineramaScreenInfo* const screens = xineramaQueryScreens (display, &numMonitors)) - { - for (int index = 0; index < numMonitors; ++index) - { - for (int j = numMonitors; --j >= 0;) - { - if (screens[j].screen_number == index) - { - Display d; - d.userArea = d.totalArea = Rectangle (screens[j].x_org, - screens[j].y_org, - screens[j].width, - screens[j].height) / masterScale; - d.isMain = (index == 0); - d.scale = masterScale; - d.dpi = getDisplayDPI (0); // (all screens share the same DPI) - - displays.add (d); - } - } - } - - XFree (screens); - } - } - } - - if (displays.size() == 0) - #endif - { - Atom hints = Atoms::getIfExists ("_NET_WORKAREA"); - - if (hints != None) - { - const int numMonitors = ScreenCount (display); - - for (int i = 0; i < numMonitors; ++i) - { - GetXProperty prop (RootWindow (display, i), hints, 0, 4, false, XA_CARDINAL); - - if (prop.success && prop.actualType == XA_CARDINAL && prop.actualFormat == 32 && prop.numItems == 4) - { - const long* const position = (const long*) prop.data; - - Display d; - d.userArea = d.totalArea = Rectangle (position[0], position[1], - position[2], position[3]) / masterScale; - d.isMain = (displays.size() == 0); - d.scale = masterScale; - d.dpi = getDisplayDPI (i); - - displays.add (d); - } - } - } - - if (displays.size() == 0) - { - Display d; - d.userArea = d.totalArea = Rectangle (DisplayWidth (display, DefaultScreen (display)), - DisplayHeight (display, DefaultScreen (display))) * masterScale; - d.isMain = true; - d.scale = masterScale; - d.dpi = getDisplayDPI (0); - - displays.add (d); - } + displays.add (d); } } @@ -3177,7 +3786,7 @@ Point MouseInputSource::getCurrentRawMousePosition() x = y = -1; } - return Point ((float) x, (float) y); + return DisplayGeometry::physicalToScaled (Point ((float) x, (float) y)); } void MouseInputSource::setRawMousePosition (Point newPosition) @@ -3186,6 +3795,7 @@ void MouseInputSource::setRawMousePosition (Point newPosition) { ScopedXLock xlock; Window root = RootWindow (display, DefaultScreen (display)); + newPosition = DisplayGeometry::scaledToPhysical (newPosition); XWarpPointer (display, None, root, 0, 0, 0, 0, roundToInt (newPosition.getX()), roundToInt (newPosition.getY())); } } @@ -3237,8 +3847,8 @@ void* CustomMouseCursorInfo::create() const return nullptr; ScopedXLock xlock; - const unsigned int imageW = image.getWidth(); - const unsigned int imageH = image.getHeight(); + const unsigned int imageW = (unsigned int) image.getWidth(); + const unsigned int imageH = (unsigned int) image.getHeight(); int hotspotX = hotspot.x; int hotspotY = hotspot.y; @@ -3275,10 +3885,10 @@ void* CustomMouseCursorInfo::create() const if (xcursorSupportsARGB != nullptr) { - if (XcursorImage* xcImage = xcursorImageCreate (imageW, imageH)) + if (XcursorImage* xcImage = xcursorImageCreate ((int) imageW, (int) imageH)) { - xcImage->xhot = hotspotX; - xcImage->yhot = hotspotY; + xcImage->xhot = (XcursorDim) hotspotX; + xcImage->yhot = (XcursorDim) hotspotY; XcursorPixel* dest = xcImage->pixels; for (int y = 0; y < (int) imageH; ++y) @@ -3300,17 +3910,17 @@ void* CustomMouseCursorInfo::create() const if (! XQueryBestCursor (display, root, imageW, imageH, &cursorW, &cursorH)) return nullptr; - Image im (Image::ARGB, cursorW, cursorH, true); + Image im (Image::ARGB, (int) cursorW, (int) cursorH, true); { Graphics g (im); if (imageW > cursorW || imageH > cursorH) { - hotspotX = (hotspotX * cursorW) / imageW; - hotspotY = (hotspotY * cursorH) / imageH; + hotspotX = (hotspotX * (int) cursorW) / (int) imageW; + hotspotY = (hotspotY * (int) cursorH) / (int) imageH; - g.drawImageWithin (image, 0, 0, imageW, imageH, + g.drawImageWithin (image, 0, 0, (int) imageW, (int) imageH, RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize, false); } @@ -3320,19 +3930,19 @@ void* CustomMouseCursorInfo::create() const } } - const int stride = (cursorW + 7) >> 3; + const unsigned int stride = (cursorW + 7) >> 3; HeapBlock maskPlane, sourcePlane; maskPlane.calloc (stride * cursorH); sourcePlane.calloc (stride * cursorH); const bool msbfirst = (BitmapBitOrder (display) == MSBFirst); - for (int y = cursorH; --y >= 0;) + for (int y = (int) cursorH; --y >= 0;) { - for (int x = cursorW; --x >= 0;) + for (int x = (int) cursorW; --x >= 0;) { const char mask = (char) (1 << (msbfirst ? (7 - (x & 7)) : (x & 7))); - const int offset = y * stride + (x >> 3); + const unsigned int offset = (unsigned int) y * stride + ((unsigned int) x >> 3); const Colour c (im.getPixelAt (x, y)); @@ -3348,7 +3958,8 @@ void* CustomMouseCursorInfo::create() const black.red = black.green = black.blue = 0; white.red = white.green = white.blue = 0xffff; - void* result = (void*) XCreatePixmapCursor (display, sourcePixmap, maskPixmap, &white, &black, hotspotX, hotspotY); + void* result = (void*) XCreatePixmapCursor (display, sourcePixmap, maskPixmap, &white, &black, + (unsigned int) hotspotX, (unsigned int) hotspotY); XFreePixmap (display, sourcePixmap); XFreePixmap (display, maskPixmap); @@ -3469,7 +4080,16 @@ void LookAndFeel::playAlertSound() { std::cout << "\a" << std::flush; } +//============================================================================== +Rectangle juce_LinuxScaledToPhysicalBounds(ComponentPeer* peer, const Rectangle& bounds) +{ + Rectangle retval = bounds; + if (LinuxComponentPeer* linuxPeer = dynamic_cast (peer)) + retval *= linuxPeer->getCurrentScale(); + + return retval; +} //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED @@ -3559,7 +4179,7 @@ const int KeyPress::numberPadSeparator = (XK_KP_Separator & 0xff)| Keys::ex const int KeyPress::numberPadDecimalPoint = (XK_KP_Decimal & 0xff)| Keys::extendedKeyModifier; const int KeyPress::numberPadEquals = (XK_KP_Equal & 0xff)| Keys::extendedKeyModifier; const int KeyPress::numberPadDelete = (XK_KP_Delete & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::playKey = (0xffeeff00) | Keys::extendedKeyModifier; -const int KeyPress::stopKey = (0xffeeff01) | Keys::extendedKeyModifier; -const int KeyPress::fastForwardKey = (0xffeeff02) | Keys::extendedKeyModifier; -const int KeyPress::rewindKey = (0xffeeff03) | Keys::extendedKeyModifier; +const int KeyPress::playKey = ((int) 0xffeeff00) | Keys::extendedKeyModifier; +const int KeyPress::stopKey = ((int) 0xffeeff01) | Keys::extendedKeyModifier; +const int KeyPress::fastForwardKey = ((int) 0xffeeff02) | Keys::extendedKeyModifier; +const int KeyPress::rewindKey = ((int) 0xffeeff03) | Keys::extendedKeyModifier; diff --git a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 8bd4d5850..cb0cf5efd 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -665,7 +665,7 @@ public: #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 const float invScale = 1.0f - (float) [ev magnification]; - if (invScale != 0.0f) + if (invScale > 0.0f) handleMagnifyGesture (0, getMousePos (ev, view), getMouseTime (ev), 1.0f / invScale); #endif (void) ev; @@ -878,6 +878,13 @@ public: return (getStyleFlags() & juce::ComponentPeer::windowIgnoresKeyPresses) == 0; } + bool canBecomeMainWindow() + { + Component* owner = &juce::ComponentPeer::getComponent(); + + return dynamic_cast (owner) != nullptr; + } + void becomeKeyWindow() { handleBroughtToFront(); @@ -1718,6 +1725,7 @@ struct JuceNSWindowClass : public ObjCClass addIvar ("owner"); addMethod (@selector (canBecomeKeyWindow), canBecomeKeyWindow, "c@:"); + addMethod (@selector (canBecomeMainWindow), canBecomeMainWindow, "c@:"); addMethod (@selector (becomeKeyWindow), becomeKeyWindow, "v@:"); addMethod (@selector (windowShouldClose:), windowShouldClose, "c@:@"); addMethod (@selector (constrainFrameRect:toScreen:), constrainFrameRect, @encode (NSRect), "@:", @encode (NSRect), "@"); @@ -1751,6 +1759,15 @@ private: && ! owner->sendModalInputAttemptIfBlocked(); } + static BOOL canBecomeMainWindow (id self, SEL) + { + NSViewComponentPeer* const owner = getOwner (self); + + return owner != nullptr + && owner->canBecomeMainWindow() + && ! owner->sendModalInputAttemptIfBlocked(); + } + static void becomeKeyWindow (id self, SEL) { sendSuperclassMessage (self, @selector (becomeKeyWindow)); diff --git a/source/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.h b/source/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.h index c1f4eb474..dbf5fcbe2 100644 --- a/source/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.h +++ b/source/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.h @@ -30,6 +30,41 @@ /** Expresses a coordinate as a dynamically evaluated expression. + When using relative coordinates to position components, the following symbols are available: + - "left", "right", "top", "bottom" refer to the position of those edges in this component, so + e.g. for a component whose width is always 100, you might set the right edge to the "left + 100". + - "[id].left", "[id].right", "[id].top", "[id].bottom", "[id].width", "[id].height", where [id] is + the identifier of one of this component's siblings. A component's identifier is set with + Component::setComponentID(). So for example if you want your component to always be 50 pixels to the + right of the one called "xyz", you could set your left edge to be "xyz.right + 50". + - Instead of an [id], you can use the name "parent" to refer to this component's parent. Like + any other component, these values are relative to their component's parent, so "parent.right" won't be + very useful for positioning a component because it refers to a position with the parent's parent.. but + "parent.width" can be used for setting positions relative to the parent's size. E.g. to make a 10x10 + component which remains 1 pixel away from its parent's bottom-right, you could use + "right - 10, bottom - 10, parent.width - 1, parent.height - 1". + - The name of one of the parent component's markers can also be used as a symbol. For markers to be + used, the parent component must implement its Component::getMarkers() method, and return at least one + valid MarkerList. So if you want your component's top edge to be 10 pixels below the + marker called "foobar", you'd set it to "foobar + 10". + + See the Expression class for details about the operators that are supported, but for example + if you wanted to make your component remains centred within its parent with a size of 100, 100, + you could express it as: + @code myComp.setBounds (RelativeBounds ("parent.width / 2 - 50, parent.height / 2 - 50, left + 100, top + 100")); + @endcode + ..or an alternative way to achieve the same thing: + @code myComp.setBounds (RelativeBounds ("right - 100, bottom - 100, parent.width / 2 + 50, parent.height / 2 + 50")); + @endcode + + Or if you wanted a 100x100 component whose top edge is lined up to a marker called "topMarker" and + which is positioned 50 pixels to the right of another component called "otherComp", you could write: + @code myComp.setBounds (RelativeBounds ("otherComp.right + 50, topMarker, left + 100, top + 100")); + @endcode + + Be careful not to make your coordinate expressions recursive, though, or exceptions and assertions will + be thrown! + @see RelativePoint, RelativeRectangle */ class JUCE_API RelativeCoordinate diff --git a/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp b/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp index 1c706e0d9..a8ff7b797 100644 --- a/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp +++ b/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp @@ -192,7 +192,7 @@ public: { } - bool registerCoordinates() + bool registerCoordinates() override { bool ok = addCoordinate (rectangle.left); ok = addCoordinate (rectangle.right) && ok; @@ -206,7 +206,7 @@ public: return rectangle == other; } - void applyToComponentBounds() + void applyToComponentBounds() override { for (int i = 32; --i >= 0;) { @@ -222,7 +222,7 @@ public: jassertfalse; // Seems to be a recursive reference! } - void applyNewBounds (const Rectangle& newBounds) + void applyNewBounds (const Rectangle& newBounds) override { if (newBounds != getComponent().getBounds()) { diff --git a/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h b/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h index 2fd3d7adc..256d0bc43 100644 --- a/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h +++ b/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h @@ -108,8 +108,6 @@ public: These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. - To change the colours of the menu that pops up - @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds diff --git a/source/modules/juce_gui_basics/properties/juce_PropertyPanel.cpp b/source/modules/juce_gui_basics/properties/juce_PropertyPanel.cpp index dc93d7cf6..cb5aa6394 100644 --- a/source/modules/juce_gui_basics/properties/juce_PropertyPanel.cpp +++ b/source/modules/juce_gui_basics/properties/juce_PropertyPanel.cpp @@ -22,15 +22,14 @@ ============================================================================== */ -class PropertyPanel::SectionComponent : public Component +struct PropertyPanel::SectionComponent : public Component { -public: SectionComponent (const String& sectionTitle, - const Array & newProperties, - const bool sectionIsOpen_) + const Array& newProperties, + const bool sectionIsOpen) : Component (sectionTitle), titleHeight (sectionTitle.isNotEmpty() ? 22 : 0), - sectionIsOpen (sectionIsOpen_) + isOpen (sectionIsOpen) { propertyComps.addArray (newProperties); @@ -49,7 +48,7 @@ public: void paint (Graphics& g) override { if (titleHeight > 0) - getLookAndFeel().drawPropertyPanelSectionHeader (g, getName(), isOpen(), getWidth(), titleHeight); + getLookAndFeel().drawPropertyPanelSectionHeader (g, getName(), isOpen, getWidth(), titleHeight); } void resized() override @@ -68,20 +67,18 @@ public: { int y = titleHeight; - if (isOpen()) - { + if (isOpen) for (int i = propertyComps.size(); --i >= 0;) y += propertyComps.getUnchecked(i)->getPreferredHeight(); - } return y; } void setOpen (const bool open) { - if (sectionIsOpen != open) + if (isOpen != open) { - sectionIsOpen = open; + isOpen = open; for (int i = propertyComps.size(); --i >= 0;) propertyComps.getUnchecked(i)->setVisible (open); @@ -91,11 +88,6 @@ public: } } - bool isOpen() const - { - return sectionIsOpen; - } - void refreshAll() const { for (int i = propertyComps.size(); --i >= 0;) @@ -105,32 +97,27 @@ public: void mouseUp (const MouseEvent& e) override { if (e.getMouseDownX() < titleHeight - && e.x < titleHeight - && e.y < titleHeight - && e.getNumberOfClicks() != 2) - { - setOpen (! isOpen()); - } + && e.x < titleHeight + && e.getNumberOfClicks() != 2) + mouseDoubleClick (e); } void mouseDoubleClick (const MouseEvent& e) override { if (e.y < titleHeight) - setOpen (! isOpen()); + setOpen (! isOpen); } -private: - OwnedArray propertyComps; - int titleHeight; - bool sectionIsOpen; + OwnedArray propertyComps; + const int titleHeight; + bool isOpen; JUCE_DECLARE_NON_COPYABLE (SectionComponent) }; //============================================================================== -class PropertyPanel::PropertyHolderComponent : public Component +struct PropertyPanel::PropertyHolderComponent : public Component { -public: PropertyHolderComponent() {} void paint (Graphics&) override {} @@ -157,21 +144,26 @@ public: sections.getUnchecked(i)->refreshAll(); } - void clear() + void insertSection (int indexToInsertAt, SectionComponent* newSection) { - sections.clear(); + sections.insert (indexToInsertAt, newSection); + addAndMakeVisible (newSection, 0); } - void addSection (SectionComponent* newSection) + SectionComponent* getSectionWithNonEmptyName (const int targetIndex) const noexcept { - sections.add (newSection); - addAndMakeVisible (newSection, 0); - } + for (int index = 0, i = 0; i < sections.size(); ++i) + { + SectionComponent* const section = sections.getUnchecked (i); - int getNumSections() const noexcept { return sections.size(); } - SectionComponent* getSection (const int index) const { return sections [index]; } + if (section->getName().isNotEmpty()) + if (index++ == targetIndex) + return section; + } + + return nullptr; + } -private: OwnedArray sections; JUCE_DECLARE_NON_COPYABLE (PropertyHolderComponent) @@ -226,14 +218,14 @@ void PropertyPanel::clear() { if (! isEmpty()) { - propertyHolderComponent->clear(); + propertyHolderComponent->sections.clear(); updatePropHolderLayout(); } } bool PropertyPanel::isEmpty() const { - return propertyHolderComponent->getNumSections() == 0; + return propertyHolderComponent->sections.size() == 0; } int PropertyPanel::getTotalContentHeight() const @@ -241,25 +233,26 @@ int PropertyPanel::getTotalContentHeight() const return propertyHolderComponent->getHeight(); } -void PropertyPanel::addProperties (const Array & newProperties) +void PropertyPanel::addProperties (const Array& newProperties) { if (isEmpty()) repaint(); - propertyHolderComponent->addSection (new SectionComponent (String::empty, newProperties, true)); + propertyHolderComponent->insertSection (-1, new SectionComponent (String::empty, newProperties, true)); updatePropHolderLayout(); } void PropertyPanel::addSection (const String& sectionTitle, - const Array & newProperties, - const bool shouldBeOpen) + const Array& newProperties, + const bool shouldBeOpen, + const int indexToInsertAt) { jassert (sectionTitle.isNotEmpty()); if (isEmpty()) repaint(); - propertyHolderComponent->addSection (new SectionComponent (sectionTitle, newProperties, shouldBeOpen)); + propertyHolderComponent->insertSection (indexToInsertAt, new SectionComponent (sectionTitle, newProperties, shouldBeOpen)); updatePropHolderLayout(); } @@ -286,9 +279,9 @@ StringArray PropertyPanel::getSectionNames() const { StringArray s; - for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i) + for (int i = 0; i < propertyHolderComponent->sections.size(); ++i) { - SectionComponent* const section = propertyHolderComponent->getSection (i); + SectionComponent* const section = propertyHolderComponent->sections.getUnchecked(i); if (section->getName().isNotEmpty()) s.add (section->getName()); @@ -299,63 +292,30 @@ StringArray PropertyPanel::getSectionNames() const bool PropertyPanel::isSectionOpen (const int sectionIndex) const { - int index = 0; - - for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i) - { - SectionComponent* const section = propertyHolderComponent->getSection (i); - - if (section->getName().isNotEmpty()) - { - if (index == sectionIndex) - return section->isOpen(); - - ++index; - } - } + if (SectionComponent* s = propertyHolderComponent->getSectionWithNonEmptyName (sectionIndex)) + return s->isOpen; return false; } void PropertyPanel::setSectionOpen (const int sectionIndex, const bool shouldBeOpen) { - int index = 0; - - for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i) - { - SectionComponent* const section = propertyHolderComponent->getSection (i); - - if (section->getName().isNotEmpty()) - { - if (index == sectionIndex) - { - section->setOpen (shouldBeOpen); - break; - } - - ++index; - } - } + if (SectionComponent* s = propertyHolderComponent->getSectionWithNonEmptyName (sectionIndex)) + s->setOpen (shouldBeOpen); } void PropertyPanel::setSectionEnabled (const int sectionIndex, const bool shouldBeEnabled) { - int index = 0; + if (SectionComponent* s = propertyHolderComponent->getSectionWithNonEmptyName (sectionIndex)) + s->setEnabled (shouldBeEnabled); +} - for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i) +void PropertyPanel::removeSection (int sectionIndex) +{ + if (SectionComponent* s = propertyHolderComponent->getSectionWithNonEmptyName (sectionIndex)) { - SectionComponent* const section = propertyHolderComponent->getSection (i); - - if (section->getName().isNotEmpty()) - { - if (index == sectionIndex) - { - section->setEnabled (shouldBeEnabled); - break; - } - - ++index; - } + propertyHolderComponent->sections.removeObject (s); + updatePropHolderLayout(); } } @@ -408,7 +368,7 @@ void PropertyPanel::setMessageWhenEmpty (const String& newMessage) } } -const String& PropertyPanel::getMessageWhenEmpty() const +const String& PropertyPanel::getMessageWhenEmpty() const noexcept { return messageWhenEmpty; } diff --git a/source/modules/juce_gui_basics/properties/juce_PropertyPanel.h b/source/modules/juce_gui_basics/properties/juce_PropertyPanel.h index 3bd611594..628957ff3 100644 --- a/source/modules/juce_gui_basics/properties/juce_PropertyPanel.h +++ b/source/modules/juce_gui_basics/properties/juce_PropertyPanel.h @@ -67,8 +67,9 @@ public: /** Adds a set of properties to the panel. - These properties are added at the bottom of the list, under a section heading with - a plus/minus button that allows it to be opened and closed. + These properties are added under a section heading with a plus/minus button that + allows it to be opened and closed. If indexToInsertAt is < 0 then it will be added + at the end of the list, or before the given index if the index is non-zero. The components in the list will be owned by this object and will be automatically deleted later on when no longer needed. @@ -77,7 +78,8 @@ public: */ void addSection (const String& sectionTitle, const Array& newPropertyComponents, - bool shouldSectionInitiallyBeOpen = true); + bool shouldSectionInitiallyBeOpen = true, + int indexToInsertAt = -1); /** Calls the refresh() method of all PropertyComponents in the panel */ void refreshAll() const; @@ -111,6 +113,11 @@ public: */ void setSectionEnabled (int sectionIndex, bool shouldBeEnabled); + /** Remove one of the sections using the section index. + The index is from 0 up to the number of items returned by getSectionNames(). + */ + void removeSection (int sectionIndex); + //============================================================================== /** Saves the current state of open/closed sections so it can be restored later. @@ -140,7 +147,7 @@ public: /** Returns the message that is displayed when there are no properties. @see setMessageWhenEmpty */ - const String& getMessageWhenEmpty() const; + const String& getMessageWhenEmpty() const noexcept; //============================================================================== /** @internal */ @@ -149,10 +156,9 @@ public: void resized() override; private: - class SectionComponent; - Viewport viewport; - class PropertyHolderComponent; + struct SectionComponent; + struct PropertyHolderComponent; PropertyHolderComponent* propertyHolderComponent; String messageWhenEmpty; diff --git a/source/modules/juce_gui_basics/widgets/juce_ComboBox.h b/source/modules/juce_gui_basics/widgets/juce_ComboBox.h index 5a8c9484f..d176f5d3e 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ComboBox.h +++ b/source/modules/juce_gui_basics/widgets/juce_ComboBox.h @@ -63,7 +63,7 @@ public: ~ComboBox(); //============================================================================== - /** Sets whether the test in the combo-box is editable. + /** Sets whether the text in the combo-box is editable. The default state for a new ComboBox is non-editable, and can only be changed by choosing from the drop-down list. @@ -338,7 +338,7 @@ public: These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. - To change the colours of the menu that pops up + To change the colours of the menu that pops up, you can set the colour IDs in PopupMenu::ColourIDs. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ diff --git a/source/modules/juce_gui_basics/widgets/juce_Label.cpp b/source/modules/juce_gui_basics/widgets/juce_Label.cpp index 2531394b6..75d376657 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Label.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_Label.cpp @@ -29,7 +29,7 @@ Label::Label (const String& name, const String& labelText) font (15.0f), justification (Justification::centredLeft), border (1, 5, 1, 5), - minimumHorizontalScale (0.7f), + minimumHorizontalScale (0.0f), keyboardType (TextEditor::textKeyboard), editSingleClick (false), editDoubleClick (false), @@ -162,7 +162,8 @@ void Label::componentMovedOrResized (Component& component, bool /*wasMoved*/, bo if (leftOfOwnerComp) { - setSize (jmin (f.getStringWidth (textValue.toString()) + 8, component.getX()), + setSize (jmin (roundToInt (f.getStringWidthFloat (textValue.toString()) + 0.5f) + getBorderSize().getLeftAndRight(), + component.getX()), component.getHeight()); setTopRightPosition (component.getX(), component.getY()); @@ -170,7 +171,7 @@ void Label::componentMovedOrResized (Component& component, bool /*wasMoved*/, bo else { setSize (component.getWidth(), - 8 + roundToInt (f.getHeight())); + getBorderSize().getTopAndBottom() + 6 + roundToInt (f.getHeight() + 0.5f)); setTopLeftPosition (component.getX(), component.getY() - getHeight()); } @@ -197,10 +198,13 @@ void Label::editorShown (TextEditor* textEditor) listeners.callChecked (checker, &LabelListener::editorShown, this, *textEditor); } -void Label::editorAboutToBeHidden (TextEditor*) +void Label::editorAboutToBeHidden (TextEditor* textEditor) { if (ComponentPeer* const peer = getPeer()) peer->dismissPendingTextInput(); + + Component::BailOutChecker checker (this); + listeners.callChecked (checker, &LabelListener::editorHidden, this, *textEditor); } void Label::showEditor() diff --git a/source/modules/juce_gui_basics/widgets/juce_Label.h b/source/modules/juce_gui_basics/widgets/juce_Label.h index e71e5d522..1c6303b3c 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Label.h +++ b/source/modules/juce_gui_basics/widgets/juce_Label.h @@ -154,7 +154,7 @@ public: bool isAttachedOnLeft() const noexcept { return leftOfOwnerComp; } /** Specifies the minimum amount that the font can be squashed horizontally before it starts - using ellipsis. + using ellipsis. Use a value of 0 for a default value. @see Graphics::drawFittedText */ @@ -188,6 +188,9 @@ public: /** Called when a Label goes into editing mode and displays a TextEditor. */ virtual void editorShown (Label*, TextEditor&) {} + + /** Called when a Label is about to delete its TextEditor and exit editing mode. */ + virtual void editorHidden (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 5364a0d09..aeb611405 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp @@ -68,7 +68,7 @@ public: if (isEnabled()) { - if (! selected) + if (owner.selectOnMouseDown && ! selected) { owner.selectRowsBasedOnModifierKeys (row, e.mods, false); @@ -106,16 +106,21 @@ public: { if (isEnabled() && ! (e.mouseWasClicked() || isDragging)) { - const SparseSet selectedRows (owner.getSelectedRows()); + SparseSet rowsToDrag; - if (selectedRows.size() > 0) + if (owner.selectOnMouseDown || owner.isRowSelected (row)) + rowsToDrag = owner.getSelectedRows(); + else + rowsToDrag.addRange (Range::withStartAndLength (row, 1)); + + if (rowsToDrag.size() > 0) { - const var dragDescription (m->getDragSourceDescription (selectedRows)); + const var dragDescription (m->getDragSourceDescription (rowsToDrag)); if (! (dragDescription.isVoid() || (dragDescription.isString() && dragDescription.toString().isEmpty()))) { isDragging = true; - owner.startDragAndDrop (e, dragDescription, true); + owner.startDragAndDrop (e, rowsToDrag, dragDescription, true); } } } @@ -370,7 +375,8 @@ ListBox::ListBox (const String& name, ListBoxModel* const m) lastRowSelected (-1), multipleSelection (false), alwaysFlipSelection (false), - hasDoneInitialUpdate (false) + hasDoneInitialUpdate (false), + selectOnMouseDown (true) { addAndMakeVisible (viewport = new ListViewport (*this)); @@ -394,15 +400,9 @@ void ListBox::setModel (ListBoxModel* const newModel) } } -void ListBox::setMultipleSelectionEnabled (bool b) noexcept -{ - multipleSelection = b; -} - -void ListBox::setClickingTogglesRowSelection (bool b) noexcept -{ - alwaysFlipSelection = b; -} +void ListBox::setMultipleSelectionEnabled (bool b) noexcept { multipleSelection = b; } +void ListBox::setClickingTogglesRowSelection (bool b) noexcept { alwaysFlipSelection = b; } +void ListBox::setRowSelectedOnMouseDown (bool b) noexcept { selectOnMouseDown = b; } void ListBox::setMouseMoveSelectsRows (bool b) { @@ -886,7 +886,7 @@ void ListBox::repaintRow (const int rowNumber) noexcept repaint (getRowPosition (rowNumber, true)); } -Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) +Image ListBox::createSnapshotOfRows (const SparseSet& rows, int& imageX, int& imageY) { Rectangle imageArea; const int firstRow = getRowContainingPosition (0, viewport->getY()); @@ -895,7 +895,7 @@ Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) { Component* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i); - if (rowComp != nullptr && isRowSelected (firstRow + i)) + if (rowComp != nullptr && rows.contains (firstRow + i)) { const Point pos (getLocalPoint (rowComp, Point())); const Rectangle rowRect (pos.getX(), pos.getY(), rowComp->getWidth(), rowComp->getHeight()); @@ -912,7 +912,7 @@ Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) { Component* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i); - if (rowComp != nullptr && isRowSelected (firstRow + i)) + if (rowComp != nullptr && rows.contains (firstRow + i)) { Graphics g (snapshot); g.setOrigin (getLocalPoint (rowComp, Point()) - imageArea.getPosition()); @@ -929,12 +929,12 @@ Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) return snapshot; } -void ListBox::startDragAndDrop (const MouseEvent& e, const var& dragDescription, bool allowDraggingToOtherWindows) +void ListBox::startDragAndDrop (const MouseEvent& e, const SparseSet& rowsToDrag, const var& dragDescription, bool allowDraggingToOtherWindows) { if (DragAndDropContainer* const dragContainer = DragAndDropContainer::findParentDragContainerFor (this)) { int x, y; - Image dragImage (createSnapshotOfSelectedRows (x, y)); + Image dragImage = createSnapshotOfRows (rowsToDrag, x, y); MouseEvent e2 (e.getEventRelativeTo (this)); const Point p (x - e2.x, y - e2.y); diff --git a/source/modules/juce_gui_basics/widgets/juce_ListBox.h b/source/modules/juce_gui_basics/widgets/juce_ListBox.h index f3a537fa0..b48061f43 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ListBox.h +++ b/source/modules/juce_gui_basics/widgets/juce_ListBox.h @@ -146,7 +146,7 @@ public: @see DragAndDropContainer::startDragging */ - virtual var getDragSourceDescription (const SparseSet& currentlySelectedRows); + virtual var getDragSourceDescription (const SparseSet& rowsToDescribe); /** You can override this to provide tool tips for specific rows. @see TooltipClient @@ -230,6 +230,11 @@ public: */ void setClickingTogglesRowSelection (bool flipRowSelection) noexcept; + /** Sets whether a row should be selected when the mouse is pressed or released. + By default this is true, but you may want to turn it off. + */ + void setRowSelectedOnMouseDown (bool isSelectedOnMouseDown) noexcept; + /** Makes the list react to mouse moves by selecting the row that the mouse if over. This function is here primarily for the ComboBox class to use, but might be @@ -513,8 +518,8 @@ public: */ void repaintRow (int rowNumber) noexcept; - /** This fairly obscure method creates an image that just shows the currently - selected row components. + /** This fairly obscure method creates an image that shows the row components specified + in rows (for example, these could be the currently selected row components). It's a handy method for doing drag-and-drop, as it can be passed to the DragAndDropContainer for use as the drag image. @@ -525,7 +530,7 @@ public: @see Component::createComponentSnapshot */ - virtual Image createSnapshotOfSelectedRows (int& x, int& y); + virtual Image createSnapshotOfRows (const SparseSet& rows, int& x, int& y); /** Returns the viewport that this ListBox uses. @@ -556,7 +561,8 @@ public: /** @internal */ void parentHierarchyChanged() override; /** @internal */ - void startDragAndDrop (const MouseEvent&, const var& dragDescription, bool allowDraggingToOtherWindows); + void startDragAndDrop (const MouseEvent&, const SparseSet& rowsToDrag, + const var& dragDescription, bool allowDraggingToOtherWindows); private: //============================================================================== @@ -571,7 +577,7 @@ private: int totalItems, rowHeight, minimumRowWidth; int outlineThickness; int lastRowSelected; - bool multipleSelection, alwaysFlipSelection, hasDoneInitialUpdate; + bool multipleSelection, alwaysFlipSelection, hasDoneInitialUpdate, selectOnMouseDown; SparseSet selected; void selectRowInternal (int rowNumber, bool dontScrollToShowThisRow, @@ -580,6 +586,9 @@ private: #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // This method's bool parameter has changed: see the new method signature. JUCE_DEPRECATED (void setSelectedRows (const SparseSet&, bool)); + // This method has been replaced by the more flexible method createSnapshotOfRows. + // Please call createSnapshotOfRows (getSelectedRows(), x, y) to get the same behaviour. + JUCE_DEPRECATED (virtual void createSnapshotOfSelectedRows (int&, int&)) {} #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ListBox) diff --git a/source/modules/juce_gui_basics/widgets/juce_Slider.cpp b/source/modules/juce_gui_basics/widgets/juce_Slider.cpp index 0d4f0da5b..4e076769d 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -99,6 +99,12 @@ public: || style == RotaryHorizontalVerticalDrag; } + bool isBar() const noexcept + { + return style == LinearBar + || style == LinearBarVertical; + } + bool incDecDragDirectionIsHorizontal() const noexcept { return incDecButtonMode == incDecButtonsDraggable_Horizontal @@ -740,7 +746,7 @@ public: void handleAbsoluteDrag (const MouseEvent& e) { const float mousePos = (isHorizontal() || style == RotaryHorizontalDrag) ? e.position.x : e.position.y; - double newPos = (mousePos - sliderRegionStart) / (double) sliderRegionSize; + double newPos = 0; if (style == RotaryHorizontalDrag || style == RotaryVerticalDrag @@ -774,6 +780,8 @@ public: } else { + newPos = (mousePos - sliderRegionStart) / (double) sliderRegionSize; + if (isVertical()) newPos = 1.0 - newPos; } @@ -860,7 +868,7 @@ public: if (parentForPopupDisplay != nullptr) parentForPopupDisplay->addChildComponent (popup); else - popup->addToDesktop (0); + popup->addToDesktop (ComponentPeer::windowIsTemporary); popup->setVisible (true); } @@ -1120,98 +1128,34 @@ public: } } - void resized (const Rectangle& localBounds, LookAndFeel& lf) + //============================================================================== + void resized (LookAndFeel& lf) { - int minXSpace = 0; - int minYSpace = 0; + SliderLayout layout = lf.getSliderLayout (owner); - if (textBoxPos == TextBoxLeft || textBoxPos == TextBoxRight) - minXSpace = 30; - else - minYSpace = 15; - - const int tbw = jmax (0, jmin (textBoxWidth, localBounds.getWidth() - minXSpace)); - const int tbh = jmax (0, jmin (textBoxHeight, localBounds.getHeight() - minYSpace)); + sliderRect = layout.sliderBounds; - if (style == LinearBar || style == LinearBarVertical) - { - if (valueBox != nullptr) - valueBox->setBounds (localBounds); - } - else - { - if (textBoxPos == NoTextBox) - { - sliderRect = localBounds; - } - else if (textBoxPos == TextBoxLeft) - { - valueBox->setBounds (0, (localBounds.getHeight() - tbh) / 2, tbw, tbh); - sliderRect.setBounds (tbw, 0, localBounds.getWidth() - tbw, localBounds.getHeight()); - } - else if (textBoxPos == TextBoxRight) - { - valueBox->setBounds (localBounds.getWidth() - tbw, (localBounds.getHeight() - tbh) / 2, tbw, tbh); - sliderRect.setBounds (0, 0, localBounds.getWidth() - tbw, localBounds.getHeight()); - } - else if (textBoxPos == TextBoxAbove) - { - valueBox->setBounds ((localBounds.getWidth() - tbw) / 2, 0, tbw, tbh); - sliderRect.setBounds (0, tbh, localBounds.getWidth(), localBounds.getHeight() - tbh); - } - else if (textBoxPos == TextBoxBelow) - { - valueBox->setBounds ((localBounds.getWidth() - tbw) / 2, localBounds.getHeight() - tbh, tbw, tbh); - sliderRect.setBounds (0, 0, localBounds.getWidth(), localBounds.getHeight() - tbh); - } - } - - const int indent = lf.getSliderThumbRadius (owner); - - if (style == LinearBar) - { - const int barIndent = 1; - sliderRegionStart = barIndent; - sliderRegionSize = localBounds.getWidth() - barIndent * 2; - - sliderRect.setBounds (sliderRegionStart, barIndent, - sliderRegionSize, localBounds.getHeight() - barIndent * 2); - } - else if (style == LinearBarVertical) - { - const int barIndent = 1; - sliderRegionStart = barIndent; - sliderRegionSize = localBounds.getHeight() - barIndent * 2; + if (valueBox != nullptr) + valueBox->setBounds (layout.textBoxBounds); - sliderRect.setBounds (barIndent, sliderRegionStart, - localBounds.getWidth() - barIndent * 2, sliderRegionSize); - } - else if (isHorizontal()) + if (isHorizontal()) { - sliderRegionStart = sliderRect.getX() + indent; - sliderRegionSize = jmax (1, sliderRect.getWidth() - indent * 2); - - sliderRect.setBounds (sliderRegionStart, sliderRect.getY(), - sliderRegionSize, sliderRect.getHeight()); + sliderRegionStart = layout.sliderBounds.getX(); + sliderRegionSize = layout.sliderBounds.getWidth(); } else if (isVertical()) { - sliderRegionStart = sliderRect.getY() + indent; - sliderRegionSize = jmax (1, sliderRect.getHeight() - indent * 2); - - sliderRect.setBounds (sliderRect.getX(), sliderRegionStart, - sliderRect.getWidth(), sliderRegionSize); + sliderRegionStart = layout.sliderBounds.getY(); + sliderRegionSize = layout.sliderBounds.getHeight(); } - else + else if (style == IncDecButtons) { - sliderRegionStart = 0; - sliderRegionSize = 100; - } - - if (style == IncDecButtons) resizeIncDecButtons(); + } } + //============================================================================== + void resizeIncDecButtons() { Rectangle buttonRect (sliderRect); @@ -1296,14 +1240,14 @@ public: setLookAndFeel (&s.getLookAndFeel()); } - void paintContent (Graphics& g, int w, int h) + void paintContent (Graphics& g, int w, int h) override { g.setFont (font); g.setColour (owner.findColour (TooltipWindow::textColourId, true)); g.drawFittedText (text, Rectangle (w, h), Justification::centred, 1); } - void getContentSize (int& w, int& h) + void getContentSize (int& w, int& h) override { w = font.getStringWidth (text) + 18; h = (int) (font.getHeight() * 1.6f); @@ -1582,12 +1526,13 @@ void Slider::setScrollWheelEnabled (const bool enabled) { pimpl->scrollWheel bool Slider::isHorizontal() const noexcept { return pimpl->isHorizontal(); } bool Slider::isVertical() const noexcept { return pimpl->isVertical(); } bool Slider::isRotary() const noexcept { return pimpl->isRotary(); } +bool Slider::isBar() const noexcept { return pimpl->isBar(); } float Slider::getPositionOfValue (const double value) { return pimpl->getPositionOfValue (value); } //============================================================================== void Slider::paint (Graphics& g) { pimpl->paint (g, getLookAndFeel()); } -void Slider::resized() { pimpl->resized (getLocalBounds(), getLookAndFeel()); } +void Slider::resized() { pimpl->resized (getLookAndFeel()); } void Slider::focusOfChildComponentChanged (FocusChangeType) { repaint(); } diff --git a/source/modules/juce_gui_basics/widgets/juce_Slider.h b/source/modules/juce_gui_basics/widgets/juce_Slider.h index 8d3e912b6..695819c7e 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Slider.h +++ b/source/modules/juce_gui_basics/widgets/juce_Slider.h @@ -756,6 +756,8 @@ public: bool isVertical() const noexcept; /** True if the slider is in a rotary mode. */ bool isRotary() const noexcept; + /** True if the slider is in a linear bar mode. */ + bool isBar() const noexcept; //============================================================================== /** A set of colour IDs to use to change the colour of various aspects of the slider. @@ -780,6 +782,16 @@ public: textBoxOutlineColourId = 0x1001700 /**< The colour to use for a border around the text-editor box. */ }; + //============================================================================== + /** A struct defining the placement of the slider area and the text box area + relative to the bounds of the whole Slider component. + */ + struct SliderLayout + { + Rectangle sliderBounds; + Rectangle textBoxBounds; + }; + //============================================================================== /** This abstract base class is implemented by LookAndFeel classes to provide slider drawing functionality. @@ -830,6 +842,8 @@ public: virtual Font getSliderPopupFont (Slider&) = 0; virtual int getSliderPopupPlacement (Slider&) = 0; + virtual SliderLayout getSliderLayout (Slider&) = 0; + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // These methods' parameters have changed: see the new method signatures. virtual void createSliderButton (bool) {} diff --git a/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h b/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h index b9eba92d4..f737d2107 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h +++ b/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h @@ -231,7 +231,6 @@ public: Rectangle getColumnPosition (int index) const; /** Finds the column ID at a given x-position in the component. - If there is a column at this point this returns its ID, or if not, it will return 0. */ int getColumnIdAtX (int xToFind) const; @@ -412,9 +411,9 @@ private: bool isVisible() const; }; - OwnedArray columns; - Array listeners; - ScopedPointer dragOverlayComp; + OwnedArray columns; + Array listeners; + ScopedPointer dragOverlayComp; class DragOverlayComp; bool columnsChanged, columnsResized, sortChanged, menuActive, stretchToFit; diff --git a/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp b/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp index cedec8db2..9ba5c6900 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp @@ -146,16 +146,21 @@ public: { if (isEnabled() && owner.getModel() != nullptr && ! (e.mouseWasClicked() || isDragging)) { - const SparseSet selectedRows (owner.getSelectedRows()); + SparseSet rowsToDrag; - if (selectedRows.size() > 0) + if (owner.selectOnMouseDown || owner.isRowSelected (row)) + rowsToDrag = owner.getSelectedRows(); + else + rowsToDrag.addRange (Range::withStartAndLength (row, 1)); + + if (rowsToDrag.size() > 0) { - const var dragDescription (owner.getModel()->getDragSourceDescription (selectedRows)); + const var dragDescription (owner.getModel()->getDragSourceDescription (rowsToDrag)); if (! (dragDescription.isVoid() || (dragDescription.isString() && dragDescription.toString().isEmpty()))) { isDragging = true; - owner.startDragAndDrop (e, dragDescription, true); + owner.startDragAndDrop (e, rowsToDrag, dragDescription, true); } } } diff --git a/source/modules/juce_gui_basics/widgets/juce_TextEditor.h b/source/modules/juce_gui_basics/widgets/juce_TextEditor.h index 006badd23..ccaa1bce8 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TextEditor.h +++ b/source/modules/juce_gui_basics/widgets/juce_TextEditor.h @@ -195,6 +195,8 @@ public: These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. + NB: You can also set the caret colour using CaretComponent::caretColourId + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds diff --git a/source/modules/juce_gui_basics/widgets/juce_Toolbar.cpp b/source/modules/juce_gui_basics/widgets/juce_Toolbar.cpp index 9b2b7cbb9..3962b696e 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Toolbar.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_Toolbar.cpp @@ -36,7 +36,7 @@ public: } bool getToolbarItemSizes (int toolbarThickness, bool /*isToolbarVertical*/, - int& preferredSize, int& minSize, int& maxSize) + int& preferredSize, int& minSize, int& maxSize) override { if (fixedSize <= 0) { @@ -158,7 +158,7 @@ public: { ToolbarItemComponent* const tc = bar.items.getUnchecked(i); - if (dynamic_cast (tc) == nullptr && ! tc->isVisible()) + if (dynamic_cast (tc) == nullptr && ! tc->isVisible()) { oldIndexes.insert (0, i); addAndMakeVisible (tc, 0); @@ -174,7 +174,7 @@ public: { for (int i = 0; i < getNumChildComponents(); ++i) { - if (ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i))) + if (ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i))) { tc->setVisible (false); const int index = oldIndexes.remove (i); @@ -196,7 +196,7 @@ public: for (int i = 0; i < getNumChildComponents(); ++i) { - if (ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i))) + if (ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i))) { int preferredSize = 1, minSize = 1, maxSize = 1; @@ -219,7 +219,7 @@ public: setSize (maxX + 8, y + height + 8); } - void getIdealSize (int& idealWidth, int& idealHeight) + void getIdealSize (int& idealWidth, int& idealHeight) override { idealWidth = getWidth(); idealHeight = getHeight(); @@ -455,7 +455,7 @@ void Toolbar::updateAllItemPositions (const bool animate) tc->setStyle (toolbarStyle); - Spacer* const spacer = dynamic_cast (tc); + Spacer* const spacer = dynamic_cast (tc); int preferredSize = 1, minSize = 1, maxSize = 1; @@ -555,7 +555,7 @@ bool Toolbar::isInterestedInDragSource (const SourceDetails& dragSourceDetails) void Toolbar::itemDragMove (const SourceDetails& dragSourceDetails) { - if (ToolbarItemComponent* const tc = dynamic_cast (dragSourceDetails.sourceComponent.get())) + if (ToolbarItemComponent* const tc = dynamic_cast (dragSourceDetails.sourceComponent.get())) { if (! items.contains (tc)) { @@ -623,7 +623,7 @@ void Toolbar::itemDragMove (const SourceDetails& dragSourceDetails) void Toolbar::itemDragExit (const SourceDetails& dragSourceDetails) { - if (ToolbarItemComponent* const tc = dynamic_cast (dragSourceDetails.sourceComponent.get())) + if (ToolbarItemComponent* const tc = dynamic_cast (dragSourceDetails.sourceComponent.get())) { if (isParentOf (tc)) { @@ -636,7 +636,7 @@ void Toolbar::itemDragExit (const SourceDetails& dragSourceDetails) void Toolbar::itemDropped (const SourceDetails& dragSourceDetails) { - if (ToolbarItemComponent* const tc = dynamic_cast (dragSourceDetails.sourceComponent.get())) + if (ToolbarItemComponent* const tc = dynamic_cast (dragSourceDetails.sourceComponent.get())) tc->setState (Button::buttonNormal); } @@ -669,7 +669,7 @@ public: bool canModalEventBeSentToComponent (const Component* comp) override { return toolbar.isParentOf (comp) - || dynamic_cast (comp) != nullptr; + || dynamic_cast (comp) != nullptr; } void positionNearBar() diff --git a/source/modules/juce_gui_basics/widgets/juce_Toolbar.h b/source/modules/juce_gui_basics/widgets/juce_Toolbar.h index 1a85726e5..32d011488 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Toolbar.h +++ b/source/modules/juce_gui_basics/widgets/juce_Toolbar.h @@ -136,7 +136,7 @@ public: /** Returns the ID of the item with the given index. If the index is less than zero or greater than the number of items, - this will return 0. + this will return nullptr. @see getNumItems */ @@ -145,7 +145,7 @@ public: /** Returns the component being used for the item with the given index. If the index is less than zero or greater than the number of items, - this will return 0. + this will return nullptr. @see getNumItems */ @@ -314,7 +314,7 @@ private: ToolbarItemStyle toolbarStyle; class MissingItemsComponent; friend class MissingItemsComponent; - OwnedArray items; + OwnedArray items; class Spacer; class CustomisationDialog; diff --git a/source/modules/juce_gui_basics/widgets/juce_TreeView.h b/source/modules/juce_gui_basics/widgets/juce_TreeView.h index 587cd8e32..a5a51e53a 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TreeView.h +++ b/source/modules/juce_gui_basics/widgets/juce_TreeView.h @@ -717,7 +717,7 @@ public: int getNumRowsInTree() const; /** Returns the item on a particular row of the tree. - If the index is out of range, this will return 0. + If the index is out of range, this will return nullptr. @see getNumRowsInTree, TreeViewItem::getRowNumberInTree() */ TreeViewItem* getItemOnRow (int index) const; diff --git a/source/modules/juce_gui_basics/windows/juce_AlertWindow.h b/source/modules/juce_gui_basics/windows/juce_AlertWindow.h index 2d3601354..733da8739 100644 --- a/source/modules/juce_gui_basics/windows/juce_AlertWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_AlertWindow.h @@ -198,25 +198,23 @@ public: void addCustomComponent (Component* component); /** Returns the number of custom components in the dialog box. - @see getCustomComponent, addCustomComponent */ int getNumCustomComponents() const; /** Returns one of the custom components in the dialog box. - @param index a value 0 to (getNumCustomComponents() - 1). Out-of-range indexes - will return 0 + @param index a value 0 to (getNumCustomComponents() - 1). + Out-of-range indexes will return nullptr @see getNumCustomComponents, addCustomComponent */ Component* getCustomComponent (int index) const; /** Removes one of the custom components in the dialog box. - Note that this won't delete it, it just removes the component from the window - @param index a value 0 to (getNumCustomComponents() - 1). Out-of-range indexes - will return 0 + @param index a value 0 to (getNumCustomComponents() - 1). + Out-of-range indexes will return nullptr @returns the component that was removed (or null) @see getNumCustomComponents, addCustomComponent */ diff --git a/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp b/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp index a4171f16a..52563768c 100644 --- a/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp +++ b/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp @@ -187,7 +187,7 @@ bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textChar { const WeakReference deletionChecker (target); - if (const Array * const keyListeners = target->keyListeners) + if (const Array* const keyListeners = target->keyListeners) { for (int i = keyListeners->size(); --i >= 0;) { @@ -238,7 +238,7 @@ bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown) if (keyWasUsed || deletionChecker == nullptr) break; - if (const Array * const keyListeners = target->keyListeners) + if (const Array* const keyListeners = target->keyListeners) { for (int i = keyListeners->size(); --i >= 0;) { @@ -275,7 +275,7 @@ TextInputTarget* ComponentPeer::findCurrentTextInputTarget() { Component* const c = Component::getCurrentlyFocusedComponent(); - if (component.isParentOf (c)) + if (c == &component || component.isParentOf (c)) if (TextInputTarget* const ti = dynamic_cast (c)) if (ti->isTextInputActive()) return ti; diff --git a/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h b/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h index 17457bdcc..86efd278d 100644 --- a/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h @@ -100,7 +100,7 @@ public: (This is overridden from Component::setName() to cause a repaint, as the name is what gets drawn across the window's title bar). */ - void setName (const String& newName); + void setName (const String& newName) override; /** Sets an icon to show in the title bar, next to the title. diff --git a/source/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp b/source/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp index 0338cc48a..abd21fade 100644 --- a/source/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp +++ b/source/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp @@ -22,35 +22,36 @@ ============================================================================== */ -ResizableWindow::ResizableWindow (const String& name, - const bool addToDesktop_) - : TopLevelWindow (name, addToDesktop_), +ResizableWindow::ResizableWindow (const String& name, bool shouldAddToDesktop) + : TopLevelWindow (name, shouldAddToDesktop), ownsContentComponent (false), resizeToFitContent (false), fullscreen (false), + canDrag (true), + dragStarted (false), constrainer (nullptr) #if JUCE_DEBUG , hasBeenResized (false) #endif { - initialise (addToDesktop_); + initialise (shouldAddToDesktop); } -ResizableWindow::ResizableWindow (const String& name, - Colour backgroundColour_, - const bool addToDesktop_) - : TopLevelWindow (name, addToDesktop_), +ResizableWindow::ResizableWindow (const String& name, Colour bkgnd, bool shouldAddToDesktop) + : TopLevelWindow (name, shouldAddToDesktop), ownsContentComponent (false), resizeToFitContent (false), fullscreen (false), + canDrag (true), + dragStarted (false), constrainer (nullptr) #if JUCE_DEBUG , hasBeenResized (false) #endif { - setBackgroundColour (backgroundColour_); + setBackgroundColour (bkgnd); - initialise (addToDesktop_); + initialise (shouldAddToDesktop); } ResizableWindow::~ResizableWindow() @@ -316,6 +317,11 @@ void ResizableWindow::setResizeLimits (const int newMinimumWidth, setBoundsConstrained (getBounds()); } +void ResizableWindow::setDraggable (bool shouldBeDraggable) noexcept +{ + canDrag = shouldBeDraggable; +} + void ResizableWindow::setConstrainer (ComponentBoundsConstrainer* newConstrainer) { if (constrainer != newConstrainer) @@ -573,16 +579,24 @@ bool ResizableWindow::restoreWindowStateFromString (const String& s) //============================================================================== void ResizableWindow::mouseDown (const MouseEvent& e) { - if (! isFullScreen()) + if (canDrag && ! isFullScreen()) + { + dragStarted = true; dragger.startDraggingComponent (this, e); + } } void ResizableWindow::mouseDrag (const MouseEvent& e) { - if (! isFullScreen()) + if (dragStarted) dragger.dragComponent (this, e, constrainer); } +void ResizableWindow::mouseUp (const MouseEvent&) +{ + dragStarted = false; +} + //============================================================================== #if JUCE_DEBUG void ResizableWindow::addChildComponent (Component* const child, int zOrder) diff --git a/source/modules/juce_gui_basics/windows/juce_ResizableWindow.h b/source/modules/juce_gui_basics/windows/juce_ResizableWindow.h index dd9f806ed..936d83828 100644 --- a/source/modules/juce_gui_basics/windows/juce_ResizableWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_ResizableWindow.h @@ -140,6 +140,12 @@ public: int newMaximumWidth, int newMaximumHeight) noexcept; + /** Can be used to enable or disable user-dragging of the window. */ + void setDraggable (bool shouldBeDraggable) noexcept; + + /** Returns true if the window can be dragged around by the user. */ + bool isDraggable() const noexcept { return canDrag; } + /** Returns the bounds constrainer object that this window is using. You can access this to change its properties. */ @@ -339,6 +345,8 @@ protected: /** @internal */ void mouseDrag (const MouseEvent&) override; /** @internal */ + void mouseUp (const MouseEvent&) override; + /** @internal */ void lookAndFeelChanged() override; /** @internal */ void childBoundsChanged (Component*) override; @@ -368,20 +376,20 @@ protected: void addAndMakeVisible (Component*, int zOrder = -1); #endif - ScopedPointer resizableCorner; - ScopedPointer resizableBorder; + ScopedPointer resizableCorner; + ScopedPointer resizableBorder; private: //============================================================================== Component::SafePointer contentComponent; - bool ownsContentComponent, resizeToFitContent, fullscreen; + bool ownsContentComponent, resizeToFitContent, fullscreen, canDrag, dragStarted; ComponentDragger dragger; Rectangle lastNonFullScreenPos; ComponentBoundsConstrainer defaultConstrainer; ComponentBoundsConstrainer* constrainer; - #if JUCE_DEBUG + #if JUCE_DEBUG bool hasBeenResized; - #endif + #endif void initialise (bool addToDesktop); void updateLastPosIfNotFullScreen(); diff --git a/source/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp b/source/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp index 4de7f9fd6..804378d19 100644 --- a/source/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp +++ b/source/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp @@ -60,16 +60,9 @@ void TooltipWindow::mouseEnter (const MouseEvent&) hideTip(); } -void TooltipWindow::updatePosition (const String& tip, Point pos, const Rectangle& parentArea) +void TooltipWindow::updatePosition (const String& tip, Point pos, Rectangle parentArea) { - int w, h; - getLookAndFeel().getTooltipSize (tip, w, h); - - setBounds (Rectangle (pos.x > parentArea.getCentreX() ? pos.x - (w + 12) : pos.x + 24, - pos.y > parentArea.getCentreY() ? pos.y - (h + 6) : pos.y + 6, - w, h) - .constrainedWithin (parentArea)); - + setBounds (getLookAndFeel().getTooltipBounds (tip, pos, parentArea)); setVisible (true); } @@ -112,7 +105,7 @@ String TooltipWindow::getTipFor (Component* const c) && Process::isForegroundProcess() && ! ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown()) { - if (TooltipClient* const ttc = dynamic_cast (c)) + if (TooltipClient* const ttc = dynamic_cast (c)) if (! c->isCurrentlyBlockedByAnotherModalComponent()) return ttc->getTooltip(); } diff --git a/source/modules/juce_gui_basics/windows/juce_TooltipWindow.h b/source/modules/juce_gui_basics/windows/juce_TooltipWindow.h index 3688ae404..0445a59b8 100644 --- a/source/modules/juce_gui_basics/windows/juce_TooltipWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_TooltipWindow.h @@ -61,7 +61,7 @@ public: @param millisecondsBeforeTipAppears the time for which the mouse has to stay still before a tooltip will be shown - @see TooltipClient, LookAndFeel::drawTooltip, LookAndFeel::getTooltipSize + @see TooltipClient, LookAndFeel::drawTooltip, LookAndFeel::getTooltipBounds */ explicit TooltipWindow (Component* parentComponent = nullptr, int millisecondsBeforeTipAppears = 700); @@ -104,8 +104,14 @@ public: { virtual ~LookAndFeelMethods() {} - virtual void getTooltipSize (const String& tipText, int& width, int& height) = 0; + /** returns the bounds for a tooltip at the given screen coordinate, constrained within the given desktop area. */ + virtual Rectangle getTooltipBounds (const String& tipText, Point screenPos, Rectangle parentArea) = 0; virtual void drawTooltip (Graphics&, const String& text, int width, int height) = 0; + + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // This method has been replaced by getTooltipBounds() + virtual int getTooltipSize (const String&, int&, int&) { return 0; } + #endif }; private: @@ -121,7 +127,7 @@ private: void paint (Graphics&) override; void mouseEnter (const MouseEvent&) override; void timerCallback() override; - void updatePosition (const String&, Point, const Rectangle&); + void updatePosition (const String&, Point, Rectangle); static String getTipFor (Component*); diff --git a/source/modules/juce_gui_basics/windows/juce_TopLevelWindow.h b/source/modules/juce_gui_basics/windows/juce_TopLevelWindow.h index ce1b10427..0a5618671 100644 --- a/source/modules/juce_gui_basics/windows/juce_TopLevelWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_TopLevelWindow.h @@ -82,7 +82,7 @@ public: them, you can use this to show it in front of the relevent parent window, which is a bit neater than just having it appear in the middle of the screen. - If componentToCentreAround is 0, then the currently active TopLevelWindow will + If componentToCentreAround is nullptr, then the currently active TopLevelWindow will be used instead. If no window is focused, it'll just default to the middle of the screen. */ 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 b2f26731d..f6820b1eb 100644 --- a/source/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp +++ b/source/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp @@ -103,11 +103,11 @@ public: break; const SyntaxToken& token = tokens.getReference(i); - as.append (token.text.removeCharacters ("\r\n"), fontToUse, owner.getColourForTokenType (token.tokenType)); + as.append (token.text.initialSectionNotContaining ("\r\n"), fontToUse, owner.getColourForTokenType (token.tokenType)); column += token.length; } - as.draw (g, Rectangle (x, (float) y, 10000.0f, (float) lineH)); + as.draw (g, Rectangle (x, (float) y, column * characterWidth + 10.0f, (float) lineH)); } private: diff --git a/source/modules/juce_gui_extra/misc/juce_ColourSelector.h b/source/modules/juce_gui_extra/misc/juce_ColourSelector.h index d16700cbf..7232fd6f6 100644 --- a/source/modules/juce_gui_extra/misc/juce_ColourSelector.h +++ b/source/modules/juce_gui_extra/misc/juce_ColourSelector.h @@ -154,7 +154,7 @@ private: void setSV (float newS, float newV); void updateHSV(); void update(); - void sliderValueChanged (Slider*); + void sliderValueChanged (Slider*) override; void paint (Graphics&) override; void resized() override; diff --git a/source/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp b/source/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp index 98311536b..8a6d5b959 100644 --- a/source/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp +++ b/source/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp @@ -347,8 +347,8 @@ public: owner.getMappings().removeChangeListener (this); } - bool mightContainSubItems() { return true; } - String getUniqueName() const { return "keys"; } + bool mightContainSubItems() override { return true; } + String getUniqueName() const override { return "keys"; } void changeListenerCallback (ChangeBroadcaster*) override { diff --git a/source/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h b/source/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h index 8ff33a279..be43f56f0 100644 --- a/source/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h +++ b/source/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h @@ -97,8 +97,6 @@ public: These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. - To change the colours of the menu that pops up - @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds diff --git a/source/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp b/source/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp index 711daacf0..ece1770cd 100644 --- a/source/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp +++ b/source/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -extern Display* display; +extern ::Display* display; //============================================================================== class SystemTrayIconComponent::Pimpl @@ -57,7 +57,7 @@ public: ev.xclient.format = 32; ev.xclient.data.l[0] = CurrentTime; ev.xclient.data.l[1] = 0 /*SYSTEM_TRAY_REQUEST_DOCK*/; - ev.xclient.data.l[2] = windowH; + ev.xclient.data.l[2] = (long) windowH; ev.xclient.data.l[3] = 0; ev.xclient.data.l[4] = 0; diff --git a/source/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp b/source/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp index de19a56c9..35ee6ea53 100644 --- a/source/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp +++ b/source/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp @@ -45,6 +45,8 @@ public: statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]; [statusItem setView: view]; + SystemTrayViewClass::frameChanged (view, SEL(), nullptr); + [[NSNotificationCenter defaultCenter] addObserver: view selector: @selector (frameChanged:) name: NSWindowDidMoveNotification @@ -134,9 +136,9 @@ private: [statusIcon setSize: NSMakeSize (20.0f, 20.0f)]; } - struct SystemTrayViewClass : public ObjCClass + struct SystemTrayViewClass : public ObjCClass { - SystemTrayViewClass() : ObjCClass ("JUCESystemTrayView_") + SystemTrayViewClass() : ObjCClass ("JUCESystemTrayView_") { addIvar ("owner"); addIvar ("image"); @@ -154,6 +156,17 @@ private: static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); } static void setImage (id self, NSImage* image) { object_setInstanceVariable (self, "image", image); } + static void frameChanged (id self, SEL, NSNotification*) + { + if (Pimpl* const owner = getOwner (self)) + { + NSRect r = [[[owner->statusItem view] window] frame]; + NSRect sr = [[[NSScreen screens] objectAtIndex: 0] frame]; + r.origin.y = sr.size.height - r.origin.y - r.size.height; + owner->owner.setBounds (convertToRectInt (r)); + } + } + private: static void handleEventDown (id self, SEL, NSEvent* e) { @@ -184,17 +197,6 @@ private: fraction: 1.0f]; } } - - static void frameChanged (id self, SEL, NSNotification*) - { - if (Pimpl* const owner = getOwner (self)) - { - NSRect r = [[[owner->statusItem view] window] frame]; - NSRect sr = [[[NSScreen screens] objectAtIndex: 0] frame]; - r.origin.y = sr.size.height - r.origin.y - r.size.height; - owner->owner.setBounds (convertToRectInt (r)); - } - } }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)