@@ -112,7 +112,7 @@ struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParamete | |||
void copyValueToValueTree() | |||
{ | |||
if (state.isValid()) | |||
state.setProperty (owner.valuePropertyID, value, owner.undoManager); | |||
state.setPropertyExcludingListener (this, owner.valuePropertyID, value, owner.undoManager); | |||
} | |||
void valueTreePropertyChanged (ValueTree&, const Identifier& property) override | |||
@@ -397,7 +397,7 @@ struct AudioProcessorValueTreeState::SliderAttachment::Pimpl : private Attached | |||
private Slider::Listener | |||
{ | |||
Pimpl (AudioProcessorValueTreeState& s, const String& p, Slider& sl) | |||
: AttachedControlBase (s, p), slider (sl) | |||
: AttachedControlBase (s, p), slider (sl), ignoreCallbacks (false) | |||
{ | |||
NormalisableRange<float> range (s.getParameterRange (paramID)); | |||
slider.setRange (range.start, range.end, range.interval); | |||
@@ -418,19 +418,28 @@ struct AudioProcessorValueTreeState::SliderAttachment::Pimpl : private Attached | |||
void setValue (float newValue) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
ignoreCallbacks = true; | |||
slider.setValue (newValue, sendNotificationSync); | |||
} | |||
void sliderValueChanged (Slider* s) override | |||
{ | |||
if (! ModifierKeys::getCurrentModifiers().isRightButtonDown()) | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
if ((! ignoreCallbacks) && (! ModifierKeys::getCurrentModifiers().isRightButtonDown())) | |||
setNewUnnormalisedValue ((float) s->getValue()); | |||
ignoreCallbacks = false; | |||
} | |||
void sliderDragStarted (Slider*) override { beginParameterChange(); } | |||
void sliderDragEnded (Slider*) override { endParameterChange(); } | |||
Slider& slider; | |||
bool ignoreCallbacks; | |||
CriticalSection selfCallbackMutex; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
@@ -447,7 +456,7 @@ struct AudioProcessorValueTreeState::ComboBoxAttachment::Pimpl : private Attach | |||
private ComboBox::Listener | |||
{ | |||
Pimpl (AudioProcessorValueTreeState& s, const String& p, ComboBox& c) | |||
: AttachedControlBase (s, p), combo (c) | |||
: AttachedControlBase (s, p), combo (c), ignoreCallbacks (false) | |||
{ | |||
sendInitialUpdate(); | |||
combo.addListener (this); | |||
@@ -461,17 +470,28 @@ struct AudioProcessorValueTreeState::ComboBoxAttachment::Pimpl : private Attach | |||
void setValue (float newValue) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
ignoreCallbacks = true; | |||
combo.setSelectedItemIndex (roundToInt (newValue), sendNotificationSync); | |||
} | |||
void comboBoxChanged (ComboBox* comboBox) override | |||
{ | |||
beginParameterChange(); | |||
setNewUnnormalisedValue ((float) comboBox->getSelectedId() - 1.0f); | |||
endParameterChange(); | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
if (! ignoreCallbacks) | |||
{ | |||
beginParameterChange(); | |||
setNewUnnormalisedValue ((float) comboBox->getSelectedId() - 1.0f); | |||
endParameterChange(); | |||
} | |||
ignoreCallbacks = false; | |||
} | |||
ComboBox& combo; | |||
bool ignoreCallbacks; | |||
CriticalSection selfCallbackMutex; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
@@ -488,7 +508,7 @@ struct AudioProcessorValueTreeState::ButtonAttachment::Pimpl : private Attached | |||
private Button::Listener | |||
{ | |||
Pimpl (AudioProcessorValueTreeState& s, const String& p, Button& b) | |||
: AttachedControlBase (s, p), button (b) | |||
: AttachedControlBase (s, p), button (b), ignoreCallbacks (false) | |||
{ | |||
sendInitialUpdate(); | |||
button.addListener (this); | |||
@@ -502,17 +522,28 @@ struct AudioProcessorValueTreeState::ButtonAttachment::Pimpl : private Attached | |||
void setValue (float newValue) override | |||
{ | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
ignoreCallbacks = true; | |||
button.setToggleState (newValue >= 0.5f, sendNotificationSync); | |||
} | |||
void buttonClicked (Button* b) override | |||
{ | |||
beginParameterChange(); | |||
setNewUnnormalisedValue (b->getToggleState() ? 1.0f : 0.0f); | |||
endParameterChange(); | |||
const ScopedLock selfCallbackLock (selfCallbackMutex); | |||
if (! ignoreCallbacks) | |||
{ | |||
beginParameterChange(); | |||
setNewUnnormalisedValue (b->getToggleState() ? 1.0f : 0.0f); | |||
endParameterChange(); | |||
} | |||
ignoreCallbacks = false; | |||
} | |||
Button& button; | |||
bool ignoreCallbacks; | |||
CriticalSection selfCallbackMutex; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
@@ -0,0 +1,183 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the juce_core module of the JUCE library. | |||
Copyright (c) 2016 - ROLI Ltd. | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
or without fee is hereby granted, provided that the above copyright notice and this | |||
permission notice appear in all copies. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
------------------------------------------------------------------------------ | |||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
using any other modules, be sure to check that you also comply with their license. | |||
For more details, visit www.juce.com | |||
============================================================================== | |||
*/ | |||
#if JUCE_UNIT_TESTS | |||
struct ListenerBase | |||
{ | |||
ListenerBase (int& counter) : c (counter) {} | |||
virtual ~ListenerBase () {} | |||
virtual void f () = 0; | |||
virtual void f (void*) = 0; | |||
virtual void f (void*, void*) = 0; | |||
virtual void f (void*, void*, void*) = 0; | |||
virtual void f (void*, void*, void*, void*) = 0; | |||
virtual void f (void*, void*, void*, void*, void*) = 0; | |||
virtual void f (void*, void*, void*, void*, void*, void*) = 0; | |||
int& c; | |||
}; | |||
struct Listener1 : public ListenerBase | |||
{ | |||
Listener1 (int& counter) : ListenerBase (counter) {} | |||
void f () override { c += 1; } | |||
void f (void*) override { c += 2; } | |||
void f (void*, void*) override { c += 3; } | |||
void f (void*, void*, void*) override { c += 4; } | |||
void f (void*, void*, void*, void*) override { c += 5; } | |||
void f (void*, void*, void*, void*, void*) override { c += 6; } | |||
void f (void*, void*, void*, void*, void*, void*) override { c += 7; } | |||
}; | |||
struct Listener2 : public ListenerBase | |||
{ | |||
Listener2 (int& counter) : ListenerBase (counter) {} | |||
void f () override { c -= 2; } | |||
void f (void*) override { c -= 4; } | |||
void f (void*, void*) override { c -= 6; } | |||
void f (void*, void*, void*) override { c -= 8; } | |||
void f (void*, void*, void*, void*) override { c -= 10; } | |||
void f (void*, void*, void*, void*, void*) override { c -= 12; } | |||
void f (void*, void*, void*, void*, void*, void*) override { c -= 14; } | |||
}; | |||
class ListenerListTests : public UnitTest | |||
{ | |||
public: | |||
ListenerListTests() : UnitTest ("ListenerList") {} | |||
template <typename T> | |||
void callHelper (std::vector<int>& expectedCounterValues, T v) | |||
{ | |||
counter = 0; | |||
listeners.call (&ListenerBase::f, v); | |||
expect (counter == expectedCounterValues[1]); | |||
counter = 0; | |||
listeners.call (&ListenerBase::f); | |||
expect (counter == expectedCounterValues[0]); | |||
ListenerList<ListenerBase>::DummyBailOutChecker boc; | |||
counter = 0; | |||
listeners.callChecked (boc, &ListenerBase::f, v); | |||
expect (counter == expectedCounterValues[1]); | |||
counter = 0; | |||
listeners.callChecked (boc, &ListenerBase::f); | |||
expect (counter == expectedCounterValues[0]); | |||
} | |||
template<typename T, typename... Args> | |||
void callHelper (std::vector<int>& expectedCounterValues, T first, Args... args) | |||
{ | |||
const int expected = expectedCounterValues[sizeof... (args) + 1]; | |||
counter = 0; | |||
listeners.call (&ListenerBase::f, first, args...); | |||
expect (counter == expected); | |||
ListenerList<ListenerBase>::DummyBailOutChecker boc; | |||
counter = 0; | |||
listeners.callChecked (boc, &ListenerBase::f, first, args...); | |||
expect (counter == expected); | |||
callHelper (expectedCounterValues, args...); | |||
} | |||
template <typename T> | |||
void callExcludingHelper (ListenerBase& listenerToExclude, | |||
std::vector<int>& expectedCounterValues, T v) | |||
{ | |||
counter = 0; | |||
listeners.callExcluding (listenerToExclude, &ListenerBase::f, v); | |||
expect (counter == expectedCounterValues[1]); | |||
counter = 0; | |||
listeners.callExcluding (listenerToExclude, &ListenerBase::f); | |||
expect (counter == expectedCounterValues[0]); | |||
ListenerList<ListenerBase>::DummyBailOutChecker boc; | |||
counter = 0; | |||
listeners.callCheckedExcluding (listenerToExclude, boc, &ListenerBase::f, v); | |||
expect (counter == expectedCounterValues[1]); | |||
counter = 0; | |||
listeners.callCheckedExcluding (listenerToExclude, boc, &ListenerBase::f); | |||
expect (counter == expectedCounterValues[0]); | |||
} | |||
template<typename T, typename... Args> | |||
void callExcludingHelper (ListenerBase& listenerToExclude, | |||
std::vector<int>& expectedCounterValues, T first, Args... args) | |||
{ | |||
const int expected = expectedCounterValues[sizeof... (args) + 1]; | |||
counter = 0; | |||
listeners.callExcluding (listenerToExclude, &ListenerBase::f, first, args...); | |||
expect (counter == expected); | |||
ListenerList<ListenerBase>::DummyBailOutChecker boc; | |||
counter = 0; | |||
listeners.callCheckedExcluding (listenerToExclude, boc, &ListenerBase::f, first, args...); | |||
expect (counter == expected); | |||
callExcludingHelper (listenerToExclude, expectedCounterValues, args...); | |||
} | |||
void runTest() override | |||
{ | |||
beginTest ("Call single listener"); | |||
listeners.add (&listener1); | |||
std::vector<int> expectedCounterValues = { 1, 2, 3, 4, 5, 6, 7 }; | |||
callHelper (expectedCounterValues, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); | |||
beginTest ("Call multiple listeners"); | |||
listeners.add (&listener2); | |||
expectedCounterValues = { -1, -2, -3, -4, -5, -6, -7 }; | |||
callHelper (expectedCounterValues, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); | |||
beginTest ("Call listeners excluding"); | |||
expectedCounterValues = { 1, 2, 3, 4, 5, 6, 7 }; | |||
callExcludingHelper (listener2, expectedCounterValues, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); | |||
} | |||
int counter = 0; | |||
ListenerList<ListenerBase> listeners; | |||
Listener1 listener1 {counter}; | |||
Listener2 listener2 {counter}; | |||
}; | |||
static ListenerListTests listenerListTests; | |||
#endif |
@@ -152,8 +152,18 @@ public: | |||
callChecked (static_cast<const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction); | |||
} | |||
/** Calls a member function, with no parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
void callExcluding (ListenerClass& listenerToExclude, void (ListenerClass::*callbackFunction) ()) | |||
{ | |||
callCheckedExcluding (listenerToExclude, | |||
static_cast<const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction); | |||
} | |||
/** Calls a member function on each listener in the list, with no parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. */ | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) ()) | |||
@@ -162,6 +172,20 @@ public: | |||
(iter.getListener()->*callbackFunction) (); | |||
} | |||
/** Calls a member function on all but the specified listener in the list with a bail-out-checker. | |||
This can be useful if the caller is also a listener and needs to exclude itself. See the class | |||
description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType> | |||
void callCheckedExcluding (ListenerClass& listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) ()) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 1 parameter. */ | |||
template <LL_TEMPLATE(1)> | |||
@@ -171,8 +195,21 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1); | |||
} | |||
/** Calls a member function on each listener in the list, with one parameter and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. */ | |||
/** Calls a member function, with 1 parameter, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1)> | |||
void callExcluding (ListenerClass& listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1); | |||
} | |||
/** Calls a member function on each listener in the list, with 1 parameter and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1), | |||
@@ -182,6 +219,21 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1); | |||
} | |||
/** Calls a member function, with 1 parameter, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1)> | |||
void callCheckedExcluding (ListenerClass& listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1), | |||
LL_PARAM(1)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 2 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2)> | |||
@@ -192,8 +244,22 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2); | |||
} | |||
/** Calls a member function, with 2 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2)> | |||
void callExcluding (ListenerClass& listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2), | |||
LL_PARAM(1), LL_PARAM(2)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2); | |||
} | |||
/** Calls a member function on each listener in the list, with 2 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. */ | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2), | |||
@@ -203,6 +269,21 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2); | |||
} | |||
/** Calls a member function, with 2 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2)> | |||
void callCheckedExcluding (ListenerClass& listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2), | |||
LL_PARAM(1), LL_PARAM(2)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 3 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)> | |||
@@ -213,8 +294,22 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3); | |||
} | |||
/** Calls a member function, with 3 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)> | |||
void callExcluding (ListenerClass& listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3); | |||
} | |||
/** Calls a member function on each listener in the list, with 3 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. */ | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3), | |||
@@ -224,6 +319,21 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3); | |||
} | |||
/** Calls a member function, with 3 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)> | |||
void callCheckedExcluding (ListenerClass& listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 4 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)> | |||
@@ -234,8 +344,22 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4); | |||
} | |||
/** Calls a member function, with 4 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)> | |||
void callExcluding (ListenerClass& listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4); | |||
} | |||
/** Calls a member function on each listener in the list, with 4 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. */ | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), | |||
@@ -245,6 +369,21 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4); | |||
} | |||
/** Calls a member function, with 4 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)> | |||
void callCheckedExcluding (ListenerClass& listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 5 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)> | |||
@@ -255,8 +394,22 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); | |||
} | |||
/** Calls a member function, with 5 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)> | |||
void callExcluding (ListenerClass& listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); | |||
} | |||
/** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. */ | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), | |||
@@ -266,8 +419,23 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); | |||
} | |||
/** Calls a member function, with 5 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)> | |||
void callCheckedExcluding (ListenerClass& listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 5 parameters. */ | |||
/** Calls a member function on each listener in the list, with 6 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)> | |||
void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) | |||
@@ -276,8 +444,22 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); | |||
} | |||
/** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. */ | |||
/** Calls a member function, with 6 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)> | |||
void callExcluding (ListenerClass& listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); | |||
} | |||
/** Calls a member function on each listener in the list, with 6 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), | |||
@@ -287,6 +469,20 @@ public: | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); | |||
} | |||
/** Calls a member function, with 5 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)> | |||
void callCheckedExcluding (ListenerClass& listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != &listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); | |||
} | |||
//============================================================================== | |||
/** A dummy bail-out checker that always returns false. | |||
@@ -129,6 +129,7 @@ namespace juce | |||
#include "containers/juce_AbstractFifo.cpp" | |||
#include "containers/juce_NamedValueSet.cpp" | |||
#include "containers/juce_ListenerList.cpp" | |||
#include "containers/juce_PropertySet.cpp" | |||
#include "containers/juce_Variant.cpp" | |||
#include "files/juce_DirectoryIterator.cpp" | |||
@@ -103,6 +103,30 @@ public: | |||
} | |||
} | |||
template <typename Method, typename ParamType> | |||
void callListenersExcluding (ValueTree::Listener* listenerToExclude, | |||
Method method, ValueTree& tree, ParamType& param2) const | |||
{ | |||
const int numListeners = valueTreesWithListeners.size(); | |||
if (numListeners == 1) | |||
{ | |||
valueTreesWithListeners.getUnchecked(0)->listeners.callExcluding (*listenerToExclude, method, tree, param2); | |||
} | |||
else if (numListeners > 0) | |||
{ | |||
const SortedSet<ValueTree*> listenersCopy (valueTreesWithListeners); | |||
for (int i = 0; i < numListeners; ++i) | |||
{ | |||
ValueTree* const v = listenersCopy.getUnchecked(i); | |||
if (i == 0 || valueTreesWithListeners.contains (v)) | |||
v->listeners.callExcluding (*listenerToExclude, method, tree, param2); | |||
} | |||
} | |||
} | |||
template <typename Method, typename ParamType1, typename ParamType2> | |||
void callListeners (Method method, ValueTree& tree, ParamType1& param2, ParamType2& param3) const | |||
{ | |||
@@ -126,12 +150,15 @@ public: | |||
} | |||
} | |||
void sendPropertyChangeMessage (const Identifier& property) | |||
void sendPropertyChangeMessage (const Identifier& property, ValueTree::Listener* listenerToExclude = nullptr) | |||
{ | |||
ValueTree tree (this); | |||
for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) | |||
t->callListeners (&ValueTree::Listener::valueTreePropertyChanged, tree, property); | |||
if (listenerToExclude == nullptr) | |||
t->callListeners (&ValueTree::Listener::valueTreePropertyChanged, tree, property); | |||
else | |||
t->callListenersExcluding (listenerToExclude, &ValueTree::Listener::valueTreePropertyChanged, tree, property); | |||
} | |||
void sendChildAddedMessage (ValueTree child) | |||
@@ -169,23 +196,24 @@ public: | |||
callListeners (&ValueTree::Listener::valueTreeParentChanged, tree); | |||
} | |||
void setProperty (const Identifier& name, const var& newValue, UndoManager* const undoManager) | |||
void setProperty (const Identifier& name, const var& newValue, UndoManager* const undoManager, | |||
ValueTree::Listener* listenerToExclude = nullptr) | |||
{ | |||
if (undoManager == nullptr) | |||
{ | |||
if (properties.set (name, newValue)) | |||
sendPropertyChangeMessage (name); | |||
sendPropertyChangeMessage (name, listenerToExclude); | |||
} | |||
else | |||
{ | |||
if (const var* const existingValue = properties.getVarPointer (name)) | |||
{ | |||
if (*existingValue != newValue) | |||
undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false)); | |||
undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false, listenerToExclude)); | |||
} | |||
else | |||
{ | |||
undoManager->perform (new SetPropertyAction (this, name, newValue, var(), true, false)); | |||
undoManager->perform (new SetPropertyAction (this, name, newValue, var(), true, false, listenerToExclude)); | |||
} | |||
} | |||
} | |||
@@ -459,9 +487,11 @@ public: | |||
{ | |||
public: | |||
SetPropertyAction (SharedObject* const so, const Identifier& propertyName, | |||
const var& newVal, const var& oldVal, bool isAdding, bool isDeleting) | |||
const var& newVal, const var& oldVal, bool isAdding, bool isDeleting, | |||
ValueTree::Listener* listenerToExclude = nullptr) | |||
: target (so), name (propertyName), newValue (newVal), oldValue (oldVal), | |||
isAddingNewProperty (isAdding), isDeletingProperty (isDeleting) | |||
isAddingNewProperty (isAdding), isDeletingProperty (isDeleting), | |||
excludeListener (listenerToExclude) | |||
{ | |||
} | |||
@@ -472,7 +502,7 @@ public: | |||
if (isDeletingProperty) | |||
target->removeProperty (name, nullptr); | |||
else | |||
target->setProperty (name, newValue, nullptr); | |||
target->setProperty (name, newValue, nullptr, excludeListener); | |||
return true; | |||
} | |||
@@ -511,6 +541,7 @@ public: | |||
const var newValue; | |||
var oldValue; | |||
const bool isAddingNewProperty : 1, isDeletingProperty : 1; | |||
ValueTree::Listener* excludeListener; | |||
JUCE_DECLARE_NON_COPYABLE (SetPropertyAction) | |||
}; | |||
@@ -762,12 +793,17 @@ const var* ValueTree::getPropertyPointer (const Identifier& name) const noexcept | |||
} | |||
ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager) | |||
{ | |||
return setPropertyExcludingListener (nullptr, name, newValue, undoManager); | |||
} | |||
ValueTree& ValueTree::setPropertyExcludingListener (Listener* listenerToExclude, const Identifier& name, const var& newValue, UndoManager* undoManager) | |||
{ | |||
jassert (name.toString().isNotEmpty()); // Must have a valid property name! | |||
jassert (object != nullptr); // Trying to add a property to a null ValueTree will fail! | |||
if (object != nullptr) | |||
object->setProperty (name, newValue, undoManager); | |||
object->setProperty (name, newValue, undoManager, listenerToExclude); | |||
return *this; | |||
} | |||
@@ -483,6 +483,11 @@ public: | |||
/** Removes a listener that was previously added with addListener(). */ | |||
void removeListener (Listener* listener); | |||
/** Changes a named property of the node, but will not notify a specified listener of the change. | |||
@see setProperty | |||
*/ | |||
ValueTree& setPropertyExcludingListener (Listener* listenerToExclude, const Identifier& name, const var& newValue, UndoManager* undoManager); | |||
/** Causes a property-change callback to be triggered for the specified property, | |||
calling any listeners that are registered. | |||
*/ | |||