Browse Source

New class: Value, which provides a way to share variants and listen for changes to them. Adapted Slider to use Value objects for its position, and changed the widgets demo to show how they can be easily tied together. Updated the VST speaker arrangement code.

tags/2021-05-28
Julian Storer 16 years ago
parent
commit
98380f4744
23 changed files with 1995 additions and 754 deletions
  1. +12
    -0
      build/macosx/Juce.xcodeproj/project.pbxproj
  2. +12
    -0
      build/win32/vc8/JUCE.vcproj
  3. +32
    -12
      extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp
  4. +23
    -2
      extras/juce demo/src/demos/WidgetsDemo.cpp
  5. +552
    -238
      juce_amalgamated.cpp
  6. +436
    -198
      juce_amalgamated.h
  7. +24
    -20
      src/audio/midi/juce_MidiMessage.cpp
  8. +217
    -0
      src/containers/juce_Value.cpp
  9. +214
    -0
      src/containers/juce_Value.h
  10. +179
    -73
      src/containers/juce_ValueTree.cpp
  11. +90
    -59
      src/containers/juce_ValueTree.h
  12. +75
    -51
      src/gui/components/controls/juce_Slider.cpp
  13. +31
    -2
      src/gui/components/controls/juce_Slider.h
  14. +28
    -32
      src/io/streams/juce_GZIPCompressorOutputStream.cpp
  15. +36
    -48
      src/io/streams/juce_GZIPDecompressorInputStream.cpp
  16. +3
    -5
      src/io/streams/juce_InputStream.cpp
  17. +1
    -0
      src/juce_amalgamated_template.cpp
  18. +3
    -0
      src/juce_core_includes.h
  19. +4
    -1
      src/native/mac/juce_mac_CameraDevice.mm
  20. +13
    -5
      src/native/mac/juce_mac_NSViewComponentPeer.mm
  21. +4
    -4
      src/native/windows/juce_win32_ActiveXComponent.cpp
  22. +3
    -1
      src/native/windows/juce_win32_CameraDevice.cpp
  23. +3
    -3
      src/text/juce_XmlElement.h

+ 12
- 0
build/macosx/Juce.xcodeproj/project.pbxproj View File

@@ -615,6 +615,8 @@
848170B110809E00008FEC33 /* juce_XmlElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F1E9D6104036D6006A1807 /* juce_XmlElement.cpp */; };
848170B210809E00008FEC33 /* juce_ZipFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F1E996104036D6006A1807 /* juce_ZipFile.cpp */; };
8481730F10832513008FEC33 /* juce_TargetPlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = 8481730E10832513008FEC33 /* juce_TargetPlatform.h */; };
84842F9510F6559300490977 /* juce_Value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84BA603E10F2017A001D9D70 /* juce_Value.cpp */; };
84842F9610F6559400490977 /* juce_Value.h in Headers */ = {isa = PBXBuildFile; fileRef = 84BA603F10F2017A001D9D70 /* juce_Value.h */; };
8484E9A5103C958A008B7C6C /* juce_mac_NativeCode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8484E9A4103C958A008B7C6C /* juce_mac_NativeCode.mm */; };
8484E9BE103C9595008B7C6C /* juce_mac_AppleRemote.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8484E9A6103C9595008B7C6C /* juce_mac_AppleRemote.mm */; };
8484E9BF103C9595008B7C6C /* juce_mac_AudioCDBurner.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8484E9A7103C9595008B7C6C /* juce_mac_AudioCDBurner.mm */; };
@@ -655,6 +657,8 @@
84AF419B10F0008E0035D74F /* juce_ScopedPointer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84AF3FE710EF9FF30035D74F /* juce_ScopedPointer.h */; };
84B2053E10D535EC008B4A79 /* juce_ValueTree.h in Headers */ = {isa = PBXBuildFile; fileRef = 843D4A3A10D3C54500624BA6 /* juce_ValueTree.h */; };
84B2053F10D535EC008B4A79 /* juce_ValueTree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 843D4A3910D3C54500624BA6 /* juce_ValueTree.cpp */; };
84BA604010F2017A001D9D70 /* juce_Value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84BA603E10F2017A001D9D70 /* juce_Value.cpp */; };
84BA604110F2017A001D9D70 /* juce_Value.h in Headers */ = {isa = PBXBuildFile; fileRef = 84BA603F10F2017A001D9D70 /* juce_Value.h */; };
84D0F00C109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84D0F00B109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm */; };
84DEDD9F10EE496500909D01 /* juce_HeapBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DEDD9E10EE496500909D01 /* juce_HeapBlock.h */; };
84F1E6E710403605006A1807 /* juce_Application.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F1E6DC10403605006A1807 /* juce_Application.cpp */; };
@@ -1264,6 +1268,8 @@
84AB91FA10A078190048FC39 /* juce_CPlusPlusCodeTokeniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_CPlusPlusCodeTokeniser.h; path = components/code_editor/juce_CPlusPlusCodeTokeniser.h; sourceTree = "<group>"; };
84AB927110A082E30048FC39 /* juce_CodeTokeniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_CodeTokeniser.h; path = components/code_editor/juce_CodeTokeniser.h; sourceTree = "<group>"; };
84AF3FE710EF9FF30035D74F /* juce_ScopedPointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = juce_ScopedPointer.h; sourceTree = "<group>"; };
84BA603E10F2017A001D9D70 /* juce_Value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = juce_Value.cpp; sourceTree = "<group>"; };
84BA603F10F2017A001D9D70 /* juce_Value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = juce_Value.h; sourceTree = "<group>"; };
84D0F00B109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_CoreGraphicsContext.mm; sourceTree = "<group>"; };
84DEDD9E10EE496500909D01 /* juce_HeapBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = juce_HeapBlock.h; sourceTree = "<group>"; };
84F1E6DC10403605006A1807 /* juce_Application.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Application.cpp; path = ../../src/application/juce_Application.cpp; sourceTree = SOURCE_ROOT; };
@@ -2176,6 +2182,8 @@
84AF3FE710EF9FF30035D74F /* juce_ScopedPointer.h */,
84F1E8DC10403671006A1807 /* juce_SortedSet.h */,
84F1E8DD10403671006A1807 /* juce_SparseSet.h */,
84BA603E10F2017A001D9D70 /* juce_Value.cpp */,
84BA603F10F2017A001D9D70 /* juce_Value.h */,
843D4A3910D3C54500624BA6 /* juce_ValueTree.cpp */,
843D4A3A10D3C54500624BA6 /* juce_ValueTree.h */,
84F1E8DE10403671006A1807 /* juce_Variant.cpp */,
@@ -3216,6 +3224,7 @@
84B2053E10D535EC008B4A79 /* juce_ValueTree.h in Headers */,
84AB6F6E10EF948B00117E64 /* juce_HeapBlock.h in Headers */,
84AF419B10F0008E0035D74F /* juce_ScopedPointer.h in Headers */,
84842F9610F6559400490977 /* juce_Value.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3542,6 +3551,7 @@
843D4A3C10D3C54500624BA6 /* juce_ValueTree.h in Headers */,
84DEDD9F10EE496500909D01 /* juce_HeapBlock.h in Headers */,
84AF3FE810EF9FF30035D74F /* juce_ScopedPointer.h in Headers */,
84BA604110F2017A001D9D70 /* juce_Value.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3889,6 +3899,7 @@
844BB95C10C5579A00DF5536 /* juce_FillType.cpp in Sources */,
844BB95E10C557A600DF5536 /* juce_mac_CoreGraphicsContext.mm in Sources */,
84B2053F10D535EC008B4A79 /* juce_ValueTree.cpp in Sources */,
84842F9510F6559300490977 /* juce_Value.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4180,6 +4191,7 @@
84AB91FF10A078190048FC39 /* juce_CPlusPlusCodeTokeniser.cpp in Sources */,
84F29A9F10C2EFA5005014DF /* juce_FillType.cpp in Sources */,
843D4A3B10D3C54500624BA6 /* juce_ValueTree.cpp in Sources */,
84BA604010F2017A001D9D70 /* juce_Value.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};


+ 12
- 0
build/win32/vc8/JUCE.vcproj View File

@@ -1117,6 +1117,10 @@
RelativePath="..\..\..\src\containers\juce_ReferenceCountedObject.h"
>
</File>
<File
RelativePath="..\..\..\src\containers\juce_ScopedPointer.h"
>
</File>
<File
RelativePath="..\..\..\src\containers\juce_SortedSet.h"
>
@@ -1125,6 +1129,14 @@
RelativePath="..\..\..\src\containers\juce_SparseSet.h"
>
</File>
<File
RelativePath="..\..\..\src\containers\juce_Value.cpp"
>
</File>
<File
RelativePath="..\..\..\src\containers\juce_Value.h"
>
</File>
<File
RelativePath="..\..\..\src\containers\juce_ValueTree.cpp"
>


+ 32
- 12
extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp View File

@@ -385,8 +385,6 @@ public:
shouldDeleteEditor = false;
speakerIn = kSpeakerArrEmpty;
speakerOut = kSpeakerArrEmpty;
speakerInChans = 0;
speakerOutChans = 0;
numInChans = JucePlugin_MaxNumInputChannels;
numOutChans = JucePlugin_MaxNumOutputChannels;
@@ -1019,26 +1017,49 @@ public:
return filter != 0 && filter->isParameterAutomatable ((int) index);
}
class ChannelConfigComparator
{
public:
static int compareElements (const short* const first, const short* const second)
{
if (first[0] < second[0])
return -1;
else if (first[0] > second[0])
return 1;
else if (first[1] < second[1])
return -1;
else if (first[1] > second[1])
return 1;
return 0;
}
};
bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput,
VstSpeakerArrangement* pluginOutput)
{
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
Array <short*> channelConfigsSorted;
ChannelConfigComparator <short*> comp;
for (int i = 0; i < numElementsInArray (channelConfigs); ++i)
channelConfigsSorted.addSorted (comp, channelConfigs[i]);
for (int i = channelConfigsSorted.size(); --i >= 0;)
{
bool configMono = (channelConfigs[i][1] == 1) && (pluginOutput->type == kSpeakerArrMono);
bool configStereo = (channelConfigs[i][1] == 2) && (pluginOutput->type == kSpeakerArrStereo);
bool inCountMatches = (channelConfigs[i][0] == pluginInput->numChannels);
bool outCountMatches = (channelConfigs[i][1] == pluginOutput->numChannels);
const short* const config = channelConfigsSorted.getUnchecked(i);
bool inCountMatches = (config[0] == pluginInput->numChannels);
bool outCountMatches = (config[1] == pluginOutput->numChannels);
if ((configMono || configStereo) && inCountMatches && outCountMatches)
if (inCountMatches && outCountMatches)
{
speakerIn = (VstSpeakerArrangementType) pluginInput->type;
speakerOut = (VstSpeakerArrangementType) pluginOutput->type;
speakerInChans = pluginInput->numChannels;
speakerOutChans = pluginOutput->numChannels;
numInChans = speakerInChans;
numOutChans = speakerOutChans;
filter->setPlayConfigDetails (speakerInChans, speakerOutChans,
filter->setPlayConfigDetails (numInChans, numOutChans,
filter->getSampleRate(),
filter->getBlockSize());
return true;
@@ -1392,7 +1413,6 @@ private:
bool firstProcessCallback;
int diffW, diffH;
VstSpeakerArrangementType speakerIn, speakerOut;
int speakerInChans, speakerOutChans;
int numInChans, numOutChans;
HeapBlock <float*> channels;
VoidArray tempChannels; // see note in processReplacing()


+ 23
- 2
extras/juce demo/src/demos/WidgetsDemo.cpp View File

@@ -381,11 +381,32 @@ static Component* createSlidersPage()
for (i = 7; i <= 10; ++i)
{
sliders[i]->setTextBoxStyle (Slider::NoTextBox, false, 0, 0);
sliders[i]->setMinValue (Random::getSystemRandom().nextDouble() * 100.0, false, false);
sliders[i]->setMaxValue (Random::getSystemRandom().nextDouble() * 100.0, false, false);
sliders[i]->setPopupDisplayEnabled (true, page);
}
/* Here, we'll create a Value object, and tell a bunch of our sliders to use it as their
value source. By telling them all to share the same Value, they'll stay in sync with
each other.
We could also optionally keep a copy of this Value elsewhere, and by changing it,
cause all the sliders to automatically update.
*/
Value sharedValue;
sharedValue = Random::getSystemRandom().nextDouble() * 100;
for (i = 0; i < 7; ++i)
sliders[i]->getValueObject().referTo (sharedValue);
// ..and now we'll do the same for all our min/max slider values..
Value sharedValueMin, sharedValueMax;
sharedValueMin = Random::getSystemRandom().nextDouble() * 60.0;
sharedValueMax = Random::getSystemRandom().nextDouble() * 60.0 + 40.0;
for (i = 7; i <= 10; ++i)
{
sliders[i]->getMinValueObject().referTo (sharedValueMin);
sliders[i]->getMaxValueObject().referTo (sharedValueMax);
}
// Create a description label...
Label* label = new Label (T("hint"), T("Try right-clicking on a slider for an options menu. \n\nAlso, holding down CTRL while dragging will turn on a slider's velocity-sensitive mode"));
label->setBounds (20, 245, 350, 150);
page->addAndMakeVisible (label);


+ 552
- 238
juce_amalgamated.cpp
File diff suppressed because it is too large
View File


+ 436
- 198
juce_amalgamated.h View File

@@ -8132,7 +8132,7 @@ private:

forEachXmlChildElement (*myParentXml, child)
{
if (child->hasTagName ("FOO"))
if (child->hasTagName (T("FOO")))
doSomethingWithXmlElement (child);
}

@@ -8188,12 +8188,12 @@ private:

Here's an example of parsing some elements: @code
// check we're looking at the right kind of document..
if (myElement->hasTagName ("ANIMALS"))
if (myElement->hasTagName (T("ANIMALS")))
{
// now we'll iterate its sub-elements looking for 'giraffe' elements..
forEachXmlChildElement (*myElement, e)
{
if (e->hasTagName ("GIRAFFE"))
if (e->hasTagName (T("GIRAFFE")))
{
// found a giraffe, so use some of its attributes..

@@ -11105,11 +11105,11 @@ private:
/********* End of inlined file: juce_SparseSet.h *********/

#endif
#ifndef __JUCE_VALUETREE_JUCEHEADER__
#ifndef __JUCE_VALUE_JUCEHEADER__

/********* Start of inlined file: juce_ValueTree.h *********/
#ifndef __JUCE_VALUETREE_JUCEHEADER__
#define __JUCE_VALUETREE_JUCEHEADER__
/********* Start of inlined file: juce_Value.h *********/
#ifndef __JUCE_VALUE_JUCEHEADER__
#define __JUCE_VALUE_JUCEHEADER__

/********* Start of inlined file: juce_Variant.h *********/
#ifndef __JUCE_VARIANT_JUCEHEADER__
@@ -11338,51 +11338,9 @@ private:
#endif // __JUCE_VARIANT_JUCEHEADER__
/********* End of inlined file: juce_Variant.h *********/

/********* Start of inlined file: juce_UndoManager.h *********/
#ifndef __JUCE_UNDOMANAGER_JUCEHEADER__
#define __JUCE_UNDOMANAGER_JUCEHEADER__

/********* Start of inlined file: juce_ChangeBroadcaster.h *********/
#ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__
#define __JUCE_CHANGEBROADCASTER_JUCEHEADER__

/********* Start of inlined file: juce_ChangeListenerList.h *********/
#ifndef __JUCE_CHANGELISTENERLIST_JUCEHEADER__
#define __JUCE_CHANGELISTENERLIST_JUCEHEADER__

/********* Start of inlined file: juce_ChangeListener.h *********/
#ifndef __JUCE_CHANGELISTENER_JUCEHEADER__
#define __JUCE_CHANGELISTENER_JUCEHEADER__

/**
Receives callbacks about changes to some kind of object.

Many objects use a ChangeListenerList to keep a set of listeners which they
will inform when something changes. A subclass of ChangeListener
is used to receive these callbacks.

Note that the major difference between an ActionListener and a ChangeListener
is that for a ChangeListener, multiple changes will be coalesced into fewer
callbacks, but ActionListeners perform one callback for every event posted.

@see ChangeListenerList, ChangeBroadcaster, ActionListener
*/
class JUCE_API ChangeListener
{
public:
/** Destructor. */
virtual ~ChangeListener() {}

/** Overridden by your subclass to receive the callback.

@param objectThatHasChanged the value that was passed to the
ChangeListenerList::sendChangeMessage() method
*/
virtual void changeListenerCallback (void* objectThatHasChanged) = 0;
};

#endif // __JUCE_CHANGELISTENER_JUCEHEADER__
/********* End of inlined file: juce_ChangeListener.h *********/
/********* Start of inlined file: juce_AsyncUpdater.h *********/
#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__
#define __JUCE_ASYNCUPDATER_JUCEHEADER__

/********* Start of inlined file: juce_MessageListener.h *********/
#ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__
@@ -11507,6 +11465,313 @@ public:
#endif // __JUCE_MESSAGELISTENER_JUCEHEADER__
/********* End of inlined file: juce_MessageListener.h *********/

/**
Has a callback method that is triggered asynchronously.

This object allows an asynchronous callback function to be triggered, for
tasks such as coalescing multiple updates into a single callback later on.

Basically, one or more calls to the triggerAsyncUpdate() will result in the
message thread calling handleAsyncUpdate() as soon as it can.
*/
class JUCE_API AsyncUpdater
{
public:

/** Creates an AsyncUpdater object. */
AsyncUpdater() throw();

/** Destructor.

If there are any pending callbacks when the object is deleted, these are lost.
*/
virtual ~AsyncUpdater();

/** Causes the callback to be triggered at a later time.

This method returns immediately, having made sure that a callback
to the handleAsyncUpdate() method will occur as soon as possible.

If an update callback is already pending but hasn't happened yet, calls
to this method will be ignored.

It's thread-safe to call this method from any number of threads without
needing to worry about locking.
*/
void triggerAsyncUpdate() throw();

/** This will stop any pending updates from happening.

If called after triggerAsyncUpdate() and before the handleAsyncUpdate()
callback happens, this will cancel the handleAsyncUpdate() callback.
*/
void cancelPendingUpdate() throw();

/** If an update has been triggered and is pending, this will invoke it
synchronously.

Use this as a kind of "flush" operation - if an update is pending, the
handleAsyncUpdate() method will be called immediately; if no update is
pending, then nothing will be done.
*/
void handleUpdateNowIfNeeded();

/** Called back to do whatever your class needs to do.

This method is called by the message thread at the next convenient time
after the triggerAsyncUpdate() method has been called.
*/
virtual void handleAsyncUpdate() = 0;

private:

class AsyncUpdaterInternal : public MessageListener
{
public:
AsyncUpdaterInternal() throw() {}
~AsyncUpdaterInternal() {}

void handleMessage (const Message&);

AsyncUpdater* owner;

private:
AsyncUpdaterInternal (const AsyncUpdaterInternal&);
const AsyncUpdaterInternal& operator= (const AsyncUpdaterInternal&);
};

AsyncUpdaterInternal internalAsyncHandler;
bool asyncMessagePending;
};

#endif // __JUCE_ASYNCUPDATER_JUCEHEADER__
/********* End of inlined file: juce_AsyncUpdater.h *********/

/**
Represents a shared variant value.

A Value object contains a reference to a var object, and can get and set its value.
Listeners can be attached to be told when the value is changed.

The Value class is a wrapper around a shared, reference-counted underlying data
object - this means that multiple Value objects can all refer to the same piece of
data, allowing all of them to be notified when any of them changes it.

The base class of Value contains a simple var object, but subclasses can be
created that map a Value onto any kind of underlying data, e.g.
ValueTree::getPropertyAsValue() returns a Value object that is a wrapper
for one of its properties.
*/
class JUCE_API Value
{
public:

/** Creates an empty Value, containing a void var. */
Value();

/** Creates a Value that refers to the same value as another one.

Note that this doesn't make a copy of the other value - both this and the other
Value will share the same underlying value, so that when either one alters it, both
will see it change.
*/
Value (const Value& other);

/** Creates a Value that is set to the specified value. */
Value (const var& initialValue);

/** Destructor. */
~Value();

/** Returns the current value. */
const var getValue() const;

/** Returns the current value. */
operator const var() const;

/** Sets the current value.

You can also use operator= to set the value.

If there are any listeners registered, they will be notified of the
change asynchronously.
*/
void setValue (const var& newValue);

/** Sets the current value.

This is the same as calling setValue().

If there are any listeners registered, they will be notified of the
change asynchronously.
*/
const Value& operator= (const var& newValue);

/** Makes this object refer to the same underlying value as another one.

*/
void referTo (const Value& valueToReferTo);

/**
*/
bool refersToSameSourceAs (const Value& other) const;

/**
*/
bool operator== (const Value& other) const;

/**
*/
bool operator!= (const Value& other) const;

/** Receives callbacks when a Value object changes.
@see Value::addListener
*/
class JUCE_API Listener
{
public:
Listener() {}
virtual ~Listener() {}

/** Called when a Value object is changed.

Note that the Value object passed as a parameter may not be exactly the same
object that you registered the listener with - it might be a copy that refers
to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs().
*/
virtual void valueChanged (Value& value) = 0;
};

/** Adds a listener to receive callbacks when the value changes.

The listener is added to this specific Value object, and not to the shared
object that it refers to. When this object is deleted, all the listeners will
be lost, even if other references to the same Value still exist. So when you're
adding a listener, make sure that you add it to a ValueTree instance that will last
for as long as you need the listener. In general, you'd never want to add a listener
to a local stack-based ValueTree, but more likely to one that's a member variable.

@see removeListener
*/
void addListener (Listener* const listener);

/** Removes a listener that was previously added with addListener(). */
void removeListener (Listener* const listener);

/**
Used internally by the Value class as the base class for its shared value objects.

The Value class is essentially a reference-counted pointer to a shared instance
of a ValueSource object. If you're feeling adventurous, you can create your own custom
ValueSource classes to allow Value objects to represent your own custom data items.
*/
class JUCE_API ValueSource : public ReferenceCountedObject,
public AsyncUpdater
{
public:
ValueSource();
virtual ~ValueSource();

/** Returns the current value of this object. */
virtual const var getValue() const = 0;
/** Changes the current value.
This must also trigger a change message if the value actually changes.
*/
virtual void setValue (const var& newValue) = 0;

/** Delivers a change message to all the listeners that are registered with
this value.

If dispatchSynchronously is true, the method will call all the listeners
before returning; otherwise it'll dispatch a message and make the call later.
*/
void sendChangeMessage (const bool dispatchSynchronously);

juce_UseDebuggingNewOperator

protected:
friend class Value;
SortedSet <Value*> valuesWithListeners;

void handleAsyncUpdate();

ValueSource (const ValueSource&);
const ValueSource& operator= (const ValueSource&);
};

/** @internal */
explicit Value (ValueSource* const valueSource);

juce_UseDebuggingNewOperator

private:
friend class ValueSource;
ReferenceCountedObjectPtr <ValueSource> value;
SortedSet <Listener*> listeners;

void callListeners();

// This is disallowed to avoid confusion about whether it should
// do a by-value or by-reference copy.
const Value& operator= (const Value& other);
};

#endif // __JUCE_VALUE_JUCEHEADER__
/********* End of inlined file: juce_Value.h *********/

#endif
#ifndef __JUCE_VALUETREE_JUCEHEADER__

/********* Start of inlined file: juce_ValueTree.h *********/
#ifndef __JUCE_VALUETREE_JUCEHEADER__
#define __JUCE_VALUETREE_JUCEHEADER__

/********* Start of inlined file: juce_UndoManager.h *********/
#ifndef __JUCE_UNDOMANAGER_JUCEHEADER__
#define __JUCE_UNDOMANAGER_JUCEHEADER__

/********* Start of inlined file: juce_ChangeBroadcaster.h *********/
#ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__
#define __JUCE_CHANGEBROADCASTER_JUCEHEADER__

/********* Start of inlined file: juce_ChangeListenerList.h *********/
#ifndef __JUCE_CHANGELISTENERLIST_JUCEHEADER__
#define __JUCE_CHANGELISTENERLIST_JUCEHEADER__

/********* Start of inlined file: juce_ChangeListener.h *********/
#ifndef __JUCE_CHANGELISTENER_JUCEHEADER__
#define __JUCE_CHANGELISTENER_JUCEHEADER__

/**
Receives callbacks about changes to some kind of object.

Many objects use a ChangeListenerList to keep a set of listeners which they
will inform when something changes. A subclass of ChangeListener
is used to receive these callbacks.

Note that the major difference between an ActionListener and a ChangeListener
is that for a ChangeListener, multiple changes will be coalesced into fewer
callbacks, but ActionListeners perform one callback for every event posted.

@see ChangeListenerList, ChangeBroadcaster, ActionListener
*/
class JUCE_API ChangeListener
{
public:
/** Destructor. */
virtual ~ChangeListener() {}

/** Overridden by your subclass to receive the callback.

@param objectThatHasChanged the value that was passed to the
ChangeListenerList::sendChangeMessage() method
*/
virtual void changeListenerCallback (void* objectThatHasChanged) = 0;
};

#endif // __JUCE_CHANGELISTENER_JUCEHEADER__
/********* End of inlined file: juce_ChangeListener.h *********/

/********* Start of inlined file: juce_ScopedLock.h *********/
#ifndef __JUCE_SCOPEDLOCK_JUCEHEADER__
#define __JUCE_SCOPEDLOCK_JUCEHEADER__
@@ -12078,111 +12343,119 @@ public:
Like an XmlElement, each ValueTree node has a type, which you can access with
getType() and hasType().
*/
ValueTree (const String& type) throw();
ValueTree (const String& type);

/** Creates a reference to another ValueTree. */
ValueTree (const ValueTree& other) throw();
ValueTree (const ValueTree& other);

/** Makes this object reference another node. */
const ValueTree& operator= (const ValueTree& other) throw();
const ValueTree& operator= (const ValueTree& other);

/** Destructor. */
~ValueTree() throw();
~ValueTree();

/** Returns true if both this and the other tree node refer to the same underlying structure.
Note that this isn't a value comparison - two independently-created trees which
contain identical data are not considered equal.
*/
bool operator== (const ValueTree& other) const throw();
bool operator== (const ValueTree& other) const;

/** Returns true if this and the other node refer to different underlying structures.
Note that this isn't a value comparison - two independently-created trees which
contain identical data are not considered equal.
*/
bool operator!= (const ValueTree& other) const throw();
bool operator!= (const ValueTree& other) const;

/** Returns true if this node refers to some valid data.
It's hard to create an invalid node, but you might get one returned, e.g. by an out-of-range
call to getChild().
*/
bool isValid() const throw() { return object != 0; }
bool isValid() const { return object != 0; }

/** Returns a deep copy of this tree and all its sub-nodes. */
ValueTree createCopy() const throw();
ValueTree createCopy() const;

/** Returns the type of this node.
The type is specified when the ValueTree is created.
@see hasType
*/
const String getType() const throw();
const String getType() const;

/** Returns true if the node has this type.
The comparison is case-sensitive.
*/
bool hasType (const String& typeName) const throw();
bool hasType (const String& typeName) const;

/** Returns the value of a named property.
If no such property has been set, this will return a void variant.
You can also use operator[] to get a property.
@see var, setProperty, hasProperty
*/
const var getProperty (const var::identifier& name) const throw();
const var getProperty (const var::identifier& name) const;

/** Returns the value of a named property.
If no such property has been set, this will return a void variant. This is the same as
calling getProperty().
@see getProperty
*/
const var operator[] (const var::identifier& name) const throw();
const var operator[] (const var::identifier& name) const;

/** Changes a named property of the node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
@see var, getProperty, removeProperty
*/
void setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager) throw();
void setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager);

/** Returns true if the node contains a named property. */
bool hasProperty (const var::identifier& name) const throw();
bool hasProperty (const var::identifier& name) const;

/** Removes a property from the node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeProperty (const var::identifier& name, UndoManager* const undoManager) throw();
void removeProperty (const var::identifier& name, UndoManager* const undoManager);

/** Removes all properties from the node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeAllProperties (UndoManager* const undoManager) throw();
void removeAllProperties (UndoManager* const undoManager);

/** Returns the total number of properties that the node contains.
@see getProperty.
*/
int getNumProperties() const throw();
int getNumProperties() const;

/** Returns the identifier of the property with a given index.
@see getNumProperties
*/
const var::identifier getPropertyName (int index) const throw();
const var::identifier getPropertyName (int index) const;

/** Returns a Value object that can be used to control and respond to one of the tree's properties.

The Value object will maintain a reference to this tree, and will use the undo manager when
it needs to change the value. Attaching a Value::Listener to the value object will provide
callbacks whenever the property changes.
*/
Value getPropertyAsValue (const var::identifier& name, UndoManager* const undoManager) const;

/** Returns the number of child nodes belonging to this one.
@see getChild
*/
int getNumChildren() const throw();
int getNumChildren() const;

/** Returns one of this node's child nodes.
If the index is out of range, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getChild (int index) const throw();
ValueTree getChild (int index) const;

/** Looks for a child node with the speficied type name.
If no such node is found, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getChildWithName (const String& type) const throw();
ValueTree getChildWithName (const String& type) const;

/** Looks for the first child node that has the speficied property value.

@@ -12192,7 +12465,7 @@ public:
If no such node is found, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const throw();
ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const;

/** Adds a child to this node.

@@ -12205,36 +12478,36 @@ public:
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void addChild (ValueTree child, int index, UndoManager* const undoManager) throw();
void addChild (ValueTree child, int index, UndoManager* const undoManager);

/** Removes the specified child from this node's child-list.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeChild (ValueTree& child, UndoManager* const undoManager) throw();
void removeChild (ValueTree& child, UndoManager* const undoManager);

/** Removes a child from this node's child-list.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeChild (const int childIndex, UndoManager* const undoManager) throw();
void removeChild (const int childIndex, UndoManager* const undoManager);

/** Removes all child-nodes from this node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeAllChildren (UndoManager* const undoManager) throw();
void removeAllChildren (UndoManager* const undoManager);

/** Returns true if this node is anywhere below the specified parent node.
This returns true if the node is a child-of-a-child, as well as a direct child.
*/
bool isAChildOf (const ValueTree& possibleParent) const throw();
bool isAChildOf (const ValueTree& possibleParent) const;

/** Returns the parent node that contains this one.
If the node has no parent, this will return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getParent() const throw();
ValueTree getParent() const;

/** Creates an XmlElement that holds a complete image of this node and all its children.

@@ -12242,14 +12515,14 @@ public:
be used to recreate a similar node by calling fromXml()
@see fromXml
*/
XmlElement* createXml() const throw();
XmlElement* createXml() const;

/** Tries to recreate a node from its XML representation.

This isn't designed to cope with random XML data - for a sensible result, it should only
be fed XML that was created by the createXml() method.
*/
static ValueTree fromXml (const XmlElement& xml) throw();
static ValueTree fromXml (const XmlElement& xml);

/** Stores this tree (and all its children) in a binary format.

@@ -12258,11 +12531,11 @@ public:
It's much faster to load/save your tree in binary form than as XML, but
obviously isn't human-readable.
*/
void writeToStream (OutputStream& output) throw();
void writeToStream (OutputStream& output);

/** Reloads a tree from a stream that was written with writeToStream().
*/
static ValueTree readFromStream (InputStream& input) throw();
static ValueTree readFromStream (InputStream& input);

/** Listener class for events that happen to a ValueTree.

@@ -12275,21 +12548,34 @@ public:
/** Destructor. */
virtual ~Listener() {}

/** This method is called when one or more of the properties of this node have changed. */
virtual void valueTreePropertyChanged (ValueTree& tree) = 0;
/** This method is called when one of the properties of this node has been changed. */
virtual void valueTreePropertyChanged (ValueTree& tree, const var::identifier& property) = 0;

/** This method is called when one or more of the children of this node have been added or removed. */
virtual void valueTreeChildrenChanged (ValueTree& tree) = 0;

/** This method is called when this node has been added or removed from a parent node. */
virtual void valueTreeParentChanged() = 0;
virtual void valueTreeParentChanged (ValueTree& tree) = 0;
};

/** Adds a listener to receive callbacks when this node is changed. */
void addListener (Listener* listener) throw();
/** Adds a listener to receive callbacks when this node is changed.

The listener is added to this specific ValueTree object, and not to the shared
object that it refers to. When this object is deleted, all the listeners will
be lost, even if other references to the same ValueTree still exist. And if you
use the operator= to make this refer to a different ValueTree, any listeners will
begin listening to changes to the new tree instead of the old one.

When you're adding a listener, make sure that you add it to a ValueTree instance that
will last for as long as you need the listener. In general, you'd never want to add a
listener to a local stack-based ValueTree, and would usually add one to a member variable.

@see removeListener
*/
void addListener (Listener* listener);

/** Removes a listener that was previously added with addListener(). */
void removeListener (Listener* listener) throw();
void removeListener (Listener* listener);

juce_UseDebuggingNewOperator

@@ -12297,16 +12583,16 @@ private:
friend class ValueTreeSetPropertyAction;
friend class ValueTreeChildChangeAction;

class SharedObject : public ReferenceCountedObject
class JUCE_API SharedObject : public ReferenceCountedObject
{
public:
SharedObject (const String& type) throw();
SharedObject (const SharedObject& other) throw();
~SharedObject() throw();
SharedObject (const String& type);
SharedObject (const SharedObject& other);
~SharedObject();

struct Property
{
Property (const var::identifier& name, const var& value) throw();
Property (const var::identifier& name, const var& value);

var::identifier name;
var value;
@@ -12315,24 +12601,29 @@ private:
const String type;
OwnedArray <Property> properties;
ReferenceCountedArray <SharedObject> children;
SortedSet <Listener*> listeners;
SortedSet <ValueTree*> valueTreesWithListeners;
SharedObject* parent;

void sendPropertyChangeMessage();
void sendPropertyChangeMessage (const var::identifier& property);
void sendChildChangeMessage();
void sendParentChangeMessage();
const var getProperty (const var::identifier& name) const throw();
void setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager) throw();
bool hasProperty (const var::identifier& name) const throw();
void removeProperty (const var::identifier& name, UndoManager* const undoManager) throw();
void removeAllProperties (UndoManager* const undoManager) throw();
bool isAChildOf (const SharedObject* const possibleParent) const throw();
ValueTree getChildWithName (const String& type) const throw();
ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const throw();
void addChild (SharedObject* child, int index, UndoManager* const undoManager) throw();
void removeChild (const int childIndex, UndoManager* const undoManager) throw();
void removeAllChildren (UndoManager* const undoManager) throw();
XmlElement* createXml() const throw();
const var getProperty (const var::identifier& name) const;
void setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager);
bool hasProperty (const var::identifier& name) const;
void removeProperty (const var::identifier& name, UndoManager* const undoManager);
void removeAllProperties (UndoManager* const undoManager);
bool isAChildOf (const SharedObject* const possibleParent) const;
ValueTree getChildWithName (const String& type) const;
ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const;
void addChild (SharedObject* child, int index, UndoManager* const undoManager);
void removeChild (const int childIndex, UndoManager* const undoManager);
void removeAllChildren (UndoManager* const undoManager);
XmlElement* createXml() const;

juce_UseDebuggingNewOperator

private:
const SharedObject& operator= (const SharedObject&);
};

friend class SharedObject;
@@ -12340,8 +12631,13 @@ private:
typedef ReferenceCountedObjectPtr <SharedObject> SharedObjectPtr;

ReferenceCountedObjectPtr <SharedObject> object;
SortedSet <Listener*> listeners;

ValueTree (SharedObject* const object_) throw();
void deliverPropertyChangeMessage (const var::identifier& property);
void deliverChildChangeMessage();
void deliverParentChangeMessage();

ValueTree (SharedObject* const object_);
};

#endif // __JUCE_VALUETREE_JUCEHEADER__
@@ -25633,92 +25929,6 @@ public:
#ifndef __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__
#define __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__

/********* Start of inlined file: juce_AsyncUpdater.h *********/
#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__
#define __JUCE_ASYNCUPDATER_JUCEHEADER__

/**
Has a callback method that is triggered asynchronously.

This object allows an asynchronous callback function to be triggered, for
tasks such as coalescing multiple updates into a single callback later on.

Basically, one or more calls to the triggerAsyncUpdate() will result in the
message thread calling handleAsyncUpdate() as soon as it can.
*/
class JUCE_API AsyncUpdater
{
public:

/** Creates an AsyncUpdater object. */
AsyncUpdater() throw();

/** Destructor.

If there are any pending callbacks when the object is deleted, these are lost.
*/
virtual ~AsyncUpdater();

/** Causes the callback to be triggered at a later time.

This method returns immediately, having made sure that a callback
to the handleAsyncUpdate() method will occur as soon as possible.

If an update callback is already pending but hasn't happened yet, calls
to this method will be ignored.

It's thread-safe to call this method from any number of threads without
needing to worry about locking.
*/
void triggerAsyncUpdate() throw();

/** This will stop any pending updates from happening.

If called after triggerAsyncUpdate() and before the handleAsyncUpdate()
callback happens, this will cancel the handleAsyncUpdate() callback.
*/
void cancelPendingUpdate() throw();

/** If an update has been triggered and is pending, this will invoke it
synchronously.

Use this as a kind of "flush" operation - if an update is pending, the
handleAsyncUpdate() method will be called immediately; if no update is
pending, then nothing will be done.
*/
void handleUpdateNowIfNeeded();

/** Called back to do whatever your class needs to do.

This method is called by the message thread at the next convenient time
after the triggerAsyncUpdate() method has been called.
*/
virtual void handleAsyncUpdate() = 0;

private:

class AsyncUpdaterInternal : public MessageListener
{
public:
AsyncUpdaterInternal() throw() {}
~AsyncUpdaterInternal() {}

void handleMessage (const Message&);

AsyncUpdater* owner;

private:
AsyncUpdaterInternal (const AsyncUpdaterInternal&);
const AsyncUpdaterInternal& operator= (const AsyncUpdaterInternal&);
};

AsyncUpdaterInternal internalAsyncHandler;
bool asyncMessagePending;
};

#endif // __JUCE_ASYNCUPDATER_JUCEHEADER__
/********* End of inlined file: juce_AsyncUpdater.h *********/

/********* Start of inlined file: juce_Desktop.h *********/
#ifndef __JUCE_DESKTOP_JUCEHEADER__
#define __JUCE_DESKTOP_JUCEHEADER__
@@ -42583,7 +42793,8 @@ class JUCE_API Slider : public Component,
public SettableTooltipClient,
private AsyncUpdater,
private ButtonListener,
private LabelListener
private LabelListener,
private Value::Listener
{
public:

@@ -42868,6 +43079,14 @@ public:
/** Returns the slider's current value. */
double getValue() const;

/** Returns the Value object that represents the slider's current position.
You can use this Value object to connect the slider's position to external values or setters,
either by taking a copy of the Value, or by using Value::referTo() to make it point to
your own Value object.
@see Value, getMaxValue, getMinValueObject
*/
Value& getValueObject() { return currentValue; }

/** Sets the limits that the slider's value can take.

@param newMinimum the lowest value allowed
@@ -42904,6 +43123,14 @@ public:
*/
double getMinValue() const;

/** For a slider with two or three thumbs, this returns the lower of its values.
You can use this Value object to connect the slider's position to external values or setters,
either by taking a copy of the Value, or by using Value::referTo() to make it point to
your own Value object.
@see Value, getMinValue, getMaxValueObject
*/
Value& getMinValueObject() { return valueMin; }

/** For a slider with two or three thumbs, this sets the lower of its values.

This will trigger a callback to SliderListener::sliderValueChanged() for any listeners
@@ -42938,6 +43165,14 @@ public:
*/
double getMaxValue() const;

/** For a slider with two or three thumbs, this returns the higher of its values.
You can use this Value object to connect the slider's position to external values or setters,
either by taking a copy of the Value, or by using Value::referTo() to make it point to
your own Value object.
@see Value, getMaxValue, getMinValueObject
*/
Value& getMaxValueObject() { return valueMax; }

/** For a slider with two or three thumbs, this sets the lower of its values.

This will trigger a callback to SliderListener::sliderValueChanged() for any listeners
@@ -43223,10 +43458,13 @@ protected:
void handleAsyncUpdate();
/** @internal */
void colourChanged();
/** @internal */
void valueChanged (Value& value);

private:
SortedSet <void*> listeners;
double currentValue, valueMin, valueMax;
Value currentValue, valueMin, valueMax;
double lastCurrentValue, lastValueMin, lastValueMax;
double minimum, maximum, interval, doubleClickReturnValue;
double valueWhenLastDragged, valueOnMouseDown, skewFactor, lastAngle;
double velocityModeSensitivity, velocityModeOffset, minMaxDiff;


+ 24
- 20
src/audio/midi/juce_MidiMessage.cpp View File

@@ -254,24 +254,24 @@ MidiMessage::MidiMessage (const uint8* src,
const MidiMessage& MidiMessage::operator= (const MidiMessage& other) throw()
{
if (this == &other)
return *this;
timeStamp = other.timeStamp;
size = other.size;
message = other.message;
if (this != &other)
{
timeStamp = other.timeStamp;
size = other.size;
message = other.message;
if (data != (uint8*) &message)
juce_free (data);
if (data != (uint8*) &message)
juce_free (data);
if (other.data != (uint8*) &other.message)
{
data = (uint8*) juce_malloc (size);
memcpy (data, other.data, size);
}
else
{
data = (uint8*) &message;
if (other.data != (uint8*) &other.message)
{
data = (uint8*) juce_malloc (size);
memcpy (data, other.data, size);
}
else
{
data = (uint8*) &message;
}
}
return *this;
@@ -293,12 +293,16 @@ int MidiMessage::getChannel() const throw()
bool MidiMessage::isForChannel (const int channel) const throw()
{
jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
return ((data[0] & 0xf) == channel - 1)
&& ((data[0] & 0xf0) != 0xf0);
}
void MidiMessage::setChannel (const int channel) throw()
{
jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
if ((data[0] & 0xf0) != (uint8) 0xf0)
data[0] = (uint8) ((data[0] & (uint8)0xf0)
| (uint8)(channel - 1));
@@ -372,7 +376,7 @@ const MidiMessage MidiMessage::aftertouchChange (const int channel,
const int noteNum,
const int aftertouchValue) throw()
{
jassert (channel > 0 && channel <= 16);
jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
jassert (((unsigned int) noteNum) <= 127);
jassert (((unsigned int) aftertouchValue) <= 127);
@@ -396,7 +400,7 @@ int MidiMessage::getChannelPressureValue() const throw()
const MidiMessage MidiMessage::channelPressureChange (const int channel,
const int pressure) throw()
{
jassert (channel > 0 && channel <= 16);
jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
jassert (((unsigned int) pressure) <= 127);
return MidiMessage (0xd0 | jlimit (0, 15, channel - 1),
@@ -416,7 +420,7 @@ int MidiMessage::getProgramChangeNumber() const throw()
const MidiMessage MidiMessage::programChange (const int channel,
const int programNumber) throw()
{
jassert (channel > 0 && channel <= 16);
jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
return MidiMessage (0xc0 | jlimit (0, 15, channel - 1),
programNumber & 0x7f);
@@ -435,7 +439,7 @@ int MidiMessage::getPitchWheelValue() const throw()
const MidiMessage MidiMessage::pitchWheel (const int channel,
const int position) throw()
{
jassert (channel > 0 && channel <= 16);
jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
jassert (((unsigned int) position) <= 0x3fff);
return MidiMessage (0xe0 | jlimit (0, 15, channel - 1),


+ 217
- 0
src/containers/juce_Value.cpp View File

@@ -0,0 +1,217 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-9 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online 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.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../core/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE
#include "juce_Value.h"
//==============================================================================
Value::ValueSource::ValueSource()
{
}
Value::ValueSource::~ValueSource()
{
}
void Value::ValueSource::sendChangeMessage (const bool synchronous)
{
if (synchronous)
{
for (int i = valuesWithListeners.size(); --i >= 0;)
{
Value* const v = valuesWithListeners[i];
if (v != 0)
v->callListeners();
}
}
else
{
triggerAsyncUpdate();
}
}
void Value::ValueSource::handleAsyncUpdate()
{
sendChangeMessage (true);
}
//==============================================================================
class SimpleValueSource : public Value::ValueSource
{
public:
SimpleValueSource()
{
}
SimpleValueSource (const var& initialValue)
: value (initialValue)
{
}
~SimpleValueSource()
{
}
const var getValue() const
{
return value;
}
void setValue (const var& newValue)
{
if (newValue != value)
{
value = newValue;
sendChangeMessage (false);
}
}
private:
var value;
SimpleValueSource (const SimpleValueSource&);
const SimpleValueSource& operator= (const SimpleValueSource&);
};
//==============================================================================
Value::Value()
: value (new SimpleValueSource())
{
}
Value::Value (ValueSource* const value_)
: value (value_)
{
jassert (value_ != 0);
}
Value::Value (const var& initialValue)
: value (new SimpleValueSource (initialValue))
{
}
Value::Value (const Value& other)
: value (other.value)
{
}
const Value& Value::operator= (const Value& other)
{
value = other.value;
return *this;
}
Value::~Value()
{
if (listeners.size() > 0)
value->valuesWithListeners.removeValue (this);
}
//==============================================================================
const var Value::getValue() const
{
return value->getValue();
}
void Value::setValue (const var& newValue)
{
value->setValue (newValue);
}
const Value& Value::operator= (const var& newValue)
{
value->setValue (newValue);
return *this;
}
void Value::referTo (const Value& valueToReferTo)
{
if (valueToReferTo.value != value)
{
if (listeners.size() > 0)
{
value->valuesWithListeners.removeValue (this);
valueToReferTo.value->valuesWithListeners.add (this);
}
value = valueToReferTo.value;
callListeners();
}
}
bool Value::refersToSameSourceAs (const Value& other) const
{
return value == other.value;
}
bool Value::operator== (const Value& other) const
{
return value == other.value || value->getValue() == other.getValue();
}
bool Value::operator!= (const Value& other) const
{
return value != other.value && value->getValue() != other.getValue();
}
//==============================================================================
void Value::addListener (Listener* const listener)
{
if (listener != 0)
{
if (listeners.size() == 0)
value->valuesWithListeners.add (this);
listeners.add (listener);
}
}
void Value::removeListener (Listener* const listener)
{
listeners.removeValue (listener);
if (listeners.size() == 0)
value->valuesWithListeners.removeValue (this);
}
void Value::callListeners()
{
Value valueCopy (*this); // Use a copy in case this object gets deleted by a callback
for (int i = listeners.size(); --i >= 0;)
{
Listener* const l = listeners[i];
if (l != 0)
l->valueChanged (valueCopy);
}
}
END_JUCE_NAMESPACE

+ 214
- 0
src/containers/juce_Value.h View File

@@ -0,0 +1,214 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-9 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online 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.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#ifndef __JUCE_VALUE_JUCEHEADER__
#define __JUCE_VALUE_JUCEHEADER__
#include "juce_Variant.h"
#include "../events/juce_AsyncUpdater.h"
#include "juce_ReferenceCountedObject.h"
#include "juce_SortedSet.h"
//==============================================================================
/**
Represents a shared variant value.
A Value object contains a reference to a var object, and can get and set its value.
Listeners can be attached to be told when the value is changed.
The Value class is a wrapper around a shared, reference-counted underlying data
object - this means that multiple Value objects can all refer to the same piece of
data, allowing all of them to be notified when any of them changes it.
The base class of Value contains a simple var object, but subclasses can be
created that map a Value onto any kind of underlying data, e.g.
ValueTree::getPropertyAsValue() returns a Value object that is a wrapper
for one of its properties.
*/
class JUCE_API Value
{
public:
//==============================================================================
/** Creates an empty Value, containing a void var. */
Value();
/** Creates a Value that refers to the same value as another one.
Note that this doesn't make a copy of the other value - both this and the other
Value will share the same underlying value, so that when either one alters it, both
will see it change.
*/
Value (const Value& other);
/** Creates a Value that is set to the specified value. */
Value (const var& initialValue);
/** Destructor. */
~Value();
//==============================================================================
/** Returns the current value. */
const var getValue() const;
/** Returns the current value. */
operator const var() const;
/** Sets the current value.
You can also use operator= to set the value.
If there are any listeners registered, they will be notified of the
change asynchronously.
*/
void setValue (const var& newValue);
/** Sets the current value.
This is the same as calling setValue().
If there are any listeners registered, they will be notified of the
change asynchronously.
*/
const Value& operator= (const var& newValue);
/** Makes this object refer to the same underlying value as another one.
*/
void referTo (const Value& valueToReferTo);
/**
*/
bool refersToSameSourceAs (const Value& other) const;
/**
*/
bool operator== (const Value& other) const;
/**
*/
bool operator!= (const Value& other) const;
//==============================================================================
/** Receives callbacks when a Value object changes.
@see Value::addListener
*/
class JUCE_API Listener
{
public:
Listener() {}
virtual ~Listener() {}
/** Called when a Value object is changed.
Note that the Value object passed as a parameter may not be exactly the same
object that you registered the listener with - it might be a copy that refers
to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs().
*/
virtual void valueChanged (Value& value) = 0;
};
/** Adds a listener to receive callbacks when the value changes.
The listener is added to this specific Value object, and not to the shared
object that it refers to. When this object is deleted, all the listeners will
be lost, even if other references to the same Value still exist. So when you're
adding a listener, make sure that you add it to a ValueTree instance that will last
for as long as you need the listener. In general, you'd never want to add a listener
to a local stack-based ValueTree, but more likely to one that's a member variable.
@see removeListener
*/
void addListener (Listener* const listener);
/** Removes a listener that was previously added with addListener(). */
void removeListener (Listener* const listener);
//==============================================================================
/**
Used internally by the Value class as the base class for its shared value objects.
The Value class is essentially a reference-counted pointer to a shared instance
of a ValueSource object. If you're feeling adventurous, you can create your own custom
ValueSource classes to allow Value objects to represent your own custom data items.
*/
class JUCE_API ValueSource : public ReferenceCountedObject,
public AsyncUpdater
{
public:
ValueSource();
virtual ~ValueSource();
/** Returns the current value of this object. */
virtual const var getValue() const = 0;
/** Changes the current value.
This must also trigger a change message if the value actually changes.
*/
virtual void setValue (const var& newValue) = 0;
/** Delivers a change message to all the listeners that are registered with
this value.
If dispatchSynchronously is true, the method will call all the listeners
before returning; otherwise it'll dispatch a message and make the call later.
*/
void sendChangeMessage (const bool dispatchSynchronously);
//==============================================================================
juce_UseDebuggingNewOperator
protected:
friend class Value;
SortedSet <Value*> valuesWithListeners;
void handleAsyncUpdate();
ValueSource (const ValueSource&);
const ValueSource& operator= (const ValueSource&);
};
//==============================================================================
/** @internal */
explicit Value (ValueSource* const valueSource);
juce_UseDebuggingNewOperator
private:
friend class ValueSource;
ReferenceCountedObjectPtr <ValueSource> value;
SortedSet <Listener*> listeners;
void callListeners();
// This is disallowed to avoid confusion about whether it should
// do a by-value or by-reference copy.
const Value& operator= (const Value& other);
};
#endif // __JUCE_VALUE_JUCEHEADER__

+ 179
- 73
src/containers/juce_ValueTree.cpp View File

@@ -35,7 +35,7 @@ class ValueTreeSetPropertyAction : public UndoableAction
{
public:
ValueTreeSetPropertyAction (const ValueTree::SharedObjectPtr& target_, const var::identifier& name_,
const var& newValue_, const bool isAddingNewProperty_, const bool isDeletingProperty_) throw()
const var& newValue_, const bool isAddingNewProperty_, const bool isDeletingProperty_)
: target (target_), name (name_), newValue (newValue_),
isAddingNewProperty (isAddingNewProperty_),
isDeletingProperty (isDeletingProperty_)
@@ -89,7 +89,7 @@ class ValueTreeChildChangeAction : public UndoableAction
{
public:
ValueTreeChildChangeAction (const ValueTree::SharedObjectPtr& target_, const int childIndex_,
const ValueTree::SharedObjectPtr& newChild_) throw()
const ValueTree::SharedObjectPtr& newChild_)
: target (target_),
child (newChild_ != 0 ? newChild_ : target_->children [childIndex_]),
childIndex (childIndex_),
@@ -136,12 +136,12 @@ private:
//==============================================================================
ValueTree::SharedObject::SharedObject (const String& type_) throw()
ValueTree::SharedObject::SharedObject (const String& type_)
: type (type_), parent (0)
{
}
ValueTree::SharedObject::SharedObject (const SharedObject& other) throw()
ValueTree::SharedObject::SharedObject (const SharedObject& other)
: type (other.type), parent (0)
{
int i;
@@ -155,7 +155,7 @@ ValueTree::SharedObject::SharedObject (const SharedObject& other) throw()
children.add (new SharedObject (*other.children.getUnchecked(i)));
}
ValueTree::SharedObject::~SharedObject() throw()
ValueTree::SharedObject::~SharedObject()
{
jassert (parent == 0); // this should never happen unless something isn't obeying the ref-counting!
@@ -168,27 +168,37 @@ ValueTree::SharedObject::~SharedObject() throw()
}
}
ValueTree::SharedObject::Property::Property (const var::identifier& name_, const var& value_) throw()
ValueTree::SharedObject::Property::Property (const var::identifier& name_, const var& value_)
: name (name_), value (value_)
{
}
//==============================================================================
void ValueTree::SharedObject::sendPropertyChangeMessage()
void ValueTree::deliverPropertyChangeMessage (const var::identifier& property)
{
ValueTree v (this);
ValueTree v (object);
for (int i = listeners.size(); --i >= 0;)
{
ValueTree::Listener* const l = listeners[i];
if (l != 0)
l->valueTreePropertyChanged (v);
l->valueTreePropertyChanged (v, property);
}
}
void ValueTree::SharedObject::sendChildChangeMessage()
void ValueTree::SharedObject::sendPropertyChangeMessage (const var::identifier& property)
{
for (int i = valueTreesWithListeners.size(); --i >= 0;)
{
ValueTree* const v = valueTreesWithListeners[i];
if (v != 0)
v->deliverPropertyChangeMessage (property);
}
}
void ValueTree::deliverChildChangeMessage()
{
ValueTree v (this);
ValueTree v (object);
for (int i = listeners.size(); --i >= 0;)
{
@@ -198,25 +208,48 @@ void ValueTree::SharedObject::sendChildChangeMessage()
}
}
void ValueTree::SharedObject::sendParentChangeMessage()
void ValueTree::SharedObject::sendChildChangeMessage()
{
for (int j = children.size(); --j >= 0;)
for (int i = valueTreesWithListeners.size(); --i >= 0;)
{
SharedObject* const t = children[j];
if (t != 0)
t->sendParentChangeMessage();
ValueTree* const v = valueTreesWithListeners[i];
if (v != 0)
v->deliverChildChangeMessage();
}
}
void ValueTree::deliverParentChangeMessage()
{
ValueTree v (object);
for (int i = listeners.size(); --i >= 0;)
{
ValueTree::Listener* const l = listeners[i];
if (l != 0)
l->valueTreeParentChanged();
l->valueTreeParentChanged (v);
}
}
void ValueTree::SharedObject::sendParentChangeMessage()
{
int i;
for (i = children.size(); --i >= 0;)
{
SharedObject* const t = children[i];
if (t != 0)
t->sendParentChangeMessage();
}
for (i = valueTreesWithListeners.size(); --i >= 0;)
{
ValueTree* const v = valueTreesWithListeners[i];
if (v != 0)
v->deliverParentChangeMessage();
}
}
//==============================================================================
const var ValueTree::SharedObject::getProperty (const var::identifier& name) const throw()
const var ValueTree::SharedObject::getProperty (const var::identifier& name) const
{
for (int i = properties.size(); --i >= 0;)
{
@@ -228,7 +261,7 @@ const var ValueTree::SharedObject::getProperty (const var::identifier& name) con
return var();
}
void ValueTree::SharedObject::setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager) throw()
void ValueTree::SharedObject::setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager)
{
for (int i = properties.size(); --i >= 0;)
{
@@ -241,7 +274,7 @@ void ValueTree::SharedObject::setProperty (const var::identifier& name, const va
if (undoManager == 0)
{
p->value = newValue;
sendPropertyChangeMessage();
sendPropertyChangeMessage (name);
}
else
{
@@ -256,7 +289,7 @@ void ValueTree::SharedObject::setProperty (const var::identifier& name, const va
if (undoManager == 0)
{
properties.add (new Property (name, newValue));
sendPropertyChangeMessage();
sendPropertyChangeMessage (name);
}
else
{
@@ -264,7 +297,7 @@ void ValueTree::SharedObject::setProperty (const var::identifier& name, const va
}
}
bool ValueTree::SharedObject::hasProperty (const var::identifier& name) const throw()
bool ValueTree::SharedObject::hasProperty (const var::identifier& name) const
{
for (int i = properties.size(); --i >= 0;)
if (properties.getUnchecked(i)->name == name)
@@ -273,7 +306,7 @@ bool ValueTree::SharedObject::hasProperty (const var::identifier& name) const th
return false;
}
void ValueTree::SharedObject::removeProperty (const var::identifier& name, UndoManager* const undoManager) throw()
void ValueTree::SharedObject::removeProperty (const var::identifier& name, UndoManager* const undoManager)
{
for (int i = properties.size(); --i >= 0;)
{
@@ -284,7 +317,7 @@ void ValueTree::SharedObject::removeProperty (const var::identifier& name, UndoM
if (undoManager == 0)
{
properties.remove (i);
sendPropertyChangeMessage();
sendPropertyChangeMessage (name);
}
else
{
@@ -296,12 +329,16 @@ void ValueTree::SharedObject::removeProperty (const var::identifier& name, UndoM
}
}
void ValueTree::SharedObject::removeAllProperties (UndoManager* const undoManager) throw()
void ValueTree::SharedObject::removeAllProperties (UndoManager* const undoManager)
{
if (undoManager == 0)
{
properties.clear();
sendPropertyChangeMessage();
while (properties.size() > 0)
{
const var::identifier name (properties.getLast()->name);
properties.removeLast();
sendPropertyChangeMessage (name);
}
}
else
{
@@ -310,7 +347,7 @@ void ValueTree::SharedObject::removeAllProperties (UndoManager* const undoManage
}
}
ValueTree ValueTree::SharedObject::getChildWithName (const String& typeToMatch) const throw()
ValueTree ValueTree::SharedObject::getChildWithName (const String& typeToMatch) const
{
for (int i = 0; i < children.size(); ++i)
if (children.getUnchecked(i)->type == typeToMatch)
@@ -319,7 +356,7 @@ ValueTree ValueTree::SharedObject::getChildWithName (const String& typeToMatch)
return (SharedObject*) 0;
}
ValueTree ValueTree::SharedObject::getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const throw()
ValueTree ValueTree::SharedObject::getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const
{
for (int i = 0; i < children.size(); ++i)
if (children.getUnchecked(i)->getProperty (propertyName) == propertyValue)
@@ -328,7 +365,7 @@ ValueTree ValueTree::SharedObject::getChildWithProperty (const var::identifier&
return (SharedObject*) 0;
}
bool ValueTree::SharedObject::isAChildOf (const SharedObject* const possibleParent) const throw()
bool ValueTree::SharedObject::isAChildOf (const SharedObject* const possibleParent) const
{
const SharedObject* p = parent;
@@ -343,7 +380,7 @@ bool ValueTree::SharedObject::isAChildOf (const SharedObject* const possiblePare
return false;
}
void ValueTree::SharedObject::addChild (SharedObject* child, int index, UndoManager* const undoManager) throw()
void ValueTree::SharedObject::addChild (SharedObject* child, int index, UndoManager* const undoManager)
{
if (child != 0 && child->parent != this)
{
@@ -381,7 +418,7 @@ void ValueTree::SharedObject::addChild (SharedObject* child, int index, UndoMana
}
}
void ValueTree::SharedObject::removeChild (const int childIndex, UndoManager* const undoManager) throw()
void ValueTree::SharedObject::removeChild (const int childIndex, UndoManager* const undoManager)
{
const SharedObjectPtr child (children [childIndex]);
@@ -401,7 +438,7 @@ void ValueTree::SharedObject::removeChild (const int childIndex, UndoManager* co
}
}
void ValueTree::SharedObject::removeAllChildren (UndoManager* const undoManager) throw()
void ValueTree::SharedObject::removeAllChildren (UndoManager* const undoManager)
{
while (children.size() > 0)
removeChild (children.size() - 1, 0);
@@ -409,73 +446,85 @@ void ValueTree::SharedObject::removeAllChildren (UndoManager* const undoManager)
//==============================================================================
ValueTree::ValueTree (const String& type_) throw()
ValueTree::ValueTree (const String& type_)
: object (new ValueTree::SharedObject (type_))
{
jassert (type_.isNotEmpty()); // All objects should be given a sensible type name!
}
ValueTree::ValueTree (SharedObject* const object_) throw()
ValueTree::ValueTree (SharedObject* const object_)
: object (object_)
{
}
ValueTree::ValueTree (const ValueTree& other) throw()
ValueTree::ValueTree (const ValueTree& other)
: object (other.object)
{
}
const ValueTree& ValueTree::operator= (const ValueTree& other) throw()
const ValueTree& ValueTree::operator= (const ValueTree& other)
{
if (listeners.size() > 0)
{
if (object != 0)
object->valueTreesWithListeners.removeValue (this);
if (other.object != 0)
other.object->valueTreesWithListeners.add (this);
}
object = other.object;
return *this;
}
ValueTree::~ValueTree() throw()
ValueTree::~ValueTree()
{
if (listeners.size() > 0 && object != 0)
object->valueTreesWithListeners.removeValue (this);
}
bool ValueTree::operator== (const ValueTree& other) const throw()
bool ValueTree::operator== (const ValueTree& other) const
{
return object == other.object;
}
bool ValueTree::operator!= (const ValueTree& other) const throw()
bool ValueTree::operator!= (const ValueTree& other) const
{
return object != other.object;
}
ValueTree ValueTree::createCopy() const throw()
ValueTree ValueTree::createCopy() const
{
return ValueTree (object != 0 ? new SharedObject (*object) : 0);
}
bool ValueTree::hasType (const String& typeName) const throw()
bool ValueTree::hasType (const String& typeName) const
{
return object != 0 && object->type == typeName;
}
const String ValueTree::getType() const throw()
const String ValueTree::getType() const
{
return object != 0 ? object->type : String::empty;
}
ValueTree ValueTree::getParent() const throw()
ValueTree ValueTree::getParent() const
{
return object != 0 ? ValueTree (object->parent) : ValueTree ((SharedObject*) 0);
}
const var ValueTree::operator[] (const var::identifier& name) const throw()
const var ValueTree::operator[] (const var::identifier& name) const
{
return object == 0 ? var() : object->getProperty (name);
}
const var ValueTree::getProperty (const var::identifier& name) const throw()
const var ValueTree::getProperty (const var::identifier& name) const
{
return object == 0 ? var() : object->getProperty (name);
}
void ValueTree::setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager) throw()
void ValueTree::setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager)
{
jassert (name.name.isNotEmpty());
@@ -483,101 +532,158 @@ void ValueTree::setProperty (const var::identifier& name, const var& newValue, U
object->setProperty (name, newValue, undoManager);
}
bool ValueTree::hasProperty (const var::identifier& name) const throw()
bool ValueTree::hasProperty (const var::identifier& name) const
{
return object != 0 && object->hasProperty (name);
}
void ValueTree::removeProperty (const var::identifier& name, UndoManager* const undoManager) throw()
void ValueTree::removeProperty (const var::identifier& name, UndoManager* const undoManager)
{
if (object != 0)
object->removeProperty (name, undoManager);
}
void ValueTree::removeAllProperties (UndoManager* const undoManager) throw()
void ValueTree::removeAllProperties (UndoManager* const undoManager)
{
if (object != 0)
object->removeAllProperties (undoManager);
}
int ValueTree::getNumProperties() const throw()
int ValueTree::getNumProperties() const
{
return object == 0 ? 0 : object->properties.size();
}
const var::identifier ValueTree::getPropertyName (int index) const throw()
const var::identifier ValueTree::getPropertyName (int index) const
{
const SharedObject::Property* const p = (object == 0) ? 0 : object->properties [index];
return p != 0 ? p->name : var::identifier (String::empty);
}
//==============================================================================
int ValueTree::getNumChildren() const throw()
class ValueTreePropertyValueSource : public Value::ValueSource,
public ValueTree::Listener
{
public:
ValueTreePropertyValueSource (const ValueTree& tree_,
const var::identifier& property_,
UndoManager* const undoManager_)
: tree (tree_),
property (property_),
undoManager (undoManager_)
{
tree.addListener (this);
}
~ValueTreePropertyValueSource()
{
tree.removeListener (this);
}
const var getValue() const
{
return tree [property];
}
void setValue (const var& newValue)
{
tree.setProperty (property, newValue, undoManager);
}
void valueTreePropertyChanged (ValueTree& tree, const var::identifier& changedProperty)
{
if (property == changedProperty)
sendChangeMessage (false);
}
void valueTreeChildrenChanged (ValueTree& tree) {}
void valueTreeParentChanged (ValueTree& tree) {}
private:
ValueTree tree;
const var::identifier property;
UndoManager* const undoManager;
const ValueTreePropertyValueSource& operator= (const ValueTreePropertyValueSource&);
};
Value ValueTree::getPropertyAsValue (const var::identifier& name, UndoManager* const undoManager) const
{
return Value (new ValueTreePropertyValueSource (*this, name, undoManager));
}
//==============================================================================
int ValueTree::getNumChildren() const
{
return object == 0 ? 0 : object->children.size();
}
ValueTree ValueTree::getChild (int index) const throw()
ValueTree ValueTree::getChild (int index) const
{
return object != 0 ? (SharedObject*) object->children [index] : ValueTree ((SharedObject*) 0);
}
ValueTree ValueTree::getChildWithName (const String& type) const throw()
ValueTree ValueTree::getChildWithName (const String& type) const
{
return object != 0 ? object->getChildWithName (type) : ValueTree ((SharedObject*) 0);
}
ValueTree ValueTree::getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const throw()
ValueTree ValueTree::getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const
{
return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree ((SharedObject*) 0);
}
bool ValueTree::isAChildOf (const ValueTree& possibleParent) const throw()
bool ValueTree::isAChildOf (const ValueTree& possibleParent) const
{
return object != 0 && object->isAChildOf (possibleParent.object);
}
void ValueTree::addChild (ValueTree child, int index, UndoManager* const undoManager) throw()
void ValueTree::addChild (ValueTree child, int index, UndoManager* const undoManager)
{
if (object != 0)
object->addChild (child.object, index, undoManager);
}
void ValueTree::removeChild (const int childIndex, UndoManager* const undoManager) throw()
void ValueTree::removeChild (const int childIndex, UndoManager* const undoManager)
{
if (object != 0)
object->removeChild (childIndex, undoManager);
}
void ValueTree::removeChild (ValueTree& child, UndoManager* const undoManager) throw()
void ValueTree::removeChild (ValueTree& child, UndoManager* const undoManager)
{
if (object != 0)
object->removeChild (object->children.indexOf (child.object), undoManager);
}
void ValueTree::removeAllChildren (UndoManager* const undoManager) throw()
void ValueTree::removeAllChildren (UndoManager* const undoManager)
{
if (object != 0)
object->removeAllChildren (undoManager);
}
//==============================================================================
void ValueTree::addListener (Listener* listener) throw()
void ValueTree::addListener (Listener* listener)
{
jassert (object != 0); // can't add listeners to a null object!
if (listener != 0)
{
if (listeners.size() == 0 && object != 0)
object->valueTreesWithListeners.add (this);
if (object != 0)
object->listeners.add (listener);
listeners.add (listener);
}
}
void ValueTree::removeListener (Listener* listener) throw()
void ValueTree::removeListener (Listener* listener)
{
if (object != 0)
object->listeners.removeValue (listener);
listeners.removeValue (listener);
if (listeners.size() == 0 && object != 0)
object->valueTreesWithListeners.removeValue (this);
}
//==============================================================================
XmlElement* ValueTree::SharedObject::createXml() const throw()
XmlElement* ValueTree::SharedObject::createXml() const
{
XmlElement* xml = new XmlElement (type);
@@ -597,12 +703,12 @@ XmlElement* ValueTree::SharedObject::createXml() const throw()
return xml;
}
XmlElement* ValueTree::createXml() const throw()
XmlElement* ValueTree::createXml() const
{
return object != 0 ? object->createXml() : 0;
}
ValueTree ValueTree::fromXml (const XmlElement& xml) throw()
ValueTree ValueTree::fromXml (const XmlElement& xml)
{
ValueTree v (xml.getTagName());
@@ -620,7 +726,7 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) throw()
}
//==============================================================================
void ValueTree::writeToStream (OutputStream& output) throw()
void ValueTree::writeToStream (OutputStream& output)
{
output.writeString (getType());
@@ -642,7 +748,7 @@ void ValueTree::writeToStream (OutputStream& output) throw()
getChild (i).writeToStream (output);
}
ValueTree ValueTree::readFromStream (InputStream& input) throw()
ValueTree ValueTree::readFromStream (InputStream& input)
{
String type (input.readString());


+ 90
- 59
src/containers/juce_ValueTree.h View File

@@ -27,11 +27,11 @@
#define __JUCE_VALUETREE_JUCEHEADER__
#include "juce_Variant.h"
#include "juce_Value.h"
#include "../utilities/juce_UndoManager.h"
#include "../text/juce_XmlElement.h"
#include "juce_ReferenceCountedArray.h"
//==============================================================================
/**
A powerful tree structure that can be used to hold free-form data, and which can
@@ -75,50 +75,50 @@ public:
Like an XmlElement, each ValueTree node has a type, which you can access with
getType() and hasType().
*/
ValueTree (const String& type) throw();
ValueTree (const String& type);
/** Creates a reference to another ValueTree. */
ValueTree (const ValueTree& other) throw();
ValueTree (const ValueTree& other);
/** Makes this object reference another node. */
const ValueTree& operator= (const ValueTree& other) throw();
const ValueTree& operator= (const ValueTree& other);
/** Destructor. */
~ValueTree() throw();
~ValueTree();
/** Returns true if both this and the other tree node refer to the same underlying structure.
Note that this isn't a value comparison - two independently-created trees which
contain identical data are not considered equal.
*/
bool operator== (const ValueTree& other) const throw();
bool operator== (const ValueTree& other) const;
/** Returns true if this and the other node refer to different underlying structures.
Note that this isn't a value comparison - two independently-created trees which
contain identical data are not considered equal.
*/
bool operator!= (const ValueTree& other) const throw();
bool operator!= (const ValueTree& other) const;
//==============================================================================
/** Returns true if this node refers to some valid data.
It's hard to create an invalid node, but you might get one returned, e.g. by an out-of-range
call to getChild().
*/
bool isValid() const throw() { return object != 0; }
bool isValid() const { return object != 0; }
/** Returns a deep copy of this tree and all its sub-nodes. */
ValueTree createCopy() const throw();
ValueTree createCopy() const;
//==============================================================================
/** Returns the type of this node.
The type is specified when the ValueTree is created.
@see hasType
*/
const String getType() const throw();
const String getType() const;
/** Returns true if the node has this type.
The comparison is case-sensitive.
*/
bool hasType (const String& typeName) const throw();
bool hasType (const String& typeName) const;
//==============================================================================
/** Returns the value of a named property.
@@ -126,64 +126,72 @@ public:
You can also use operator[] to get a property.
@see var, setProperty, hasProperty
*/
const var getProperty (const var::identifier& name) const throw();
const var getProperty (const var::identifier& name) const;
/** Returns the value of a named property.
If no such property has been set, this will return a void variant. This is the same as
calling getProperty().
@see getProperty
*/
const var operator[] (const var::identifier& name) const throw();
const var operator[] (const var::identifier& name) const;
/** Changes a named property of the node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
@see var, getProperty, removeProperty
*/
void setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager) throw();
void setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager);
/** Returns true if the node contains a named property. */
bool hasProperty (const var::identifier& name) const throw();
bool hasProperty (const var::identifier& name) const;
/** Removes a property from the node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeProperty (const var::identifier& name, UndoManager* const undoManager) throw();
void removeProperty (const var::identifier& name, UndoManager* const undoManager);
/** Removes all properties from the node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeAllProperties (UndoManager* const undoManager) throw();
void removeAllProperties (UndoManager* const undoManager);
/** Returns the total number of properties that the node contains.
@see getProperty.
*/
int getNumProperties() const throw();
int getNumProperties() const;
/** Returns the identifier of the property with a given index.
@see getNumProperties
*/
const var::identifier getPropertyName (int index) const throw();
const var::identifier getPropertyName (int index) const;
/** Returns a Value object that can be used to control and respond to one of the tree's properties.
The Value object will maintain a reference to this tree, and will use the undo manager when
it needs to change the value. Attaching a Value::Listener to the value object will provide
callbacks whenever the property changes.
*/
Value getPropertyAsValue (const var::identifier& name, UndoManager* const undoManager) const;
//==============================================================================
/** Returns the number of child nodes belonging to this one.
@see getChild
*/
int getNumChildren() const throw();
int getNumChildren() const;
/** Returns one of this node's child nodes.
If the index is out of range, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getChild (int index) const throw();
ValueTree getChild (int index) const;
/** Looks for a child node with the speficied type name.
If no such node is found, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getChildWithName (const String& type) const throw();
ValueTree getChildWithName (const String& type) const;
/** Looks for the first child node that has the speficied property value.
@@ -193,7 +201,7 @@ public:
If no such node is found, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const throw();
ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const;
/** Adds a child to this node.
@@ -206,36 +214,36 @@ public:
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void addChild (ValueTree child, int index, UndoManager* const undoManager) throw();
void addChild (ValueTree child, int index, UndoManager* const undoManager);
/** Removes the specified child from this node's child-list.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeChild (ValueTree& child, UndoManager* const undoManager) throw();
void removeChild (ValueTree& child, UndoManager* const undoManager);
/** Removes a child from this node's child-list.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeChild (const int childIndex, UndoManager* const undoManager) throw();
void removeChild (const int childIndex, UndoManager* const undoManager);
/** Removes all child-nodes from this node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeAllChildren (UndoManager* const undoManager) throw();
void removeAllChildren (UndoManager* const undoManager);
/** Returns true if this node is anywhere below the specified parent node.
This returns true if the node is a child-of-a-child, as well as a direct child.
*/
bool isAChildOf (const ValueTree& possibleParent) const throw();
bool isAChildOf (const ValueTree& possibleParent) const;
/** Returns the parent node that contains this one.
If the node has no parent, this will return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getParent() const throw();
ValueTree getParent() const;
//==============================================================================
/** Creates an XmlElement that holds a complete image of this node and all its children.
@@ -244,14 +252,14 @@ public:
be used to recreate a similar node by calling fromXml()
@see fromXml
*/
XmlElement* createXml() const throw();
XmlElement* createXml() const;
/** Tries to recreate a node from its XML representation.
This isn't designed to cope with random XML data - for a sensible result, it should only
be fed XML that was created by the createXml() method.
*/
static ValueTree fromXml (const XmlElement& xml) throw();
static ValueTree fromXml (const XmlElement& xml);
//==============================================================================
/** Stores this tree (and all its children) in a binary format.
@@ -261,11 +269,11 @@ public:
It's much faster to load/save your tree in binary form than as XML, but
obviously isn't human-readable.
*/
void writeToStream (OutputStream& output) throw();
void writeToStream (OutputStream& output);
/** Reloads a tree from a stream that was written with writeToStream().
*/
static ValueTree readFromStream (InputStream& input) throw();
static ValueTree readFromStream (InputStream& input);
//==============================================================================
/** Listener class for events that happen to a ValueTree.
@@ -279,21 +287,34 @@ public:
/** Destructor. */
virtual ~Listener() {}
/** This method is called when one or more of the properties of this node have changed. */
virtual void valueTreePropertyChanged (ValueTree& tree) = 0;
/** This method is called when one of the properties of this node has been changed. */
virtual void valueTreePropertyChanged (ValueTree& tree, const var::identifier& property) = 0;
/** This method is called when one or more of the children of this node have been added or removed. */
virtual void valueTreeChildrenChanged (ValueTree& tree) = 0;
/** This method is called when this node has been added or removed from a parent node. */
virtual void valueTreeParentChanged() = 0;
virtual void valueTreeParentChanged (ValueTree& tree) = 0;
};
/** Adds a listener to receive callbacks when this node is changed. */
void addListener (Listener* listener) throw();
/** Adds a listener to receive callbacks when this node is changed.
The listener is added to this specific ValueTree object, and not to the shared
object that it refers to. When this object is deleted, all the listeners will
be lost, even if other references to the same ValueTree still exist. And if you
use the operator= to make this refer to a different ValueTree, any listeners will
begin listening to changes to the new tree instead of the old one.
When you're adding a listener, make sure that you add it to a ValueTree instance that
will last for as long as you need the listener. In general, you'd never want to add a
listener to a local stack-based ValueTree, and would usually add one to a member variable.
@see removeListener
*/
void addListener (Listener* listener);
/** Removes a listener that was previously added with addListener(). */
void removeListener (Listener* listener) throw();
void removeListener (Listener* listener);
//==============================================================================
juce_UseDebuggingNewOperator
@@ -302,16 +323,16 @@ private:
friend class ValueTreeSetPropertyAction;
friend class ValueTreeChildChangeAction;
class SharedObject : public ReferenceCountedObject
class JUCE_API SharedObject : public ReferenceCountedObject
{
public:
SharedObject (const String& type) throw();
SharedObject (const SharedObject& other) throw();
~SharedObject() throw();
SharedObject (const String& type);
SharedObject (const SharedObject& other);
~SharedObject();
struct Property
{
Property (const var::identifier& name, const var& value) throw();
Property (const var::identifier& name, const var& value);
var::identifier name;
var value;
@@ -320,24 +341,29 @@ private:
const String type;
OwnedArray <Property> properties;
ReferenceCountedArray <SharedObject> children;
SortedSet <Listener*> listeners;
SortedSet <ValueTree*> valueTreesWithListeners;
SharedObject* parent;
void sendPropertyChangeMessage();
void sendPropertyChangeMessage (const var::identifier& property);
void sendChildChangeMessage();
void sendParentChangeMessage();
const var getProperty (const var::identifier& name) const throw();
void setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager) throw();
bool hasProperty (const var::identifier& name) const throw();
void removeProperty (const var::identifier& name, UndoManager* const undoManager) throw();
void removeAllProperties (UndoManager* const undoManager) throw();
bool isAChildOf (const SharedObject* const possibleParent) const throw();
ValueTree getChildWithName (const String& type) const throw();
ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const throw();
void addChild (SharedObject* child, int index, UndoManager* const undoManager) throw();
void removeChild (const int childIndex, UndoManager* const undoManager) throw();
void removeAllChildren (UndoManager* const undoManager) throw();
XmlElement* createXml() const throw();
const var getProperty (const var::identifier& name) const;
void setProperty (const var::identifier& name, const var& newValue, UndoManager* const undoManager);
bool hasProperty (const var::identifier& name) const;
void removeProperty (const var::identifier& name, UndoManager* const undoManager);
void removeAllProperties (UndoManager* const undoManager);
bool isAChildOf (const SharedObject* const possibleParent) const;
ValueTree getChildWithName (const String& type) const;
ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const;
void addChild (SharedObject* child, int index, UndoManager* const undoManager);
void removeChild (const int childIndex, UndoManager* const undoManager);
void removeAllChildren (UndoManager* const undoManager);
XmlElement* createXml() const;
juce_UseDebuggingNewOperator
private:
const SharedObject& operator= (const SharedObject&);
};
friend class SharedObject;
@@ -345,8 +371,13 @@ private:
typedef ReferenceCountedObjectPtr <SharedObject> SharedObjectPtr;
ReferenceCountedObjectPtr <SharedObject> object;
SortedSet <Listener*> listeners;
void deliverPropertyChangeMessage (const var::identifier& property);
void deliverChildChangeMessage();
void deliverParentChangeMessage();
ValueTree (SharedObject* const object_) throw();
ValueTree (SharedObject* const object_);
};


+ 75
- 51
src/gui/components/controls/juce_Slider.cpp View File

@@ -92,9 +92,9 @@ private:
Slider::Slider (const String& name)
: Component (name),
listeners (2),
currentValue (0.0),
valueMin (0.0),
valueMax (0.0),
lastCurrentValue (0),
lastValueMin (0),
lastValueMax (0),
minimum (0),
maximum (10),
interval (0),
@@ -137,10 +137,17 @@ Slider::Slider (const String& name)
lookAndFeelChanged();
updateText();
currentValue.addListener (this);
valueMin.addListener (this);
valueMax.addListener (this);
}
Slider::~Slider()
{
currentValue.removeListener (this);
valueMin.removeListener (this);
valueMax.removeListener (this);
popupDisplay = 0;
deleteAllChildren();
}
@@ -334,7 +341,7 @@ void Slider::colourChanged()
void Slider::lookAndFeelChanged()
{
const String previousTextBoxContent (valueBox != 0 ? valueBox->getText()
: getTextFromValue (currentValue));
: getTextFromValue (currentValue.getValue()));
deleteAllChildren();
valueBox = 0;
@@ -420,7 +427,7 @@ void Slider::setRange (const double newMin,
// keep the current values inside the new range..
if (style != TwoValueHorizontal && style != TwoValueVertical)
{
setValue (currentValue, false, false);
setValue (getValue(), false, false);
}
else
{
@@ -442,13 +449,26 @@ void Slider::triggerChangeMessage (const bool synchronous)
valueChanged();
}
void Slider::valueChanged (Value& value)
{
if (value.refersToSameSourceAs (currentValue))
{
if (style != TwoValueHorizontal && style != TwoValueVertical)
setValue (currentValue.getValue(), false, false);
}
else if (value.refersToSameSourceAs (valueMin))
setMinValue (valueMin.getValue(), false, false);
else if (value.refersToSameSourceAs (valueMax))
setMaxValue (valueMax.getValue(), false, false);
}
double Slider::getValue() const
{
// for a two-value style slider, you should use the getMinValue() and getMaxValue()
// methods to get the two values.
jassert (style != TwoValueHorizontal && style != TwoValueVertical);
return currentValue;
return currentValue.getValue();
}
void Slider::setValue (double newValue,
@@ -463,22 +483,26 @@ void Slider::setValue (double newValue,
if (style == ThreeValueHorizontal || style == ThreeValueVertical)
{
jassert (valueMin <= valueMax);
newValue = jlimit (valueMin, valueMax, newValue);
jassert ((double) valueMin.getValue() <= (double) valueMax.getValue());
newValue = jlimit ((double) valueMin.getValue(),
(double) valueMax.getValue(),
newValue);
}
if (currentValue != newValue)
if (newValue != lastCurrentValue)
{
if (valueBox != 0)
valueBox->hideEditor (true);
currentValue = newValue;
lastCurrentValue = newValue;
currentValue = var (newValue);
updateText();
repaint();
if (popupDisplay != 0)
{
((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (currentValue));
((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (newValue));
popupDisplay->repaint();
}
@@ -493,7 +517,7 @@ double Slider::getMinValue() const
jassert (style == TwoValueHorizontal || style == TwoValueVertical
|| style == ThreeValueHorizontal || style == ThreeValueVertical);
return valueMin;
return valueMin.getValue();
}
double Slider::getMaxValue() const
@@ -502,7 +526,7 @@ double Slider::getMaxValue() const
jassert (style == TwoValueHorizontal || style == TwoValueVertical
|| style == ThreeValueHorizontal || style == ThreeValueVertical);
return valueMax;
return valueMax.getValue();
}
void Slider::setMinValue (double newValue, const bool sendUpdateMessage, const bool sendMessageSynchronously, const bool allowNudgingOfOtherValues)
@@ -515,27 +539,28 @@ void Slider::setMinValue (double newValue, const bool sendUpdateMessage, const b
if (style == TwoValueHorizontal || style == TwoValueVertical)
{
if (allowNudgingOfOtherValues && newValue > valueMax)
if (allowNudgingOfOtherValues && newValue > (double) valueMax.getValue())
setMaxValue (newValue, sendUpdateMessage, sendMessageSynchronously);
newValue = jmin (valueMax, newValue);
newValue = jmin ((double) valueMax.getValue(), newValue);
}
else
{
if (allowNudgingOfOtherValues && newValue > currentValue)
if (allowNudgingOfOtherValues && newValue > (double) currentValue.getValue())
setValue (newValue, sendUpdateMessage, sendMessageSynchronously);
newValue = jmin (currentValue, newValue);
newValue = jmin ((double) currentValue.getValue(), newValue);
}
if (valueMin != newValue)
if (lastValueMin != newValue)
{
lastValueMin = newValue;
valueMin = newValue;
repaint();
if (popupDisplay != 0)
{
((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (valueMin));
((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (newValue));
popupDisplay->repaint();
}
@@ -554,27 +579,28 @@ void Slider::setMaxValue (double newValue, const bool sendUpdateMessage, const b
if (style == TwoValueHorizontal || style == TwoValueVertical)
{
if (allowNudgingOfOtherValues && newValue < valueMin)
if (allowNudgingOfOtherValues && newValue < (double) valueMin.getValue())
setMinValue (newValue, sendUpdateMessage, sendMessageSynchronously);
newValue = jmax (valueMin, newValue);
newValue = jmax ((double) valueMin.getValue(), newValue);
}
else
{
if (allowNudgingOfOtherValues && newValue < currentValue)
if (allowNudgingOfOtherValues && newValue < (double) currentValue.getValue())
setValue (newValue, sendUpdateMessage, sendMessageSynchronously);
newValue = jmax (currentValue, newValue);
newValue = jmax ((double) currentValue.getValue(), newValue);
}
if (valueMax != newValue)
if (lastValueMax != newValue)
{
lastValueMax = newValue;
valueMax = newValue;
repaint();
if (popupDisplay != 0)
{
((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (valueMax));
((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (valueMax.getValue()));
popupDisplay->repaint();
}
@@ -599,7 +625,7 @@ double Slider::getDoubleClickReturnValue (bool& isEnabled_) const
void Slider::updateText()
{
if (valueBox != 0)
valueBox->setText (getTextFromValue (currentValue), false);
valueBox->setText (getTextFromValue (currentValue.getValue()), false);
}
void Slider::setTextValueSuffix (const String& suffix)
@@ -687,7 +713,7 @@ void Slider::labelTextChanged (Label* label)
{
const double newValue = snapValue (getValueFromText (label->getText()), false);
if (getValue() != newValue)
if (newValue != (double) currentValue.getValue())
{
sendDragStart();
setValue (newValue, true, true);
@@ -798,7 +824,7 @@ void Slider::paint (Graphics& g)
{
if (style == Rotary || style == RotaryHorizontalDrag || style == RotaryVerticalDrag)
{
const float sliderPos = (float) valueToProportionOfLength (currentValue);
const float sliderPos = (float) valueToProportionOfLength (currentValue.getValue());
jassert (sliderPos >= 0 && sliderPos <= 1.0f);
getLookAndFeel().drawRotarySlider (g,
@@ -817,9 +843,9 @@ void Slider::paint (Graphics& g)
sliderRect.getY(),
sliderRect.getWidth(),
sliderRect.getHeight(),
getLinearSliderPos (currentValue),
getLinearSliderPos (valueMin),
getLinearSliderPos (valueMax),
getLinearSliderPos (currentValue.getValue()),
getLinearSliderPos (valueMin.getValue()),
getLinearSliderPos (valueMax.getValue()),
style,
*this);
}
@@ -1027,9 +1053,9 @@ void Slider::mouseDown (const MouseEvent& e)
{
const float mousePos = (float) (isVertical() ? e.y : e.x);
const float normalPosDistance = fabsf (getLinearSliderPos (currentValue) - mousePos);
const float minPosDistance = fabsf (getLinearSliderPos (valueMin) - 0.1f - mousePos);
const float maxPosDistance = fabsf (getLinearSliderPos (valueMax) + 0.1f - mousePos);
const float normalPosDistance = fabsf (getLinearSliderPos (currentValue.getValue()) - mousePos);
const float minPosDistance = fabsf (getLinearSliderPos (valueMin.getValue()) - 0.1f - mousePos);
const float maxPosDistance = fabsf (getLinearSliderPos (valueMax.getValue()) + 0.1f - mousePos);
if (style == TwoValueHorizontal || style == TwoValueVertical)
{
@@ -1047,17 +1073,14 @@ void Slider::mouseDown (const MouseEvent& e)
}
}
minMaxDiff = valueMax - valueMin;
minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue();
lastAngle = rotaryStart + (rotaryEnd - rotaryStart)
* valueToProportionOfLength (currentValue);
* valueToProportionOfLength (currentValue.getValue());
if (sliderBeingDragged == 2)
valueWhenLastDragged = valueMax;
else if (sliderBeingDragged == 1)
valueWhenLastDragged = valueMin;
else
valueWhenLastDragged = currentValue;
valueWhenLastDragged = ((sliderBeingDragged == 2) ? valueMax
: ((sliderBeingDragged == 1) ? valueMin
: currentValue)).getValue();
valueOnMouseDown = valueWhenLastDragged;
@@ -1094,7 +1117,7 @@ void Slider::mouseUp (const MouseEvent&)
{
restoreMouseIfHidden();
if (sendChangeOnlyOnRelease && valueOnMouseDown != currentValue)
if (sendChangeOnlyOnRelease && valueOnMouseDown != (double) currentValue.getValue())
triggerChangeMessage (false);
sendDragEnd();
@@ -1124,7 +1147,7 @@ void Slider::restoreMouseIfHidden()
const double pos = (sliderBeingDragged == 2) ? getMaxValue()
: ((sliderBeingDragged == 1) ? getMinValue()
: currentValue);
: (double) currentValue.getValue());
if (style == RotaryHorizontalDrag || style == RotaryVerticalDrag)
{
@@ -1336,7 +1359,7 @@ void Slider::mouseDrag (const MouseEvent& e)
if (e.mods.isShiftDown())
setMaxValue (getMinValue() + minMaxDiff, false, false, true);
else
minMaxDiff = valueMax - valueMin;
minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue();
}
else
{
@@ -1348,7 +1371,7 @@ void Slider::mouseDrag (const MouseEvent& e)
if (e.mods.isShiftDown())
setMinValue (getMaxValue() - minMaxDiff, false, false, true);
else
minMaxDiff = valueMax - valueMin;
minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue();
}
mouseXWhenLastDragged = e.x;
@@ -1381,18 +1404,19 @@ void Slider::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float w
if (valueBox != 0)
valueBox->hideEditor (false);
const double value = (double) currentValue.getValue();
const double proportionDelta = (wheelIncrementX != 0 ? -wheelIncrementX : wheelIncrementY) * 0.15f;
const double currentPos = valueToProportionOfLength (currentValue);
const double currentPos = valueToProportionOfLength (value);
const double newValue = proportionOfLengthToValue (jlimit (0.0, 1.0, currentPos + proportionDelta));
double delta = (newValue != currentValue)
? jmax (fabs (newValue - currentValue), interval) : 0;
double delta = (newValue != value)
? jmax (fabs (newValue - value), interval) : 0;
if (currentValue > newValue)
if (value > newValue)
delta = -delta;
sendDragStart();
setValue (snapValue (currentValue + delta, false), true, true);
setValue (snapValue (value + delta, false), true, true);
sendDragEnd();
}
}


+ 31
- 2
src/gui/components/controls/juce_Slider.h View File

@@ -31,6 +31,7 @@
#include "../buttons/juce_Button.h"
#include "../../../events/juce_AsyncUpdater.h"
#include "../../../containers/juce_SortedSet.h"
#include "../../../containers/juce_Value.h"
//==============================================================================
@@ -58,7 +59,8 @@ class JUCE_API Slider : public Component,
public SettableTooltipClient,
private AsyncUpdater,
private ButtonListener,
private LabelListener
private LabelListener,
private Value::Listener
{
public:
//==============================================================================
@@ -351,6 +353,14 @@ public:
/** Returns the slider's current value. */
double getValue() const;
/** Returns the Value object that represents the slider's current position.
You can use this Value object to connect the slider's position to external values or setters,
either by taking a copy of the Value, or by using Value::referTo() to make it point to
your own Value object.
@see Value, getMaxValue, getMinValueObject
*/
Value& getValueObject() { return currentValue; }
//==============================================================================
/** Sets the limits that the slider's value can take.
@@ -389,6 +399,14 @@ public:
*/
double getMinValue() const;
/** For a slider with two or three thumbs, this returns the lower of its values.
You can use this Value object to connect the slider's position to external values or setters,
either by taking a copy of the Value, or by using Value::referTo() to make it point to
your own Value object.
@see Value, getMinValue, getMaxValueObject
*/
Value& getMinValueObject() { return valueMin; }
/** For a slider with two or three thumbs, this sets the lower of its values.
This will trigger a callback to SliderListener::sliderValueChanged() for any listeners
@@ -423,6 +441,14 @@ public:
*/
double getMaxValue() const;
/** For a slider with two or three thumbs, this returns the higher of its values.
You can use this Value object to connect the slider's position to external values or setters,
either by taking a copy of the Value, or by using Value::referTo() to make it point to
your own Value object.
@see Value, getMaxValue, getMinValueObject
*/
Value& getMaxValueObject() { return valueMax; }
/** For a slider with two or three thumbs, this sets the lower of its values.
This will trigger a callback to SliderListener::sliderValueChanged() for any listeners
@@ -720,10 +746,13 @@ protected:
void handleAsyncUpdate();
/** @internal */
void colourChanged();
/** @internal */
void valueChanged (Value& value);
private:
SortedSet <void*> listeners;
double currentValue, valueMin, valueMax;
Value currentValue, valueMin, valueMax;
double lastCurrentValue, lastValueMin, lastValueMax;
double minimum, maximum, interval, doubleClickReturnValue;
double valueWhenLastDragged, valueOnMouseDown, skewFactor, lastAngle;
double velocityModeSensitivity, velocityModeOffset, minMaxDiff;


+ 28
- 32
src/io/streams/juce_GZIPCompressorOutputStream.cpp View File

@@ -48,41 +48,28 @@ using namespace zlibNamespace;
// included publicly.
class GZIPCompressorHelper
{
private:
HeapBlock <z_stream> stream;
uint8* data;
int dataSize, compLevel, strategy;
bool setParams;
public:
bool finished, shouldFinish;
GZIPCompressorHelper (const int compressionLevel, const bool nowrap)
: data (0),
dataSize (0),
compLevel (compressionLevel),
strategy (0),
setParams (true),
streamIsValid (false),
finished (false),
shouldFinish (false)
{
stream.calloc (1);
if (deflateInit2 (stream,
compLevel,
Z_DEFLATED,
nowrap ? -MAX_WBITS : MAX_WBITS,
8,
strategy) != Z_OK)
{
stream.free();
}
zerostruct (stream);
streamIsValid = (deflateInit2 (&stream, compLevel, Z_DEFLATED,
nowrap ? -MAX_WBITS : MAX_WBITS,
8, strategy) == Z_OK);
}
~GZIPCompressorHelper()
{
if (stream != 0)
deflateEnd (stream);
if (streamIsValid)
deflateEnd (&stream);
}
bool needsInput() const throw()
@@ -98,15 +85,15 @@ public:
int doNextBlock (uint8* const dest, const int destSize) throw()
{
if (stream != 0)
if (streamIsValid)
{
stream->next_in = data;
stream->next_out = dest;
stream->avail_in = dataSize;
stream->avail_out = destSize;
stream.next_in = data;
stream.next_out = dest;
stream.avail_in = dataSize;
stream.avail_out = destSize;
const int result = setParams ? deflateParams (stream, compLevel, strategy)
: deflate (stream, shouldFinish ? Z_FINISH : Z_NO_FLUSH);
const int result = setParams ? deflateParams (&stream, compLevel, strategy)
: deflate (&stream, shouldFinish ? Z_FINISH : Z_NO_FLUSH);
setParams = false;
@@ -114,12 +101,12 @@ public:
{
case Z_STREAM_END:
finished = true;
// Deliberate fall-through..
case Z_OK:
data += dataSize - stream->avail_in;
dataSize = stream->avail_in;
data += dataSize - stream.avail_in;
dataSize = stream.avail_in;
return destSize - stream->avail_out;
return destSize - stream.avail_out;
default:
break;
@@ -128,6 +115,15 @@ public:
return 0;
}
private:
z_stream stream;
uint8* data;
int dataSize, compLevel, strategy;
bool setParams, streamIsValid;
public:
bool finished, shouldFinish;
};


+ 36
- 48
src/io/streams/juce_GZIPDecompressorInputStream.cpp View File

@@ -80,36 +80,24 @@ using namespace zlibNamespace;
// included publicly.
class GZIPDecompressHelper
{
private:
HeapBlock <z_stream> stream;
uint8* data;
int dataSize;
public:
bool finished, needsDictionary, error;
GZIPDecompressHelper (const bool noWrap) throw()
: data (0),
dataSize (0),
finished (false),
finished (true),
needsDictionary (false),
error (false)
error (true),
streamIsValid (false)
{
stream.calloc (1);
if (inflateInit2 (stream, (noWrap) ? -MAX_WBITS
: MAX_WBITS) != Z_OK)
{
stream.free();
error = true;
finished = true;
}
zerostruct (stream);
streamIsValid = (inflateInit2 (&stream, noWrap ? -MAX_WBITS : MAX_WBITS) == Z_OK);
finished = error = ! streamIsValid;
}
~GZIPDecompressHelper() throw()
{
if (stream != 0)
inflateEnd (stream);
if (streamIsValid)
inflateEnd (&stream);
}
bool needsInput() const throw() { return dataSize <= 0; }
@@ -122,28 +110,27 @@ public:
int doNextBlock (uint8* const dest, const int destSize) throw()
{
if (stream != 0 && data != 0 && ! finished)
if (streamIsValid && data != 0 && ! finished)
{
stream->next_in = data;
stream->next_out = dest;
stream->avail_in = dataSize;
stream->avail_out = destSize;
stream.next_in = data;
stream.next_out = dest;
stream.avail_in = dataSize;
stream.avail_out = destSize;
switch (inflate (stream, Z_PARTIAL_FLUSH))
switch (inflate (&stream, Z_PARTIAL_FLUSH))
{
case Z_STREAM_END:
finished = true;
// deliberate fall-through
case Z_OK:
data += dataSize - stream->avail_in;
dataSize = stream->avail_in;
return destSize - stream->avail_out;
data += dataSize - stream.avail_in;
dataSize = stream.avail_in;
return destSize - stream.avail_out;
case Z_NEED_DICT:
needsDictionary = true;
data += dataSize - stream->avail_in;
dataSize = stream->avail_in;
data += dataSize - stream.avail_in;
dataSize = stream.avail_in;
break;
case Z_DATA_ERROR:
@@ -157,6 +144,14 @@ public:
return 0;
}
private:
z_stream stream;
uint8* data;
int dataSize;
public:
bool finished, needsDictionary, error, streamIsValid;
};
//==============================================================================
@@ -255,25 +250,18 @@ int64 GZIPDecompressorInputStream::getPosition()
bool GZIPDecompressorInputStream::setPosition (int64 newPos)
{
if (newPos != currentPos)
if (newPos < currentPos)
{
if (newPos > currentPos)
{
skipNextBytes (newPos - currentPos);
}
else
{
// reset the stream and start again..
isEof = false;
activeBufferSize = 0;
currentPos = 0;
helper = new GZIPDecompressHelper (noWrap);
sourceStream->setPosition (originalSourcePos);
skipNextBytes (newPos);
}
// to go backwards, reset the stream and start again..
isEof = false;
activeBufferSize = 0;
currentPos = 0;
helper = new GZIPDecompressHelper (noWrap);
sourceStream->setPosition (originalSourcePos);
}
skipNextBytes (newPos - currentPos);
return true;
}


+ 3
- 5
src/io/streams/juce_InputStream.cpp View File

@@ -270,12 +270,10 @@ void InputStream::skipNextBytes (int64 numBytesToSkip)
if (numBytesToSkip > 0)
{
const int skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384);
MemoryBlock temp (skipBufferSize);
HeapBlock <char> temp (skipBufferSize);
while ((numBytesToSkip > 0) && ! isExhausted())
{
numBytesToSkip -= read (temp.getData(), (int) jmin (numBytesToSkip, (int64) skipBufferSize));
}
while (numBytesToSkip > 0 && ! isExhausted())
numBytesToSkip -= read (temp, (int) jmin (numBytesToSkip, (int64) skipBufferSize));
}
}


+ 1
- 0
src/juce_amalgamated_template.cpp View File

@@ -120,6 +120,7 @@
#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
#include "containers/juce_ValueTree.cpp"
#include "containers/juce_Value.cpp"
#include "application/juce_Application.cpp"
#include "application/juce_ApplicationCommandInfo.cpp"
#include "application/juce_ApplicationCommandManager.cpp"


+ 3
- 0
src/juce_core_includes.h View File

@@ -65,6 +65,9 @@
#ifndef __JUCE_SPARSESET_JUCEHEADER__
#include "containers/juce_SparseSet.h"
#endif
#ifndef __JUCE_VALUE_JUCEHEADER__
#include "containers/juce_Value.h"
#endif
#ifndef __JUCE_VALUETREE_JUCEHEADER__
#include "containers/juce_ValueTree.h"
#endif


+ 4
- 1
src/native/mac/juce_mac_CameraDevice.mm View File

@@ -211,7 +211,10 @@ END_JUCE_NAMESPACE
fromConnection: (QTCaptureConnection*) connection
{
if (firstRecordedTime == 0)
firstRecordedTime = new Time (Time::getCurrentTime() - RelativeTime::milliseconds (50));
{
const Time now (Time::getCurrentTime());
firstRecordedTime = new Time (now - RelativeTime (0.1));
}
}
@end


+ 13
- 5
src/native/mac/juce_mac_NSViewComponentPeer.mm View File

@@ -942,12 +942,20 @@ void NSViewComponentPeer::setFullScreen (bool shouldBeFullScreen)
if (fullScreen != shouldBeFullScreen)
{
if (shouldBeFullScreen)
r = Desktop::getInstance().getMainMonitorArea();
if (shouldBeFullScreen && (getStyleFlags() & windowHasTitleBar) != 0)
{
fullScreen = true;
[window performZoom: nil];
}
else
{
if (shouldBeFullScreen)
r = Desktop::getInstance().getMainMonitorArea();
// (can't call the component's setBounds method because that'll reset our fullscreen flag)
if (r != getComponent()->getBounds() && ! r.isEmpty())
setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen);
// (can't call the component's setBounds method because that'll reset our fullscreen flag)
if (r != getComponent()->getBounds() && ! r.isEmpty())
setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen);
}
}
}
}


+ 4
- 4
src/native/windows/juce_win32_ActiveXComponent.cpp View File

@@ -479,12 +479,12 @@ bool ActiveXControlComponent::createControl (const void* controlIID)
control = info.release();
setControlBounds (Rectangle (x, y, getWidth(), getHeight()));
info->controlHWND = getHWND (this);
((ActiveXControlData*) control)->controlHWND = getHWND (this);
if (info->controlHWND != 0)
if (((ActiveXControlData*) control)->controlHWND != 0)
{
originalWndProc = (void*) (pointer_sized_int) GetWindowLongPtr ((HWND) info->controlHWND, GWLP_WNDPROC);
SetWindowLongPtr ((HWND) info->controlHWND, GWLP_WNDPROC, (LONG_PTR) activeXHookWndProc);
originalWndProc = (void*) (pointer_sized_int) GetWindowLongPtr ((HWND) ((ActiveXControlData*) control)->controlHWND, GWLP_WNDPROC);
SetWindowLongPtr ((HWND) ((ActiveXControlData*) control)->controlHWND, GWLP_WNDPROC, (LONG_PTR) activeXHookWndProc);
}
return true;


+ 3
- 1
src/native/windows/juce_win32_CameraDevice.cpp View File

@@ -189,7 +189,9 @@ public:
{
if (recordNextFrameTime)
{
firstRecordedTime = Time::getCurrentTime();
const double defaultCameraLatency = 0.1;
firstRecordedTime = Time::getCurrentTime() - RelativeTime (defaultCameraLatency);
recordNextFrameTime = false;
ComSmartPtr <IPin> pin;


+ 3
- 3
src/text/juce_XmlElement.h View File

@@ -42,7 +42,7 @@
forEachXmlChildElement (*myParentXml, child)
{
if (child->hasTagName ("FOO"))
if (child->hasTagName (T("FOO")))
doSomethingWithXmlElement (child);
}
@@ -100,12 +100,12 @@
Here's an example of parsing some elements: @code
// check we're looking at the right kind of document..
if (myElement->hasTagName ("ANIMALS"))
if (myElement->hasTagName (T("ANIMALS")))
{
// now we'll iterate its sub-elements looking for 'giraffe' elements..
forEachXmlChildElement (*myElement, e)
{
if (e->hasTagName ("GIRAFFE"))
if (e->hasTagName (T("GIRAFFE")))
{
// found a giraffe, so use some of its attributes..


Loading…
Cancel
Save