Browse Source

Update to latest juce

tags/1.9.4
falkTX 10 years ago
parent
commit
bf9d9b9a6e
49 changed files with 1029 additions and 352 deletions
  1. +3
    -3
      data/copy-juce-carla
  2. +5
    -0
      source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
  3. +3
    -0
      source/modules/juce_audio_basics/midi/juce_MidiMessage.h
  4. +3
    -12
      source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  5. +49
    -23
      source/modules/juce_audio_processors/format_types/juce_VST3Common.h
  6. +3
    -4
      source/modules/juce_audio_processors/format_types/juce_VST3Headers.h
  7. +13
    -16
      source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  8. +3
    -34
      source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  9. +38
    -0
      source/modules/juce_audio_processors/juce_audio_processors.cpp
  10. +2
    -2
      source/modules/juce_audio_processors/juce_audio_processors.h
  11. +0
    -8
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  12. +1
    -4
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  13. +25
    -1
      source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp
  14. +18
    -3
      source/modules/juce_audio_processors/scanning/juce_KnownPluginList.h
  15. +1
    -0
      source/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp
  16. +2
    -0
      source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp
  17. +1
    -1
      source/modules/juce_core/containers/juce_HashMap.h
  18. +23
    -8
      source/modules/juce_core/containers/juce_NamedValueSet.cpp
  19. +11
    -8
      source/modules/juce_core/containers/juce_NamedValueSet.h
  20. +1
    -1
      source/modules/juce_core/juce_core.cpp
  21. +17
    -18
      source/modules/juce_core/native/juce_posix_SharedCode.h
  22. +10
    -18
      source/modules/juce_core/native/juce_win32_Threads.cpp
  23. +4
    -0
      source/modules/juce_core/streams/juce_OutputStream.cpp
  24. +1
    -1
      source/modules/juce_core/text/juce_String.h
  25. +20
    -0
      source/modules/juce_core/threads/juce_ChildProcess.cpp
  26. +3
    -0
      source/modules/juce_core/threads/juce_ChildProcess.h
  27. +61
    -57
      source/modules/juce_core/threads/juce_ThreadPool.cpp
  28. +8
    -1
      source/modules/juce_core/threads/juce_ThreadPool.h
  29. +22
    -4
      source/modules/juce_core/xml/juce_XmlDocument.cpp
  30. +10
    -10
      source/modules/juce_core/xml/juce_XmlDocument.h
  31. +1
    -1
      source/modules/juce_core/xml/juce_XmlElement.cpp
  32. +11
    -8
      source/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp
  33. +259
    -0
      source/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp
  34. +179
    -0
      source/modules/juce_events/interprocess/juce_ConnectedChildProcess.h
  35. +27
    -14
      source/modules/juce_events/interprocess/juce_InterprocessConnection.cpp
  36. +8
    -3
      source/modules/juce_events/interprocess/juce_InterprocessConnection.h
  37. +1
    -0
      source/modules/juce_events/juce_events.cpp
  38. +1
    -0
      source/modules/juce_events/juce_events.h
  39. +13
    -11
      source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp
  40. +14
    -0
      source/modules/juce_graphics/contexts/juce_GraphicsContext.h
  41. +18
    -12
      source/modules/juce_graphics/native/juce_RenderingHelpers.h
  42. +12
    -0
      source/modules/juce_gui_basics/drawables/juce_Drawable.cpp
  43. +5
    -0
      source/modules/juce_gui_basics/drawables/juce_Drawable.h
  44. +18
    -0
      source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp
  45. +2
    -0
      source/modules/juce_gui_basics/drawables/juce_DrawableShape.h
  46. +4
    -2
      source/modules/juce_gui_basics/layout/juce_Viewport.h
  47. +1
    -1
      source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp
  48. +17
    -24
      source/modules/juce_gui_basics/widgets/juce_TextEditor.cpp
  49. +77
    -39
      source/modules/juce_gui_extra/native/juce_mac_NSViewComponent.mm

+ 3
- 3
data/copy-juce-carla View File

@@ -2,10 +2,10 @@

set -e

JUCE_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/DISTRHO/libs/juce/source/modules/"
CARLA_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/Carla/source/modules"
JUCE_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/distrho/DISTRHO/libs/juce/source/modules/"
CARLA_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/falktx/Carla/source/modules/"

MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics")
MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics juce_gui_extra")

for M in $MODULES; do
echo $M;


+ 5
- 0
source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp View File

@@ -941,6 +941,11 @@ double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOf
return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0);
}
bool MidiMessage::isMidiNoteBlack (int noteNumber) noexcept
{
return ((1 << (noteNumber % 12)) & 0x054a) != 0;
}
const char* MidiMessage::getGMInstrumentName (const int n)
{
static const char* names[] =


+ 3
- 0
source/modules/juce_audio_basics/midi/juce_MidiMessage.h View File

@@ -889,6 +889,9 @@ public:
*/
static double getMidiNoteInHertz (int noteNumber, const double frequencyOfA = 440.0) noexcept;
/** Returns true if the given midi note number is a black key. */
static bool isMidiNoteBlack (int noteNumber) noexcept;
/** Returns the standard name of a GM instrument, or nullptr if unknown for this index.
@param midiInstrumentNumber the program number 0 to 127


+ 3
- 12
source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp View File

@@ -231,24 +231,15 @@ public:
NSString* nameNSString = nil;
size = sizeof (nameNSString);
#if JUCE_CLANG
// Very irritating that AudioDeviceGetProperty is marked as deprecated, since
// there seems to be no replacement way of getting the channel names.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
pa.mSelector = kAudioObjectPropertyElementName;
pa.mElement = chanNum + 1;
if (AudioDeviceGetProperty (deviceID, chanNum + 1, input, kAudioDevicePropertyChannelNameCFString,
&size, &nameNSString) == noErr)
if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr)
{
name = nsStringToJuce (nameNSString);
[nameNSString release];
}
#if JUCE_CLANG
#pragma clang diagnostic pop
#endif
if ((input ? activeInputChans : activeOutputChans) [chanNum])
{
CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels };


+ 49
- 23
source/modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -59,7 +59,7 @@ static juce::String toString (const Steinberg::char16* string) noexcept { re
static juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
static juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
static void toString (Steinberg::Vst::String128 result, const juce::String& source)
static void toString128 (Steinberg::Vst::String128 result, const juce::String& source)
{
Steinberg::UString (result, 128).fromAscii (source.toUTF8());
}
@@ -69,6 +69,13 @@ static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept
return reinterpret_cast<Steinberg::Vst::TChar*> (source.toUTF16().getAddress());
}
#if JUCE_WINDOWS
static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND;
#else
static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView;
#endif
//==============================================================================
/** The equivalent numChannels and speaker arrangements should always
match between this function and fillWithCorrespondingSpeakerArrangements().
@@ -81,22 +88,32 @@ static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numC
{
using namespace Steinberg::Vst::SpeakerArr;
if (numChannels >= 14) return k131;
if (numChannels >= 13) return k130;
if (numChannels >= 12) return k111;
if (numChannels >= 11) return k101;
if (numChannels >= 10) return k91;
if (numChannels >= 9) return k90;
if (numChannels >= 8) return k71CineFullFront;
if (numChannels >= 7) return k61Cine;
if (numChannels >= 6) return k51;
if (numChannels >= 5) return k50;
if (numChannels >= 4) return k31Cine;
if (numChannels >= 3) return k30Cine;
if (numChannels >= 2) return kStereo;
if (numChannels >= 1) return kMono;
return kEmpty;
switch (numChannels)
{
case 0: return kEmpty;
case 1: return kMono;
case 2: return kStereo;
case 3: return k30Cine;
case 4: return k31Cine;
case 5: return k50;
case 6: return k51;
case 7: return k61Cine;
case 8: return k71CineFullFront;
case 9: return k90;
case 10: return k91;
case 11: return k101;
case 12: return k111;
case 13: return k130;
case 14: return k131;
case 24: return (Steinberg::Vst::SpeakerArrangement) 1929904127; // k222
default: break;
}
jassert (numChannels >= 0);
juce::BigInteger bi;
bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true);
return (Steinberg::Vst::SpeakerArrangement) bi.toInt64();
}
/** The equivalent numChannels and speaker arrangements should always
@@ -119,10 +136,17 @@ static void fillWithCorrespondingSpeakerArrangements (Array<Steinberg::Vst::Spea
return;
}
/*
The order of the arrangement checks must be descending, since most plugins test for
the first arrangement to match their number of specified channels.
*/
// The order of the arrangement checks must be descending, since most plugins test for
/// the first arrangement to match their number of specified channels.
if (numChannels > 24)
{
juce::BigInteger bi;
bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true);
destination.add ((Steinberg::Vst::SpeakerArrangement) bi.toInt64());
}
if (numChannels >= 24) destination.add ((Steinberg::Vst::SpeakerArrangement) 1929904127); // k222
if (numChannels >= 14) destination.add (k131);
if (numChannels >= 13) destination.add (k130);
if (numChannels >= 12) destination.add (k111);
@@ -225,7 +249,9 @@ public:
//==============================================================================
static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList)
{
for (Steinberg::int32 i = 0; i < eventList.getEventCount(); ++i)
const int32 numEvents = eventList.getEventCount();
for (Steinberg::int32 i = 0; i < numEvents; ++i)
{
Steinberg::Vst::Event e;
@@ -407,4 +433,4 @@ namespace VST3BufferExchange
}
}
#endif //JUCE_VST3COMMON_H_INCLUDED
#endif // JUCE_VST3COMMON_H_INCLUDED

+ 3
- 4
source/modules/juce_audio_processors/format_types/juce_VST3Headers.h View File

@@ -22,8 +22,8 @@
==============================================================================
*/
#ifndef JUCE_VST3HEADER_H_INCLUDED
#define JUCE_VST3HEADER_H_INCLUDED
#ifndef JUCE_VST3HEADERS_H_INCLUDED
#define JUCE_VST3HEADERS_H_INCLUDED
#undef Point
#undef Component
@@ -159,7 +159,6 @@ namespace Steinberg
#undef OBJ_METHODS
#undef QUERY_INTERFACE
#undef LICENCE_UID
#undef DEF_CLASS_IID
#undef BEGIN_FACTORY
#undef DEF_CLASS
#undef DEF_CLASS1
@@ -169,4 +168,4 @@ namespace Steinberg
#undef Point
#undef Component
#endif //JUCE_VST3HEADER_H_INCLUDED
#endif // JUCE_VST3HEADERS_H_INCLUDED

+ 13
- 16
source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -998,7 +998,8 @@ private:
void releaseFactory()
{
const Steinberg::FReleaser releaser (factory);
if (factory != nullptr)
factory->release();
}
#if JUCE_WINDOWS
@@ -1219,10 +1220,9 @@ public:
#if JUCE_MAC
dummyComponent.setView (nullptr);
[pluginHandle release];
#endif
const Steinberg::FReleaser releaser (view);
view = nullptr;
}
JUCE_DECLARE_VST3_COM_REF_METHODS
@@ -1284,7 +1284,8 @@ public:
dummyComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight());
#endif
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); // Some plugins don't update their cursor correctly when mousing out the window
// Some plugins don't update their cursor correctly when mousing out the window
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
recursiveResize = false;
}
@@ -1316,7 +1317,7 @@ public:
private:
//==============================================================================
Atomic<int> refCount;
IPlugView* view; // N.B.: Don't use a ComSmartPtr here! The view should start with a refCount of 1, and does NOT need to be incremented!
ComSmartPtr<IPlugView> view;
#if JUCE_WINDOWS
class ChildComponent : public Component
@@ -1335,14 +1336,14 @@ private:
ScopedPointer<ComponentPeer> peer;
typedef HWND HandleFormat;
#elif JUCE_MAC
NSViewComponent dummyComponent;
AutoResizingNSViewComponentWithParent dummyComponent;
typedef NSView* HandleFormat;
#else
Component dummyComponent;
typedef void* HandleFormat;
#endif
HandleFormat pluginHandle; // Don't delete this
HandleFormat pluginHandle;
bool recursiveResize;
//==============================================================================
@@ -1368,17 +1369,12 @@ private:
#elif JUCE_MAC
dummyComponent.setBounds (getBounds().withZeroOrigin());
addAndMakeVisible (dummyComponent);
pluginHandle = [[NSView alloc] init];
dummyComponent.setView (pluginHandle);
pluginHandle = (NSView*) dummyComponent.getView();
jassert (pluginHandle != nil);
#endif
if (pluginHandle != nullptr)
view->attached (pluginHandle,
#if JUCE_WINDOWS
kPlatformTypeHWND);
#else
kPlatformTypeNSView);
#endif
warnOnFailure (view->attached (pluginHandle, defaultVST3WindowType));
}
}
@@ -1452,7 +1448,8 @@ public:
if (! fetchComponentAndController (factory, factory->countClasses()))
return false;
editController->initialize (host->getFUnknown()); // (May return an error if the plugin combines the IComponent and IEditController implementations)
// (May return an error if the plugin combines the IComponent and IEditController implementations)
editController->initialize (host->getFUnknown());
isControllerInitialised = true;
editController->setComponentHandler (host);


+ 3
- 34
source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -187,7 +187,6 @@ static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstI
static int shellUIDToCreate = 0;
static int insideVSTCallback = 0;
#ifndef JUCE_PLUGIN_HOST_NO_UI
class IdleCallRecursionPreventer
{
public:
@@ -211,9 +210,6 @@ private:
};
class VSTPluginWindow;
#else
struct IdleCallRecursionPreventer{};
#endif
//==============================================================================
// Change this to disable logging of various VST activities
@@ -240,7 +236,6 @@ static void* NewCFMFromMachO (void* const machofp) noexcept
}
#endif
#ifndef JUCE_PLUGIN_HOST_NO_UI
//==============================================================================
#if JUCE_LINUX
@@ -345,7 +340,6 @@ namespace
}
}
#endif
#endif
//==============================================================================
@@ -716,13 +710,9 @@ static const int defaultVSTBlockSizeValue = 512;
//==============================================================================
//==============================================================================
#ifndef JUCE_PLUGIN_HOST_NO_UI
class VSTPluginInstance : public AudioPluginInstance,
private Timer,
private AsyncUpdater
#else
class VSTPluginInstance : public AudioPluginInstance
#endif
{
public:
VSTPluginInstance (const ModuleHandle::Ptr& module_)
@@ -793,10 +783,8 @@ public:
UseResFile (module->resFileId);
#endif
#ifndef JUCE_PLUGIN_HOST_NO_UI
// Must delete any editors before deleting the plugin instance!
jassert (getActiveEditor() == 0);
#endif
_fpreset(); // some dodgy plugs fuck around with this
@@ -1254,7 +1242,6 @@ public:
void setCurrentProgramStateInformation (const void* data, int size) override { loadFromFXBFile (data, size); }
//==============================================================================
#ifndef JUCE_PLUGIN_HOST_NO_UI
void timerCallback() override
{
if (dispatch (effIdle, 0, 0, 0, 0) == 0)
@@ -1266,7 +1253,6 @@ public:
// indicates that something about the plugin has changed..
updateHostDisplay();
}
#endif
VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
{
@@ -1284,7 +1270,6 @@ public:
#pragma warning (pop)
#endif
#ifndef JUCE_PLUGIN_HOST_NO_UI
case audioMasterIdle:
if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread())
{
@@ -1313,7 +1298,6 @@ public:
case audioMasterUpdateDisplay: triggerAsyncUpdate(); break;
case audioMasterIOChanged: setLatencySamples (effect->initialDelay); break;
case audioMasterNeedIdle: startTimer (50); break;
#endif
case audioMasterGetSampleRate: return (VstIntPtr) (getSampleRate() > 0 ? getSampleRate() : defaultVSTSampleRateValue);
case audioMasterGetBlockSize: return (VstIntPtr) (getBlockSize() > 0 ? getBlockSize() : defaultVSTBlockSizeValue);
@@ -1399,12 +1383,10 @@ public:
case audioMasterGetVendorString:
case audioMasterGetProductString:
{
String hostName ("Carla");
String hostName ("Juce VST Host");
#ifndef JUCE_PLUGIN_HOST_NO_UI
if (JUCEApplicationBase* app = JUCEApplicationBase::getInstance())
hostName = app->getApplicationName();
#endif
hostName.copyToUTF8 ((char*) ptr, (size_t) jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1);
break;
@@ -1911,7 +1893,6 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginInstance)
};
#ifndef JUCE_PLUGIN_HOST_NO_UI
//==============================================================================
static Array <VSTPluginWindow*> activeVSTWindows;
@@ -1946,17 +1927,10 @@ public:
#elif JUCE_MAC
#if JUCE_SUPPORT_CARBON
if (! plug.usesCocoaNSView)
{
addAndMakeVisible (carbonWrapper = new CarbonWrapperComponent (*this));
}
else
#endif
{
addAndMakeVisible (cocoaWrapper = new AutoResizingNSViewComponent());
NSView* innerView = [[NSView alloc] init];
cocoaWrapper->setView (innerView);
[innerView release];
}
addAndMakeVisible (cocoaWrapper = new AutoResizingNSViewComponentWithParent());
#endif
activeVSTWindows.add (this);
@@ -2619,7 +2593,7 @@ private:
ScopedPointer<CarbonWrapperComponent> carbonWrapper;
#endif
ScopedPointer<NSViewComponent> cocoaWrapper;
ScopedPointer<AutoResizingNSViewComponentWithParent> cocoaWrapper;
void resized() override
{
@@ -2635,17 +2609,12 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginWindow)
};
#endif
//==============================================================================
AudioProcessorEditor* VSTPluginInstance::createEditor()
{
#ifndef JUCE_PLUGIN_HOST_NO_UI
return hasEditor() ? new VSTPluginWindow (*this)
: nullptr;
#else
return nullptr;
#endif
}
//==============================================================================


+ 38
- 0
source/modules/juce_audio_processors/juce_audio_processors.cpp View File

@@ -57,6 +57,11 @@
#undef KeyPress
#endif
#if ! JUCE_WINDOWS && ! JUCE_MAC
#undef JUCE_PLUGINHOST_VST3
#define JUCE_PLUGINHOST_VST3 0
#endif
//==============================================================================
namespace juce
{
@@ -72,6 +77,7 @@ static inline bool arrayContainsPlugin (const OwnedArray<PluginDescription>& lis
}
#if JUCE_MAC
//==============================================================================
struct AutoResizingNSViewComponent : public NSViewComponent,
private AsyncUpdater
{
@@ -95,6 +101,38 @@ struct AutoResizingNSViewComponent : public NSViewComponent,
bool recursive;
};
//==============================================================================
struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent,
private Timer
{
AutoResizingNSViewComponentWithParent()
{
NSView* v = [[NSView alloc] init];
setView (v);
[v release];
startTimer (100);
}
void timerCallback() override
{
if (NSView* parent = (NSView*) getView())
{
if (NSView* child = [[parent subviews] firstObject])
{
NSRect f = [parent frame];
NSSize newSize = [child frame].size;
if (f.size.width != newSize.width || f.size.height != newSize.height)
{
f.size = newSize;
[parent setFrame: f];
}
}
}
}
};
#endif
#if JUCE_CLANG


+ 2
- 2
source/modules/juce_audio_processors/juce_audio_processors.h View File

@@ -44,7 +44,7 @@
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be
installed on your machine.
@see VSTPluginFormat, VVST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU
@see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU
*/
#ifndef JUCE_PLUGINHOST_VST3
#define JUCE_PLUGINHOST_VST3 0
@@ -60,7 +60,7 @@
#endif
#if ! (JUCE_PLUGINHOST_AU || JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_VST3)
// #error "You need to set either the JUCE_PLUGINHOST_AU anr/or JUCE_PLUGINHOST_VST flags if you're using this module!"
// #error "You need to set either the JUCE_PLUGINHOST_AU and/or JUCE_PLUGINHOST_VST and/or JUCE_PLUGINHOST_VST3 flags if you're using this module!"
#endif
#if ! (defined (JUCE_SUPPORT_CARBON) || JUCE_64BIT)


+ 0
- 8
source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp View File

@@ -44,11 +44,9 @@ AudioProcessor::AudioProcessor()
AudioProcessor::~AudioProcessor()
{
#ifndef JUCE_PLUGIN_HOST_NO_UI
// ooh, nasty - the editor should have been deleted before the filter
// that it refers to is deleted..
jassert (activeEditor == nullptr);
#endif
#if JUCE_DEBUG
// This will fail if you've called beginParameterChangeGesture() for one
@@ -221,15 +219,12 @@ void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) noe
{
const ScopedLock sl (callbackLock);
#ifndef JUCE_PLUGIN_HOST_NO_UI
if (activeEditor == editor)
activeEditor = nullptr;
#endif
}
AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
{
#ifndef JUCE_PLUGIN_HOST_NO_UI
if (activeEditor != nullptr)
return activeEditor;
@@ -248,9 +243,6 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
}
return ed;
#else
return nullptr;
#endif
}
//==============================================================================


+ 1
- 4
source/modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -366,13 +366,11 @@ public:
*/
virtual bool hasEditor() const = 0;
#ifndef JUCE_PLUGIN_HOST_NO_UI
//==============================================================================
/** Returns the active editor, if there is one.
Bear in mind this can return nullptr, even if an editor has previously been opened.
*/
AudioProcessorEditor* getActiveEditor() const noexcept { return activeEditor; }
#endif
/** Returns the active editor, or if there isn't one, it will create one.
This may call createEditor() internally to create the component.
@@ -616,6 +614,7 @@ public:
{
wrapperType_Undefined = 0,
wrapperType_VST,
wrapperType_VST3,
wrapperType_AudioUnit,
wrapperType_RTAS,
wrapperType_AAX,
@@ -658,9 +657,7 @@ protected:
private:
Array<AudioProcessorListener*> listeners;
#ifndef JUCE_PLUGIN_HOST_NO_UI
Component::SafePointer<AudioProcessorEditor> activeEditor;
#endif
double sampleRate;
int blockSize, numInputChannels, numOutputChannels, latencySamples;
bool suspended, nonRealtime;


+ 25
- 1
source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp View File

@@ -202,6 +202,14 @@ void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& f
}
}
}
scanFinished();
}
void KnownPluginList::scanFinished()
{
if (scanner != nullptr)
scanner->scanFinished();
}
const StringArray& KnownPluginList::getBlacklistedFiles() const
@@ -280,10 +288,16 @@ void KnownPluginList::sort (const SortMethod method, bool forwards)
{
if (method != defaultOrder)
{
Array<PluginDescription*> oldOrder, newOrder;
oldOrder.addArray (types);
PluginSorter sorter (method, forwards);
types.sort (sorter, true);
sendChangeMessage();
newOrder.addArray (types);
if (oldOrder != newOrder)
sendChangeMessage();
}
}
@@ -523,3 +537,13 @@ int KnownPluginList::getIndexChosenByMenu (const int menuResultCode) const
//==============================================================================
KnownPluginList::CustomScanner::CustomScanner() {}
KnownPluginList::CustomScanner::~CustomScanner() {}
void KnownPluginList::CustomScanner::scanFinished() {}
bool KnownPluginList::CustomScanner::shouldExit() const noexcept
{
if (ThreadPoolJob* job = ThreadPoolJob::getCurrentThreadPoolJob())
return job->shouldExit();
return false;
}

+ 18
- 3
source/modules/juce_audio_processors/scanning/juce_KnownPluginList.h View File

@@ -97,6 +97,9 @@ public:
OwnedArray <PluginDescription>& typesFound,
AudioPluginFormat& formatToUse);
/** Tells a custom scanner that a scan has finished, and it can release any resources. */
void scanFinished();
/** Returns true if the specified file is already known about and if it
hasn't been modified since our entry was created.
*/
@@ -170,8 +173,8 @@ public:
struct PluginTree
{
String folder; /**< The name of this folder in the tree */
OwnedArray <PluginTree> subFolders;
Array <const PluginDescription*> plugins;
OwnedArray<PluginTree> subFolders;
Array<const PluginDescription*> plugins;
};
/** Creates a PluginTree object containing all the known plugins. */
@@ -190,9 +193,21 @@ public:
virtual bool findPluginTypesFor (AudioPluginFormat& format,
OwnedArray <PluginDescription>& result,
const String& fileOrIdentifier) = 0;
/** Called when a scan has finished, to allow clean-up of resources. */
virtual void scanFinished();
/** Returns true if the current scan should be abandoned.
Any blocking methods should check this value repeatedly and return if
if becomes true.
*/
bool shouldExit() const noexcept;
};
void setCustomScanner (CustomScanner* scanner);
/** Supplies a custom scanner to be used in future scans.
The KnownPluginList will take ownership of the object passed in.
*/
void setCustomScanner (CustomScanner*);
private:
//==============================================================================


+ 1
- 0
source/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp View File

@@ -63,6 +63,7 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo,
PluginDirectoryScanner::~PluginDirectoryScanner()
{
list.scanFinished();
}
//==============================================================================


+ 2
- 0
source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp View File

@@ -162,6 +162,7 @@ PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, Kno
setSize (400, 600);
list.addChangeListener (this);
updateList();
table.getHeader().reSortTable();
PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (list, deadMansPedalFile);
deadMansPedalFile.deleteFile();
@@ -196,6 +197,7 @@ void PluginListComponent::resized()
void PluginListComponent::changeListenerCallback (ChangeBroadcaster*)
{
table.getHeader().reSortTable();
updateList();
}


+ 1
- 1
source/modules/juce_core/containers/juce_HashMap.h View File

@@ -60,7 +60,7 @@ struct DefaultHashFunctions
@code
struct MyHashGenerator
{
int generateHash (MyKeyType key, int upperLimit)
int generateHash (MyKeyType key, int upperLimit) const
{
// The function must return a value 0 <= x < upperLimit
return someFunctionOfMyKeyType (key) % upperLimit;


+ 23
- 8
source/modules/juce_core/containers/juce_NamedValueSet.cpp View File

@@ -141,7 +141,7 @@ int NamedValueSet::size() const noexcept
return values.size();
}
const var& NamedValueSet::operator[] (const Identifier name) const
const var& NamedValueSet::operator[] (Identifier name) const
{
for (NamedValue* i = values; i != nullptr; i = i->nextListItem)
if (i->name == name)
@@ -150,7 +150,7 @@ const var& NamedValueSet::operator[] (const Identifier name) const
return var::null;
}
var NamedValueSet::getWithDefault (const Identifier name, const var& defaultReturnValue) const
var NamedValueSet::getWithDefault (Identifier name, const var& defaultReturnValue) const
{
if (const var* const v = getVarPointer (name))
return *v;
@@ -158,7 +158,7 @@ var NamedValueSet::getWithDefault (const Identifier name, const var& defaultRetu
return defaultReturnValue;
}
var* NamedValueSet::getVarPointer (const Identifier name) const noexcept
var* NamedValueSet::getVarPointer (Identifier name) const noexcept
{
for (NamedValue* i = values; i != nullptr; i = i->nextListItem)
if (i->name == name)
@@ -168,7 +168,7 @@ var* NamedValueSet::getVarPointer (const Identifier name) const noexcept
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
bool NamedValueSet::set (const Identifier name, var&& newValue)
bool NamedValueSet::set (Identifier name, var&& newValue)
{
LinkedListPointer<NamedValue>* i = &values;
@@ -193,7 +193,7 @@ bool NamedValueSet::set (const Identifier name, var&& newValue)
}
#endif
bool NamedValueSet::set (const Identifier name, const var& newValue)
bool NamedValueSet::set (Identifier name, const var& newValue)
{
LinkedListPointer<NamedValue>* i = &values;
@@ -217,12 +217,27 @@ bool NamedValueSet::set (const Identifier name, const var& newValue)
return true;
}
bool NamedValueSet::contains (const Identifier name) const
bool NamedValueSet::contains (Identifier name) const
{
return getVarPointer (name) != nullptr;
}
bool NamedValueSet::remove (const Identifier name)
int NamedValueSet::indexOf (Identifier name) const noexcept
{
int index = 0;
for (NamedValue* i = values; i != nullptr; i = i->nextListItem)
{
if (i->name == name)
return index;
++index;
}
return -1;
}
bool NamedValueSet::remove (Identifier name)
{
LinkedListPointer<NamedValue>* i = &values;
@@ -245,7 +260,7 @@ bool NamedValueSet::remove (const Identifier name)
return false;
}
const Identifier NamedValueSet::getName (const int index) const
Identifier NamedValueSet::getName (const int index) const
{
const NamedValue* const v = values[index];
jassert (v != nullptr);


+ 11
- 8
source/modules/juce_core/containers/juce_NamedValueSet.h View File

@@ -67,46 +67,49 @@ public:
If the name isn't found, this will return a void variant.
@see getProperty
*/
const var& operator[] (const Identifier name) const;
const var& operator[] (Identifier name) const;
/** Tries to return the named value, but if no such value is found, this will
instead return the supplied default value.
*/
var getWithDefault (const Identifier name, const var& defaultReturnValue) const;
var getWithDefault (Identifier name, const var& defaultReturnValue) const;
/** Changes or adds a named value.
@returns true if a value was changed or added; false if the
value was already set the the value passed-in.
*/
bool set (const Identifier name, const var& newValue);
bool set (Identifier name, const var& newValue);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
/** Changes or adds a named value.
@returns true if a value was changed or added; false if the
value was already set the the value passed-in.
*/
bool set (const Identifier name, var&& newValue);
bool set (Identifier name, var&& newValue);
#endif
/** Returns true if the set contains an item with the specified name. */
bool contains (const Identifier name) const;
bool contains (Identifier name) const;
/** Removes a value from the set.
@returns true if a value was removed; false if there was no value
with the name that was given.
*/
bool remove (const Identifier name);
bool remove (Identifier name);
/** Returns the name of the value at a given index.
The index must be between 0 and size() - 1.
*/
const Identifier getName (int index) const;
Identifier getName (int index) const;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1.
*/
const var& getValueAt (int index) const;
/** Returns the index of the given name, or -1 if it's not found. */
int indexOf (Identifier name) const noexcept;
/** Removes all values. */
void clear();
@@ -117,7 +120,7 @@ public:
Do not use this method unless you really need access to the internal var object
for some reason - for normal reading and writing always prefer operator[]() and set().
*/
var* getVarPointer (const Identifier name) const noexcept;
var* getVarPointer (Identifier name) const noexcept;
//==============================================================================
/** Sets properties to the values of all of an XML element's attributes. */


+ 1
- 1
source/modules/juce_core/juce_core.cpp View File

@@ -151,7 +151,6 @@ namespace juce
#include "text/juce_StringPairArray.cpp"
#include "text/juce_StringPool.cpp"
#include "text/juce_TextDiff.cpp"
#include "threads/juce_ChildProcess.cpp"
#include "threads/juce_ReadWriteLock.cpp"
#include "threads/juce_Thread.cpp"
#include "threads/juce_ThreadPool.cpp"
@@ -218,6 +217,7 @@ namespace juce
#endif
#include "threads/juce_ChildProcess.cpp"
#include "threads/juce_HighResolutionTimer.cpp"
}

+ 17
- 18
source/modules/juce_core/native/juce_posix_SharedCode.h View File

@@ -1053,7 +1053,7 @@ public:
close (pipeHandle);
}
bool isRunning() const
bool isRunning() const noexcept
{
if (childPID != 0)
{
@@ -1065,7 +1065,7 @@ public:
return false;
}
int read (void* const dest, const int numBytes)
int read (void* const dest, const int numBytes) noexcept
{
jassert (dest != nullptr);
@@ -1082,11 +1082,25 @@ public:
return 0;
}
bool killProcess() const
bool killProcess() const noexcept
{
return ::kill (childPID, SIGKILL) == 0;
}
uint32 getExitCode() const noexcept
{
if (childPID != 0)
{
int childState = 0;
const int pid = waitpid (childPID, &childState, WNOHANG);
if (pid >= 0 && WIFEXITED (childState))
return WEXITSTATUS (childState);
}
return 0;
}
int childPID;
private:
@@ -1114,21 +1128,6 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
return activeProcess != nullptr;
}
bool ChildProcess::isRunning() const
{
return activeProcess != nullptr && activeProcess->isRunning();
}
int ChildProcess::readProcessOutput (void* dest, int numBytes)
{
return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0;
}
bool ChildProcess::kill()
{
return activeProcess == nullptr || activeProcess->killProcess();
}
//==============================================================================
struct HighResolutionTimer::Pimpl
{


+ 10
- 18
source/modules/juce_core/native/juce_win32_Threads.cpp View File

@@ -482,12 +482,12 @@ public:
CloseHandle (writePipe);
}
bool isRunning() const
bool isRunning() const noexcept
{
return WaitForSingleObject (processInfo.hProcess, 0) != WAIT_OBJECT_0;
}
int read (void* dest, int numNeeded) const
int read (void* dest, int numNeeded) const noexcept
{
int total = 0;
@@ -522,11 +522,18 @@ public:
return total;
}
bool killProcess() const
bool killProcess() const noexcept
{
return TerminateProcess (processInfo.hProcess, 0) != FALSE;
}
uint32 getExitCode() const noexcept
{
DWORD exitCode = 0;
GetExitCodeProcess (processInfo.hProcess, &exitCode);
return (uint32) exitCode;
}
bool ok;
private:
@@ -551,21 +558,6 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
return start (args.joinIntoString (" "), streamFlags);
}
bool ChildProcess::isRunning() const
{
return activeProcess != nullptr && activeProcess->isRunning();
}
int ChildProcess::readProcessOutput (void* dest, int numBytes)
{
return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0;
}
bool ChildProcess::kill()
{
return activeProcess == nullptr || activeProcess->killProcess();
}
//==============================================================================
struct HighResolutionTimer::Pimpl
{


+ 4
- 0
source/modules/juce_core/streams/juce_OutputStream.cpp View File

@@ -174,12 +174,16 @@ bool OutputStream::writeDoubleBigEndian (double value)
bool OutputStream::writeString (const String& text)
{
#if (JUCE_STRING_UTF_TYPE == 8)
return write (text.toRawUTF8(), text.getNumBytesAsUTF8() + 1);
#else
// (This avoids using toUTF8() to prevent the memory bloat that it would leave behind
// if lots of large, persistent strings were to be written to streams).
const size_t numBytes = text.getNumBytesAsUTF8() + 1;
HeapBlock<char> temp (numBytes);
text.copyToUTF8 (temp, numBytes);
return write (temp, numBytes);
#endif
}
bool OutputStream::writeText (const String& text, const bool asUTF16,


+ 1
- 1
source/modules/juce_core/text/juce_String.h View File

@@ -84,7 +84,7 @@ public:
because there's no other way to represent unicode strings in a way that isn't dependent
on the compiler, source code editor and platform.
This will use up the the first maxChars characters of the string (or less if the string
This will use up to the first maxChars characters of the string (or less if the string
is actually shorter).
*/
String (const char* text, size_t maxChars);


+ 20
- 0
source/modules/juce_core/threads/juce_ChildProcess.cpp View File

@@ -29,6 +29,26 @@
ChildProcess::ChildProcess() {}
ChildProcess::~ChildProcess() {}
bool ChildProcess::isRunning() const
{
return activeProcess != nullptr && activeProcess->isRunning();
}
int ChildProcess::readProcessOutput (void* dest, int numBytes)
{
return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0;
}
bool ChildProcess::kill()
{
return activeProcess == nullptr || activeProcess->killProcess();
}
uint32 ChildProcess::getExitCode() const
{
return activeProcess != nullptr ? activeProcess->getExitCode() : 0;
}
bool ChildProcess::waitForProcessToFinish (const int timeoutMs) const
{
const uint32 timeoutTime = Time::getMillisecondCounter() + (uint32) timeoutMs;


+ 3
- 0
source/modules/juce_core/threads/juce_ChildProcess.h View File

@@ -97,6 +97,9 @@ public:
/** Blocks until the process is no longer running. */
bool waitForProcessToFinish (int timeoutMs) const;
/** If the process has finished, this returns its exit code. */
uint32 getExitCode() const;
/** Attempts to kill the child process.
Returns true if it succeeded. Trying to read from the process after calling this may
result in undefined behaviour.


+ 61
- 57
source/modules/juce_core/threads/juce_ThreadPool.cpp View File

@@ -26,12 +26,31 @@
==============================================================================
*/
class ThreadPool::ThreadPoolThread : public Thread
{
public:
ThreadPoolThread (ThreadPool& p)
: Thread ("Pool"), currentJob (nullptr), pool (p)
{
}
void run() override
{
while (! threadShouldExit())
if (! pool.runNextJob (*this))
wait (500);
}
ThreadPoolJob* volatile currentJob;
ThreadPool& pool;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread)
};
//==============================================================================
ThreadPoolJob::ThreadPoolJob (const String& name)
: jobName (name),
pool (nullptr),
shouldStop (false),
isActive (false),
shouldBeDeleted (false)
: jobName (name), pool (nullptr),
shouldStop (false), isActive (false), shouldBeDeleted (false)
{
}
@@ -57,30 +76,13 @@ void ThreadPoolJob::signalJobShouldExit()
shouldStop = true;
}
//==============================================================================
class ThreadPool::ThreadPoolThread : public Thread
ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob()
{
public:
ThreadPoolThread (ThreadPool& pool_)
: Thread ("Pool"),
pool (pool_)
{
}
void run() override
{
while (! threadShouldExit())
{
if (! pool.runNextJob())
wait (500);
}
}
if (ThreadPool::ThreadPoolThread* t = dynamic_cast<ThreadPool::ThreadPoolThread*> (Thread::getCurrentThread()))
return t->currentJob;
private:
ThreadPool& pool;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread)
};
return nullptr;
}
//==============================================================================
ThreadPool::ThreadPool (const int numThreads)
@@ -164,8 +166,7 @@ bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const
return jobs.contains (const_cast <ThreadPoolJob*> (job)) && job->isActive;
}
bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job,
const int timeOutMs) const
bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job, const int timeOutMs) const
{
if (job != nullptr)
{
@@ -215,7 +216,7 @@ bool ThreadPool::removeJob (ThreadPoolJob* const job,
}
bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs,
ThreadPool::JobSelector* selectedJobsToRemove)
ThreadPool::JobSelector* const selectedJobsToRemove)
{
Array <ThreadPoolJob*> jobsToWaitFor;
@@ -328,46 +329,49 @@ ThreadPoolJob* ThreadPool::pickNextJobToRun()
return nullptr;
}
bool ThreadPool::runNextJob()
bool ThreadPool::runNextJob (ThreadPoolThread& thread)
{
ThreadPoolJob* const job = pickNextJobToRun();
if (job == nullptr)
return false;
ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished;
JUCE_TRY
if (ThreadPoolJob* const job = pickNextJobToRun())
{
result = job->runJob();
}
JUCE_CATCH_ALL_ASSERT
ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished;
thread.currentJob = job;
OwnedArray<ThreadPoolJob> deletionList;
JUCE_TRY
{
result = job->runJob();
}
JUCE_CATCH_ALL_ASSERT
{
const ScopedLock sl (lock);
thread.currentJob = nullptr;
OwnedArray<ThreadPoolJob> deletionList;
if (jobs.contains (job))
{
job->isActive = false;
const ScopedLock sl (lock);
if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop)
if (jobs.contains (job))
{
jobs.removeFirstMatchingValue (job);
addToDeleteList (deletionList, job);
job->isActive = false;
jobFinishedSignal.signal();
}
else
{
// move the job to the end of the queue if it wants another go
jobs.move (jobs.indexOf (job), -1);
if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop)
{
jobs.removeFirstMatchingValue (job);
addToDeleteList (deletionList, job);
jobFinishedSignal.signal();
}
else
{
// move the job to the end of the queue if it wants another go
jobs.move (jobs.indexOf (job), -1);
}
}
}
return true;
}
return true;
return false;
}
void ThreadPool::addToDeleteList (OwnedArray<ThreadPoolJob>& deletionList, ThreadPoolJob* const job) const


+ 8
- 1
source/modules/juce_core/threads/juce_ThreadPool.h View File

@@ -119,6 +119,12 @@ public:
*/
void signalJobShouldExit();
//==============================================================================
/** If the calling thread is being invoked inside a runJob() method, this will
return the ThreadPoolJob that it belongs to.
*/
static ThreadPoolJob* getCurrentThreadPoolJob();
//==============================================================================
private:
friend class ThreadPool;
@@ -290,6 +296,7 @@ private:
Array <ThreadPoolJob*> jobs;
class ThreadPoolThread;
friend class ThreadPoolJob;
friend class ThreadPoolThread;
friend struct ContainerDeletePolicy<ThreadPoolThread>;
OwnedArray<ThreadPoolThread> threads;
@@ -297,7 +304,7 @@ private:
CriticalSection lock;
WaitableEvent jobFinishedSignal;
bool runNextJob();
bool runNextJob (ThreadPoolThread&);
ThreadPoolJob* pickNextJobToRun();
void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
void createThreads (int numThreads);


+ 22
- 4
source/modules/juce_core/xml/juce_XmlDocument.cpp View File

@@ -371,8 +371,8 @@ void XmlDocument::readQuotedString (String& result)
}
else if (character == 0)
{
outOfData = true;
setLastError ("unmatched quotes", false);
outOfData = true;
break;
}
@@ -432,7 +432,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements)
++input;
if (alsoParseSubElements)
readChildElements (node);
readChildElements (*node);
break;
}
@@ -487,9 +487,9 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements)
return node;
}
void XmlDocument::readChildElements (XmlElement* parent)
void XmlDocument::readChildElements (XmlElement& parent)
{
LinkedListPointer<XmlElement>::Appender childAppender (parent->firstChildElement);
LinkedListPointer<XmlElement>::Appender childAppender (parent.firstChildElement);
for (;;)
{
@@ -563,7 +563,25 @@ void XmlDocument::readChildElements (XmlElement* parent)
const juce_wchar c = *input;
if (c == '<')
{
if (input[1] == '!' && input[2] == '-' && input[3] == '-')
{
input += 4;
const int closeComment = input.indexOf (CharPointer_ASCII ("-->"));
if (closeComment < 0)
{
setLastError ("unterminated comment", false);
outOfData = true;
return;
}
input += closeComment + 3;
continue;
}
break;
}
if (c == 0)
{


+ 10
- 10
source/modules/juce_core/xml/juce_XmlDocument.h View File

@@ -156,23 +156,23 @@ private:
String lastError, dtdText;
StringArray tokenisedDTD;
bool needToLoadDTD, ignoreEmptyTextElements;
ScopedPointer <InputSource> inputSource;
ScopedPointer<InputSource> inputSource;
XmlElement* parseDocumentElement (String::CharPointerType, bool outer);
void setLastError (const String& desc, bool carryOn);
void setLastError (const String&, bool carryOn);
bool parseHeader();
bool parseDTD();
void skipNextWhiteSpace();
juce_wchar readNextChar() noexcept;
XmlElement* readNextElement (bool alsoParseSubElements);
void readChildElements (XmlElement* parent);
void readQuotedString (String& result);
void readEntity (String& result);
String getFileContents (const String& filename) const;
String expandEntity (const String& entity);
String expandExternalEntity (const String& entity);
String getParameterEntity (const String& entity);
void readChildElements (XmlElement&);
void readQuotedString (String&);
void readEntity (String&);
String getFileContents (const String&) const;
String expandEntity (const String&);
String expandExternalEntity (const String&);
String getParameterEntity (const String&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlDocument)
};


+ 1
- 1
source/modules/juce_core/xml/juce_XmlElement.cpp View File

@@ -55,7 +55,7 @@ XmlElement::XmlElement (const String& tag) noexcept
jassert (tag.containsNonWhitespaceChars())
// The tag can't contain spaces or other characters that would create invalid XML!
jassert (! tag.containsAnyOf (" <>/&"));
jassert (! tag.containsAnyOf (" <>/&(){}"));
}
XmlElement::XmlElement (int /*dummy*/) noexcept


+ 11
- 8
source/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp View File

@@ -215,18 +215,18 @@ bool PropertiesFile::loadAsXml()
bool PropertiesFile::saveAsXml()
{
XmlElement doc (PropertyFileConstants::fileTag);
const StringPairArray& props = getAllProperties();
for (int i = 0; i < getAllProperties().size(); ++i)
for (int i = 0; i < props.size(); ++i)
{
XmlElement* const e = doc.createNewChildElement (PropertyFileConstants::valueTag);
e->setAttribute (PropertyFileConstants::nameAttribute, getAllProperties().getAllKeys() [i]);
e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]);
// if the value seems to contain xml, store it as such..
if (XmlElement* const childElement = XmlDocument::parse (getAllProperties().getAllValues() [i]))
if (XmlElement* const childElement = XmlDocument::parse (props.getAllValues() [i]))
e->addChildElement (childElement);
else
e->setAttribute (PropertyFileConstants::valueAttribute,
getAllProperties().getAllValues() [i]);
e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]);
}
ProcessScopedLock pl (createProcessLock());
@@ -311,14 +311,17 @@ bool PropertiesFile::saveAsBinary()
out->writeInt (PropertyFileConstants::magicNumber);
}
const int numProperties = getAllProperties().size();
const StringPairArray& props = getAllProperties();
const int numProperties = props.size();
const StringArray& keys = props.getAllKeys();
const StringArray& values = props.getAllValues();
out->writeInt (numProperties);
for (int i = 0; i < numProperties; ++i)
{
out->writeString (getAllProperties().getAllKeys() [i]);
out->writeString (getAllProperties().getAllValues() [i]);
out->writeString (keys[i]);
out->writeString (values[i]);
}
out = nullptr;


+ 259
- 0
source/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp View File

@@ -0,0 +1,259 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
enum { magicMastSlaveConnectionHeader = 0x712baf04 };
static const char* startMessage = "__ipc_st";
static const char* killMessage = "__ipc_k_";
static const char* pingMessage = "__ipc_p_";
enum { specialMessageSize = 8 };
static String getCommandLinePrefix (const String& commandLineUniqueID)
{
return "--" + commandLineUniqueID + ":";
}
//==============================================================================
// This thread sends and receives ping messages every second, so that it
// can find out if the other process has stopped running.
struct ChildProcessPingThread : public Thread,
private AsyncUpdater
{
ChildProcessPingThread() : Thread ("IPC ping"), timeoutMs (8000)
{
pingReceived();
}
static bool isPingMessage (const MemoryBlock& m) noexcept
{
return memcmp (m.getData(), pingMessage, specialMessageSize) == 0;
}
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
virtual bool sendPingMessage (const MemoryBlock&) = 0;
virtual void pingFailed() = 0;
int timeoutMs;
private:
Atomic<int> countdown;
void handleAsyncUpdate() override { pingFailed(); }
void run() override
{
while (! threadShouldExit())
{
if (--countdown <= 0 || ! sendPingMessage (MemoryBlock (pingMessage, specialMessageSize)))
{
triggerConnectionLostMessage();
break;
}
wait (1000);
}
}
JUCE_DECLARE_NON_COPYABLE (ChildProcessPingThread)
};
//==============================================================================
struct ChildProcessMaster::Connection : public InterprocessConnection,
private ChildProcessPingThread
{
Connection (ChildProcessMaster& m, const String& pipeName)
: InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (m)
{
if (createPipe (pipeName, timeoutMs))
startThread (4);
}
~Connection()
{
stopThread (10000);
}
private:
void connectionMade() override {}
void connectionLost() override { owner.handleConnectionLost(); }
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); }
void pingFailed() override { connectionLost(); }
void messageReceived (const MemoryBlock& m) override
{
pingReceived();
if (m.getSize() != specialMessageSize || ! isPingMessage (m))
owner.handleMessageFromSlave (m);
}
ChildProcessMaster& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
};
//==============================================================================
ChildProcessMaster::ChildProcessMaster() {}
ChildProcessMaster::~ChildProcessMaster()
{
if (connection != nullptr)
{
sendMessageToSlave (MemoryBlock (killMessage, specialMessageSize));
connection->disconnect();
connection = nullptr;
}
}
void ChildProcessMaster::handleConnectionLost() {}
bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb)
{
if (connection != nullptr)
return connection->sendMessage (mb);
jassertfalse; // this can only be used when the connection is active!
return false;
}
bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID)
{
connection = nullptr;
jassert (childProcess.kill());
const String pipeName ("p" + String::toHexString (Random().nextInt64()));
StringArray args;
args.add (executable.getFullPathName());
args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName);
if (childProcess.start (args))
{
connection = new Connection (*this, pipeName);
if (connection->isConnected())
{
sendMessageToSlave (MemoryBlock (startMessage, specialMessageSize));
return true;
}
connection = nullptr;
}
return false;
}
//==============================================================================
struct ChildProcessSlave::Connection : public InterprocessConnection,
private ChildProcessPingThread
{
Connection (ChildProcessSlave& p, const String& pipeName)
: InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (p)
{
connectToPipe (pipeName, timeoutMs);
startThread (4);
}
~Connection()
{
stopThread (10000);
}
private:
ChildProcessSlave& owner;
void connectionMade() override {}
void connectionLost() override { owner.handleConnectionLost(); }
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); }
void pingFailed() override { connectionLost(); }
void messageReceived (const MemoryBlock& m) override
{
pingReceived();
if (m.getSize() == specialMessageSize)
{
if (isPingMessage (m))
return;
if (memcmp (m.getData(), killMessage, specialMessageSize) == 0)
{
triggerConnectionLostMessage();
return;
}
if (memcmp (m.getData(), startMessage, specialMessageSize) == 0)
{
owner.handleConnectionMade();
return;
}
}
owner.handleMessageFromMaster (m);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
};
//==============================================================================
ChildProcessSlave::ChildProcessSlave() {}
ChildProcessSlave::~ChildProcessSlave() {}
void ChildProcessSlave::handleConnectionMade() {}
void ChildProcessSlave::handleConnectionLost() {}
bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb)
{
if (connection != nullptr)
return connection->sendMessage (mb);
jassertfalse; // this can only be used when the connection is active!
return false;
}
bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine,
const String& commandLineUniqueID)
{
String prefix (getCommandLinePrefix (commandLineUniqueID));
if (commandLine.trim().startsWith (prefix))
{
String pipeName (commandLine.fromFirstOccurrenceOf (prefix, false, false)
.upToFirstOccurrenceOf (" ", false, false).trim());
if (pipeName.isNotEmpty())
{
connection = new Connection (*this, pipeName);
if (! connection->isConnected())
connection = nullptr;
}
}
return connection != nullptr;
}

+ 179
- 0
source/modules/juce_events/interprocess/juce_ConnectedChildProcess.h View File

@@ -0,0 +1,179 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED
#define JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED
//==============================================================================
/**
Acts as the slave end of a master/slave pair of connected processes.
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app
to spawn a child process, and to manage a 2-way messaging connection to control it.
To use the system, you need to create subclasses of both ChildProcessSlave and
ChildProcessMaster. To instantiate the ChildProcessSlave object, you must
add some code to your main() or JUCEApplication::initialise() function that
calls the initialiseFromCommandLine() method to check the app's command-line
parameters to see whether it's being launched as a child process. If this returns
true then the slave process can be allowed to run, and its handleMessageFromMaster()
method will be called whenever a message arrives.
The juce demo app has a good example of this class in action.
@see ChildProcessMaster, InterprocessConnection, ChildProcess
*/
class JUCE_API ChildProcessSlave
{
public:
/** Creates a non-connected slave process.
Use initialiseFromCommandLine to connect to a master process.
*/
ChildProcessSlave();
/** Destructor. */
virtual ~ChildProcessSlave();
/** This checks some command-line parameters to see whether they were generated by
ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process.
In an exe that can be used as a child process, you should add some code to your
main() or JUCEApplication::initialise() that calls this method.
The commandLineUniqueID should be a short alphanumeric identifier (no spaces!)
that matches the string passed to ChildProcessMaster::launchSlaveProcess().
Returns true if the command-line matches and the connection is made successfully.
*/
bool initialiseFromCommandLine (const String& commandLine,
const String& commandLineUniqueID);
//==============================================================================
/** This will be called to deliver messages from the master process.
The call will probably be made on a background thread, so be careful with your
thread-safety! You may want to respond by sending back a message with
sendMessageToMaster()
*/
virtual void handleMessageFromMaster (const MemoryBlock&) = 0;
/** This will be called when the master process finishes connecting to this slave.
The call will probably be made on a background thread, so be careful with your thread-safety!
*/
virtual void handleConnectionMade();
/** This will be called when the connection to the master process is lost.
The call may be made from any thread (including the message thread).
Typically, if your process only exists to act as a slave, you should probably exit
when this happens.
*/
virtual void handleConnectionLost();
/** Tries to send a message to the master process.
This returns true if the message was sent, but doesn't check that it actually gets
delivered at the other end. If successful, the data will emerge in a call to your
ChildProcessMaster::handleMessageFromSlave().
*/
bool sendMessageToMaster (const MemoryBlock&);
private:
struct Connection;
friend struct Connection;
friend struct ContainerDeletePolicy<Connection>;
ScopedPointer<Connection> connection;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave)
};
//==============================================================================
/**
Acts as the master in a master/slave pair of connected processes.
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app
to spawn a child process, and to manage a 2-way messaging connection to control it.
To use the system, you need to create subclasses of both ChildProcessSlave and
ChildProcessMaster. When you want your master process to launch the slave, you
just call launchSlaveProcess(), and it'll attempt to launch the executable that
you specify (which may be the same exe), and assuming it has been set-up to
correctly parse the command-line parameters (see ChildProcessSlave) then a
two-way connection will be created.
The juce demo app has a good example of this class in action.
@see ChildProcessSlave, InterprocessConnection, ChildProcess
*/
class JUCE_API ChildProcessMaster
{
public:
/** Creates an uninitialised master process object.
Use launchSlaveProcess to launch and connect to a child process.
*/
ChildProcessMaster();
/** Destructor. */
virtual ~ChildProcessMaster();
/** Attempts to launch and connect to a slave process.
This will start the given executable, passing it a special command-line
parameter based around the commandLineUniqueID string, which must be a
short alphanumeric string (no spaces!) that identifies your app. The exe
that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine()
in its startup code, and must use a matching ID to commandLineUniqueID.
If this all works, the method returns true, and you can begin sending and
receiving messages with the slave process.
*/
bool launchSlaveProcess (const File& executableToLaunch,
const String& commandLineUniqueID);
/** This will be called to deliver a message from the slave process.
The call will probably be made on a background thread, so be careful with your thread-safety!
*/
virtual void handleMessageFromSlave (const MemoryBlock&) = 0;
/** This will be called when the slave process dies or is somehow disconnected.
The call will probably be made on a background thread, so be careful with your thread-safety!
*/
virtual void handleConnectionLost();
/** Attempts to send a message to the slave process.
This returns true if the message was dispatched, but doesn't check that it actually
gets delivered at the other end. If successful, the data will emerge in a call to
your ChildProcessSlave::handleMessageFromMaster().
*/
bool sendMessageToSlave (const MemoryBlock&);
private:
ChildProcess childProcess;
struct Connection;
friend struct Connection;
friend struct ContainerDeletePolicy<Connection>;
ScopedPointer<Connection> connection;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster)
};
#endif // JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED

+ 27
- 14
source/modules/juce_events/interprocess/juce_InterprocessConnection.cpp View File

@@ -22,14 +22,27 @@
==============================================================================
*/
struct InterprocessConnection::ConnectionThread : public Thread
{
ConnectionThread (InterprocessConnection& c) : Thread ("JUCE IPC"), owner (c) {}
void run() override { owner.runThread(); }
private:
InterprocessConnection& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionThread);
};
//==============================================================================
InterprocessConnection::InterprocessConnection (const bool callbacksOnMessageThread,
const uint32 magicMessageHeaderNumber)
: Thread ("Juce IPC connection"),
callbackConnectionState (false),
: callbackConnectionState (false),
useMessageThread (callbacksOnMessageThread),
magicMessageHeader (magicMessageHeaderNumber),
pipeReceiveMessageTimeout (-1)
{
thread = new ConnectionThread (*this);
}
InterprocessConnection::~InterprocessConnection()
@@ -37,9 +50,9 @@ InterprocessConnection::~InterprocessConnection()
callbackConnectionState = false;
disconnect();
masterReference.clear();
thread = nullptr;
}
//==============================================================================
bool InterprocessConnection::connectToSocket (const String& hostName,
const int portNumber,
@@ -53,7 +66,7 @@ bool InterprocessConnection::connectToSocket (const String& hostName,
if (socket->connect (hostName, portNumber, timeOutMillisecs))
{
connectionMadeInt();
startThread();
thread->startThread();
return true;
}
else
@@ -99,7 +112,7 @@ bool InterprocessConnection::createPipe (const String& pipeName, const int timeo
void InterprocessConnection::disconnect()
{
signalThreadShouldExit();
thread->signalThreadShouldExit();
{
const ScopedLock sl (pipeAndSocketLock);
@@ -107,7 +120,7 @@ void InterprocessConnection::disconnect()
if (pipe != nullptr) pipe->close();
}
stopThread (4000);
thread->stopThread (4000);
deletePipeAndSocket();
connectionLostInt();
}
@@ -125,7 +138,7 @@ bool InterprocessConnection::isConnected() const
return ((socket != nullptr && socket->isConnected())
|| (pipe != nullptr && pipe->isOpen()))
&& isThreadRunning();
&& thread->isThreadRunning();
}
String InterprocessConnection::getConnectedHostName() const
@@ -173,7 +186,7 @@ void InterprocessConnection::initialiseWithSocket (StreamingSocket* newSocket)
jassert (socket == nullptr && pipe == nullptr);
socket = newSocket;
connectionMadeInt();
startThread();
thread->startThread();
}
void InterprocessConnection::initialiseWithPipe (NamedPipe* newPipe)
@@ -181,7 +194,7 @@ void InterprocessConnection::initialiseWithPipe (NamedPipe* newPipe)
jassert (socket == nullptr && pipe == nullptr);
pipe = newPipe;
connectionMadeInt();
startThread();
thread->startThread();
}
//==============================================================================
@@ -279,7 +292,7 @@ bool InterprocessConnection::readNextMessageInt()
while (bytesInMessage > 0)
{
if (threadShouldExit())
if (thread->threadShouldExit())
return false;
const int numThisTime = jmin (bytesInMessage, 65536);
@@ -311,9 +324,9 @@ bool InterprocessConnection::readNextMessageInt()
return true;
}
void InterprocessConnection::run()
void InterprocessConnection::runThread()
{
while (! threadShouldExit())
while (! thread->threadShouldExit())
{
if (socket != nullptr)
{
@@ -328,7 +341,7 @@ void InterprocessConnection::run()
if (ready == 0)
{
wait (1);
thread->wait (1);
continue;
}
}
@@ -346,7 +359,7 @@ void InterprocessConnection::run()
break;
}
if (threadShouldExit() || ! readNextMessageInt())
if (thread->threadShouldExit() || ! readNextMessageInt())
break;
}
}

+ 8
- 3
source/modules/juce_events/interprocess/juce_InterprocessConnection.h View File

@@ -47,7 +47,7 @@ class MemoryBlock;
@see InterprocessConnectionServer, Socket, NamedPipe
*/
class JUCE_API InterprocessConnection : private Thread
class JUCE_API InterprocessConnection
{
public:
//==============================================================================
@@ -71,7 +71,7 @@ public:
uint32 magicMessageHeaderNumber = 0xf2b49e2c);
/** Destructor. */
~InterprocessConnection();
virtual ~InterprocessConnection();
//==============================================================================
/** Tries to connect this object to a socket.
@@ -195,7 +195,12 @@ private:
void connectionLostInt();
void deliverDataInt (const MemoryBlock&);
bool readNextMessageInt();
void run() override;
struct ConnectionThread;
friend struct ConnectionThread;
friend struct ContainerDeletePolicy<ConnectionThread>;
ScopedPointer<ConnectionThread> thread;
void runThread();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection)
};


+ 1
- 0
source/modules/juce_events/juce_events.cpp View File

@@ -73,6 +73,7 @@ namespace juce
#include "timers/juce_Timer.cpp"
#include "interprocess/juce_InterprocessConnection.cpp"
#include "interprocess/juce_InterprocessConnectionServer.cpp"
#include "interprocess/juce_ConnectedChildProcess.cpp"
//==============================================================================
#if JUCE_MAC


+ 1
- 0
source/modules/juce_events/juce_events.h View File

@@ -49,6 +49,7 @@ namespace juce
#include "timers/juce_MultiTimer.h"
#include "interprocess/juce_InterprocessConnection.h"
#include "interprocess/juce_InterprocessConnectionServer.h"
#include "interprocess/juce_ConnectedChildProcess.h"
#include "native/juce_ScopedXLock.h"
}


+ 13
- 11
source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp View File

@@ -281,28 +281,30 @@ void Graphics::drawMultiLineText (const String& text, const int startX,
}
}
void Graphics::drawText (const String& text, const Rectangle<int>& area,
Justification justificationType,
const bool useEllipsesIfTooBig) const
void Graphics::drawText (const String& text, const Rectangle<float>& area,
Justification justificationType, bool useEllipsesIfTooBig) const
{
if (text.isNotEmpty() && context.clipRegionIntersects (area))
if (text.isNotEmpty() && context.clipRegionIntersects (area.getSmallestIntegerContainer()))
{
GlyphArrangement arr;
arr.addCurtailedLineOfText (context.getFont(), text,
0.0f, 0.0f, (float) area.getWidth(),
useEllipsesIfTooBig);
arr.addCurtailedLineOfText (context.getFont(), text, 0.0f, 0.0f,
area.getWidth(), useEllipsesIfTooBig);
arr.justifyGlyphs (0, arr.getNumGlyphs(),
(float) area.getX(), (float) area.getY(),
(float) area.getWidth(), (float) area.getHeight(),
area.getX(), area.getY(), area.getWidth(), area.getHeight(),
justificationType);
arr.draw (*this);
}
}
void Graphics::drawText (const String& text, const Rectangle<int>& area,
Justification justificationType, bool useEllipsesIfTooBig) const
{
drawText (text, area.toFloat(), justificationType, useEllipsesIfTooBig);
}
void Graphics::drawText (const String& text, const int x, const int y, const int width, const int height,
Justification justificationType,
const bool useEllipsesIfTooBig) const
Justification justificationType, const bool useEllipsesIfTooBig) const
{
drawText (text, Rectangle<int> (x, y, width, height), justificationType, useEllipsesIfTooBig);
}


+ 14
- 0
source/modules/juce_graphics/contexts/juce_GraphicsContext.h View File

@@ -174,6 +174,20 @@ public:
Justification justificationType,
bool useEllipsesIfTooBig) const;
/** Draws a line of text within a specified rectangle.
The text will be positioned within the rectangle based on the justification
flags passed-in. If the string is too long to fit inside the rectangle, it will
either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig
flag is true).
@see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText
*/
void drawText (const String& text,
const Rectangle<float>& area,
Justification justificationType,
bool useEllipsesIfTooBig) const;
/** Tries to draw a text string inside a given space.
This does its best to make the given text readable within the specified rectangle,


+ 18
- 12
source/modules/juce_graphics/native/juce_RenderingHelpers.h View File

@@ -806,16 +806,19 @@ namespace EdgeTableFillers
alphaLevel = (alphaLevel * extraAlpha) >> 8;
x -= xOffset;
jassert (repeatPattern || (x >= 0 && x + width <= srcData.width));
if (alphaLevel < 0xfe)
if (repeatPattern)
{
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (repeatPattern ? (x++ % srcData.width) : x++), (uint32) alphaLevel))
if (alphaLevel < 0xfe)
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width), (uint32) alphaLevel))
else
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width)))
}
else
{
if (repeatPattern)
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width)))
jassert (x >= 0 && x + width <= srcData.width);
if (alphaLevel < 0xfe)
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++), (uint32) alphaLevel))
else
copyRow (dest, getSrcPixel (x), width);
}
@@ -826,16 +829,19 @@ namespace EdgeTableFillers
DestPixelType* dest = getDestPixel (x);
x -= xOffset;
jassert (repeatPattern || (x >= 0 && x + width <= srcData.width));
if (extraAlpha < 0xfe)
if (repeatPattern)
{
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (repeatPattern ? (x++ % srcData.width) : x++), (uint32) extraAlpha))
if (extraAlpha < 0xfe)
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width), (uint32) extraAlpha))
else
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width)))
}
else
{
if (repeatPattern)
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width)))
jassert (x >= 0 && x + width <= srcData.width);
if (extraAlpha < 0xfe)
JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++), (uint32) extraAlpha))
else
copyRow (dest, getSrcPixel (x), width);
}


+ 12
- 0
source/modules/juce_gui_basics/drawables/juce_Drawable.cpp View File

@@ -107,6 +107,18 @@ void Drawable::setBoundsToEnclose (const Rectangle<float>& area)
setBounds (newBounds);
}
//==============================================================================
bool Drawable::replaceColour (Colour original, Colour replacement)
{
bool changed = false;
for (int i = getNumChildComponents(); --i >= 0;)
if (Drawable* d = dynamic_cast<Drawable*> (getChildComponent(i)))
changed = d->replaceColour (original, replacement) || changed;
return changed;
}
//==============================================================================
void Drawable::setOriginWithOriginalSize (Point<float> originWithinParent)
{


+ 5
- 0
source/modules/juce_gui_basics/drawables/juce_Drawable.h View File

@@ -175,6 +175,11 @@ public:
*/
virtual Rectangle<float> getDrawableBounds() const = 0;
/** Recursively replaces a colour that might be used for filling or stroking.
return true if any instances of this colour were found.
*/
virtual bool replaceColour (Colour originalColour, Colour replacementColour);
//==============================================================================
/** Internal class used to manage ValueTrees that represent Drawables. */
class ValueTreeWrapperBase


+ 18
- 0
source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp View File

@@ -452,3 +452,21 @@ void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& new
state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt
? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
}
static bool replaceColourInFill (DrawableShape::RelativeFillType& fill, Colour original, Colour replacement)
{
if (fill.fill.colour == original && fill.fill.isColour())
{
fill = FillType (replacement);
return true;
}
return false;
}
bool DrawableShape::replaceColour (Colour original, Colour replacement)
{
bool changed1 = replaceColourInFill (mainFill, original, replacement);
bool changed2 = replaceColourInFill (strokeFill, original, replacement);
return changed1 || changed2;
}

+ 2
- 0
source/modules/juce_gui_basics/drawables/juce_DrawableShape.h View File

@@ -147,6 +147,8 @@ public:
void paint (Graphics&) override;
/** @internal */
bool hitTest (int x, int y) override;
/** @internal */
bool replaceColour (Colour originalColour, Colour replacementColour) override;
protected:
//==============================================================================


+ 4
- 2
source/modules/juce_gui_basics/layout/juce_Viewport.h View File

@@ -131,10 +131,12 @@ public:
*/
bool autoScroll (int mouseX, int mouseY, int distanceFromEdge, int maximumSpeed);
/** Returns the position within the child component of the top-left of its visible area.
*/
/** Returns the position within the child component of the top-left of its visible area. */
Point<int> getViewPosition() const noexcept { return lastVisibleArea.getPosition(); }
/** Returns the visible area of the child component, relative to its top-left */
Rectangle<int> getViewArea() const noexcept { return lastVisibleArea; }
/** Returns the position within the child component of the top-left of its visible area.
@see getViewWidth, setViewPosition
*/


+ 1
- 1
source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp View File

@@ -198,7 +198,7 @@ void FileChooser::showPlatformDialog (Array<File>& results, const String& title_
}
else
{
DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_ENABLESIZING;
if (warnAboutOverwritingExistingFiles)
flags |= OFN_OVERWRITEPROMPT;


+ 17
- 24
source/modules/juce_gui_basics/widgets/juce_TextEditor.cpp View File

@@ -1424,47 +1424,40 @@ void TextEditor::scrollToMakeSureCursorIsVisible()
if (keepCaretOnScreen)
{
int x = viewport->getViewPositionX();
int y = viewport->getViewPositionY();
Point<int> viewPos (viewport->getViewPosition());
const Rectangle<int> caretRect (getCaretRectangle());
const Rectangle<int> caretPos (getCaretRectangle());
const Point<int> relativeCursor = caretRect.getPosition() - viewPos;
const int relativeCursorX = caretPos.getX() - x;
const int relativeCursorY = caretPos.getY() - y;
if (relativeCursorX < jmax (1, proportionOfWidth (0.05f)))
if (relativeCursor.x < jmax (1, proportionOfWidth (0.05f)))
{
x += relativeCursorX - proportionOfWidth (0.2f);
viewPos.x += relativeCursor.x - proportionOfWidth (0.2f);
}
else if (relativeCursorX > jmax (0, viewport->getMaximumVisibleWidth() - (wordWrap ? 2 : 10)))
else if (relativeCursor.x > jmax (0, viewport->getMaximumVisibleWidth() - (wordWrap ? 2 : 10)))
{
x += relativeCursorX + (isMultiLine() ? proportionOfWidth (0.2f) : 10) - viewport->getMaximumVisibleWidth();
viewPos.x += relativeCursor.x + (isMultiLine() ? proportionOfWidth (0.2f) : 10) - viewport->getMaximumVisibleWidth();
}
x = jlimit (0, jmax (0, textHolder->getWidth() + 8 - viewport->getMaximumVisibleWidth()), x);
viewPos.x = jlimit (0, jmax (0, textHolder->getWidth() + 8 - viewport->getMaximumVisibleWidth()), viewPos.x);
if (! isMultiLine())
{
y = (getHeight() - textHolder->getHeight() - topIndent) / -2;
viewPos.y = (getHeight() - textHolder->getHeight() - topIndent) / -2;
}
else
else if (relativeCursor.y < 0)
{
if (relativeCursorY < 0)
{
y = jmax (0, relativeCursorY + y);
}
else if (relativeCursorY > jmax (0, viewport->getMaximumVisibleHeight() - topIndent - caretPos.getHeight()))
{
y += relativeCursorY + 2 + caretPos.getHeight() + topIndent - viewport->getMaximumVisibleHeight();
}
viewPos.y = jmax (0, relativeCursor.y + viewPos.y);
}
else if (relativeCursor.y > jmax (0, viewport->getMaximumVisibleHeight() - topIndent - caretRect.getHeight()))
{
viewPos.y += relativeCursor.y + 2 + caretRect.getHeight() + topIndent - viewport->getMaximumVisibleHeight();
}
viewport->setViewPosition (x, y);
viewport->setViewPosition (viewPos);
}
}
void TextEditor::moveCaretTo (const int newPosition,
const bool isSelecting)
void TextEditor::moveCaretTo (const int newPosition, const bool isSelecting)
{
if (isSelecting)
{


+ 77
- 39
source/modules/juce_gui_extra/native/juce_mac_NSViewComponent.mm View File

@@ -22,14 +22,82 @@
==============================================================================
*/
struct NSViewResizeWatcher
{
NSViewResizeWatcher() : callback (nil) {}
virtual ~NSViewResizeWatcher()
{
// must call detachViewWatcher() first
jassert (callback == nil);
}
void attachViewWatcher (NSView* view)
{
static ViewFrameChangeCallbackClass cls;
callback = [cls.createInstance() init];
ViewFrameChangeCallbackClass::setTarget (callback, this);
[[NSNotificationCenter defaultCenter] addObserver: callback
selector: @selector (frameChanged:)
name: NSViewFrameDidChangeNotification
object: view];
}
void detachViewWatcher()
{
if (callback != nil)
{
[[NSNotificationCenter defaultCenter] removeObserver: callback];
[callback release];
callback = nil;
}
}
virtual void viewResized() = 0;
private:
id callback;
//==============================================================================
struct ViewFrameChangeCallbackClass : public ObjCClass<NSObject>
{
ViewFrameChangeCallbackClass() : ObjCClass<NSObject> ("JUCE_NSViewCallback_")
{
addIvar<NSViewResizeWatcher*> ("target");
addMethod (@selector (frameChanged:), frameChanged, "v@:@");
registerClass();
}
static void setTarget (id self, NSViewResizeWatcher* c)
{
object_setInstanceVariable (self, "target", c);
}
private:
static void frameChanged (id self, SEL, NSNotification*)
{
if (NSViewResizeWatcher* const target = getIvar<NSViewResizeWatcher*> (self, "target"))
target->viewResized();
}
JUCE_DECLARE_NON_COPYABLE (ViewFrameChangeCallbackClass);
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewResizeWatcher)
};
//==============================================================================
class NSViewAttachment : public ReferenceCountedObject,
public ComponentMovementWatcher
public ComponentMovementWatcher,
private NSViewResizeWatcher
{
public:
NSViewAttachment (NSView* const v, Component& comp)
: ComponentMovementWatcher (&comp),
view (v), owner (comp),
currentPeer (nullptr), frameChangeCallback (nullptr)
currentPeer (nullptr)
{
[view retain];
[view setPostsFrameChangedNotifications: YES];
@@ -37,21 +105,12 @@ public:
if (owner.isShowing())
componentPeerChanged();
static ViewFrameChangeCallbackClass cls;
frameChangeCallback = [cls.createInstance() init];
ViewFrameChangeCallbackClass::setTarget (frameChangeCallback, &owner);
[[NSNotificationCenter defaultCenter] addObserver: frameChangeCallback
selector: @selector (frameChanged:)
name: NSViewFrameDidChangeNotification
object: view];
attachViewWatcher (view);
}
~NSViewAttachment()
{
[[NSNotificationCenter defaultCenter] removeObserver: frameChangeCallback];
[frameChangeCallback release];
detachViewWatcher();
removeFromParent();
[view release];
}
@@ -103,12 +162,16 @@ public:
componentPeerChanged();
}
void viewResized() override
{
owner.childBoundsChanged (nullptr);
}
NSView* const view;
private:
Component& owner;
ComponentPeer* currentPeer;
id frameChangeCallback;
void removeFromParent()
{
@@ -117,31 +180,6 @@ private:
// override the call and use it as a sign that they're being deleted, which breaks everything..
}
//==============================================================================
struct ViewFrameChangeCallbackClass : public ObjCClass<NSObject>
{
ViewFrameChangeCallbackClass() : ObjCClass<NSObject> ("JUCE_NSViewCallback_")
{
addIvar<Component*> ("target");
addMethod (@selector (frameChanged:), frameChanged, "v@:@");
registerClass();
}
static void setTarget (id self, Component* c)
{
object_setInstanceVariable (self, "target", c);
}
private:
static void frameChanged (id self, SEL, NSNotification*)
{
if (Component* const target = getIvar<Component*> (self, "target"))
target->childBoundsChanged (nullptr);
}
JUCE_DECLARE_NON_COPYABLE (ViewFrameChangeCallbackClass);
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewAttachment)
};


Loading…
Cancel
Save