diff --git a/build/macosx/platform_specific_code/juce_mac_MessageManager.mm b/build/macosx/platform_specific_code/juce_mac_MessageManager.mm index 750e504055..2e27766c60 100644 --- a/build/macosx/platform_specific_code/juce_mac_MessageManager.mm +++ b/build/macosx/platform_specific_code/juce_mac_MessageManager.mm @@ -271,6 +271,45 @@ void MessageManager::stopDispatchLoop() [NSApp stop: nil]; } +static bool isEventBlockedByModalComps (NSEvent* e) +{ + if (Component::getNumCurrentlyModalComponents() == 0) + return false; + + [[NSApp mainMenu] update]; + NSWindow* const w = [e window]; + if (w == 0 || [w worksWhenModal]) + return false; + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + { + ComponentPeer* const peer = ComponentPeer::getPeer (i); + NSView* const compView = (NSView*) peer->getNativeHandle(); + + if ([compView window] == w + && (NSPointInRect ([compView convertPoint: [e locationInWindow] fromView: nil], + [compView bounds]) + || peer->getComponent()->isMouseButtonDown())) + { + return false; + } + } + + if ([e type] == NSLeftMouseDown + || [e type] == NSRightMouseDown + || [e type] == NSOtherMouseDown) + { + if (! [NSApp isActive]) + [NSApp activateIgnoringOtherApps: YES]; + + Component* const modal = Component::getCurrentlyModalComponent (0); + if (modal != 0) + modal->inputAttemptWhenModal(); + } + + return true; +} + bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { const ScopedAutoReleasePool pool; @@ -290,7 +329,9 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) untilDate: endDate inMode: NSDefaultRunLoopMode dequeue: YES]; - [NSApp sendEvent: e]; + + if (! isEventBlockedByModalComps (e)) + [NSApp sendEvent: e]; } return ! quitMessagePosted; @@ -325,8 +366,6 @@ void MessageManager::doPlatformSpecificShutdown() getInstance()->runDispatchLoopUntil (10); } - jassert (numPendingMessages == 0); // failed to get all the pending messages cleared before quitting.. - [juceAppDelegate release]; juceAppDelegate = 0; } diff --git a/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm b/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm index 77cdfffdc1..a69af6cde2 100644 --- a/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm +++ b/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm @@ -54,7 +54,9 @@ END_JUCE_NAMESPACE - (void) drawRect: (NSRect) r; - (void) mouseDown: (NSEvent*) ev; +- (void) asyncMouseDown: (NSEvent*) ev; - (void) mouseUp: (NSEvent*) ev; +- (void) asyncMouseUp: (NSEvent*) ev; - (void) mouseDragged: (NSEvent*) ev; - (void) mouseMoved: (NSEvent*) ev; - (void) mouseEntered: (NSEvent*) ev; @@ -252,12 +254,38 @@ END_JUCE_NAMESPACE //============================================================================== - (void) mouseDown: (NSEvent*) ev +{ + // In some host situations, the host will stop modal loops from working + // correctly if they're called from a mouse event, so we'll trigger + // the event asynchronously.. + if (JUCEApplication::getInstance() == 0) + [self performSelectorOnMainThread: @selector (asyncMouseDown:) + withObject: ev + waitUntilDone: NO]; + else + [self asyncMouseDown: ev]; +} + +- (void) asyncMouseDown: (NSEvent*) ev { if (owner != 0) owner->redirectMouseDown (ev); } - (void) mouseUp: (NSEvent*) ev +{ + // In some host situations, the host will stop modal loops from working + // correctly if they're called from a mouse event, so we'll trigger + // the event asynchronously.. + if (JUCEApplication::getInstance() == 0) + [self performSelectorOnMainThread: @selector (asyncMouseUp:) + withObject: ev + waitUntilDone: NO]; + else + [self asyncMouseUp: ev]; +} + +- (void) asyncMouseUp: (NSEvent*) ev { if (owner != 0) owner->redirectMouseUp (ev); diff --git a/build/win32/platform_specific_code/juce_win32_Messaging.cpp b/build/win32/platform_specific_code/juce_win32_Messaging.cpp index 6e7333b74c..4810e43be6 100644 --- a/build/win32/platform_specific_code/juce_win32_Messaging.cpp +++ b/build/win32/platform_specific_code/juce_win32_Messaging.cpp @@ -92,6 +92,59 @@ static LRESULT CALLBACK juce_MessageWndProc (HWND h, return DefWindowProc (h, message, wParam, lParam); } +static bool isEventBlockedByModalComps (MSG& m) +{ + if (Component::getNumCurrentlyModalComponents() == 0 + || GetWindowLong (m.hwnd, GWLP_USERDATA) == improbableWindowNumber) + return false; + + switch (m.message) + { + case WM_MOUSEMOVE: + case WM_NCMOUSEMOVE: + case 0x020A: /* WM_MOUSEWHEEL */ + case 0x020E: /* WM_MOUSEHWHEEL */ + case WM_KEYUP: + case WM_SYSKEYUP: + case WM_CHAR: + case WM_APPCOMMAND: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_MOUSEACTIVATE: + case WM_NCMOUSEHOVER: + case WM_MOUSEHOVER: + return true; + + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONDBLCLK: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONDBLCLK: + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + Component* const modal = Component::getCurrentlyModalComponent (0); + if (modal != 0) + modal->inputAttemptWhenModal(); + + return true; + } + + default: + break; + } + + return false; +} + bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages) { MSG m; @@ -106,7 +159,7 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages { MessageManager::getInstance()->deliverMessage ((void*) m.lParam); } - else + else if (! isEventBlockedByModalComps (m)) { if (GetWindowLong (m.hwnd, GWLP_USERDATA) != improbableWindowNumber && (m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN)) diff --git a/extras/audio plugins/wrapper/Standalone/juce_AudioFilterStreamer.cpp b/extras/audio plugins/wrapper/Standalone/juce_AudioFilterStreamer.cpp index 2db7a15942..c94386b3fb 100644 --- a/extras/audio plugins/wrapper/Standalone/juce_AudioFilterStreamer.cpp +++ b/extras/audio plugins/wrapper/Standalone/juce_AudioFilterStreamer.cpp @@ -155,7 +155,7 @@ void AudioFilterStreamingDeviceManager::setFilter (AudioProcessor* filterToStrea if (streamer != 0) { removeMidiInputCallback (String::empty, streamer); - setAudioCallback (0); + removeAudioCallback (streamer); delete streamer; streamer = 0; @@ -165,7 +165,7 @@ void AudioFilterStreamingDeviceManager::setFilter (AudioProcessor* filterToStrea { streamer = new AudioFilterStreamer (*filterToStream); - setAudioCallback (streamer); + addAudioCallback (streamer); addMidiInputCallback (String::empty, streamer); } } diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index ed92fba1dc..b11a74319a 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -3562,6 +3562,12 @@ var::var (DynamicObject* const object) throw() object->incReferenceCount(); } +var::var (MethodFunction method_) throw() + : type (methodType) +{ + value.methodValue = method_; +} + const var& var::operator= (const var& valueToCopy) throw() { if (this != &valueToCopy) @@ -3643,6 +3649,14 @@ const var& var::operator= (DynamicObject* const value_) throw() return *this; } +const var& var::operator= (MethodFunction method_) throw() +{ + releaseValue(); + type = doubleType; + value.methodValue = method_; + return *this; +} + var::operator int() const throw() { switch (type) @@ -3714,6 +3728,74 @@ DynamicObject* var::getObject() const throw() return type == objectType ? value.objectValue : 0; } +const var var::operator[] (const var::identifier& propertyName) const throw() +{ + if (type == objectType && value.objectValue != 0) + return value.objectValue->getProperty (propertyName); + + return var(); +} + +const var var::invoke (const var::identifier& method, const var* arguments, int numArguments) const +{ + if (type == objectType && value.objectValue != 0) + return value.objectValue->invokeMethod (method, arguments, numArguments); + + return var(); +} + +const var var::invoke (const var& targetObject, const var* arguments, int numArguments) const +{ + if (isMethod()) + return value.methodValue (targetObject, arguments, numArguments); + + return var(); +} + +const var var::call (const var::identifier& method) const +{ + return invoke (method, 0, 0); +} + +const var var::call (const var::identifier& method, const var& arg1) const +{ + return invoke (method, &arg1, 1); +} + +const var var::call (const var::identifier& method, const var& arg1, const var& arg2) const +{ + var args[] = { arg1, arg2 }; + return invoke (method, args, 2); +} + +const var var::call (const var::identifier& method, const var& arg1, const var& arg2, const var& arg3) +{ + var args[] = { arg1, arg2, arg3 }; + return invoke (method, args, 3); +} + +const var var::call (const var::identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const +{ + var args[] = { arg1, arg2, arg3, arg4 }; + return invoke (method, args, 4); +} + +var::identifier::identifier (const String& name_) throw() + : name (name_), + hashCode (name_.hashCode()) +{ +} + +var::identifier::identifier (const char* const name_) throw() + : name (name_), + hashCode (name.hashCode()) +{ +} + +var::identifier::~identifier() throw() +{ +} + DynamicObject::DynamicObject() { } @@ -3722,23 +3804,24 @@ DynamicObject::~DynamicObject() { } -bool DynamicObject::hasProperty (const String& propertyName) const +bool DynamicObject::hasProperty (const var::identifier& propertyName) const { - return propertyNames.contains (propertyName, false); + const int index = propertyIds.indexOf (propertyName.hashCode); + return index >= 0 && ! propertyValues.getUnchecked (index)->isMethod(); } -const var DynamicObject::getProperty (const String& propertyName) const +const var DynamicObject::getProperty (const var::identifier& propertyName) const { - const int index = propertyNames.indexOf (propertyName, false); + const int index = propertyIds.indexOf (propertyName.hashCode); if (index >= 0) return *propertyValues.getUnchecked (index); return var(); } -void DynamicObject::setProperty (const String& propertyName, const var& newValue) +void DynamicObject::setProperty (const var::identifier& propertyName, const var& newValue) { - const int index = propertyNames.indexOf (propertyName, false); + const int index = propertyIds.indexOf (propertyName.hashCode); if (index >= 0) { @@ -3746,60 +3829,32 @@ void DynamicObject::setProperty (const String& propertyName, const var& newValue } else { - propertyNames.add (propertyName); + propertyIds.add (propertyName.hashCode); propertyValues.add (new var (newValue)); } } -void DynamicObject::removeProperty (const String& propertyName) +void DynamicObject::removeProperty (const var::identifier& propertyName) { - const int index = propertyNames.indexOf (propertyName, false); + const int index = propertyIds.indexOf (propertyName.hashCode); if (index >= 0) { - propertyNames.remove (index); + propertyIds.remove (index); propertyValues.remove (index); } } -bool DynamicObject::hasMethod (const String& methodName) const +bool DynamicObject::hasMethod (const var::identifier& methodName) const { - return false; + return getProperty (methodName).isMethod(); } -const var DynamicObject::invokeMethod (const String& methodName, +const var DynamicObject::invokeMethod (const var::identifier& methodName, const var* parameters, int numParameters) { - return var(); -} - -const var DynamicObject::invoke (const String& methodName) -{ - return invokeMethod (methodName, 0, 0); -} - -const var DynamicObject::invoke (const String& methodName, const var& arg1) -{ - return invokeMethod (methodName, &arg1, 1); -} - -const var DynamicObject::invoke (const String& methodName, const var& arg1, const var& arg2) -{ - var params[] = { arg1, arg2 }; - return invokeMethod (methodName, params, 2); -} - -const var DynamicObject::invoke (const String& methodName, const var& arg1, const var& arg2, const var& arg3) -{ - var params[] = { arg1, arg2, arg3 }; - return invokeMethod (methodName, params, 3); -} - -const var DynamicObject::invoke (const String& methodName, const var& arg1, const var& arg2, const var& arg3, const var& arg4) -{ - var params[] = { arg1, arg2, arg3, arg4 }; - return invokeMethod (methodName, params, 4); + return getProperty (methodName).invoke (this, parameters, numParameters); } END_JUCE_NAMESPACE @@ -23037,7 +23092,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat { const double callbackStartTime = Time::getMillisecondCounterHiRes(); - tempBuffer.setSize (numOutputChannels, numSamples, false, false, true); + tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true); callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples); @@ -61038,6 +61093,31 @@ void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, } } +void LookAndFeel::drawSpinningWaitAnimation (Graphics& g, int x, int y, int w, int h) +{ + const float radius = jmin (w, h) * 0.4f; + const float thickness = radius * 0.15f; + Path p; + p.addRoundedRectangle (radius * 0.4f, thickness * -0.5f, + radius * 0.6f, thickness, + thickness * 0.5f); + + const float cx = x + w * 0.5f; + const float cy = y + h * 0.5f; + + const uint32 animationIndex = (Time::getMillisecondCounter() / (1000 / 10)) % 12; + const Colour col (g.getCurrentColour()); + + for (int i = 0; i < 12; ++i) + { + const int n = (i + 12 - animationIndex) % 12; + g.setColour (col.withMultipliedAlpha ((n + 1) / 12.0f)); + + g.fillPath (p, AffineTransform::rotation (i * (float_Pi / 6.0f)) + .translated (cx, cy)); + } +} + void LookAndFeel::drawScrollbarButton (Graphics& g, ScrollBar& scrollbar, int width, int height, @@ -242763,6 +242843,59 @@ static LRESULT CALLBACK juce_MessageWndProc (HWND h, return DefWindowProc (h, message, wParam, lParam); } +static bool isEventBlockedByModalComps (MSG& m) +{ + if (Component::getNumCurrentlyModalComponents() == 0 + || GetWindowLong (m.hwnd, GWLP_USERDATA) == improbableWindowNumber) + return false; + + switch (m.message) + { + case WM_MOUSEMOVE: + case WM_NCMOUSEMOVE: + case 0x020A: /* WM_MOUSEWHEEL */ + case 0x020E: /* WM_MOUSEHWHEEL */ + case WM_KEYUP: + case WM_SYSKEYUP: + case WM_CHAR: + case WM_APPCOMMAND: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_MOUSEACTIVATE: + case WM_NCMOUSEHOVER: + case WM_MOUSEHOVER: + return true; + + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONDBLCLK: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONDBLCLK: + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + Component* const modal = Component::getCurrentlyModalComponent (0); + if (modal != 0) + modal->inputAttemptWhenModal(); + + return true; + } + + default: + break; + } + + return false; +} + bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages) { MSG m; @@ -242777,7 +242910,7 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages { MessageManager::getInstance()->deliverMessage ((void*) m.lParam); } - else + else if (! isEventBlockedByModalComps (m)) { if (GetWindowLong (m.hwnd, GWLP_USERDATA) != improbableWindowNumber && (m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN)) @@ -266715,7 +266848,9 @@ END_JUCE_NAMESPACE - (void) drawRect: (NSRect) r; - (void) mouseDown: (NSEvent*) ev; +- (void) asyncMouseDown: (NSEvent*) ev; - (void) mouseUp: (NSEvent*) ev; +- (void) asyncMouseUp: (NSEvent*) ev; - (void) mouseDragged: (NSEvent*) ev; - (void) mouseMoved: (NSEvent*) ev; - (void) mouseEntered: (NSEvent*) ev; @@ -266904,12 +267039,38 @@ END_JUCE_NAMESPACE } - (void) mouseDown: (NSEvent*) ev +{ + // In some host situations, the host will stop modal loops from working + // correctly if they're called from a mouse event, so we'll trigger + // the event asynchronously.. + if (JUCEApplication::getInstance() == 0) + [self performSelectorOnMainThread: @selector (asyncMouseDown:) + withObject: ev + waitUntilDone: NO]; + else + [self asyncMouseDown: ev]; +} + +- (void) asyncMouseDown: (NSEvent*) ev { if (owner != 0) owner->redirectMouseDown (ev); } - (void) mouseUp: (NSEvent*) ev +{ + // In some host situations, the host will stop modal loops from working + // correctly if they're called from a mouse event, so we'll trigger + // the event asynchronously.. + if (JUCEApplication::getInstance() == 0) + [self performSelectorOnMainThread: @selector (asyncMouseUp:) + withObject: ev + waitUntilDone: NO]; + else + [self asyncMouseUp: ev]; +} + +- (void) asyncMouseUp: (NSEvent*) ev { if (owner != 0) owner->redirectMouseUp (ev); @@ -270811,6 +270972,45 @@ void MessageManager::stopDispatchLoop() [NSApp stop: nil]; } +static bool isEventBlockedByModalComps (NSEvent* e) +{ + if (Component::getNumCurrentlyModalComponents() == 0) + return false; + + [[NSApp mainMenu] update]; + NSWindow* const w = [e window]; + if (w == 0 || [w worksWhenModal]) + return false; + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + { + ComponentPeer* const peer = ComponentPeer::getPeer (i); + NSView* const compView = (NSView*) peer->getNativeHandle(); + + if ([compView window] == w + && (NSPointInRect ([compView convertPoint: [e locationInWindow] fromView: nil], + [compView bounds]) + || peer->getComponent()->isMouseButtonDown())) + { + return false; + } + } + + if ([e type] == NSLeftMouseDown + || [e type] == NSRightMouseDown + || [e type] == NSOtherMouseDown) + { + if (! [NSApp isActive]) + [NSApp activateIgnoringOtherApps: YES]; + + Component* const modal = Component::getCurrentlyModalComponent (0); + if (modal != 0) + modal->inputAttemptWhenModal(); + } + + return true; +} + bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { const ScopedAutoReleasePool pool; @@ -270830,7 +271030,9 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) untilDate: endDate inMode: NSDefaultRunLoopMode dequeue: YES]; - [NSApp sendEvent: e]; + + if (! isEventBlockedByModalComps (e)) + [NSApp sendEvent: e]; } return ! quitMessagePosted; @@ -270864,8 +271066,6 @@ void MessageManager::doPlatformSpecificShutdown() getInstance()->runDispatchLoopUntil (10); } - jassert (numPendingMessages == 0); // failed to get all the pending messages cleared before quitting.. - [juceAppDelegate release]; juceAppDelegate = 0; } diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 02ff47d3cc..a70f1bb3f8 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -9812,12 +9812,12 @@ public: @see getUnchecked */ - inline ObjectClass* operator[] (const int index) const throw() + inline const ReferenceCountedObjectPtr operator[] (const int index) const throw() { lock.enter(); - ObjectClass* const result = (((unsigned int) index) < (unsigned int) numUsed) - ? this->elements [index] - : (ObjectClass*) 0; + const ReferenceCountedObjectPtr result ((((unsigned int) index) < (unsigned int) numUsed) + ? this->elements [index] + : (ObjectClass*) 0); lock.exit(); return result; } @@ -9827,11 +9827,11 @@ public: This is a faster and less safe version of operator[] which doesn't check the index passed in, so it can be used when you're sure the index if always going to be legal. */ - inline ObjectClass* getUnchecked (const int index) const throw() + inline const ReferenceCountedObjectPtr getUnchecked (const int index) const throw() { lock.enter(); jassert (((unsigned int) index) < (unsigned int) numUsed); - ObjectClass* const result = this->elements [index]; + const ReferenceCountedObjectPtr result (this->elements [index]); lock.exit(); return result; } @@ -9841,11 +9841,11 @@ public: This will return a null pointer if the array's empty. @see getLast */ - inline ObjectClass* getFirst() const throw() + inline const ReferenceCountedObjectPtr getFirst() const throw() { lock.enter(); - ObjectClass* const result = (numUsed > 0) ? this->elements [0] - : (ObjectClass*) 0; + const ReferenceCountedObjectPtr result ((numUsed > 0) ? this->elements [0] + : (ObjectClass*) 0); lock.exit(); return result; @@ -9856,11 +9856,11 @@ public: This will return a null pointer if the array's empty. @see getFirst */ - inline ObjectClass* getLast() const throw() + inline const ReferenceCountedObjectPtr getLast() const throw() { lock.enter(); - ObjectClass* const result = (numUsed > 0) ? this->elements [numUsed - 1] - : (ObjectClass*) 0; + const ReferenceCountedObjectPtr result ((numUsed > 0) ? this->elements [numUsed - 1] + : (ObjectClass*) 0); lock.exit(); return result; @@ -11384,6 +11384,8 @@ class JUCE_API var { public: + typedef const var (MethodFunction) (const var& thisObject, const var* arguments, int numArguments); + /** Creates a void variant. */ var() throw(); @@ -11398,6 +11400,7 @@ public: var (const juce_wchar* const value) throw(); var (const String& value) throw(); var (DynamicObject* const object) throw(); + var (MethodFunction method) throw(); const var& operator= (const var& valueToCopy) throw(); const var& operator= (const int value) throw(); @@ -11407,6 +11410,7 @@ public: const var& operator= (const juce_wchar* const value) throw(); const var& operator= (const String& value) throw(); const var& operator= (DynamicObject* const object) throw(); + const var& operator= (MethodFunction method) throw(); operator int() const throw(); operator bool() const throw(); @@ -11420,6 +11424,40 @@ public: bool isDouble() const throw() { return type == doubleType; } bool isString() const throw() { return type == stringType; } bool isObject() const throw() { return type == objectType; } + bool isMethod() const throw() { return type == methodType; } + + class identifier + { + public: + identifier (const char* const name) throw(); + identifier (const String& name) throw(); + ~identifier() throw(); + + bool operator== (const identifier& other) const throw() { return hashCode == other.hashCode; } + + String name; + int hashCode; + }; + + /** If this variant is an object, this returns one of its properties. */ + const var operator[] (const identifier& propertyName) const throw(); + + /** If this variant is an object, this invokes one of its methods with no arguments. */ + const var call (const identifier& method) const; + /** If this variant is an object, this invokes one of its methods with one argument. */ + const var call (const identifier& method, const var& arg1) const; + /** If this variant is an object, this invokes one of its methods with 2 arguments. */ + const var call (const identifier& method, const var& arg1, const var& arg2) const; + /** If this variant is an object, this invokes one of its methods with 3 arguments. */ + const var call (const identifier& method, const var& arg1, const var& arg2, const var& arg3); + /** If this variant is an object, this invokes one of its methods with 4 arguments. */ + const var call (const identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; + + /** If this variant is an object, this invokes one of its methods with a list of arguments. */ + const var invoke (const identifier& method, const var* arguments, int numArguments) const; + + /** If this variant is a method pointer, this invokes it on a target object. */ + const var invoke (const var& targetObject, const var* arguments, int numArguments) const; juce_UseDebuggingNewOperator @@ -11431,7 +11469,8 @@ private: boolType, doubleType, stringType, - objectType + objectType, + methodType }; Type type; @@ -11443,6 +11482,7 @@ private: double doubleValue; String* stringValue; DynamicObject* objectValue; + MethodFunction* methodValue; } value; void releaseValue() throw(); @@ -11466,32 +11506,21 @@ public: /** Destructor. */ virtual ~DynamicObject(); - virtual bool hasProperty (const String& propertyName) const; - virtual const var getProperty (const String& propertyName) const; - virtual void setProperty (const String& propertyName, const var& newValue); - virtual void removeProperty (const String& propertyName); + virtual bool hasProperty (const var::identifier& propertyName) const; + virtual const var getProperty (const var::identifier& propertyName) const; + virtual void setProperty (const var::identifier& propertyName, const var& newValue); + virtual void removeProperty (const var::identifier& propertyName); - virtual bool hasMethod (const String& methodName) const; + virtual bool hasMethod (const var::identifier& methodName) const; - virtual const var invokeMethod (const String& methodName, + virtual const var invokeMethod (const var::identifier& methodName, const var* parameters, int numParameters); - /** Shortcut method for invoking a method with no arguments. */ - const var invoke (const String& methodName); - /** Shortcut method for invoking a method with one argument. */ - const var invoke (const String& methodName, const var& arg1); - /** Shortcut method for invoking a method with 2 arguments. */ - const var invoke (const String& methodName, const var& arg1, const var& arg2); - /** Shortcut method for invoking a method with 3 arguments. */ - const var invoke (const String& methodName, const var& arg1, const var& arg2, const var& arg3); - /** Shortcut method for invoking a method with 4 arguments. */ - const var invoke (const String& methodName, const var& arg1, const var& arg2, const var& arg3, const var& arg4); - juce_UseDebuggingNewOperator private: - StringArray propertyNames; + Array propertyIds; OwnedArray propertyValues; }; @@ -53529,6 +53558,11 @@ public: int width, int height, double progress, const String& textToShow); + // Draws a small image that spins to indicate that something's happening.. + // This method should use the current time to animate itself, so just keep + // repainting it every so often. + virtual void drawSpinningWaitAnimation (Graphics& g, int x, int y, int w, int h); + /** Draws one of the buttons on a scrollbar. @param g the context to draw into diff --git a/src/juce_appframework/audio/devices/juce_AudioDeviceManager.cpp b/src/juce_appframework/audio/devices/juce_AudioDeviceManager.cpp index 4f59c4ec36..a843415376 100644 --- a/src/juce_appframework/audio/devices/juce_AudioDeviceManager.cpp +++ b/src/juce_appframework/audio/devices/juce_AudioDeviceManager.cpp @@ -663,7 +663,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat { const double callbackStartTime = Time::getMillisecondCounterHiRes(); - tempBuffer.setSize (numOutputChannels, numSamples, false, false, true); + tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true); callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples); diff --git a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp index cc8dbd02c4..3f1aa078ac 100644 --- a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp +++ b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp @@ -656,6 +656,31 @@ void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, } } +void LookAndFeel::drawSpinningWaitAnimation (Graphics& g, int x, int y, int w, int h) +{ + const float radius = jmin (w, h) * 0.4f; + const float thickness = radius * 0.15f; + Path p; + p.addRoundedRectangle (radius * 0.4f, thickness * -0.5f, + radius * 0.6f, thickness, + thickness * 0.5f); + + const float cx = x + w * 0.5f; + const float cy = y + h * 0.5f; + + const uint32 animationIndex = (Time::getMillisecondCounter() / (1000 / 10)) % 12; + const Colour col (g.getCurrentColour()); + + for (int i = 0; i < 12; ++i) + { + const int n = (i + 12 - animationIndex) % 12; + g.setColour (col.withMultipliedAlpha ((n + 1) / 12.0f)); + + g.fillPath (p, AffineTransform::rotation (i * (float_Pi / 6.0f)) + .translated (cx, cy)); + } +} + void LookAndFeel::drawScrollbarButton (Graphics& g, ScrollBar& scrollbar, int width, int height, diff --git a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h index 1150ebfd1c..bd9f93d4b4 100644 --- a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h +++ b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h @@ -210,6 +210,12 @@ public: int width, int height, double progress, const String& textToShow); + //============================================================================== + // Draws a small image that spins to indicate that something's happening.. + // This method should use the current time to animate itself, so just keep + // repainting it every so often. + virtual void drawSpinningWaitAnimation (Graphics& g, int x, int y, int w, int h); + //============================================================================== /** Draws one of the buttons on a scrollbar. diff --git a/src/juce_core/containers/juce_ReferenceCountedArray.h b/src/juce_core/containers/juce_ReferenceCountedArray.h index f90545d608..b57ad51fa6 100644 --- a/src/juce_core/containers/juce_ReferenceCountedArray.h +++ b/src/juce_core/containers/juce_ReferenceCountedArray.h @@ -158,12 +158,12 @@ public: @see getUnchecked */ - inline ObjectClass* operator[] (const int index) const throw() + inline const ReferenceCountedObjectPtr operator[] (const int index) const throw() { lock.enter(); - ObjectClass* const result = (((unsigned int) index) < (unsigned int) numUsed) - ? this->elements [index] - : (ObjectClass*) 0; + const ReferenceCountedObjectPtr result ((((unsigned int) index) < (unsigned int) numUsed) + ? this->elements [index] + : (ObjectClass*) 0); lock.exit(); return result; } @@ -173,11 +173,11 @@ public: This is a faster and less safe version of operator[] which doesn't check the index passed in, so it can be used when you're sure the index if always going to be legal. */ - inline ObjectClass* getUnchecked (const int index) const throw() + inline const ReferenceCountedObjectPtr getUnchecked (const int index) const throw() { lock.enter(); jassert (((unsigned int) index) < (unsigned int) numUsed); - ObjectClass* const result = this->elements [index]; + const ReferenceCountedObjectPtr result (this->elements [index]); lock.exit(); return result; } @@ -187,11 +187,11 @@ public: This will return a null pointer if the array's empty. @see getLast */ - inline ObjectClass* getFirst() const throw() + inline const ReferenceCountedObjectPtr getFirst() const throw() { lock.enter(); - ObjectClass* const result = (numUsed > 0) ? this->elements [0] - : (ObjectClass*) 0; + const ReferenceCountedObjectPtr result ((numUsed > 0) ? this->elements [0] + : (ObjectClass*) 0); lock.exit(); return result; @@ -202,11 +202,11 @@ public: This will return a null pointer if the array's empty. @see getFirst */ - inline ObjectClass* getLast() const throw() + inline const ReferenceCountedObjectPtr getLast() const throw() { lock.enter(); - ObjectClass* const result = (numUsed > 0) ? this->elements [numUsed - 1] - : (ObjectClass*) 0; + const ReferenceCountedObjectPtr result ((numUsed > 0) ? this->elements [numUsed - 1] + : (ObjectClass*) 0); lock.exit(); return result; diff --git a/src/juce_core/containers/juce_Variant.cpp b/src/juce_core/containers/juce_Variant.cpp index 2d34953fa8..d2ea2296cd 100644 --- a/src/juce_core/containers/juce_Variant.cpp +++ b/src/juce_core/containers/juce_Variant.cpp @@ -1,361 +1,416 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - 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. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../basics/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - -#include "juce_Variant.h" - - -//============================================================================== -var::var() throw() - : type (voidType) -{ - value.doubleValue = 0; -} - -void var::releaseValue() throw() -{ - if (type == stringType) - delete value.stringValue; - else if (type == objectType && value.objectValue != 0) - value.objectValue->decReferenceCount(); -} - -var::~var() -{ - releaseValue(); -} - -//============================================================================== -var::var (const var& valueToCopy) throw() - : type (valueToCopy.type), - value (valueToCopy.value) -{ - if (type == stringType) - value.stringValue = new String (*(value.stringValue)); - else if (type == objectType && value.objectValue != 0) - value.objectValue->incReferenceCount(); -} - -var::var (const int value_) throw() - : type (intType) -{ - value.intValue = value_; -} - -var::var (const bool value_) throw() - : type (boolType) -{ - value.boolValue = value_; -} - -var::var (const double value_) throw() - : type (doubleType) -{ - value.doubleValue = value_; -} - -var::var (const String& value_) throw() - : type (stringType) -{ - value.stringValue = new String (value_); -} - -var::var (const char* const value_) throw() - : type (stringType) -{ - value.stringValue = new String (value_); -} - -var::var (const juce_wchar* const value_) throw() - : type (stringType) -{ - value.stringValue = new String (value_); -} - -var::var (DynamicObject* const object) throw() - : type (objectType) -{ - value.objectValue = object; - - if (object != 0) - object->incReferenceCount(); -} - -//============================================================================== -const var& var::operator= (const var& valueToCopy) throw() -{ - if (this != &valueToCopy) - { - if (type == stringType) - delete value.stringValue; - - DynamicObject* const oldObject = getObject(); - - type = valueToCopy.type; - value = valueToCopy.value; - - if (type == stringType) - value.stringValue = new String (*(value.stringValue)); - else if (type == objectType && value.objectValue != 0) - value.objectValue->incReferenceCount(); - - if (oldObject != 0) - oldObject->decReferenceCount(); - } - - return *this; -} - -const var& var::operator= (const int value_) throw() -{ - releaseValue(); - type = intType; - value.intValue = value_; - return *this; -} - -const var& var::operator= (const bool value_) throw() -{ - releaseValue(); - type = boolType; - value.boolValue = value_; - return *this; -} - -const var& var::operator= (const double value_) throw() -{ - releaseValue(); - type = doubleType; - value.doubleValue = value_; - return *this; -} - -const var& var::operator= (const char* const value_) throw() -{ - releaseValue(); - type = stringType; - value.stringValue = new String (value_); - return *this; -} - -const var& var::operator= (const juce_wchar* const value_) throw() -{ - releaseValue(); - type = stringType; - value.stringValue = new String (value_); - return *this; -} - -const var& var::operator= (const String& value_) throw() -{ - releaseValue(); - type = stringType; - value.stringValue = new String (value_); - return *this; -} - -const var& var::operator= (DynamicObject* const value_) throw() -{ - value_->incReferenceCount(); - releaseValue(); - type = objectType; - value.objectValue = value_; - return *this; -} - -//============================================================================== -var::operator int() const throw() -{ - switch (type) - { - case voidType: - case objectType: break; - case intType: return value.intValue; - case boolType: return value.boolValue ? 1 : 0; - case doubleType: return (int) value.doubleValue; - case stringType: return value.stringValue->getIntValue(); - default: jassertfalse; break; - } - - return 0; -} - -var::operator bool() const throw() -{ - switch (type) - { - case voidType: break; - case objectType: return value.objectValue != 0; - case intType: return value.intValue != 0; - case boolType: return value.boolValue; - case doubleType: return value.doubleValue != 0; - case stringType: return value.stringValue->getIntValue() != 0 - || value.stringValue->trim().equalsIgnoreCase (T("true")) - || value.stringValue->trim().equalsIgnoreCase (T("yes")); - default: jassertfalse; break; - } - - return false; -} - -var::operator double() const throw() -{ - switch (type) - { - case voidType: - case objectType: break; - case intType: return value.intValue; - case boolType: return value.boolValue ? 1.0 : 0.0; - case doubleType: return value.doubleValue; - case stringType: return value.stringValue->getDoubleValue(); - default: jassertfalse; break; - } - - return 0; -} - -const String var::toString() const throw() -{ - switch (type) - { - case voidType: - case objectType: return "Object 0x" + String::toHexString ((pointer_sized_int) value.objectValue); - case intType: return String (value.intValue); - case boolType: return value.boolValue ? T("1") : T("0"); - case doubleType: return String (value.doubleValue); - case stringType: return *(value.stringValue); - default: jassertfalse; break; - } - - return String::empty; -} - -DynamicObject* var::getObject() const throw() -{ - return type == objectType ? value.objectValue : 0; -} - - -//============================================================================== -//============================================================================== -DynamicObject::DynamicObject() -{ -} - -DynamicObject::~DynamicObject() -{ -} - -bool DynamicObject::hasProperty (const String& propertyName) const -{ - return propertyNames.contains (propertyName, false); -} - -const var DynamicObject::getProperty (const String& propertyName) const -{ - const int index = propertyNames.indexOf (propertyName, false); - if (index >= 0) - return *propertyValues.getUnchecked (index); - - return var(); -} - -void DynamicObject::setProperty (const String& propertyName, const var& newValue) -{ - const int index = propertyNames.indexOf (propertyName, false); - - if (index >= 0) - { - propertyValues.set (index, new var (newValue)); - } - else - { - propertyNames.add (propertyName); - propertyValues.add (new var (newValue)); - } -} - -void DynamicObject::removeProperty (const String& propertyName) -{ - const int index = propertyNames.indexOf (propertyName, false); - - if (index >= 0) - { - propertyNames.remove (index); - propertyValues.remove (index); - } -} - -bool DynamicObject::hasMethod (const String& methodName) const -{ - return false; -} - -const var DynamicObject::invokeMethod (const String& methodName, - const var* parameters, - int numParameters) -{ - return var(); -} - -const var DynamicObject::invoke (const String& methodName) -{ - return invokeMethod (methodName, 0, 0); -} - -const var DynamicObject::invoke (const String& methodName, const var& arg1) -{ - return invokeMethod (methodName, &arg1, 1); -} - -const var DynamicObject::invoke (const String& methodName, const var& arg1, const var& arg2) -{ - var params[] = { arg1, arg2 }; - return invokeMethod (methodName, params, 2); -} - -const var DynamicObject::invoke (const String& methodName, const var& arg1, const var& arg2, const var& arg3) -{ - var params[] = { arg1, arg2, arg3 }; - return invokeMethod (methodName, params, 3); -} - -const var DynamicObject::invoke (const String& methodName, const var& arg1, const var& arg2, const var& arg3, const var& arg4) -{ - var params[] = { arg1, arg2, arg3, arg4 }; - return invokeMethod (methodName, params, 4); -} - - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_Variant.h" + + +//============================================================================== +var::var() throw() + : type (voidType) +{ + value.doubleValue = 0; +} + +void var::releaseValue() throw() +{ + if (type == stringType) + delete value.stringValue; + else if (type == objectType && value.objectValue != 0) + value.objectValue->decReferenceCount(); +} + +var::~var() +{ + releaseValue(); +} + +//============================================================================== +var::var (const var& valueToCopy) throw() + : type (valueToCopy.type), + value (valueToCopy.value) +{ + if (type == stringType) + value.stringValue = new String (*(value.stringValue)); + else if (type == objectType && value.objectValue != 0) + value.objectValue->incReferenceCount(); +} + +var::var (const int value_) throw() + : type (intType) +{ + value.intValue = value_; +} + +var::var (const bool value_) throw() + : type (boolType) +{ + value.boolValue = value_; +} + +var::var (const double value_) throw() + : type (doubleType) +{ + value.doubleValue = value_; +} + +var::var (const String& value_) throw() + : type (stringType) +{ + value.stringValue = new String (value_); +} + +var::var (const char* const value_) throw() + : type (stringType) +{ + value.stringValue = new String (value_); +} + +var::var (const juce_wchar* const value_) throw() + : type (stringType) +{ + value.stringValue = new String (value_); +} + +var::var (DynamicObject* const object) throw() + : type (objectType) +{ + value.objectValue = object; + + if (object != 0) + object->incReferenceCount(); +} + +var::var (MethodFunction method_) throw() + : type (methodType) +{ + value.methodValue = method_; +} + +//============================================================================== +const var& var::operator= (const var& valueToCopy) throw() +{ + if (this != &valueToCopy) + { + if (type == stringType) + delete value.stringValue; + + DynamicObject* const oldObject = getObject(); + + type = valueToCopy.type; + value = valueToCopy.value; + + if (type == stringType) + value.stringValue = new String (*(value.stringValue)); + else if (type == objectType && value.objectValue != 0) + value.objectValue->incReferenceCount(); + + if (oldObject != 0) + oldObject->decReferenceCount(); + } + + return *this; +} + +const var& var::operator= (const int value_) throw() +{ + releaseValue(); + type = intType; + value.intValue = value_; + return *this; +} + +const var& var::operator= (const bool value_) throw() +{ + releaseValue(); + type = boolType; + value.boolValue = value_; + return *this; +} + +const var& var::operator= (const double value_) throw() +{ + releaseValue(); + type = doubleType; + value.doubleValue = value_; + return *this; +} + +const var& var::operator= (const char* const value_) throw() +{ + releaseValue(); + type = stringType; + value.stringValue = new String (value_); + return *this; +} + +const var& var::operator= (const juce_wchar* const value_) throw() +{ + releaseValue(); + type = stringType; + value.stringValue = new String (value_); + return *this; +} + +const var& var::operator= (const String& value_) throw() +{ + releaseValue(); + type = stringType; + value.stringValue = new String (value_); + return *this; +} + +const var& var::operator= (DynamicObject* const value_) throw() +{ + value_->incReferenceCount(); + releaseValue(); + type = objectType; + value.objectValue = value_; + return *this; +} + +const var& var::operator= (MethodFunction method_) throw() +{ + releaseValue(); + type = doubleType; + value.methodValue = method_; + return *this; +} + +//============================================================================== +var::operator int() const throw() +{ + switch (type) + { + case voidType: + case objectType: break; + case intType: return value.intValue; + case boolType: return value.boolValue ? 1 : 0; + case doubleType: return (int) value.doubleValue; + case stringType: return value.stringValue->getIntValue(); + default: jassertfalse; break; + } + + return 0; +} + +var::operator bool() const throw() +{ + switch (type) + { + case voidType: break; + case objectType: return value.objectValue != 0; + case intType: return value.intValue != 0; + case boolType: return value.boolValue; + case doubleType: return value.doubleValue != 0; + case stringType: return value.stringValue->getIntValue() != 0 + || value.stringValue->trim().equalsIgnoreCase (T("true")) + || value.stringValue->trim().equalsIgnoreCase (T("yes")); + default: jassertfalse; break; + } + + return false; +} + +var::operator double() const throw() +{ + switch (type) + { + case voidType: + case objectType: break; + case intType: return value.intValue; + case boolType: return value.boolValue ? 1.0 : 0.0; + case doubleType: return value.doubleValue; + case stringType: return value.stringValue->getDoubleValue(); + default: jassertfalse; break; + } + + return 0; +} + +const String var::toString() const throw() +{ + switch (type) + { + case voidType: + case objectType: return "Object 0x" + String::toHexString ((pointer_sized_int) value.objectValue); + case intType: return String (value.intValue); + case boolType: return value.boolValue ? T("1") : T("0"); + case doubleType: return String (value.doubleValue); + case stringType: return *(value.stringValue); + default: jassertfalse; break; + } + + return String::empty; +} + +DynamicObject* var::getObject() const throw() +{ + return type == objectType ? value.objectValue : 0; +} + +const var var::operator[] (const var::identifier& propertyName) const throw() +{ + if (type == objectType && value.objectValue != 0) + return value.objectValue->getProperty (propertyName); + + return var(); +} + +const var var::invoke (const var::identifier& method, const var* arguments, int numArguments) const +{ + if (type == objectType && value.objectValue != 0) + return value.objectValue->invokeMethod (method, arguments, numArguments); + + return var(); +} + +const var var::invoke (const var& targetObject, const var* arguments, int numArguments) const +{ + if (isMethod()) + return value.methodValue (targetObject, arguments, numArguments); + + return var(); +} + +const var var::call (const var::identifier& method) const +{ + return invoke (method, 0, 0); +} + +const var var::call (const var::identifier& method, const var& arg1) const +{ + return invoke (method, &arg1, 1); +} + +const var var::call (const var::identifier& method, const var& arg1, const var& arg2) const +{ + var args[] = { arg1, arg2 }; + return invoke (method, args, 2); +} + +const var var::call (const var::identifier& method, const var& arg1, const var& arg2, const var& arg3) +{ + var args[] = { arg1, arg2, arg3 }; + return invoke (method, args, 3); +} + +const var var::call (const var::identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const +{ + var args[] = { arg1, arg2, arg3, arg4 }; + return invoke (method, args, 4); +} + +//============================================================================== +var::identifier::identifier (const String& name_) throw() + : name (name_), + hashCode (name_.hashCode()) +{ +} + +var::identifier::identifier (const char* const name_) throw() + : name (name_), + hashCode (name.hashCode()) +{ +} + +var::identifier::~identifier() throw() +{ +} + +//============================================================================== +//============================================================================== +DynamicObject::DynamicObject() +{ +} + +DynamicObject::~DynamicObject() +{ +} + +bool DynamicObject::hasProperty (const var::identifier& propertyName) const +{ + const int index = propertyIds.indexOf (propertyName.hashCode); + return index >= 0 && ! propertyValues.getUnchecked (index)->isMethod(); +} + +const var DynamicObject::getProperty (const var::identifier& propertyName) const +{ + const int index = propertyIds.indexOf (propertyName.hashCode); + if (index >= 0) + return *propertyValues.getUnchecked (index); + + return var(); +} + +void DynamicObject::setProperty (const var::identifier& propertyName, const var& newValue) +{ + const int index = propertyIds.indexOf (propertyName.hashCode); + + if (index >= 0) + { + propertyValues.set (index, new var (newValue)); + } + else + { + propertyIds.add (propertyName.hashCode); + propertyValues.add (new var (newValue)); + } +} + +void DynamicObject::removeProperty (const var::identifier& propertyName) +{ + const int index = propertyIds.indexOf (propertyName.hashCode); + + if (index >= 0) + { + propertyIds.remove (index); + propertyValues.remove (index); + } +} + +bool DynamicObject::hasMethod (const var::identifier& methodName) const +{ + return getProperty (methodName).isMethod(); +} + +const var DynamicObject::invokeMethod (const var::identifier& methodName, + const var* parameters, + int numParameters) +{ + return getProperty (methodName).invoke (this, parameters, numParameters); +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_core/containers/juce_Variant.h b/src/juce_core/containers/juce_Variant.h index a94423ea6d..b69a34bd62 100644 --- a/src/juce_core/containers/juce_Variant.h +++ b/src/juce_core/containers/juce_Variant.h @@ -52,6 +52,9 @@ class JUCE_API DynamicObject; class JUCE_API var { public: + //============================================================================== + typedef const var (MethodFunction) (const var& thisObject, const var* arguments, int numArguments); + //============================================================================== /** Creates a void variant. */ var() throw(); @@ -67,6 +70,7 @@ public: var (const juce_wchar* const value) throw(); var (const String& value) throw(); var (DynamicObject* const object) throw(); + var (MethodFunction method) throw(); const var& operator= (const var& valueToCopy) throw(); const var& operator= (const int value) throw(); @@ -76,6 +80,7 @@ public: const var& operator= (const juce_wchar* const value) throw(); const var& operator= (const String& value) throw(); const var& operator= (DynamicObject* const object) throw(); + const var& operator= (MethodFunction method) throw(); operator int() const throw(); operator bool() const throw(); @@ -89,6 +94,43 @@ public: bool isDouble() const throw() { return type == doubleType; } bool isString() const throw() { return type == stringType; } bool isObject() const throw() { return type == objectType; } + bool isMethod() const throw() { return type == methodType; } + + //============================================================================== + class identifier + { + public: + identifier (const char* const name) throw(); + identifier (const String& name) throw(); + ~identifier() throw(); + + bool operator== (const identifier& other) const throw() { return hashCode == other.hashCode; } + + String name; + int hashCode; + }; + + /** If this variant is an object, this returns one of its properties. */ + const var operator[] (const identifier& propertyName) const throw(); + + //============================================================================== + /** If this variant is an object, this invokes one of its methods with no arguments. */ + const var call (const identifier& method) const; + /** If this variant is an object, this invokes one of its methods with one argument. */ + const var call (const identifier& method, const var& arg1) const; + /** If this variant is an object, this invokes one of its methods with 2 arguments. */ + const var call (const identifier& method, const var& arg1, const var& arg2) const; + /** If this variant is an object, this invokes one of its methods with 3 arguments. */ + const var call (const identifier& method, const var& arg1, const var& arg2, const var& arg3); + /** If this variant is an object, this invokes one of its methods with 4 arguments. */ + const var call (const identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; + + /** If this variant is an object, this invokes one of its methods with a list of arguments. */ + const var invoke (const identifier& method, const var* arguments, int numArguments) const; + + //============================================================================== + /** If this variant is a method pointer, this invokes it on a target object. */ + const var invoke (const var& targetObject, const var* arguments, int numArguments) const; //============================================================================== juce_UseDebuggingNewOperator @@ -101,7 +143,8 @@ private: boolType, doubleType, stringType, - objectType + objectType, + methodType }; Type type; @@ -113,6 +156,7 @@ private: double doubleValue; String* stringValue; DynamicObject* objectValue; + MethodFunction* methodValue; } value; void releaseValue() throw(); @@ -138,35 +182,23 @@ public: virtual ~DynamicObject(); //============================================================================== - virtual bool hasProperty (const String& propertyName) const; - virtual const var getProperty (const String& propertyName) const; - virtual void setProperty (const String& propertyName, const var& newValue); - virtual void removeProperty (const String& propertyName); + virtual bool hasProperty (const var::identifier& propertyName) const; + virtual const var getProperty (const var::identifier& propertyName) const; + virtual void setProperty (const var::identifier& propertyName, const var& newValue); + virtual void removeProperty (const var::identifier& propertyName); //============================================================================== - virtual bool hasMethod (const String& methodName) const; + virtual bool hasMethod (const var::identifier& methodName) const; - virtual const var invokeMethod (const String& methodName, + virtual const var invokeMethod (const var::identifier& methodName, const var* parameters, int numParameters); - //============================================================================== - /** Shortcut method for invoking a method with no arguments. */ - const var invoke (const String& methodName); - /** Shortcut method for invoking a method with one argument. */ - const var invoke (const String& methodName, const var& arg1); - /** Shortcut method for invoking a method with 2 arguments. */ - const var invoke (const String& methodName, const var& arg1, const var& arg2); - /** Shortcut method for invoking a method with 3 arguments. */ - const var invoke (const String& methodName, const var& arg1, const var& arg2, const var& arg3); - /** Shortcut method for invoking a method with 4 arguments. */ - const var invoke (const String& methodName, const var& arg1, const var& arg2, const var& arg3, const var& arg4); - //============================================================================== juce_UseDebuggingNewOperator private: - StringArray propertyNames; + Array propertyIds; OwnedArray propertyValues; };