diff --git a/Builds/Linux/Makefile b/Builds/Linux/Makefile
index eca33e3f89..e049d3f5e3 100644
--- a/Builds/Linux/Makefile
+++ b/Builds/Linux/Makefile
@@ -94,6 +94,7 @@ OBJECTS := \
$(OBJDIR)/juce_Synthesiser_2bffa1dd.o \
$(OBJDIR)/juce_BigInteger_63589133.o \
$(OBJDIR)/juce_DynamicObject_69d02ab3.o \
+ $(OBJDIR)/juce_Expression_1e9a5aad.o \
$(OBJDIR)/juce_Identifier_89fa043e.o \
$(OBJDIR)/juce_MemoryBlock_edd65761.o \
$(OBJDIR)/juce_NamedValueSet_6b0793df.o \
@@ -631,6 +632,11 @@ $(OBJDIR)/juce_DynamicObject_69d02ab3.o: ../../src/containers/juce_DynamicObject
@echo "Compiling juce_DynamicObject.cpp"
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+$(OBJDIR)/juce_Expression_1e9a5aad.o: ../../src/containers/juce_Expression.cpp
+ -@mkdir -p $(OBJDIR)
+ @echo "Compiling juce_Expression.cpp"
+ @$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+
$(OBJDIR)/juce_Identifier_89fa043e.o: ../../src/containers/juce_Identifier.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling juce_Identifier.cpp"
diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
index f074ce082b..0420d33e03 100644
--- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
+++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
@@ -63,6 +63,7 @@
E8DFABC1603D55B97429A8E4 = { isa = PBXBuildFile; fileRef = 35668D8EEA19957C6C9AC83A; };
BE25871C34D79FEFFD1B94B6 = { isa = PBXBuildFile; fileRef = 895D742F49DA9F100990879C; };
4AB5E55BDF79028F82F83D8E = { isa = PBXBuildFile; fileRef = F77C9170829579FABA5679AD; };
+ 25018C91F79D918FEA084630 = { isa = PBXBuildFile; fileRef = 199DFD1C5A282FE13A585FEA; };
95577AE91AA6CBA7FE9434F3 = { isa = PBXBuildFile; fileRef = 1CF7CC0EB057F995BBBEFC90; };
21BA256CBCC9C15265928A23 = { isa = PBXBuildFile; fileRef = FF40DA899AE16A5E1D8AA54A; };
9D2D1BA65C27BDA1F7C44769 = { isa = PBXBuildFile; fileRef = 70E5409425A76782B6188B31; };
@@ -481,6 +482,8 @@
F77C9170829579FABA5679AD = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DynamicObject.cpp; path = ../../src/containers/juce_DynamicObject.cpp; sourceTree = SOURCE_ROOT; };
34C402EF9ADCAD34FB657D43 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_DynamicObject.h; path = ../../src/containers/juce_DynamicObject.h; sourceTree = SOURCE_ROOT; };
7DA9AC75A4D9227C8FC4B2F7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ElementComparator.h; path = ../../src/containers/juce_ElementComparator.h; sourceTree = SOURCE_ROOT; };
+ 199DFD1C5A282FE13A585FEA = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Expression.cpp; path = ../../src/containers/juce_Expression.cpp; sourceTree = SOURCE_ROOT; };
+ 3C12A5E0EBBB0916C01CFC58 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Expression.h; path = ../../src/containers/juce_Expression.h; sourceTree = SOURCE_ROOT; };
F364AA2637B7CB89D3657DFF = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_HeapBlock.h; path = ../../src/containers/juce_HeapBlock.h; sourceTree = SOURCE_ROOT; };
1CF7CC0EB057F995BBBEFC90 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Identifier.cpp; path = ../../src/containers/juce_Identifier.cpp; sourceTree = SOURCE_ROOT; };
C16848F86DF014F1CBECE248 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Identifier.h; path = ../../src/containers/juce_Identifier.h; sourceTree = SOURCE_ROOT; };
@@ -1195,6 +1198,8 @@
F77C9170829579FABA5679AD,
34C402EF9ADCAD34FB657D43,
7DA9AC75A4D9227C8FC4B2F7,
+ 199DFD1C5A282FE13A585FEA,
+ 3C12A5E0EBBB0916C01CFC58,
F364AA2637B7CB89D3657DFF,
1CF7CC0EB057F995BBBEFC90,
C16848F86DF014F1CBECE248,
@@ -1952,6 +1957,7 @@
E8DFABC1603D55B97429A8E4,
BE25871C34D79FEFFD1B94B6,
4AB5E55BDF79028F82F83D8E,
+ 25018C91F79D918FEA084630,
95577AE91AA6CBA7FE9434F3,
21BA256CBCC9C15265928A23,
9D2D1BA65C27BDA1F7C44769,
diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj
index 66d5e407ef..a8f383abfc 100644
--- a/Builds/VisualStudio2005/Juce.vcproj
+++ b/Builds/VisualStudio2005/Juce.vcproj
@@ -350,6 +350,8 @@
+
+
diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj
index a3a4c64c3d..e2ed8bc41e 100644
--- a/Builds/VisualStudio2008/Juce.vcproj
+++ b/Builds/VisualStudio2008/Juce.vcproj
@@ -350,6 +350,8 @@
+
+
diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj
index c7b24936b9..9742652310 100644
--- a/Builds/VisualStudio2008_DLL/Juce.vcproj
+++ b/Builds/VisualStudio2008_DLL/Juce.vcproj
@@ -352,6 +352,8 @@
+
+
diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj
index 5f2053d587..599b605302 100644
--- a/Builds/VisualStudio2010/Juce.vcxproj
+++ b/Builds/VisualStudio2010/Juce.vcxproj
@@ -177,6 +177,7 @@
+
@@ -502,6 +503,7 @@
+
diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters
index d25834c473..91293ea6cf 100644
--- a/Builds/VisualStudio2010/Juce.vcxproj.filters
+++ b/Builds/VisualStudio2010/Juce.vcxproj.filters
@@ -379,6 +379,9 @@
Juce\Source\containers
+
+ Juce\Source\containers
+
Juce\Source\containers
@@ -1428,6 +1431,9 @@
Juce\Source\containers
+
+ Juce\Source\containers
+
Juce\Source\containers
diff --git a/Builds/iPhone/Juce.xcodeproj/project.pbxproj b/Builds/iPhone/Juce.xcodeproj/project.pbxproj
index dbc4cd2a9f..17dcffd6b5 100644
--- a/Builds/iPhone/Juce.xcodeproj/project.pbxproj
+++ b/Builds/iPhone/Juce.xcodeproj/project.pbxproj
@@ -63,6 +63,7 @@
E8DFABC1603D55B97429A8E4 = { isa = PBXBuildFile; fileRef = 35668D8EEA19957C6C9AC83A; };
BE25871C34D79FEFFD1B94B6 = { isa = PBXBuildFile; fileRef = 895D742F49DA9F100990879C; };
4AB5E55BDF79028F82F83D8E = { isa = PBXBuildFile; fileRef = F77C9170829579FABA5679AD; };
+ 25018C91F79D918FEA084630 = { isa = PBXBuildFile; fileRef = 199DFD1C5A282FE13A585FEA; };
95577AE91AA6CBA7FE9434F3 = { isa = PBXBuildFile; fileRef = 1CF7CC0EB057F995BBBEFC90; };
21BA256CBCC9C15265928A23 = { isa = PBXBuildFile; fileRef = FF40DA899AE16A5E1D8AA54A; };
9D2D1BA65C27BDA1F7C44769 = { isa = PBXBuildFile; fileRef = 70E5409425A76782B6188B31; };
@@ -481,6 +482,8 @@
F77C9170829579FABA5679AD = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DynamicObject.cpp; path = ../../src/containers/juce_DynamicObject.cpp; sourceTree = SOURCE_ROOT; };
34C402EF9ADCAD34FB657D43 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_DynamicObject.h; path = ../../src/containers/juce_DynamicObject.h; sourceTree = SOURCE_ROOT; };
7DA9AC75A4D9227C8FC4B2F7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ElementComparator.h; path = ../../src/containers/juce_ElementComparator.h; sourceTree = SOURCE_ROOT; };
+ 199DFD1C5A282FE13A585FEA = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Expression.cpp; path = ../../src/containers/juce_Expression.cpp; sourceTree = SOURCE_ROOT; };
+ 3C12A5E0EBBB0916C01CFC58 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Expression.h; path = ../../src/containers/juce_Expression.h; sourceTree = SOURCE_ROOT; };
F364AA2637B7CB89D3657DFF = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_HeapBlock.h; path = ../../src/containers/juce_HeapBlock.h; sourceTree = SOURCE_ROOT; };
1CF7CC0EB057F995BBBEFC90 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Identifier.cpp; path = ../../src/containers/juce_Identifier.cpp; sourceTree = SOURCE_ROOT; };
C16848F86DF014F1CBECE248 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Identifier.h; path = ../../src/containers/juce_Identifier.h; sourceTree = SOURCE_ROOT; };
@@ -1195,6 +1198,8 @@
F77C9170829579FABA5679AD,
34C402EF9ADCAD34FB657D43,
7DA9AC75A4D9227C8FC4B2F7,
+ 199DFD1C5A282FE13A585FEA,
+ 3C12A5E0EBBB0916C01CFC58,
F364AA2637B7CB89D3657DFF,
1CF7CC0EB057F995BBBEFC90,
C16848F86DF014F1CBECE248,
@@ -1952,6 +1957,7 @@
E8DFABC1603D55B97429A8E4,
BE25871C34D79FEFFD1B94B6,
4AB5E55BDF79028F82F83D8E,
+ 25018C91F79D918FEA084630,
95577AE91AA6CBA7FE9434F3,
21BA256CBCC9C15265928A23,
9D2D1BA65C27BDA1F7C44769,
diff --git a/Juce.jucer b/Juce.jucer
index 93b8a645eb..b36cfeba5d 100644
--- a/Juce.jucer
+++ b/Juce.jucer
@@ -359,6 +359,10 @@
file="src/containers/juce_DynamicObject.h"/>
+
+
getWidth());
- if (edge == RelativeCoordinate::Strings::bottom) return RelativeCoordinate ((double) parent->getHeight());
+ if (edge == RelativeCoordinate::Strings::right) return Expression ((double) parent->getWidth());
+ if (edge == RelativeCoordinate::Strings::bottom) return Expression ((double) parent->getHeight());
}
if (objectName.isNotEmpty() && edge.isNotEmpty())
@@ -349,10 +352,10 @@ const RelativeCoordinate RelativeRectangleLayoutManager::findNamedCoordinate (co
if (c->name == objectName)
{
- if (edge == RelativeCoordinate::Strings::left) return c->coords.left;
- if (edge == RelativeCoordinate::Strings::right) return c->coords.right;
- if (edge == RelativeCoordinate::Strings::top) return c->coords.top;
- if (edge == RelativeCoordinate::Strings::bottom) return c->coords.bottom;
+ if (edge == RelativeCoordinate::Strings::left) return c->coords.left.getTerm();
+ if (edge == RelativeCoordinate::Strings::right) return c->coords.right.getTerm();
+ if (edge == RelativeCoordinate::Strings::top) return c->coords.top.getTerm();
+ if (edge == RelativeCoordinate::Strings::bottom) return c->coords.bottom.getTerm();
}
}
}
@@ -362,10 +365,10 @@ const RelativeCoordinate RelativeRectangleLayoutManager::findNamedCoordinate (co
MarkerPosition* m = markers.getUnchecked(i);
if (m->markerName == objectName)
- return m->position;
+ return m->position.getTerm();
}
- return RelativeCoordinate();
+ return Expression();
}
void RelativeRectangleLayoutManager::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
diff --git a/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h b/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h
index b537343935..cd06f82402 100644
--- a/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h
+++ b/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h
@@ -142,7 +142,7 @@ private:
/**
*/
class RelativeRectangleLayoutManager : public ComponentListener,
- public RelativeCoordinate::NamedCoordinateFinder,
+ public Expression::EvaluationContext,
public AsyncUpdater
{
public:
@@ -169,7 +169,7 @@ public:
//==============================================================================
/** @internal */
- const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const;
+ const Expression getSymbolValue (const String& symbol) const;
/** @internal */
void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized);
/** @internal */
diff --git a/extras/audio plugins/demo/Source/PluginEditor.h b/extras/audio plugins/demo/Source/PluginEditor.h
index 28a1d5e039..3e260c0f98 100644
--- a/extras/audio plugins/demo/Source/PluginEditor.h
+++ b/extras/audio plugins/demo/Source/PluginEditor.h
@@ -20,7 +20,7 @@
/** This is the editor component that our filter will display.
*/
class JuceDemoPluginAudioProcessorEditor : public AudioProcessorEditor,
- public Slider::Listener,
+ public SliderListener,
public Timer
{
public:
diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp
index 9e0eb8ae92..26deab2d01 100644
--- a/juce_amalgamated.cpp
+++ b/juce_amalgamated.cpp
@@ -4593,6 +4593,911 @@ END_JUCE_NAMESPACE
/*** End of inlined file: juce_DynamicObject.cpp ***/
+/*** Start of inlined file: juce_Expression.cpp ***/
+BEGIN_JUCE_NAMESPACE
+
+class Expression::Helpers
+{
+public:
+
+ class Constant : public Term
+ {
+ public:
+ Constant (const double value_) : value (value_), isResolutionTarget (false) {}
+
+ Term* clone() const { return new Constant (value); }
+ double evaluate (const EvaluationContext&, int) const { return value; }
+ int getNumInputs() const { return 0; }
+ Term* getInput (int) const { return 0; }
+
+ const String toString() const
+ {
+ if (isResolutionTarget)
+ return "@" + String (value);
+
+ return String (value);
+ }
+
+ double value;
+ bool isResolutionTarget;
+ };
+
+ class Symbol : public Term
+ {
+ public:
+ Symbol (const String& symbol_) : symbol (symbol_) {}
+
+ double evaluate (const EvaluationContext& c, int recursionDepth) const
+ {
+ if (++recursionDepth > 256)
+ throw EvaluationError ("Recursive symbol references");
+
+ return c.getSymbolValue (symbol).term->evaluate (c, recursionDepth);
+ }
+
+ Term* clone() const { return new Symbol (symbol); }
+ int getNumInputs() const { return 0; }
+ Term* getInput (int) const { return 0; }
+ const String toString() const { return symbol; }
+
+ bool referencesSymbol (const String& s, const EvaluationContext& c, int recursionDepth) const
+ {
+ if (s == symbol)
+ return true;
+
+ if (++recursionDepth > 256)
+ throw EvaluationError ("Recursive symbol references");
+
+ return c.getSymbolValue (symbol).term->referencesSymbol (s, c, recursionDepth);
+ }
+
+ String symbol;
+ };
+
+ class Function : public Term
+ {
+ public:
+ Function (const String& functionName_, const ReferenceCountedArray& parameters_)
+ : functionName (functionName_), parameters (parameters_)
+ {}
+
+ Term* clone() const { return new Function (functionName, parameters); }
+
+ double evaluate (const EvaluationContext& c, int recursionDepth) const
+ {
+ HeapBlock params (parameters.size());
+ for (int i = 0; i < parameters.size(); ++i)
+ params[i] = parameters.getUnchecked(i)->evaluate (c, recursionDepth);
+
+ return c.evaluateFunction (functionName, params, parameters.size());
+ }
+
+ int getInputIndexFor (const Term* possibleInput) const { return parameters.indexOf (possibleInput); }
+ int getNumInputs() const { return parameters.size(); }
+ Term* getInput (int i) const { return parameters [i]; }
+
+ bool referencesSymbol (const String& s, const EvaluationContext& c, int recursionDepth) const
+ {
+ for (int i = 0; i < parameters.size(); ++i)
+ if (parameters.getUnchecked(i)->referencesSymbol (s, c, recursionDepth))
+ return true;
+
+ return false;
+ }
+
+ const String toString() const
+ {
+ if (parameters.size() == 0)
+ return functionName + "()";
+
+ String s (functionName + " (");
+
+ for (int i = 0; i < parameters.size(); ++i)
+ {
+ s << parameters.getUnchecked(i)->toString();
+
+ if (i < parameters.size() - 1)
+ s << ", ";
+ }
+
+ s << ')';
+ return s;
+ }
+
+ const String functionName;
+ ReferenceCountedArray parameters;
+ };
+
+ class Negate : public Term
+ {
+ public:
+ Negate (Term* const input_) : input (input_)
+ {
+ jassert (input_ != 0);
+ }
+
+ int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; }
+ int getNumInputs() const { return 1; }
+ Term* getInput (int index) const { return index == 0 ? static_cast (input) : 0; }
+ Term* clone() const { return new Negate (input->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return -input->evaluate (c, recursionDepth); }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext&, Term* input_, double overallTarget, Term* topLevelTerm) const
+ {
+ jassert (input_ == input);
+
+ const Term* const dest = findDestinationFor (topLevelTerm, this);
+ Term* newDest;
+ if (dest == 0)
+ newDest = new Constant (overallTarget);
+ else
+ newDest = dest->clone();
+
+ return new Negate (newDest);
+ }
+
+ const String toString() const
+ {
+ if (input->getOperatorPrecedence() > 0)
+ return "-(" + input->toString() + ")";
+ else
+ return "-" + input->toString();
+ }
+
+ bool referencesSymbol (const String& s, const EvaluationContext& c, int recursionDepth) const
+ {
+ return input->referencesSymbol (s, c, recursionDepth);
+ }
+
+ private:
+ const ReferenceCountedObjectPtr input;
+ };
+
+ class BinaryTerm : public Term
+ {
+ public:
+ BinaryTerm (Term* const left_, Term* const right_) : left (left_), right (right_)
+ {
+ jassert (left_ != 0 && right_ != 0);
+ }
+
+ int getInputIndexFor (const Term* possibleInput) const
+ {
+ return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1);
+ }
+
+ int getNumInputs() const { return 2; }
+ Term* getInput (int index) const { return index == 0 ? static_cast (left) : (index == 1 ? static_cast (right) : 0); }
+
+ bool referencesSymbol (const String& s, const EvaluationContext& c, int recursionDepth) const
+ {
+ return left->referencesSymbol (s, c, recursionDepth)
+ || right->referencesSymbol (s, c, recursionDepth);
+ }
+
+ protected:
+ const ReferenceCountedObjectPtr left, right;
+
+ const String createString (const String& op) const
+ {
+ String s;
+
+ const int ourPrecendence = getOperatorPrecedence();
+ if (left->getOperatorPrecedence() > ourPrecendence)
+ s << '(' << left->toString() << ')';
+ else
+ s = left->toString();
+
+ s << ' ' << op << ' ';
+
+ if (right->getOperatorPrecedence() >= ourPrecendence)
+ s << '(' << right->toString() << ')';
+ else
+ s << right->toString();
+
+ return s;
+ }
+
+ Term* createDestinationTerm (const EvaluationContext&, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ jassert (input == left || input == right);
+ if (input != left && input != right)
+ return 0;
+
+ const Term* const dest = findDestinationFor (topLevelTerm, this);
+
+ if (dest == 0)
+ return new Constant (overallTarget);
+
+ return dest->clone();
+ }
+ };
+
+ class Add : public BinaryTerm
+ {
+ public:
+ Add (Term* const left_, Term* const right_) : BinaryTerm (left_, right_) {}
+
+ Term* clone() const { return new Add (left->clone(), right->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return left->evaluate (c, recursionDepth) + right->evaluate (c, recursionDepth); }
+ const String toString() const { return createString ("+"); }
+ int getOperatorPrecedence() const { return 2; }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext& c, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ Term* const newDest = createDestinationTerm (c, input, overallTarget, topLevelTerm);
+ if (newDest == 0)
+ return 0;
+
+ return new Subtract (newDest, (input == left ? right : left)->clone());
+ }
+ };
+
+ class Subtract : public BinaryTerm
+ {
+ public:
+ Subtract (Term* const left_, Term* const right_) : BinaryTerm (left_, right_) {}
+
+ Term* clone() const { return new Subtract (left->clone(), right->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return left->evaluate (c, recursionDepth) - right->evaluate (c, recursionDepth); }
+ const String toString() const { return createString ("-"); }
+ int getOperatorPrecedence() const { return 2; }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext& c, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ Term* const newDest = createDestinationTerm (c, input, overallTarget, topLevelTerm);
+ if (newDest == 0)
+ return 0;
+
+ if (input == left)
+ return new Add (newDest, right->clone());
+ else
+ return new Subtract (left->clone(), newDest);
+ }
+ };
+
+ class Multiply : public BinaryTerm
+ {
+ public:
+ Multiply (Term* const left_, Term* const right_) : BinaryTerm (left_, right_) {}
+
+ Term* clone() const { return new Multiply (left->clone(), right->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return left->evaluate (c, recursionDepth) * right->evaluate (c, recursionDepth); }
+ const String toString() const { return createString ("*"); }
+ int getOperatorPrecedence() const { return 1; }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext& c, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ Term* const newDest = createDestinationTerm (c, input, overallTarget, topLevelTerm);
+ if (newDest == 0)
+ return 0;
+
+ return new Divide (newDest, (input == left ? right : left)->clone());
+ }
+ };
+
+ class Divide : public BinaryTerm
+ {
+ public:
+ Divide (Term* const left_, Term* const right_) : BinaryTerm (left_, right_) {}
+
+ Term* clone() const { return new Divide (left->clone(), right->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return left->evaluate (c, recursionDepth) / right->evaluate (c, recursionDepth); }
+ const String toString() const { return createString ("/"); }
+ int getOperatorPrecedence() const { return 1; }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext& c, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ Term* const newDest = createDestinationTerm (c, input, overallTarget, topLevelTerm);
+ if (newDest == 0)
+ return 0;
+
+ if (input == left)
+ return new Multiply (newDest, right->clone());
+ else
+ return new Divide (left->clone(), newDest);
+ }
+ };
+
+ static Term* findDestinationFor (Term* const topLevel, const Term* const inputTerm)
+ {
+ const int inputIndex = topLevel->getInputIndexFor (inputTerm);
+ if (inputIndex >= 0)
+ return topLevel;
+
+ for (int i = topLevel->getNumInputs(); --i >= 0;)
+ {
+ Term* t = findDestinationFor (topLevel->getInput (i), inputTerm);
+
+ if (t != 0)
+ return t;
+ }
+
+ return 0;
+ }
+
+ static Constant* findTermToAdjust (Term* const term, const bool mustBeFlagged)
+ {
+ Constant* c = dynamic_cast (term);
+ if (c != 0 && (c->isResolutionTarget || ! mustBeFlagged))
+ return c;
+
+ int i;
+ for (i = term->getNumInputs(); --i >= 0;)
+ {
+ Constant* c = dynamic_cast (term->getInput (i));
+ if (c != 0 && (c->isResolutionTarget || ! mustBeFlagged))
+ return c;
+ }
+
+ for (i = term->getNumInputs(); --i >= 0;)
+ {
+ Constant* c = findTermToAdjust (term->getInput (i), mustBeFlagged);
+ if (c != 0)
+ return c;
+ }
+
+ return 0;
+ }
+
+ static bool containsAnySymbols (const Term* const t)
+ {
+ if (dynamic_cast (t) != 0)
+ return true;
+
+ for (int i = t->getNumInputs(); --i >= 0;)
+ if (containsAnySymbols (t->getInput (i)))
+ return true;
+
+ return false;
+ }
+
+ static bool renameSymbol (Term* const t, const String& oldName, const String& newName)
+ {
+ Symbol* sym = dynamic_cast (t);
+
+ if (sym != 0)
+ {
+ if (sym->symbol == oldName)
+ {
+ sym->symbol = newName;
+ return true;
+ }
+ else if (sym->symbol.startsWith (oldName + "."))
+ {
+ sym->symbol = newName + "." + sym->symbol.substring (0, oldName.length() + 1);
+ return true;
+ }
+ }
+
+ bool anyChanged = false;
+
+ for (int i = t->getNumInputs(); --i >= 0;)
+ if (renameSymbol (t->getInput (i), oldName, newName))
+ anyChanged = true;
+
+ return anyChanged;
+ }
+
+ class Parser
+ {
+ public:
+
+ Parser (const String& stringToParse, int& textIndex_)
+ : textString (stringToParse), textIndex (textIndex_)
+ {
+ text = textString;
+ }
+
+ Term* readExpression()
+ {
+ ScopedPointer lhs (readMultiplyOrDivideExpression());
+
+ char opType;
+ while (lhs != 0 && readOperator ("+-", &opType))
+ {
+ Term* rhs = readMultiplyOrDivideExpression();
+
+ if (rhs == 0)
+ throw ParseError ("Expected expression after \"" + String::charToString (opType) + "\"");
+
+ if (opType == '+')
+ lhs = new Add (lhs.release(), rhs);
+ else
+ lhs = new Subtract (lhs.release(), rhs);
+ }
+
+ return lhs.release();
+ }
+
+ private:
+ const String textString;
+ const juce_wchar* text;
+ int& textIndex;
+
+ static inline bool isDecimalDigit (const juce_wchar c) throw()
+ {
+ return c >= '0' && c <= '9';
+ }
+
+ void skipWhitespace (int& i)
+ {
+ while (CharacterFunctions::isWhitespace (text [i]))
+ ++i;
+ }
+
+ bool readChar (const juce_wchar required)
+ {
+ if (text[textIndex] == required)
+ {
+ ++textIndex;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool readOperator (const char* ops, char* const opType = 0)
+ {
+ skipWhitespace (textIndex);
+
+ while (*ops != 0)
+ {
+ if (readChar (*ops))
+ {
+ if (opType != 0)
+ *opType = *ops;
+
+ return true;
+ }
+
+ ++ops;
+ }
+
+ return false;
+ }
+
+ bool readIdentifier (String& identifier)
+ {
+ skipWhitespace (textIndex);
+ int i = textIndex;
+
+ if (CharacterFunctions::isLetter (text[i]) || text[i] == '_')
+ {
+ ++i;
+
+ while (CharacterFunctions::isLetterOrDigit (text[i]) || text[i] == '_' || text[i] == '.')
+ ++i;
+ }
+
+ if (i > textIndex)
+ {
+ identifier = String (text + textIndex, i - textIndex);
+ textIndex = i;
+ return true;
+ }
+
+ return false;
+ }
+
+ Term* readNumber()
+ {
+ skipWhitespace (textIndex);
+ int i = textIndex;
+
+ const bool isResolutionTarget = (text[i] == '@');
+ if (isResolutionTarget)
+ {
+ ++i;
+ skipWhitespace (i);
+ }
+
+ int numDigits = 0;
+
+ while (isDecimalDigit (text[i]))
+ {
+ ++i;
+ ++numDigits;
+ }
+
+ const bool hasPoint = (text[i] == '.');
+
+ if (hasPoint)
+ {
+ ++i;
+
+ while (isDecimalDigit (text[i]))
+ {
+ ++i;
+ ++numDigits;
+ }
+ }
+
+ if (numDigits == 0)
+ return 0;
+
+ juce_wchar c = text[i];
+ const bool hasExponent = (c == 'e' || c == 'E');
+
+ if (hasExponent)
+ {
+ ++i;
+ c = text[i];
+ if (c == '+' || c == '-')
+ ++i;
+
+ int numExpDigits = 0;
+ while (isDecimalDigit (text[i]))
+ {
+ ++i;
+ ++numExpDigits;
+ }
+
+ if (numExpDigits == 0)
+ return 0;
+ }
+
+ Constant* t = new Constant (String (text + textIndex, i - textIndex).getDoubleValue());
+ t->isResolutionTarget = isResolutionTarget;
+ textIndex = i;
+ return t;
+ }
+
+ Term* readMultiplyOrDivideExpression()
+ {
+ ScopedPointer lhs (readUnaryExpression());
+
+ char opType;
+ while (lhs != 0 && readOperator ("*/", &opType))
+ {
+ Term* rhs = readUnaryExpression();
+
+ if (rhs == 0)
+ throw ParseError ("Expected expression after \"" + String::charToString (opType) + "\"");
+
+ if (opType == '*')
+ lhs = new Multiply (lhs.release(), rhs);
+ else
+ lhs = new Divide (lhs.release(), rhs);
+ }
+
+ return lhs.release();
+ }
+
+ Term* readUnaryExpression()
+ {
+ char opType;
+ if (readOperator ("+-", &opType))
+ {
+ Term* term = readUnaryExpression();
+
+ if (term == 0)
+ throw ParseError ("Expected expression after \"" + String::charToString (opType) + "\"");
+
+ if (opType == '-')
+ term = new Negate (term);
+
+ return term;
+ }
+
+ return readPrimaryExpression();
+ }
+
+ Term* readPrimaryExpression()
+ {
+ Term* e = readParenthesisedExpression();
+ if (e != 0)
+ return e;
+
+ e = readNumber();
+ if (e != 0)
+ return e;
+
+ String identifier;
+ if (readIdentifier (identifier))
+ {
+ if (readOperator ("(")) // method call...
+ {
+ Function* f = new Function (identifier, ReferenceCountedArray());
+ ScopedPointer func (f); // (can't use ScopedPointer in MSVC)
+
+ Term* param = readExpression();
+
+ if (param == 0)
+ {
+ if (readOperator (")"))
+ return func.release();
+
+ throw ParseError ("Expected parameters after \"" + identifier + " (\"");
+ }
+
+ f->parameters.add (param);
+
+ while (readOperator (","))
+ {
+ param = readExpression();
+
+ if (param == 0)
+ throw ParseError ("Expected expression after \",\"");
+
+ f->parameters.add (param);
+ }
+
+ if (readOperator (")"))
+ return func.release();
+
+ throw ParseError ("Expected \")\"");
+ }
+ else // just a symbol..
+ {
+ return new Symbol (identifier);
+ }
+ }
+
+ return 0;
+ }
+
+ Term* readParenthesisedExpression()
+ {
+ if (! readOperator ("("))
+ return 0;
+
+ ScopedPointer e (readExpression());
+ if (e == 0)
+ return 0;
+
+ if (! readOperator (")"))
+ e = 0;
+
+ return e.release();
+ }
+
+ Parser (const Parser&);
+ Parser& operator= (const Parser&);
+ };
+};
+
+Expression::Expression()
+ : term (new Expression::Helpers::Constant (0))
+{
+}
+
+Expression::~Expression()
+{
+}
+
+Expression::Expression (Term* const term_)
+ : term (term_)
+{
+ jassert (term != 0);
+}
+
+Expression::Expression (const double constant)
+ : term (new Expression::Helpers::Constant (constant))
+{
+}
+
+Expression::Expression (const Expression& other)
+ : term (other.term)
+{
+}
+
+Expression& Expression::operator= (const Expression& other)
+{
+ term = other.term;
+ return *this;
+}
+
+Expression::Expression (const String& stringToParse)
+{
+ int i = 0;
+ Helpers::Parser parser (stringToParse, i);
+ term = parser.readExpression();
+
+ if (term == 0)
+ term = new Helpers::Constant (0);
+}
+
+const Expression Expression::parse (const String& stringToParse, int& textIndexToStartFrom)
+{
+ Helpers::Parser parser (stringToParse, textIndexToStartFrom);
+ Term* term = parser.readExpression();
+
+ if (term != 0)
+ return Expression (term);
+
+ return Expression();
+}
+
+double Expression::evaluate() const
+{
+ return evaluate (Expression::EvaluationContext());
+}
+
+double Expression::evaluate (const Expression::EvaluationContext& context) const
+{
+ return term->evaluate (context, 0);
+}
+
+const Expression Expression::operator+ (const Expression& other) const
+{
+ return Expression (new Helpers::Add (term, other.term));
+}
+
+const Expression Expression::operator- (const Expression& other) const
+{
+ return Expression (new Helpers::Subtract (term, other.term));
+}
+
+const Expression Expression::operator* (const Expression& other) const
+{
+ return Expression (new Helpers::Multiply (term, other.term));
+}
+
+const Expression Expression::operator/ (const Expression& other) const
+{
+ return Expression (new Helpers::Divide (term, other.term));
+}
+
+const Expression Expression::operator-() const
+{
+ return Expression (new Helpers::Negate (term));
+}
+
+const String Expression::toString() const
+{
+ return term->toString();
+}
+
+const Expression Expression::symbol (const String& symbol)
+{
+ return Expression (new Helpers::Symbol (symbol));
+}
+
+const Expression Expression::function (const String& functionName, const Array& parameters)
+{
+ ReferenceCountedArray params;
+ for (int i = 0; i < parameters.size(); ++i)
+ params.add (parameters.getReference(i).term);
+
+ return Expression (new Helpers::Function (functionName, params));
+}
+
+const Expression Expression::adjustedToGiveNewResult (const double targetValue,
+ const Expression::EvaluationContext& context) const
+{
+ ScopedPointer newTerm (term->clone());
+
+ Helpers::Constant* termToAdjust = Helpers::findTermToAdjust (newTerm, true);
+
+ if (termToAdjust == 0)
+ termToAdjust = Helpers::findTermToAdjust (newTerm, false);
+
+ if (termToAdjust == 0)
+ {
+ newTerm = new Helpers::Add (newTerm.release(), new Helpers::Constant (0));
+ termToAdjust = Helpers::findTermToAdjust (newTerm, false);
+ }
+
+ jassert (termToAdjust != 0);
+
+ const Term* parent = Helpers::findDestinationFor (newTerm, termToAdjust);
+
+ if (parent == 0)
+ {
+ termToAdjust->value = targetValue;
+ }
+ else
+ {
+ const ReferenceCountedObjectPtr reverseTerm (parent->createTermToEvaluateInput (context, termToAdjust, targetValue, newTerm));
+
+ if (reverseTerm == 0)
+ return Expression();
+
+ termToAdjust->value = reverseTerm->evaluate (context, 0);
+ }
+
+ return Expression (newTerm.release());
+}
+
+const Expression Expression::withRenamedSymbol (const String& oldSymbol, const String& newSymbol) const
+{
+ jassert (newSymbol.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
+
+ Expression newExpression (term->clone());
+ Helpers::renameSymbol (newExpression.term, oldSymbol, newSymbol);
+ return newExpression;
+}
+
+bool Expression::referencesSymbol (const String& symbol, const EvaluationContext& context) const
+{
+ return term->referencesSymbol (symbol, context, 0);
+}
+
+bool Expression::usesAnySymbols() const
+{
+ return Helpers::containsAnySymbols (term);
+}
+
+int Expression::Term::getOperatorPrecedence() const
+{
+ return 0;
+}
+
+bool Expression::Term::referencesSymbol (const String&, const EvaluationContext&, int) const
+{
+ return false;
+}
+
+int Expression::Term::getInputIndexFor (const Term*) const
+{
+ return -1;
+}
+
+const ReferenceCountedObjectPtr Expression::Term::createTermToEvaluateInput (const EvaluationContext&, Term*, double, Term*) const
+{
+ jassertfalse;
+ return 0;
+}
+
+Expression::ParseError::ParseError (const String& message)
+ : description (message)
+{
+}
+
+Expression::EvaluationError::EvaluationError (const String& message)
+ : description (message)
+{
+}
+
+Expression::EvaluationContext::EvaluationContext() {}
+Expression::EvaluationContext::~EvaluationContext() {}
+
+const Expression Expression::EvaluationContext::getSymbolValue (const String& symbol) const
+{
+ throw EvaluationError ("Unknown symbol: \"" + symbol + "\"");
+}
+
+double Expression::EvaluationContext::evaluateFunction (const String& functionName, const double* parameters, int numParams) const
+{
+ if (numParams > 0)
+ {
+ if (functionName == "min")
+ {
+ double v = parameters[0];
+ for (int i = 1; i < numParams; ++i)
+ v = jmin (v, parameters[i]);
+
+ return v;
+ }
+ else if (functionName == "max")
+ {
+ double v = parameters[0];
+ for (int i = 1; i < numParams; ++i)
+ v = jmax (v, parameters[i]);
+
+ return v;
+ }
+ else if (numParams == 1)
+ {
+ if (functionName == "sin")
+ return sin (parameters[0]);
+ else if (functionName == "cos")
+ return cos (parameters[0]);
+ else if (functionName == "tan")
+ return tan (parameters[0]);
+ else if (functionName == "abs")
+ return std::abs (parameters[0]);
+ }
+ }
+
+ throw EvaluationError ("Unknown function: \"" + functionName + "\"");
+}
+
+END_JUCE_NAMESPACE
+/*** End of inlined file: juce_Expression.cpp ***/
+
+
/*** Start of inlined file: juce_BlowFish.cpp ***/
BEGIN_JUCE_NAMESPACE
@@ -13725,8 +14630,7 @@ void XmlDocument::readChildElements (XmlElement* parent)
++len;
}
- XmlElement* const e = new XmlElement ((int) 0);
- e->setText (String (inputStart, len));
+ XmlElement* const e = XmlElement::createTextElement (String (inputStart, len));
if (lastChildNode != 0)
lastChildNode->nextElement = e;
@@ -22522,7 +23426,7 @@ void AudioFormatReaderSource::setNextReadPosition (int newPosition)
nextPlayPos = newPosition;
}
-void AudioFormatReaderSource::setLooping (const bool shouldLoop) throw()
+void AudioFormatReaderSource::setLooping (bool shouldLoop)
{
looping = shouldLoop;
}
@@ -30919,6 +31823,8 @@ public:
{
if (viewComponent != 0)
{
+ log ("Closing AU GUI: " + plugin.getName());
+
CloseComponent (viewComponent);
viewComponent = 0;
}
@@ -30971,8 +31877,6 @@ private:
void removeView (HIViewRef)
{
- log ("Closing AU GUI: " + owner->plugin.getName());
-
owner->closeViewComponent();
}
@@ -50836,6 +51740,7 @@ public:
row = newRow;
isSelected = isNowSelected;
repaint();
+ deleteAllChildren();
}
if (row < owner.getNumRows())
@@ -50847,13 +51752,12 @@ public:
const TableHeaderComponent* const header = owner.getHeader();
const int numColumns = header->getNumColumns (true);
- int i;
columnsWithComponents.clear();
if (owner.getModel() != 0)
{
- for (i = 0; i < numColumns; ++i)
+ for (int i = 0; i < numColumns; ++i)
{
const int columnId = header->getColumnIdOfIndex (i, true);
@@ -50875,7 +51779,7 @@ public:
}
}
- for (i = getNumChildComponents(); --i >= 0;)
+ for (int i = getNumChildComponents(); --i >= 0;)
{
Component* const c = getChildComponent (i);
@@ -76458,6 +77362,9 @@ CallOutBox::CallOutBox (Component& contentComponent,
}
else
{
+ if (! JUCEApplication::isStandaloneApp())
+ setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level
+
updatePosition (componentToPointTo.getScreenBounds(),
componentToPointTo.getParentMonitorArea());
@@ -78765,164 +79672,14 @@ BEGIN_JUCE_NAMESPACE
namespace RelativeCoordinateHelpers
{
- static bool isOrigin (const String& name)
- {
- return name.isEmpty()
- || name == RelativeCoordinate::Strings::parentLeft
- || name == RelativeCoordinate::Strings::parentTop;
- }
-
- static const String getExtentAnchorName (const bool isHorizontal) throw()
- {
- return isHorizontal ? RelativeCoordinate::Strings::parentRight
- : RelativeCoordinate::Strings::parentBottom;
- }
-
- static const String getObjectName (const String& fullName)
- {
- return fullName.upToFirstOccurrenceOf (".", false, false);
- }
-
- static const String getEdgeName (const String& fullName)
- {
- return fullName.fromFirstOccurrenceOf (".", false, false);
- }
-
- static const RelativeCoordinate findCoordinate (const String& name, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
- {
- return nameFinder != 0 ? nameFinder->findNamedCoordinate (getObjectName (name), getEdgeName (name))
- : RelativeCoordinate();
- }
-
- struct RecursionException : public std::runtime_error
- {
- RecursionException() : std::runtime_error ("Recursive RelativeCoordinate expression")
- {
- }
- };
-
- static void skipWhitespace (const juce_wchar* const s, int& i)
+ static void skipComma (const juce_wchar* const s, int& i)
{
while (CharacterFunctions::isWhitespace (s[i]))
++i;
- }
- static void skipComma (const juce_wchar* const s, int& i)
- {
- skipWhitespace (s, i);
if (s[i] == ',')
++i;
}
-
- static const String readAnchorName (const juce_wchar* const s, int& i)
- {
- skipWhitespace (s, i);
-
- if (CharacterFunctions::isLetter (s[i]) || s[i] == '_')
- {
- int start = i;
- while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.')
- ++i;
-
- return String (s + start, i - start);
- }
-
- return String::empty;
- }
-
- static double readNumber (const juce_wchar* const s, int& i)
- {
- skipWhitespace (s, i);
-
- int start = i;
- if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-')
- ++i;
-
- while (CharacterFunctions::isDigit (s[i]) || s[i] == '.')
- ++i;
-
- if ((s[i] == 'e' || s[i] == 'E')
- && (CharacterFunctions::isDigit (s[i + 1])
- || s[i + 1] == '-'
- || s[i + 1] == '+'))
- {
- i += 2;
-
- while (CharacterFunctions::isDigit (s[i]))
- ++i;
- }
-
- const double value = String (s + start, i - start).getDoubleValue();
- while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',')
- ++i;
-
- return value;
- }
-
- static const RelativeCoordinate readNextCoordinate (const juce_wchar* const s, int& i, const bool isHorizontal)
- {
- String anchor1 (readAnchorName (s, i));
- double value = 0;
-
- if (anchor1.isNotEmpty())
- {
- skipWhitespace (s, i);
-
- if (s[i] == '+')
- value = readNumber (s, ++i);
- else if (s[i] == '-')
- value = -readNumber (s, ++i);
-
- return RelativeCoordinate (value, anchor1);
- }
- else
- {
- value = readNumber (s, i);
- skipWhitespace (s, i);
-
- if (s[i] == '%')
- {
- value /= 100.0;
- skipWhitespace (s, ++i);
- String anchor2;
-
- if (s[i] == '*')
- {
- anchor1 = readAnchorName (s, ++i);
-
- skipWhitespace (s, i);
-
- if (s[i] == '-' && s[i + 1] == '>')
- {
- i += 2;
- anchor2 = readAnchorName (s, i);
- }
- else
- {
- anchor2 = anchor1;
- anchor1 = String::empty;
- }
- }
- else
- {
- anchor1 = String::empty;
- anchor2 = getExtentAnchorName (isHorizontal);
- }
-
- return RelativeCoordinate (value, anchor1, anchor2);
- }
-
- return RelativeCoordinate (value);
- }
- }
-
- static const String limitedAccuracyString (const double n)
- {
- if (! (n < -0.001 || n > 0.001)) // to detect NaN and inf as well as for rounding
- return "0";
-
- return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd (".");
- }
}
const String RelativeCoordinate::Strings::parent ("parent");
@@ -78936,33 +79693,38 @@ const String RelativeCoordinate::Strings::parentRight ("parent.right");
const String RelativeCoordinate::Strings::parentBottom ("parent.bottom");
RelativeCoordinate::RelativeCoordinate()
- : value (0)
{
}
+RelativeCoordinate::RelativeCoordinate (const Expression& term_)
+ : term (term_)
+{
+}
+
+RelativeCoordinate::RelativeCoordinate (const RelativeCoordinate& other)
+ : term (other.term)
+{
+}
+
+RelativeCoordinate& RelativeCoordinate::operator= (const RelativeCoordinate& other)
+{
+ term = other.term;
+ return *this;
+}
+
RelativeCoordinate::RelativeCoordinate (const double absoluteDistanceFromOrigin)
- : value (absoluteDistanceFromOrigin)
+ : term (absoluteDistanceFromOrigin)
{
}
-RelativeCoordinate::RelativeCoordinate (const double absoluteDistance, const String& source)
- : anchor1 (source.trim()),
- value (absoluteDistance)
+RelativeCoordinate::RelativeCoordinate (const String& s)
{
-}
-
-RelativeCoordinate::RelativeCoordinate (const double relativeProportion, const String& pos1, const String& pos2)
- : anchor1 (pos1.trim()),
- anchor2 (pos2.trim()),
- value (relativeProportion)
-{
-}
-
-RelativeCoordinate::RelativeCoordinate (const String& s, const bool isHorizontal)
- : value (0)
-{
- int i = 0;
- *this = RelativeCoordinateHelpers::readNextCoordinate (s, i, isHorizontal);
+ try
+ {
+ term = Expression (s);
+ }
+ catch (...)
+ {}
}
RelativeCoordinate::~RelativeCoordinate()
@@ -78971,7 +79733,7 @@ RelativeCoordinate::~RelativeCoordinate()
bool RelativeCoordinate::operator== (const RelativeCoordinate& other) const throw()
{
- return value == other.value && anchor1 == other.anchor1 && anchor2 == other.anchor2;
+ return term.toString() == other.term.toString();
}
bool RelativeCoordinate::operator!= (const RelativeCoordinate& other) const throw()
@@ -78979,57 +79741,31 @@ bool RelativeCoordinate::operator!= (const RelativeCoordinate& other) const thro
return ! operator== (other);
}
-const RelativeCoordinate RelativeCoordinate::getAnchorCoordinate1() const
-{
- return RelativeCoordinate (0.0, anchor1);
-}
-
-const RelativeCoordinate RelativeCoordinate::getAnchorCoordinate2() const
-{
- return RelativeCoordinate (0.0, anchor2);
-}
-
-double RelativeCoordinate::resolveAnchor (const String& anchorName, const NamedCoordinateFinder* nameFinder, int recursionCounter)
-{
- if (RelativeCoordinateHelpers::isOrigin (anchorName))
- return 0.0;
-
- return RelativeCoordinateHelpers::findCoordinate (anchorName, nameFinder).resolve (nameFinder, recursionCounter + 1);
-}
-
-double RelativeCoordinate::resolve (const NamedCoordinateFinder* nameFinder, int recursionCounter) const
-{
- if (recursionCounter > 150)
- {
- jassertfalse
- throw RelativeCoordinateHelpers::RecursionException();
- }
-
- const double pos1 = resolveAnchor (anchor1, nameFinder, recursionCounter);
-
- return isProportional() ? pos1 + (resolveAnchor (anchor2, nameFinder, recursionCounter) - pos1) * value
- : pos1 + value;
-}
-
-double RelativeCoordinate::resolve (const NamedCoordinateFinder* nameFinder) const
+double RelativeCoordinate::resolve (const Expression::EvaluationContext* context) const
{
try
{
- return resolve (nameFinder, 0);
+ if (context != 0)
+ return term.evaluate (*context);
+ else
+ return term.evaluate();
}
- catch (RelativeCoordinateHelpers::RecursionException&)
+ catch (...)
{}
return 0.0;
}
-bool RelativeCoordinate::isRecursive (const NamedCoordinateFinder* nameFinder) const
+bool RelativeCoordinate::isRecursive (const Expression::EvaluationContext* context) const
{
try
{
- (void) resolve (nameFinder, 0);
+ if (context != 0)
+ term.evaluate (*context);
+ else
+ term.evaluate();
}
- catch (RelativeCoordinateHelpers::RecursionException&)
+ catch (...)
{
return true;
}
@@ -79037,150 +79773,61 @@ bool RelativeCoordinate::isRecursive (const NamedCoordinateFinder* nameFinder) c
return false;
}
-void RelativeCoordinate::moveToAbsolute (double newPos, const NamedCoordinateFinder* nameFinder)
+void RelativeCoordinate::moveToAbsolute (double newPos, const Expression::EvaluationContext* context)
{
try
{
- const double pos1 = resolveAnchor (anchor1, nameFinder, 0);
-
- if (isProportional())
+ if (context != 0)
{
- const double size = resolveAnchor (anchor2, nameFinder, 0) - pos1;
-
- if (size != 0)
- value = (newPos - pos1) / size;
+ term = term.adjustedToGiveNewResult (newPos, *context);
}
else
{
- value = newPos - pos1;
+ Expression::EvaluationContext defaultContext;
+ term = term.adjustedToGiveNewResult (newPos, defaultContext);
}
}
- catch (RelativeCoordinateHelpers::RecursionException&)
+ catch (...)
{}
}
-void RelativeCoordinate::toggleProportionality (const NamedCoordinateFinder* nameFinder,
- const String& proportionalAnchor1, const String& proportionalAnchor2)
+bool RelativeCoordinate::references (const String& coordName, const Expression::EvaluationContext* context) const
{
- const double oldValue = resolve (nameFinder);
+ try
+ {
+ if (context != 0)
+ return term.referencesSymbol (coordName, *context);
- anchor1 = proportionalAnchor1;
- anchor2 = isProportional() ? String::empty : proportionalAnchor2;
+ Expression::EvaluationContext defaultContext;
+ return term.referencesSymbol (coordName, defaultContext);
+ }
+ catch (...)
+ {}
- moveToAbsolute (oldValue, nameFinder);
-}
-
-bool RelativeCoordinate::references (const String& coordName, const NamedCoordinateFinder* nameFinder) const
-{
- using namespace RelativeCoordinateHelpers;
-
- if (isOrigin (anchor1) && ! isProportional())
- return isOrigin (coordName);
-
- return anchor1 == coordName
- || anchor2 == coordName
- || findCoordinate (anchor1, nameFinder).references (coordName, nameFinder)
- || (isProportional() && findCoordinate (anchor2, nameFinder).references (coordName, nameFinder));
+ return false;
}
bool RelativeCoordinate::isDynamic() const
{
- return anchor2.isNotEmpty() || ! RelativeCoordinateHelpers::isOrigin (anchor1);
+ return term.usesAnySymbols();
}
const String RelativeCoordinate::toString() const
{
- using namespace RelativeCoordinateHelpers;
+ return term.toString();
+}
- if (isProportional())
+void RelativeCoordinate::renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* context)
+{
+ jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
+
+ if (term.referencesSymbol (oldName, *context))
{
- const String percent (limitedAccuracyString (value * 100.0));
+ const double oldValue = resolve (context);
- if (isOrigin (anchor1))
- {
- if (anchor2 == Strings::parentRight || anchor2 == Strings::parentBottom)
- return percent + "%";
- else
- return percent + "% * " + anchor2;
- }
- else
- return percent + "% * " + anchor1 + " -> " + anchor2;
- }
- else
- {
- if (isOrigin (anchor1))
- return limitedAccuracyString (value);
- else if (value > 0)
- return anchor1 + " + " + limitedAccuracyString (value);
- else if (value < 0)
- return anchor1 + " - " + limitedAccuracyString (-value);
- else
- return anchor1;
- }
-}
-
-const double RelativeCoordinate::getEditableNumber() const
-{
- return isProportional() ? value * 100.0 : value;
-}
-
-void RelativeCoordinate::setEditableNumber (const double newValue)
-{
- value = isProportional() ? newValue / 100.0 : newValue;
-}
-
-const String RelativeCoordinate::getAnchorName1 (const String& returnValueIfOrigin) const
-{
- return RelativeCoordinateHelpers::isOrigin (anchor1) ? returnValueIfOrigin : anchor1;
-}
-
-const String RelativeCoordinate::getAnchorName2 (const String& returnValueIfOrigin) const
-{
- return RelativeCoordinateHelpers::isOrigin (anchor2) ? returnValueIfOrigin : anchor2;
-}
-
-void RelativeCoordinate::changeAnchor1 (const String& newAnchorName, const NamedCoordinateFinder* nameFinder)
-{
- jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
-
- const double oldValue = resolve (nameFinder);
- anchor1 = RelativeCoordinateHelpers::isOrigin (newAnchorName) ? String::empty : newAnchorName;
- moveToAbsolute (oldValue, nameFinder);
-}
-
-void RelativeCoordinate::changeAnchor2 (const String& newAnchorName, const NamedCoordinateFinder* nameFinder)
-{
- jassert (isProportional());
- jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
-
- const double oldValue = resolve (nameFinder);
- anchor2 = RelativeCoordinateHelpers::isOrigin (newAnchorName) ? String::empty : newAnchorName;
- moveToAbsolute (oldValue, nameFinder);
-}
-
-void RelativeCoordinate::renameAnchorIfUsed (const String& oldName, const String& newName, const NamedCoordinateFinder* nameFinder)
-{
- using namespace RelativeCoordinateHelpers;
- jassert (oldName.isNotEmpty());
- jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_"));
-
- if (newName.isEmpty())
- {
- if (getObjectName (anchor1) == oldName
- || getObjectName (anchor2) == oldName)
- {
- value = resolve (nameFinder);
- anchor1 = String::empty;
- anchor2 = String::empty;
- }
- }
- else
- {
- if (getObjectName (anchor1) == oldName)
- anchor1 = newName + "." + getEdgeName (anchor1);
-
- if (getObjectName (anchor2) == oldName)
- anchor2 = newName + "." + getEdgeName (anchor2);
+ term = term.withRenamedSymbol (oldName, newName);
+ moveToAbsolute (oldValue, context);
}
}
@@ -79206,9 +79853,9 @@ RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordi
RelativePoint::RelativePoint (const String& s)
{
int i = 0;
- x = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
+ x = RelativeCoordinate (Expression::parse (s, i));
RelativeCoordinateHelpers::skipComma (s, i);
- y = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
+ y = RelativeCoordinate (Expression::parse (s, i));
}
bool RelativePoint::operator== (const RelativePoint& other) const throw()
@@ -79221,16 +79868,16 @@ bool RelativePoint::operator!= (const RelativePoint& other) const throw()
return ! operator== (other);
}
-const Point RelativePoint::resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
+const Point RelativePoint::resolve (const Expression::EvaluationContext* context) const
{
- return Point ((float) x.resolve (nameFinder),
- (float) y.resolve (nameFinder));
+ return Point ((float) x.resolve (context),
+ (float) y.resolve (context));
}
-void RelativePoint::moveToAbsolute (const Point& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
+void RelativePoint::moveToAbsolute (const Point& newPos, const Expression::EvaluationContext* context)
{
- x.moveToAbsolute (newPos.getX(), nameFinder);
- y.moveToAbsolute (newPos.getY(), nameFinder);
+ x.moveToAbsolute (newPos.getX(), context);
+ y.moveToAbsolute (newPos.getY(), context);
}
const String RelativePoint::toString() const
@@ -79238,10 +79885,10 @@ const String RelativePoint::toString() const
return x.toString() + ", " + y.toString();
}
-void RelativePoint::renameAnchorIfUsed (const String& oldName, const String& newName, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
+void RelativePoint::renameSymbolIfUsed (const String& oldName, const String& newName, const Expression::EvaluationContext* context)
{
- x.renameAnchorIfUsed (oldName, newName, nameFinder);
- y.renameAnchorIfUsed (oldName, newName, nameFinder);
+ x.renameSymbolIfUsed (oldName, newName, context);
+ y.renameSymbolIfUsed (oldName, newName, context);
}
bool RelativePoint::isDynamic() const
@@ -79261,22 +79908,22 @@ RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const Rel
RelativeRectangle::RelativeRectangle (const Rectangle& rect, const String& componentName)
: left (rect.getX()),
- right (rect.getWidth(), componentName + "." + RelativeCoordinate::Strings::left),
+ right (componentName + "." + RelativeCoordinate::Strings::left + " + " + String (rect.getWidth())),
top (rect.getY()),
- bottom (rect.getHeight(), componentName + "." + RelativeCoordinate::Strings::top)
+ bottom (componentName + "." + RelativeCoordinate::Strings::top + " + " + String (rect.getHeight()))
{
}
RelativeRectangle::RelativeRectangle (const String& s)
{
int i = 0;
- left = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
+ left = RelativeCoordinate (Expression::parse (s, i));
RelativeCoordinateHelpers::skipComma (s, i);
- top = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
+ top = RelativeCoordinate (Expression::parse (s, i));
RelativeCoordinateHelpers::skipComma (s, i);
- right = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
+ right = RelativeCoordinate (Expression::parse (s, i));
RelativeCoordinateHelpers::skipComma (s, i);
- bottom = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
+ bottom = RelativeCoordinate (Expression::parse (s, i));
}
bool RelativeRectangle::operator== (const RelativeRectangle& other) const throw()
@@ -79289,22 +79936,22 @@ bool RelativeRectangle::operator!= (const RelativeRectangle& other) const throw(
return ! operator== (other);
}
-const Rectangle RelativeRectangle::resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
+const Rectangle RelativeRectangle::resolve (const Expression::EvaluationContext* context) const
{
- const double l = left.resolve (nameFinder);
- const double r = right.resolve (nameFinder);
- const double t = top.resolve (nameFinder);
- const double b = bottom.resolve (nameFinder);
+ const double l = left.resolve (context);
+ const double r = right.resolve (context);
+ const double t = top.resolve (context);
+ const double b = bottom.resolve (context);
return Rectangle ((float) l, (float) t, (float) (r - l), (float) (b - t));
}
-void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
+void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* context)
{
- left.moveToAbsolute (newPos.getX(), nameFinder);
- right.moveToAbsolute (newPos.getRight(), nameFinder);
- top.moveToAbsolute (newPos.getY(), nameFinder);
- bottom.moveToAbsolute (newPos.getBottom(), nameFinder);
+ left.moveToAbsolute (newPos.getX(), context);
+ right.moveToAbsolute (newPos.getRight(), context);
+ top.moveToAbsolute (newPos.getY(), context);
+ bottom.moveToAbsolute (newPos.getBottom(), context);
}
const String RelativeRectangle::toString() const
@@ -79312,13 +79959,13 @@ const String RelativeRectangle::toString() const
return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
}
-void RelativeRectangle::renameAnchorIfUsed (const String& oldName, const String& newName,
- const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
+void RelativeRectangle::renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* context)
{
- left.renameAnchorIfUsed (oldName, newName, nameFinder);
- right.renameAnchorIfUsed (oldName, newName, nameFinder);
- top.renameAnchorIfUsed (oldName, newName, nameFinder);
- bottom.renameAnchorIfUsed (oldName, newName, nameFinder);
+ left.renameSymbolIfUsed (oldName, newName, context);
+ right.renameSymbolIfUsed (oldName, newName, context);
+ top.renameSymbolIfUsed (oldName, newName, context);
+ bottom.renameSymbolIfUsed (oldName, newName, context);
}
RelativePointPath::RelativePointPath()
@@ -79421,7 +80068,7 @@ void RelativePointPath::swapWith (RelativePointPath& other) throw()
swapVariables (usesNonZeroWinding, other.usesNonZeroWinding);
}
-void RelativePointPath::createPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder)
+void RelativePointPath::createPath (Path& path, Expression::EvaluationContext* coordFinder)
{
for (int i = 0; i < elements.size(); ++i)
elements.getUnchecked(i)->addToPath (path, coordFinder);
@@ -79448,7 +80095,7 @@ const ValueTree RelativePointPath::StartSubPath::createTree() const
return v;
}
-void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
+void RelativePointPath::StartSubPath::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const
{
path.startNewSubPath (startPos.resolve (coordFinder));
}
@@ -79469,7 +80116,7 @@ const ValueTree RelativePointPath::CloseSubPath::createTree() const
return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement);
}
-void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const
+void RelativePointPath::CloseSubPath::addToPath (Path& path, Expression::EvaluationContext*) const
{
path.closeSubPath();
}
@@ -79492,7 +80139,7 @@ const ValueTree RelativePointPath::LineTo::createTree() const
return v;
}
-void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
+void RelativePointPath::LineTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const
{
path.lineTo (endPoint.resolve (coordFinder));
}
@@ -79518,7 +80165,7 @@ const ValueTree RelativePointPath::QuadraticTo::createTree() const
return v;
}
-void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
+void RelativePointPath::QuadraticTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const
{
path.quadraticTo (controlPoints[0].resolve (coordFinder),
controlPoints[1].resolve (coordFinder));
@@ -79547,7 +80194,7 @@ const ValueTree RelativePointPath::CubicTo::createTree() const
return v;
}
-void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
+void RelativePointPath::CubicTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const
{
path.cubicTo (controlPoints[0].resolve (coordFinder),
controlPoints[1].resolve (coordFinder),
@@ -79578,27 +80225,27 @@ RelativeParallelogram::~RelativeParallelogram()
{
}
-void RelativeParallelogram::resolveThreePoints (Point* points, RelativeCoordinate::NamedCoordinateFinder* const coordFinder) const
+void RelativeParallelogram::resolveThreePoints (Point* points, Expression::EvaluationContext* const coordFinder) const
{
points[0] = topLeft.resolve (coordFinder);
points[1] = topRight.resolve (coordFinder);
points[2] = bottomLeft.resolve (coordFinder);
}
-void RelativeParallelogram::resolveFourCorners (Point* points, RelativeCoordinate::NamedCoordinateFinder* const coordFinder) const
+void RelativeParallelogram::resolveFourCorners (Point* points, Expression::EvaluationContext* const coordFinder) const
{
resolveThreePoints (points, coordFinder);
points[3] = points[1] + (points[2] - points[0]);
}
-const Rectangle RelativeParallelogram::getBounds (RelativeCoordinate::NamedCoordinateFinder* const coordFinder) const
+const Rectangle RelativeParallelogram::getBounds (Expression::EvaluationContext* const coordFinder) const
{
Point points[4];
resolveFourCorners (points, coordFinder);
return Rectangle::findAreaContainingPoints (points, 4);
}
-void RelativeParallelogram::getPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* const coordFinder) const
+void RelativeParallelogram::getPath (Path& path, Expression::EvaluationContext* const coordFinder) const
{
Point points[4];
resolveFourCorners (points, coordFinder);
@@ -79610,7 +80257,7 @@ void RelativeParallelogram::getPath (Path& path, RelativeCoordinate::NamedCoordi
path.closeSubPath();
}
-const AffineTransform RelativeParallelogram::resetToPerpendicular (RelativeCoordinate::NamedCoordinateFinder* const coordFinder)
+const AffineTransform RelativeParallelogram::resetToPerpendicular (Expression::EvaluationContext* const coordFinder)
{
Point corners[3];
resolveThreePoints (corners, coordFinder);
@@ -85204,7 +85851,7 @@ void Drawable::ValueTreeWrapperBase::setID (const String& newID, UndoManager* co
}
const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v, RelativePoint* const gp1, RelativePoint* const gp2, RelativePoint* const gp3,
- RelativeCoordinate::NamedCoordinateFinder* const nameFinder, ImageProvider* imageProvider)
+ Expression::EvaluationContext* const nameFinder, ImageProvider* imageProvider)
{
const String newType (v[type].toString());
@@ -85532,33 +86179,26 @@ void DrawableComposite::render (const Drawable::RenderingContext& context) const
}
}
-const RelativeCoordinate DrawableComposite::findNamedCoordinate (const String& objectName, const String& edge) const
+const Expression DrawableComposite::getSymbolValue (const String& symbol) const
{
- if (objectName == RelativeCoordinate::Strings::parent)
- {
- if (edge == RelativeCoordinate::Strings::right || edge == RelativeCoordinate::Strings::bottom)
- {
- jassertfalse; // a Drawable doesn't have a fixed right-hand or bottom edge - use a marker instead if you need a point of reference.
- return RelativeCoordinate (100.0);
- }
- }
+ jassert (! symbol.containsChar ('.')) // the only symbols available in a Drawable are markers.
int i;
for (i = 0; i < markersX.size(); ++i)
{
Marker* const m = markersX.getUnchecked(i);
- if (m->name == objectName)
- return m->position;
+ if (m->name == symbol)
+ return m->position.getExpression();
}
for (i = 0; i < markersY.size(); ++i)
{
Marker* const m = markersY.getUnchecked(i);
- if (m->name == objectName)
- return m->position;
+ if (m->name == symbol)
+ return m->position.getExpression();
}
- return RelativeCoordinate();
+ return Expression::EvaluationContext::getSymbolValue (symbol);
}
const Rectangle DrawableComposite::getUntransformedBounds (const bool includeMarkers) const
@@ -85818,7 +86458,7 @@ const DrawableComposite::Marker DrawableComposite::ValueTreeWrapper::getMarker (
{
jassert (containsMarker (xAxis, state));
- return Marker (state [nameProperty], RelativeCoordinate (state [posProperty].toString(), xAxis));
+ return Marker (state [nameProperty], RelativeCoordinate (state [posProperty].toString()));
}
void DrawableComposite::ValueTreeWrapper::setMarker (bool xAxis, const Marker& m, UndoManager* undoManager)
@@ -86374,6 +87014,8 @@ void DrawablePath::render (const Drawable::RenderingContext& context) const
FillType f (mainFill);
if (f.isGradient())
f.gradient->multiplyOpacity (context.opacity);
+ else
+ f.setOpacity (f.getOpacity() * context.opacity);
f.transform = f.transform.followedBy (context.transform);
context.g.setFillType (f);
@@ -86385,6 +87027,8 @@ void DrawablePath::render (const Drawable::RenderingContext& context) const
FillType f (strokeFill);
if (f.isGradient())
f.gradient->multiplyOpacity (context.opacity);
+ else
+ f.setOpacity (f.getOpacity() * context.opacity);
f.transform = f.transform.followedBy (context.transform);
context.g.setFillType (f);
@@ -86455,7 +87099,7 @@ ValueTree DrawablePath::ValueTreeWrapper::getStrokeFillState()
return getStrokeFillState();
}
-const FillType DrawablePath::ValueTreeWrapper::getMainFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+const FillType DrawablePath::ValueTreeWrapper::getMainFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const
{
return readFillType (state.getChildWithName (fill), 0, 0, 0, nameFinder, imageProvider);
@@ -86469,7 +87113,7 @@ void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const
writeFillType (v, newFill, gp1, gp2, gp3, imageProvider, undoManager);
}
-const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const
{
return readFillType (state.getChildWithName (stroke), 0, 0, 0, nameFinder, imageProvider);
@@ -86596,7 +87240,7 @@ const RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const
return RelativePoint();
}
-float DrawablePath::ValueTreeWrapper::Element::getLength (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
+float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::EvaluationContext* nameFinder) const
{
const Identifier i (state.getType());
@@ -86647,7 +87291,7 @@ void DrawablePath::ValueTreeWrapper::Element::convertToLine (UndoManager* undoMa
}
}
-void DrawablePath::ValueTreeWrapper::Element::convertToCubic (RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager)
+void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::EvaluationContext* nameFinder, UndoManager* undoManager)
{
const Identifier i (state.getType());
@@ -86701,7 +87345,7 @@ static const Point findQuadraticSubdivisionPoint (float proportion, const
return mid1 + (mid2 - mid1) * proportion;
}
-float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
+float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, Expression::EvaluationContext* nameFinder) const
{
const Identifier i (state.getType());
float bestProp = 0;
@@ -86757,7 +87401,7 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po
return bestProp;
}
-ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager)
+ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, Expression::EvaluationContext* nameFinder, UndoManager* undoManager)
{
ValueTree newTree;
const Identifier i (state.getType());
@@ -266701,8 +267345,6 @@ public:
[renderContext setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
[view setOpenGLContext: renderContext];
- [renderContext setView: view];
-
[format release];
viewHolder = new NSViewComponentInternal (view, component);
@@ -266726,6 +267368,10 @@ public:
bool makeActive() const throw()
{
jassert (renderContext != 0);
+
+ if ([renderContext view] != view)
+ [renderContext setView: view];
+
[view makeActive];
return isActive();
}
@@ -272281,8 +272927,6 @@ public:
[renderContext setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
[view setOpenGLContext: renderContext];
- [renderContext setView: view];
-
[format release];
viewHolder = new NSViewComponentInternal (view, component);
@@ -272306,6 +272950,10 @@ public:
bool makeActive() const throw()
{
jassert (renderContext != 0);
+
+ if ([renderContext view] != view)
+ [renderContext setView: view];
+
[view makeActive];
return isActive();
}
diff --git a/juce_amalgamated.h b/juce_amalgamated.h
index 46c3d2939f..534b5f1146 100644
--- a/juce_amalgamated.h
+++ b/juce_amalgamated.h
@@ -64,7 +64,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
-#define JUCE_BUILDNUMBER 49
+#define JUCE_BUILDNUMBER 50
/** Current Juce version number.
@@ -536,7 +536,7 @@
@see Logger::outputDebugString
*/
- #define DBG(dbgtext) { String tempDbgBuf; tempDbgBuf << dbgtext; JUCE_NAMESPACE::Logger::outputDebugString (tempDbgBuf); }
+ #define DBG(dbgtext) { JUCE_NAMESPACE::String tempDbgBuf; tempDbgBuf << dbgtext; JUCE_NAMESPACE::Logger::outputDebugString (tempDbgBuf); }
// Assertions..
@@ -6370,23 +6370,11 @@ private:
#ifndef __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__
#endif
-#ifndef __JUCE_HEAPBLOCK_JUCEHEADER__
+#ifndef __JUCE_EXPRESSION_JUCEHEADER__
-#endif
-#ifndef __JUCE_IDENTIFIER_JUCEHEADER__
-
-#endif
-#ifndef __JUCE_MEMORYBLOCK_JUCEHEADER__
-
-#endif
-#ifndef __JUCE_NAMEDVALUESET_JUCEHEADER__
-
-#endif
-#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__
-
-/*** Start of inlined file: juce_OwnedArray.h ***/
-#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__
-#define __JUCE_OWNEDARRAY_JUCEHEADER__
+/*** Start of inlined file: juce_Expression.h ***/
+#ifndef __JUCE_EXPRESSION_JUCEHEADER__
+#define __JUCE_EXPRESSION_JUCEHEADER__
/*** Start of inlined file: juce_ScopedPointer.h ***/
@@ -6562,6 +6550,206 @@ inline bool operator!= (const ScopedPointer& pointer1, const ObjectT
#endif // __JUCE_SCOPEDPOINTER_JUCEHEADER__
/*** End of inlined file: juce_ScopedPointer.h ***/
+/**
+ A class for dynamically evaluating simple numeric expressions.
+
+ This class can parse a simple C-style string expression involving floating point
+ numbers, named symbols and functions. The basic arithmetic operations of +, -, *, /
+ are supported, as well as parentheses, and any alphanumeric identifiers are
+ assumed to be named symbols which will be resolved when the expression is
+ evaluated.
+
+ Expressions which use identifiers and functions require a subclass of
+ Expression::EvaluationContext to be supplied when evaluating them, and this object
+ is expected to be able to resolve the symbol names and perform the functions that
+ are used.
+*/
+class JUCE_API Expression
+{
+public:
+
+ /** Creates a simple expression with a value of 0. */
+ Expression();
+
+ /** Destructor. */
+ ~Expression();
+
+ /** Creates a simple expression with a specified constant value. */
+ explicit Expression (const double constant);
+
+ /** Creates a copy of an expression. */
+ Expression (const Expression& other);
+
+ /** Copies another expression. */
+ Expression& operator= (const Expression& other);
+
+ /** Creates an expression by parsing a string.
+ If there's a syntax error in the string, this will throw a ParseError exception.
+ @throws ParseError
+ */
+ explicit Expression (const String& stringToParse);
+
+ /** Returns a string version of the expression. */
+ const String toString() const;
+
+ /** Returns an expression which is an addtion operation of two existing expressions. */
+ const Expression operator+ (const Expression& other) const;
+ /** Returns an expression which is a subtraction operation of two existing expressions. */
+ const Expression operator- (const Expression& other) const;
+ /** Returns an expression which is a multiplication operation of two existing expressions. */
+ const Expression operator* (const Expression& other) const;
+ /** Returns an expression which is a division operation of two existing expressions. */
+ const Expression operator/ (const Expression& other) const;
+ /** Returns an expression which is a negation operation of two existing expressions. */
+ const Expression operator-() const;
+
+ /** Returns an Expression which is an identifier reference. */
+ static const Expression symbol (const String& symbol);
+
+ /** Returns an Expression which is a function call. */
+ static const Expression function (const String& functionName, const Array& parameters);
+
+ /** Returns an Expression which parses a string from a specified character index.
+
+ The index value is incremented so that on return, it indicates the character that follows
+ the end of the expression that was parsed.
+
+ If there's a syntax error in the string, this will throw a ParseError exception.
+ @throws ParseError
+ */
+ static const Expression parse (const String& stringToParse, int& textIndexToStartFrom);
+
+ /** When evaluating an Expression object, this class is used to resolve symbols and
+ perform functions that the expression uses.
+ */
+ class EvaluationContext
+ {
+ public:
+ EvaluationContext();
+ virtual ~EvaluationContext();
+
+ /** Returns the value of a symbol.
+ If the symbol is unknown, this can throw an Expression::EvaluationError exception.
+ @throws Expression::EvaluationError
+ */
+ virtual const Expression getSymbolValue (const String& symbol) const;
+
+ /** Executes a named function.
+ If the function name is unknown, this can throw an Expression::EvaluationError exception.
+ @throws Expression::EvaluationError
+ */
+ virtual double evaluateFunction (const String& functionName, const double* parameters, int numParams) const;
+ };
+
+ /** Evaluates this expression, without using an EvaluationContext.
+ Without an EvaluationContext, no symbols can be used, and only basic functions such as sin, cos, tan,
+ min, max are available.
+ */
+ double evaluate() const;
+
+ /** Evaluates this expression, providing a context that should be able to evaluate any symbols
+ or functions that it uses.
+ */
+ double evaluate (const EvaluationContext& context) const;
+
+ /** Attempts to return an expression which is a copy of this one, but with a constant adjusted
+ to make the expression resolve to a target value.
+
+ E.g. if the expression is "x + 10" and x is 5, then asking for a target value of 8 will return
+ the expression "x + 3". Obviously some expressions can't be reversed in this way, in which
+ case they might just be adjusted by adding a constant to them.
+ */
+ const Expression adjustedToGiveNewResult (double targetValue, const EvaluationContext& context) const;
+
+ /** Returns a copy of this expression in which all instances of a given symbol have been renamed. */
+ const Expression withRenamedSymbol (const String& oldSymbol, const String& newSymbol) const;
+
+ /** Returns true if this expression makes use of the specified symbol.
+ If a suitable context is supplied, the search will dereference and recursively check
+ all symbols, so that it can be determined whether this expression relies on the given
+ symbol at any level in its evaluation.
+ */
+ bool referencesSymbol (const String& symbol, const EvaluationContext& context) const;
+
+ /** Returns true if this expression contains any symbols. */
+ bool usesAnySymbols() const;
+
+ /** An exception that can be thrown by Expression::parse(). */
+ class ParseError : public std::exception
+ {
+ public:
+ ParseError (const String& message);
+
+ String description;
+ };
+
+ /** An exception that can be thrown by Expression::evaluate(). */
+ class EvaluationError : public std::exception
+ {
+ public:
+ EvaluationError (const String& message);
+
+ String description;
+ };
+
+ juce_UseDebuggingNewOperator
+
+private:
+ class Helpers;
+ friend class Helpers;
+
+ class Term : public ReferenceCountedObject
+ {
+ public:
+ Term() {}
+ virtual ~Term() {}
+
+ virtual Term* clone() const = 0;
+ virtual double evaluate (const EvaluationContext&, int recursionDepth) const = 0;
+ virtual int getNumInputs() const = 0;
+ virtual Term* getInput (int index) const = 0;
+ virtual int getInputIndexFor (const Term* possibleInput) const;
+ virtual const String toString() const = 0;
+ virtual int getOperatorPrecedence() const;
+ virtual bool referencesSymbol (const String& symbol, const EvaluationContext&, int recursionDepth) const;
+ virtual const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext&, Term* inputTerm,
+ double overallTarget, Term* topLevelTerm) const;
+ juce_UseDebuggingNewOperator
+
+ private:
+ Term (const Term& other);
+ Term& operator= (const Term&);
+ };
+
+ friend class ScopedPointer;
+ ReferenceCountedObjectPtr term;
+
+ explicit Expression (Term* term);
+};
+
+#endif // __JUCE_EXPRESSION_JUCEHEADER__
+/*** End of inlined file: juce_Expression.h ***/
+
+
+#endif
+#ifndef __JUCE_HEAPBLOCK_JUCEHEADER__
+
+#endif
+#ifndef __JUCE_IDENTIFIER_JUCEHEADER__
+
+#endif
+#ifndef __JUCE_MEMORYBLOCK_JUCEHEADER__
+
+#endif
+#ifndef __JUCE_NAMEDVALUESET_JUCEHEADER__
+
+#endif
+#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__
+
+/*** Start of inlined file: juce_OwnedArray.h ***/
+#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__
+#define __JUCE_OWNEDARRAY_JUCEHEADER__
+
/** An array designed for holding objects.
This holds a list of pointers to objects, and will automatically
@@ -29868,6 +30056,15 @@ struct JUCE_API AudioSourceChannelInfo
/**
Base class for objects that can produce a continuous stream of audio.
+ An AudioSource has two states: 'prepared' and 'unprepared'.
+
+ When a source needs to be played, it is first put into a 'prepared' state by a call to
+ prepareToPlay(), and then repeated calls will be made to its getNextAudioBlock() method to
+ process the audio data.
+
+ Once playback has finished, the releaseResources() method is called to put the stream
+ back into an 'unprepared' state.
+
@see AudioFormatReaderSource, ResamplingAudioSource
*/
class JUCE_API AudioSource
@@ -29883,7 +30080,14 @@ public:
/** Tells the source to prepare for playing.
- The source can use this opportunity to initialise anything it needs to.
+ An AudioSource has two states: prepared and unprepared.
+
+ The prepareToPlay() method is guaranteed to be called at least once on an 'unpreprared'
+ source to put it into a 'prepared' state before any calls will be made to getNextAudioBlock().
+ This callback allows the source to initialise any resources it might need when playing.
+
+ Once playback has finished, the releaseResources() method is called to put the stream
+ back into an 'unprepared' state.
Note that this method could be called more than once in succession without
a matching call to releaseResources(), so make sure your code is robust and
@@ -31295,6 +31499,9 @@ public:
/** Returns true if this source is actually playing in a loop. */
virtual bool isLooping() const = 0;
+
+ /** Tells the source whether you'd like it to play in a loop. */
+ virtual void setLooping (bool shouldLoop) { (void) shouldLoop; }
};
#endif // __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__
@@ -31329,7 +31536,7 @@ public:
@see isLooping
*/
- void setLooping (const bool shouldLoop) throw();
+ void setLooping (bool shouldLoop);
/** Returns whether loop-mode is turned on or not. */
bool isLooping() const { return looping; }
@@ -36616,7 +36823,8 @@ public:
@param newItemText the text of the item to show in the list
@param newItemId an associated ID number that can be set or retrieved - see
- getSelectedId() and setSelectedId()
+ getSelectedId() and setSelectedId(). Note that this value can not
+ be 0!
@see setItemEnabled, addSeparator, addSectionHeading, removeItem, getNumItems, getItemText, getItemId
*/
void addItem (const String& newItemText,
@@ -42813,21 +43021,7 @@ private:
#define __JUCE_RELATIVECOORDINATE_JUCEHEADER__
/**
- Expresses a coordinate as an absolute or proportional distance from other
- named coordinates.
-
- A RelativeCoordinate represents a position as either:
- - an absolute distance from the origin
- - an absolute distance from another named RelativeCoordinate
- - a proportion of the distance between two other named RelativeCoordinates
-
- Of course, the coordinates that this one is relative to may themselves be relative
- to other coordinates, so complex arrangements can be built up (as long as you're careful
- not to create recursive loops!)
-
- Rather than keeping pointers to the coordinates that this one is dependent on, it
- stores their names, and when resolving this coordinate to an absolute value, a
- NamedCoordinateFinder class is required to retrieve these coordinates by name.
+ Expresses a coordinate as a dynamically evaluated expression.
@see RelativePoint, RelativeRectangle
*/
@@ -42837,6 +43031,9 @@ public:
/** Creates a zero coordinate. */
RelativeCoordinate();
+ RelativeCoordinate (const Expression& expression);
+ RelativeCoordinate (const RelativeCoordinate& other);
+ RelativeCoordinate& operator= (const RelativeCoordinate& other);
/** Creates an absolute position from the parent origin on either the X or Y axis.
@@ -42844,52 +43041,16 @@ public:
*/
RelativeCoordinate (double absoluteDistanceFromOrigin);
- /** Creates an absolute position relative to a named coordinate.
-
- @param absoluteDistanceFromAnchor the distance to add to the named anchor point
- @param anchorPoint the name of the coordinate from which this one will be relative. See the constructor
- notes for a description of the syntax for coordinate names.
- */
- RelativeCoordinate (double absoluteDistanceFromAnchor, const String& anchorPoint);
-
- /** Creates a relative position between two named points.
-
- @param relativeProportionBetweenAnchors a value between 0 and 1 indicating this coordinate's relative position
- between anchorPoint1 and anchorPoint2.
- @param anchorPoint1 the name of the first coordinate from which this one will be relative. See the constructor
- notes for a description of the syntax for coordinate names.
- @param anchorPoint2 the name of the first coordinate from which this one will be relative. See the constructor
- notes for a description of the syntax for coordinate names.
- */
- RelativeCoordinate (double relativeProportionBetweenAnchors, const String& anchorPoint1, const String& anchorPoint2);
-
/** Recreates a coordinate from a string description.
- The string can be in one of the following formats:
- - "123" = 123 pixels from parent origin (this is equivalent to "parent.left + 123"
- or "parent.top + 123", depending on which axis the coordinate is using)
- - "anchor" = the same position as the coordinate named "anchor"
- - "anchor + 123" = the coordinate named "anchor" + 123 pixels
- - "anchor - 123" = the coordinate named "anchor" - 123 pixels
- - "50%" = 50% of the distance between the coordinate space's top-left origin and its extent
- (this is equivalent to "50% * parent.left -> parent.right" or "50% * parent.top -> parent.bottom")
- - "50% * anchor" = 50% of the distance between the coordinate space's origin and the coordinate named "anchor"
- (this is equivalent to "50% * parent.left -> anchor" or "50% * parent.top -> anchor")
- - "50% * anchor1 -> anchor2" = 50% of the distance between the coordinate "anchor1" and the coordinate "anchor2"
+ The string will be parsed by ExpressionParser::parse().
- An anchor name can either be just a single identifier (letters, digits and underscores only - no spaces),
- e.g. "marker1", or it can be a two-part name in the form "objectName.edge". For example "parent.left" is
- the origin, or "myComponent.top" is the top edge of a component called "myComponent". The exact names that
- will be recognised are dependent on the NamedCoordinateFinder that you provide for looking them up, but
- "parent.left" and "parent.top" are always available, meaning the origin. "parent.right" and "parent.bottom"
- may also be available if the coordinate space has a fixed size.
-
- @param stringVersion the string to parse
+ @param stringVersion the expression to use
@param isHorizontal this must be true if this is an X coordinate, or false if it's on the Y axis.
@see toString
*/
- RelativeCoordinate (const String& stringVersion, bool isHorizontal);
+ RelativeCoordinate (const String& stringVersion);
/** Destructor. */
~RelativeCoordinate();
@@ -42897,44 +43058,20 @@ public:
bool operator== (const RelativeCoordinate& other) const throw();
bool operator!= (const RelativeCoordinate& other) const throw();
- /**
- Provides an interface for looking up the position of a named anchor when resolving a RelativeCoordinate.
-
- When using RelativeCoordinates, to resolve their names you need to provide a subclass of this which
- can retrieve a coordinate by name.
- */
- class JUCE_API NamedCoordinateFinder
- {
- public:
- /** Destructor. */
- virtual ~NamedCoordinateFinder() {}
-
- /** Returns the coordinate for a given name.
-
- The objectName parameter will be the first section of the name, and the edge the name of the second part.
- E.g. for "parent.right", objectName would be "parent" and edge would be "right". If the name
- has no dot, the edge parameter will be an empty string.
-
- This method must be able to resolve "parent.left", "parent.top", "parent.right" and "parent.bottom", as
- well as any other objects that your application uses.
- */
- virtual const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const = 0;
- };
-
/** Calculates the absolute position of this coordinate.
- You'll need to provide a suitable NamedCoordinateFinder for looking up any coordinates that may
+ You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may
be needed to calculate the result.
*/
- double resolve (const NamedCoordinateFinder* nameFinder) const;
+ double resolve (const Expression::EvaluationContext* evaluationContext) const;
/** Returns true if this coordinate uses the specified coord name at any level in its evaluation.
This will recursively check any coordinates upon which this one depends.
*/
- bool references (const String& coordName, const NamedCoordinateFinder* nameFinder) const;
+ bool references (const String& coordName, const Expression::EvaluationContext* evaluationContext) const;
/** Returns true if there's a recursive loop when trying to resolve this coordinate's position. */
- bool isRecursive (const NamedCoordinateFinder* nameFinder) const;
+ bool isRecursive (const Expression::EvaluationContext* evaluationContext) const;
/** Returns true if this coordinate depends on any other coordinates for its position. */
bool isDynamic() const;
@@ -42945,62 +43082,7 @@ public:
or relative position to whatever value is necessary to make its resultant position
match the position that is provided.
*/
- void moveToAbsolute (double absoluteTargetPosition, const NamedCoordinateFinder* nameFinder);
-
- /** Returns true if the coordinate is calculated as a proportion of the distance between two other points.
- @see toggleProportionality
- */
- bool isProportional() const throw() { return anchor2.isNotEmpty(); }
-
- /** Toggles the coordinate between using a proportional or absolute position.
- Note that calling this will reset the names of any anchor points, and just make the
- coordinate relative to the parent origin and parent size.
- */
- void toggleProportionality (const NamedCoordinateFinder* nameFinder,
- const String& proportionalAnchor1, const String& proportionalAnchor2);
-
- /** Returns a value that can be edited to set this coordinate's position.
- The meaning of this number depends on the coordinate's mode. If the coordinate is
- proportional, the number will be a percentage between 0 and 100. If the
- coordinate is absolute, then it will be the number of pixels from its anchor point.
- @see setEditableNumber
- */
- const double getEditableNumber() const;
-
- /** Sets the value that controls this coordinate's position.
- The meaning of this number depends on the coordinate's mode. If the coordinate is
- proportional, the number must be a percentage between 0 and 100. If the
- coordinate is absolute, then it indicates the number of pixels from its anchor point.
- @see setEditableNumber
- */
- void setEditableNumber (const double newValue);
-
- /** Returns the name of the first anchor point from which this coordinate is relative.
- */
- const String getAnchorName1 (const String& returnValueIfOrigin) const;
-
- /** Returns the name of the second anchor point from which this coordinate is relative.
- The second anchor is only valid if the coordinate is in proportional mode.
- */
- const String getAnchorName2 (const String& returnValueIfOrigin) const;
-
- /** Returns the first anchor point as a coordinate. */
- const RelativeCoordinate getAnchorCoordinate1() const;
-
- /** Returns the first anchor point as a coordinate.
- The second anchor is only valid if the coordinate is in proportional mode.
- */
- const RelativeCoordinate getAnchorCoordinate2() const;
-
- /** Changes the first anchor point, keeping the resultant position of this coordinate in
- the same place it was previously.
- */
- void changeAnchor1 (const String& newAnchor, const NamedCoordinateFinder* nameFinder);
-
- /** Changes the second anchor point, keeping the resultant position of this coordinate in
- the same place it was previously.
- */
- void changeAnchor2 (const String& newAnchor, const NamedCoordinateFinder* nameFinder);
+ void moveToAbsolute (double absoluteTargetPosition, const Expression::EvaluationContext* evaluationContext);
/** Tells the coordinate that an object is changing its name or being deleted.
@@ -43009,8 +43091,11 @@ public:
this coordinate was using it, the coordinate is changed to be relative to the origin
instead.
*/
- void renameAnchorIfUsed (const String& oldName, const String& newName,
- const NamedCoordinateFinder* nameFinder);
+ void renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* evaluationContext);
+
+ /** Returns the expression that defines this coordinate. */
+ const Expression& getExpression() const { return term; }
/** Returns a string which represents this coordinate.
For details of the string syntax, see the constructor notes.
@@ -43039,11 +43124,10 @@ public:
private:
- String anchor1, anchor2;
- double value;
+ Expression term;
- double resolve (const NamedCoordinateFinder* nameFinder, int recursionCounter) const;
- static double resolveAnchor (const String& anchorName, const NamedCoordinateFinder* nameFinder, int recursionCounter);
+// double resolve (const Expression::EvaluationContext* evaluationContext, int recursionCounter) const;
+ // static double resolveAnchor (const String& anchorName, const Expression::EvaluationContext* evaluationContext, int recursionCounter);
};
/**
@@ -43078,10 +43162,10 @@ public:
/** Calculates the absolute position of this point.
- You'll need to provide a suitable NamedCoordinateFinder for looking up any coordinates that may
+ You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may
be needed to calculate the result.
*/
- const Point resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
+ const Point resolve (const Expression::EvaluationContext* evaluationContext) const;
/** Changes the values of this point's coordinates to make it resolve to the specified position.
@@ -43089,7 +43173,7 @@ public:
or relative positions to whatever values are necessary to make the resultant position
match the position that is provided.
*/
- void moveToAbsolute (const Point& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder);
+ void moveToAbsolute (const Point& newPos, const Expression::EvaluationContext* evaluationContext);
/** Returns a string which represents this point.
This returns a comma-separated pair of coordinates. For details of the string syntax used by the
@@ -43101,8 +43185,8 @@ public:
/** Tells the point that an object is changing its name or being deleted.
This calls RelativeCoordinate::renameAnchorIfUsed() on its X and Y coordinates.
*/
- void renameAnchorIfUsed (const String& oldName, const String& newName,
- const RelativeCoordinate::NamedCoordinateFinder* nameFinder);
+ void renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* evaluationContext);
/** Returns true if this point depends on any other coordinates for its position. */
bool isDynamic() const;
@@ -43145,10 +43229,10 @@ public:
/** Calculates the absolute position of this rectangle.
- You'll need to provide a suitable NamedCoordinateFinder for looking up any coordinates that may
+ You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may
be needed to calculate the result.
*/
- const Rectangle resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
+ const Rectangle resolve (const Expression::EvaluationContext* evaluationContext) const;
/** Changes the values of this rectangle's coordinates to make it resolve to the specified position.
@@ -43156,7 +43240,7 @@ public:
or relative positions to whatever values are necessary to make the resultant position
match the position that is provided.
*/
- void moveToAbsolute (const Rectangle& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder);
+ void moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* evaluationContext);
/** Returns a string which represents this point.
This returns a comma-separated list of coordinates, in the order left, top, right, bottom. For details of
@@ -43166,10 +43250,10 @@ public:
const String toString() const;
/** Tells the rectangle that an object is changing its name or being deleted.
- This calls RelativeCoordinate::renameAnchorIfUsed() on the rectangle's coordinates.
+ This calls RelativeCoordinate::renameSymbolIfUsed() on the rectangle's coordinates.
*/
- void renameAnchorIfUsed (const String& oldName, const String& newName,
- const RelativeCoordinate::NamedCoordinateFinder* nameFinder);
+ void renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* evaluationContext);
// The actual rectangle coords...
RelativeCoordinate left, right, top, bottom;
@@ -43194,7 +43278,7 @@ public:
~RelativePointPath();
/** Resolves this points in this path and adds them to a normal Path object. */
- void createPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder);
+ void createPath (Path& path, Expression::EvaluationContext* coordFinder);
/** Returns true if the path contains any non-fixed points. */
bool containsAnyDynamicPoints() const;
@@ -43226,7 +43310,7 @@ public:
ElementBase (ElementType type);
virtual ~ElementBase() {}
virtual const ValueTree createTree() const = 0;
- virtual void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const = 0;
+ virtual void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const = 0;
virtual RelativePoint* getControlPoints (int& numPoints) = 0;
const ElementType type;
@@ -43242,7 +43326,7 @@ public:
StartSubPath (const RelativePoint& pos);
~StartSubPath() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
RelativePoint startPos;
@@ -43258,7 +43342,7 @@ public:
CloseSubPath();
~CloseSubPath() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
private:
@@ -43272,7 +43356,7 @@ public:
LineTo (const RelativePoint& endPoint);
~LineTo() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
RelativePoint endPoint;
@@ -43288,7 +43372,7 @@ public:
QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint);
~QuadraticTo() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
RelativePoint controlPoints[2];
@@ -43304,7 +43388,7 @@ public:
CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint);
~CubicTo() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
RelativePoint controlPoints[3];
@@ -43339,11 +43423,11 @@ public:
RelativeParallelogram (const String& topLeft, const String& topRight, const String& bottomLeft);
~RelativeParallelogram();
- void resolveThreePoints (Point* points, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
- void resolveFourCorners (Point* points, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
- const Rectangle getBounds (RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
- void getPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
- const AffineTransform resetToPerpendicular (RelativeCoordinate::NamedCoordinateFinder* coordFinder);
+ void resolveThreePoints (Point* points, Expression::EvaluationContext* coordFinder) const;
+ void resolveFourCorners (Point* points, Expression::EvaluationContext* coordFinder) const;
+ const Rectangle getBounds (Expression::EvaluationContext* coordFinder) const;
+ void getPath (Path& path, Expression::EvaluationContext* coordFinder) const;
+ const AffineTransform resetToPerpendicular (Expression::EvaluationContext* coordFinder);
bool operator== (const RelativeParallelogram& other) const throw();
bool operator!= (const RelativeParallelogram& other) const throw();
@@ -43566,7 +43650,7 @@ public:
static const FillType readFillType (const ValueTree& v, RelativePoint* gradientPoint1,
RelativePoint* gradientPoint2, RelativePoint* gradientPoint3,
- RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+ Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider);
static void writeFillType (ValueTree& v, const FillType& fillType,
@@ -59069,7 +59153,7 @@ protected:
@see Drawable
*/
class JUCE_API DrawableComposite : public Drawable,
- public RelativeCoordinate::NamedCoordinateFinder
+ public Expression::EvaluationContext
{
public:
@@ -59230,7 +59314,7 @@ public:
/** @internal */
const Identifier getValueTreeType() const { return valueTreeType; }
/** @internal */
- const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const;
+ const Expression getSymbolValue (const String& symbol) const;
/** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */
class ValueTreeWrapper : public ValueTreeWrapperBase
@@ -59506,14 +59590,14 @@ public:
public:
ValueTreeWrapper (const ValueTree& state);
- const FillType getMainFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+ const FillType getMainFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const;
ValueTree getMainFillState();
void setMainFill (const FillType& newFill, const RelativePoint* gradientPoint1,
const RelativePoint* gradientPoint2, const RelativePoint* gradientPoint3,
ImageProvider* imageProvider, UndoManager* undoManager);
- const FillType getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+ const FillType getStrokeFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const;
ValueTree getStrokeFillState();
void setStrokeFill (const FillType& newFill, const RelativePoint* gradientPoint1,
@@ -59540,7 +59624,7 @@ public:
const RelativePoint getStartPoint() const;
const RelativePoint getEndPoint() const;
void setControlPoint (int index, const RelativePoint& point, UndoManager* undoManager);
- float getLength (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
+ float getLength (Expression::EvaluationContext* nameFinder) const;
ValueTreeWrapper getParent() const;
Element getPreviousElement() const;
@@ -59549,11 +59633,11 @@ public:
void setModeOfEndPoint (const String& newMode, UndoManager* undoManager);
void convertToLine (UndoManager* undoManager);
- void convertToCubic (RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager);
+ void convertToCubic (Expression::EvaluationContext* nameFinder, UndoManager* undoManager);
void convertToPathBreak (UndoManager* undoManager);
- ValueTree insertPoint (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager);
+ ValueTree insertPoint (const Point& targetPoint, Expression::EvaluationContext* nameFinder, UndoManager* undoManager);
void removePoint (UndoManager* undoManager);
- float findProportionAlongLine (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
+ float findProportionAlongLine (const Point& targetPoint, Expression::EvaluationContext* nameFinder) const;
static const Identifier mode, startSubPathElement, closeSubPathElement,
lineToElement, quadraticToElement, cubicToElement;
diff --git a/src/audio/audio_sources/juce_AudioFormatReaderSource.cpp b/src/audio/audio_sources/juce_AudioFormatReaderSource.cpp
index b628195d43..0152a74678 100644
--- a/src/audio/audio_sources/juce_AudioFormatReaderSource.cpp
+++ b/src/audio/audio_sources/juce_AudioFormatReaderSource.cpp
@@ -55,7 +55,7 @@ void AudioFormatReaderSource::setNextReadPosition (int newPosition)
nextPlayPos = newPosition;
}
-void AudioFormatReaderSource::setLooping (const bool shouldLoop) throw()
+void AudioFormatReaderSource::setLooping (bool shouldLoop)
{
looping = shouldLoop;
}
diff --git a/src/audio/audio_sources/juce_AudioFormatReaderSource.h b/src/audio/audio_sources/juce_AudioFormatReaderSource.h
index 00ddc0b3dc..9b73579843 100644
--- a/src/audio/audio_sources/juce_AudioFormatReaderSource.h
+++ b/src/audio/audio_sources/juce_AudioFormatReaderSource.h
@@ -63,7 +63,7 @@ public:
@see isLooping
*/
- void setLooping (const bool shouldLoop) throw();
+ void setLooping (bool shouldLoop);
/** Returns whether loop-mode is turned on or not. */
bool isLooping() const { return looping; }
diff --git a/src/audio/audio_sources/juce_AudioSource.h b/src/audio/audio_sources/juce_AudioSource.h
index f69b769bec..a2ab680739 100644
--- a/src/audio/audio_sources/juce_AudioSource.h
+++ b/src/audio/audio_sources/juce_AudioSource.h
@@ -74,6 +74,15 @@ struct JUCE_API AudioSourceChannelInfo
/**
Base class for objects that can produce a continuous stream of audio.
+ An AudioSource has two states: 'prepared' and 'unprepared'.
+
+ When a source needs to be played, it is first put into a 'prepared' state by a call to
+ prepareToPlay(), and then repeated calls will be made to its getNextAudioBlock() method to
+ process the audio data.
+
+ Once playback has finished, the releaseResources() method is called to put the stream
+ back into an 'unprepared' state.
+
@see AudioFormatReaderSource, ResamplingAudioSource
*/
class JUCE_API AudioSource
@@ -90,7 +99,14 @@ public:
//==============================================================================
/** Tells the source to prepare for playing.
- The source can use this opportunity to initialise anything it needs to.
+ An AudioSource has two states: prepared and unprepared.
+
+ The prepareToPlay() method is guaranteed to be called at least once on an 'unpreprared'
+ source to put it into a 'prepared' state before any calls will be made to getNextAudioBlock().
+ This callback allows the source to initialise any resources it might need when playing.
+
+ Once playback has finished, the releaseResources() method is called to put the stream
+ back into an 'unprepared' state.
Note that this method could be called more than once in succession without
a matching call to releaseResources(), so make sure your code is robust and
diff --git a/src/audio/audio_sources/juce_PositionableAudioSource.h b/src/audio/audio_sources/juce_PositionableAudioSource.h
index a89dc4c2c4..7ae9995d99 100644
--- a/src/audio/audio_sources/juce_PositionableAudioSource.h
+++ b/src/audio/audio_sources/juce_PositionableAudioSource.h
@@ -72,6 +72,9 @@ public:
/** Returns true if this source is actually playing in a loop. */
virtual bool isLooping() const = 0;
+
+ /** Tells the source whether you'd like it to play in a loop. */
+ virtual void setLooping (bool shouldLoop) { (void) shouldLoop; }
};
diff --git a/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm b/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm
index c9af242ef8..83c4e9a7d6 100644
--- a/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm
+++ b/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm
@@ -1,1525 +1,1525 @@
-/*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-10 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_TargetPlatform.h"
-#include "../../../../juce_Config.h"
-
-#if JUCE_PLUGINHOST_AU && ! (JUCE_LINUX || JUCE_WINDOWS)
-
-#include
-#include
-#include
-
-#if JUCE_SUPPORT_CARBON
-#include
-#include
-#endif
-
-#include "../../../core/juce_StandardHeader.h"
-
-BEGIN_JUCE_NAMESPACE
-
-#include "juce_AudioUnitPluginFormat.h"
-#include "../juce_PluginDescription.h"
-#include "../../../threads/juce_ScopedLock.h"
-#include "../../../events/juce_Timer.h"
-#include "../../../core/juce_PlatformUtilities.h"
-#include "../../../gui/components/layout/juce_ComponentMovementWatcher.h"
-#include "../../../gui/components/windows/juce_ComponentPeer.h"
-#include "../../../gui/components/special/juce_NSViewComponent.h"
-#if JUCE_MAC && JUCE_SUPPORT_CARBON
-#include "../../../native/mac/juce_mac_CarbonViewWrapperComponent.h"
-#endif
-
-#if JUCE_MAC
-
-// Change this to disable logging of various activities
-#ifndef AU_LOGGING
- #define AU_LOGGING 1
-#endif
-
-#if AU_LOGGING
- #define log(a) Logger::writeToLog(a);
-#else
- #define log(a)
-#endif
-
-namespace AudioUnitFormatHelpers
-{
- static int insideCallback = 0;
-
- static const String osTypeToString (OSType type)
- {
- char s[4];
- s[0] = (char) (((uint32) type) >> 24);
- s[1] = (char) (((uint32) type) >> 16);
- s[2] = (char) (((uint32) type) >> 8);
- s[3] = (char) ((uint32) type);
- return String (s, 4);
- }
-
- static OSType stringToOSType (const String& s1)
- {
- const String s (s1 + " ");
-
- return (((OSType) (unsigned char) s[0]) << 24)
- | (((OSType) (unsigned char) s[1]) << 16)
- | (((OSType) (unsigned char) s[2]) << 8)
- | ((OSType) (unsigned char) s[3]);
- }
-
- static const char* auIdentifierPrefix = "AudioUnit:";
-
- static const String createAUPluginIdentifier (const ComponentDescription& desc)
- {
- jassert (osTypeToString ('abcd') == "abcd"); // agh, must have got the endianness wrong..
- jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto
-
- String s (auIdentifierPrefix);
-
- if (desc.componentType == kAudioUnitType_MusicDevice)
- s << "Synths/";
- else if (desc.componentType == kAudioUnitType_MusicEffect
- || desc.componentType == kAudioUnitType_Effect)
- s << "Effects/";
- else if (desc.componentType == kAudioUnitType_Generator)
- s << "Generators/";
- else if (desc.componentType == kAudioUnitType_Panner)
- s << "Panners/";
-
- s << osTypeToString (desc.componentType) << ","
- << osTypeToString (desc.componentSubType) << ","
- << osTypeToString (desc.componentManufacturer);
-
- return s;
- }
-
- static void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer)
- {
- Handle componentNameHandle = NewHandle (sizeof (void*));
- Handle componentInfoHandle = NewHandle (sizeof (void*));
-
- if (componentNameHandle != 0 && componentInfoHandle != 0)
- {
- ComponentDescription desc;
-
- if (GetComponentInfo (comp, &desc, componentNameHandle, componentInfoHandle, 0) == noErr)
- {
- ConstStr255Param nameString = (ConstStr255Param) (*componentNameHandle);
- ConstStr255Param infoString = (ConstStr255Param) (*componentInfoHandle);
-
- if (nameString != 0 && nameString[0] != 0)
- {
- const String all ((const char*) nameString + 1, nameString[0]);
- DBG ("name: "+ all);
-
- manufacturer = all.upToFirstOccurrenceOf (":", false, false).trim();
- name = all.fromFirstOccurrenceOf (":", false, false).trim();
- }
-
- if (infoString != 0 && infoString[0] != 0)
- {
- DBG ("info: " + String ((const char*) infoString + 1, infoString[0]));
- }
-
- if (name.isEmpty())
- name = "";
- }
-
- DisposeHandle (componentNameHandle);
- DisposeHandle (componentInfoHandle);
- }
- }
-
- static bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc,
- String& name, String& version, String& manufacturer)
- {
- zerostruct (desc);
-
- if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
- {
- String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (':'),
- fileOrIdentifier.lastIndexOfChar ('/')) + 1));
-
- StringArray tokens;
- tokens.addTokens (s, ",", String::empty);
- tokens.trim();
- tokens.removeEmptyStrings();
-
- if (tokens.size() == 3)
- {
- desc.componentType = stringToOSType (tokens[0]);
- desc.componentSubType = stringToOSType (tokens[1]);
- desc.componentManufacturer = stringToOSType (tokens[2]);
-
- ComponentRecord* comp = FindNextComponent (0, &desc);
-
- if (comp != 0)
- {
- getAUDetails (comp, name, manufacturer);
- return true;
- }
- }
- }
-
- return false;
- }
-}
-
-//==============================================================================
-class AudioUnitPluginWindowCarbon;
-class AudioUnitPluginWindowCocoa;
-
-//==============================================================================
-class AudioUnitPluginInstance : public AudioPluginInstance
-{
-public:
- //==============================================================================
- ~AudioUnitPluginInstance();
-
- //==============================================================================
- // AudioPluginInstance methods:
-
- void fillInPluginDescription (PluginDescription& desc) const
- {
- desc.name = pluginName;
- desc.fileOrIdentifier = AudioUnitFormatHelpers::createAUPluginIdentifier (componentDesc);
- desc.uid = ((int) componentDesc.componentType)
- ^ ((int) componentDesc.componentSubType)
- ^ ((int) componentDesc.componentManufacturer);
- desc.lastFileModTime = 0;
- desc.pluginFormatName = "AudioUnit";
- desc.category = getCategory();
- desc.manufacturerName = manufacturer;
- desc.version = version;
- desc.numInputChannels = getNumInputChannels();
- desc.numOutputChannels = getNumOutputChannels();
- desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice);
- }
-
- const String getName() const { return pluginName; }
- bool acceptsMidi() const { return wantsMidiMessages; }
- bool producesMidi() const { return false; }
-
- //==============================================================================
- // AudioProcessor methods:
-
- void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
- void releaseResources();
- void processBlock (AudioSampleBuffer& buffer,
- MidiBuffer& midiMessages);
-
- AudioProcessorEditor* createEditor();
-
- const String getInputChannelName (const int index) const;
- bool isInputChannelStereoPair (int index) const;
-
- const String getOutputChannelName (const int index) const;
- bool isOutputChannelStereoPair (int index) const;
-
- //==============================================================================
- int getNumParameters();
- float getParameter (int index);
- void setParameter (int index, float newValue);
- const String getParameterName (int index);
- const String getParameterText (int index);
- bool isParameterAutomatable (int index) const;
-
- //==============================================================================
- int getNumPrograms();
- int getCurrentProgram();
- void setCurrentProgram (int index);
- const String getProgramName (int index);
- void changeProgramName (int index, const String& newName);
-
- //==============================================================================
- void getStateInformation (MemoryBlock& destData);
- void getCurrentProgramStateInformation (MemoryBlock& destData);
- void setStateInformation (const void* data, int sizeInBytes);
- void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
-
- //==============================================================================
- juce_UseDebuggingNewOperator
-
-private:
- friend class AudioUnitPluginWindowCarbon;
- friend class AudioUnitPluginWindowCocoa;
- friend class AudioUnitPluginFormat;
-
- ComponentDescription componentDesc;
- String pluginName, manufacturer, version;
- String fileOrIdentifier;
- CriticalSection lock;
- bool initialised, wantsMidiMessages, wasPlaying;
-
- HeapBlock outputBufferList;
- AudioTimeStamp timeStamp;
- AudioSampleBuffer* currentBuffer;
-
- AudioUnit audioUnit;
- Array parameterIds;
-
- //==============================================================================
- bool getComponentDescFromFile (const String& fileOrIdentifier);
- void initialise();
-
- //==============================================================================
- OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
- const AudioTimeStamp* inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList* ioData) const;
-
- static OSStatus renderGetInputCallback (void* inRefCon,
- AudioUnitRenderActionFlags* ioActionFlags,
- const AudioTimeStamp* inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList* ioData)
- {
- return ((AudioUnitPluginInstance*) inRefCon)
- ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
- }
-
- OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const;
- OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator,
- UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const;
- OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged,
- Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
- Float64* outCycleStartBeat, Float64* outCycleEndBeat);
-
- static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo)
- {
- return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo);
- }
-
- static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat,
- Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator,
- Float64* outCurrentMeasureDownBeat)
- {
- return ((AudioUnitPluginInstance*) inHostUserData)
- ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator,
- outTimeSig_Denominator, outCurrentMeasureDownBeat);
- }
-
- static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged,
- Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
- Float64* outCycleStartBeat, Float64* outCycleEndBeat)
- {
- return ((AudioUnitPluginInstance*) inHostUserData)
- ->getTransportState (outIsPlaying, outTransportStateChanged,
- outCurrentSampleInTimeLine, outIsCycling,
- outCycleStartBeat, outCycleEndBeat);
- }
-
- //==============================================================================
- void getNumChannels (int& numIns, int& numOuts)
- {
- numIns = 0;
- numOuts = 0;
-
- AUChannelInfo supportedChannels [128];
- UInt32 supportedChannelsSize = sizeof (supportedChannels);
-
- if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global,
- 0, supportedChannels, &supportedChannelsSize) == noErr
- && supportedChannelsSize > 0)
- {
- for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
- {
- numIns = jmax (numIns, (int) supportedChannels[i].inChannels);
- numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels);
- }
- }
- else
- {
- // (this really means the plugin will take any number of ins/outs as long
- // as they are the same)
- numIns = numOuts = 2;
- }
- }
-
- const String getCategory() const;
-
- //==============================================================================
- AudioUnitPluginInstance (const String& fileOrIdentifier);
-};
-
-//==============================================================================
-AudioUnitPluginInstance::AudioUnitPluginInstance (const String& fileOrIdentifier)
- : fileOrIdentifier (fileOrIdentifier),
- initialised (false),
- wantsMidiMessages (false),
- audioUnit (0),
- currentBuffer (0)
-{
- using namespace AudioUnitFormatHelpers;
-
- try
- {
- ++insideCallback;
-
- log ("Opening AU: " + fileOrIdentifier);
-
- if (getComponentDescFromFile (fileOrIdentifier))
- {
- ComponentRecord* const comp = FindNextComponent (0, &componentDesc);
-
- if (comp != 0)
- {
- audioUnit = (AudioUnit) OpenComponent (comp);
-
- wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice
- || componentDesc.componentType == kAudioUnitType_MusicEffect;
- }
- }
-
- --insideCallback;
- }
- catch (...)
- {
- --insideCallback;
- }
-}
-
-AudioUnitPluginInstance::~AudioUnitPluginInstance()
-{
- const ScopedLock sl (lock);
-
- jassert (AudioUnitFormatHelpers::insideCallback == 0);
-
- if (audioUnit != 0)
- {
- AudioUnitUninitialize (audioUnit);
- CloseComponent (audioUnit);
- audioUnit = 0;
- }
-}
-
-bool AudioUnitPluginInstance::getComponentDescFromFile (const String& fileOrIdentifier)
-{
- zerostruct (componentDesc);
-
- if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, componentDesc, pluginName, version, manufacturer))
- return true;
-
- const File file (fileOrIdentifier);
- if (! file.hasFileExtension (".component"))
- return false;
-
- const char* const utf8 = fileOrIdentifier.toUTF8();
- CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8,
- strlen (utf8), file.isDirectory());
- if (url != 0)
- {
- CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
- CFRelease (url);
-
- if (bundleRef != 0)
- {
- CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"));
-
- if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID())
- pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name);
-
- if (pluginName.isEmpty())
- pluginName = file.getFileNameWithoutExtension();
-
- CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion"));
-
- if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID())
- version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString);
-
- CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString"));
-
- if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID())
- manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString);
-
- short resFileId = CFBundleOpenBundleResourceMap (bundleRef);
- UseResFile (resFileId);
-
- for (int i = 1; i <= Count1Resources ('thng'); ++i)
- {
- Handle h = Get1IndResource ('thng', i);
-
- if (h != 0)
- {
- HLock (h);
- const uint32* const types = (const uint32*) *h;
-
- if (types[0] == kAudioUnitType_MusicDevice
- || types[0] == kAudioUnitType_MusicEffect
- || types[0] == kAudioUnitType_Effect
- || types[0] == kAudioUnitType_Generator
- || types[0] == kAudioUnitType_Panner)
- {
- componentDesc.componentType = types[0];
- componentDesc.componentSubType = types[1];
- componentDesc.componentManufacturer = types[2];
- break;
- }
-
- HUnlock (h);
- ReleaseResource (h);
- }
- }
-
- CFBundleCloseBundleResourceMap (bundleRef, resFileId);
- CFRelease (bundleRef);
- }
- }
-
- return componentDesc.componentType != 0 && componentDesc.componentSubType != 0;
-}
-
-//==============================================================================
-void AudioUnitPluginInstance::initialise()
-{
- if (initialised || audioUnit == 0)
- return;
-
- log ("Initialising AU: " + pluginName);
-
- parameterIds.clear();
-
- {
- UInt32 paramListSize = 0;
- AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
- 0, 0, ¶mListSize);
-
- if (paramListSize > 0)
- {
- parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int));
-
- AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
- 0, ¶meterIds.getReference(0), ¶mListSize);
- }
- }
-
- {
- AURenderCallbackStruct info;
- zerostruct (info);
- info.inputProcRefCon = this;
- info.inputProc = renderGetInputCallback;
-
- AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
- 0, &info, sizeof (info));
- }
-
- {
- HostCallbackInfo info;
- zerostruct (info);
- info.hostUserData = this;
- info.beatAndTempoProc = getBeatAndTempoCallback;
- info.musicalTimeLocationProc = getMusicalTimeLocationCallback;
- info.transportStateProc = getTransportStateCallback;
-
- AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global,
- 0, &info, sizeof (info));
- }
-
- int numIns, numOuts;
- getNumChannels (numIns, numOuts);
- setPlayConfigDetails (numIns, numOuts, 0, 0);
-
- initialised = AudioUnitInitialize (audioUnit) == noErr;
-
- setLatencySamples (0);
-}
-
-
-//==============================================================================
-void AudioUnitPluginInstance::prepareToPlay (double sampleRate_,
- int samplesPerBlockExpected)
-{
- if (audioUnit != 0)
- {
- Float64 sampleRateIn = 0, sampleRateOut = 0;
- UInt32 sampleRateSize = sizeof (sampleRateIn);
- AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRateIn, &sampleRateSize);
- AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRateOut, &sampleRateSize);
-
- if (sampleRateIn != sampleRate_ || sampleRateOut != sampleRate_)
- {
- if (initialised)
- {
- AudioUnitUninitialize (audioUnit);
- initialised = false;
- }
-
- Float64 sr = sampleRate_;
- AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sr, sizeof (Float64));
- AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sr, sizeof (Float64));
- }
- }
-
- initialise();
-
- if (initialised)
- {
- int numIns, numOuts;
- getNumChannels (numIns, numOuts);
-
- setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected);
-
- Float64 latencySecs = 0.0;
- UInt32 latencySize = sizeof (latencySecs);
- AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
- 0, &latencySecs, &latencySize);
-
- setLatencySamples (roundToInt (latencySecs * sampleRate_));
-
- AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
- AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
- AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
-
- AudioStreamBasicDescription stream;
- zerostruct (stream);
- stream.mSampleRate = sampleRate_;
- stream.mFormatID = kAudioFormatLinearPCM;
- stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
- stream.mFramesPerPacket = 1;
- stream.mBytesPerPacket = 4;
- stream.mBytesPerFrame = 4;
- stream.mBitsPerChannel = 32;
- stream.mChannelsPerFrame = numIns;
-
- OSStatus err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- 0, &stream, sizeof (stream));
-
- stream.mChannelsPerFrame = numOuts;
-
- err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
- 0, &stream, sizeof (stream));
-
- outputBufferList.calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1), 1);
- outputBufferList->mNumberBuffers = numOuts;
-
- for (int i = numOuts; --i >= 0;)
- outputBufferList->mBuffers[i].mNumberChannels = 1;
-
- zerostruct (timeStamp);
- timeStamp.mSampleTime = 0;
- timeStamp.mHostTime = AudioGetCurrentHostTime();
- timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid;
-
- currentBuffer = 0;
- wasPlaying = false;
- }
-}
-
-void AudioUnitPluginInstance::releaseResources()
-{
- if (initialised)
- {
- AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
- AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
- AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
-
- outputBufferList.free();
- currentBuffer = 0;
- }
-}
-
-OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
- const AudioTimeStamp* inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList* ioData) const
-{
- if (inBusNumber == 0
- && currentBuffer != 0)
- {
- jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling
-
- for (int i = 0; i < ioData->mNumberBuffers; ++i)
- {
- if (i < currentBuffer->getNumChannels())
- {
- memcpy (ioData->mBuffers[i].mData,
- currentBuffer->getSampleData (i, 0),
- sizeof (float) * inNumberFrames);
- }
- else
- {
- zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames);
- }
- }
- }
-
- return noErr;
-}
-
-void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer,
- MidiBuffer& midiMessages)
-{
- const int numSamples = buffer.getNumSamples();
-
- if (initialised)
- {
- AudioUnitRenderActionFlags flags = 0;
-
- timeStamp.mHostTime = AudioGetCurrentHostTime();
-
- for (int i = getNumOutputChannels(); --i >= 0;)
- {
- outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples;
- outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0);
- }
-
- currentBuffer = &buffer;
-
- if (wantsMidiMessages)
- {
- const uint8* midiEventData;
- int midiEventSize, midiEventPosition;
- MidiBuffer::Iterator i (midiMessages);
-
- while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
- {
- if (midiEventSize <= 3)
- MusicDeviceMIDIEvent (audioUnit,
- midiEventData[0], midiEventData[1], midiEventData[2],
- midiEventPosition);
- else
- MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize);
- }
-
- midiMessages.clear();
- }
-
- AudioUnitRender (audioUnit, &flags, &timeStamp,
- 0, numSamples, outputBufferList);
-
- timeStamp.mSampleTime += numSamples;
- }
- else
- {
- // Not initialised, so just bypass..
- for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
- buffer.clear (i, 0, buffer.getNumSamples());
- }
-}
-
-//==============================================================================
-OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const
-{
- AudioPlayHead* const ph = getPlayHead();
- AudioPlayHead::CurrentPositionInfo result;
-
- if (ph != 0 && ph->getCurrentPosition (result))
- {
- if (outCurrentBeat != 0)
- *outCurrentBeat = result.ppqPosition;
-
- if (outCurrentTempo != 0)
- *outCurrentTempo = result.bpm;
- }
- else
- {
- if (outCurrentBeat != 0)
- *outCurrentBeat = 0;
-
- if (outCurrentTempo != 0)
- *outCurrentTempo = 120.0;
- }
-
- return noErr;
-}
-
-OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat,
- Float32* outTimeSig_Numerator,
- UInt32* outTimeSig_Denominator,
- Float64* outCurrentMeasureDownBeat) const
-{
- AudioPlayHead* const ph = getPlayHead();
- AudioPlayHead::CurrentPositionInfo result;
-
- if (ph != 0 && ph->getCurrentPosition (result))
- {
- if (outTimeSig_Numerator != 0)
- *outTimeSig_Numerator = result.timeSigNumerator;
-
- if (outTimeSig_Denominator != 0)
- *outTimeSig_Denominator = result.timeSigDenominator;
-
- if (outDeltaSampleOffsetToNextBeat != 0)
- *outDeltaSampleOffsetToNextBeat = 0; //xxx
-
- if (outCurrentMeasureDownBeat != 0)
- *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong
- }
- else
- {
- if (outDeltaSampleOffsetToNextBeat != 0)
- *outDeltaSampleOffsetToNextBeat = 0;
-
- if (outTimeSig_Numerator != 0)
- *outTimeSig_Numerator = 4;
-
- if (outTimeSig_Denominator != 0)
- *outTimeSig_Denominator = 4;
-
- if (outCurrentMeasureDownBeat != 0)
- *outCurrentMeasureDownBeat = 0;
- }
-
- return noErr;
-}
-
-OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying,
- Boolean* outTransportStateChanged,
- Float64* outCurrentSampleInTimeLine,
- Boolean* outIsCycling,
- Float64* outCycleStartBeat,
- Float64* outCycleEndBeat)
-{
- AudioPlayHead* const ph = getPlayHead();
- AudioPlayHead::CurrentPositionInfo result;
-
- if (ph != 0 && ph->getCurrentPosition (result))
- {
- if (outIsPlaying != 0)
- *outIsPlaying = result.isPlaying;
-
- if (outTransportStateChanged != 0)
- {
- *outTransportStateChanged = result.isPlaying != wasPlaying;
- wasPlaying = result.isPlaying;
- }
-
- if (outCurrentSampleInTimeLine != 0)
- *outCurrentSampleInTimeLine = roundToInt (result.timeInSeconds * getSampleRate());
-
- if (outIsCycling != 0)
- *outIsCycling = false;
-
- if (outCycleStartBeat != 0)
- *outCycleStartBeat = 0;
-
- if (outCycleEndBeat != 0)
- *outCycleEndBeat = 0;
- }
- else
- {
- if (outIsPlaying != 0)
- *outIsPlaying = false;
-
- if (outTransportStateChanged != 0)
- *outTransportStateChanged = false;
-
- if (outCurrentSampleInTimeLine != 0)
- *outCurrentSampleInTimeLine = 0;
-
- if (outIsCycling != 0)
- *outIsCycling = false;
-
- if (outCycleStartBeat != 0)
- *outCycleStartBeat = 0;
-
- if (outCycleEndBeat != 0)
- *outCycleEndBeat = 0;
- }
-
- return noErr;
-}
-
-
-//==============================================================================
-class AudioUnitPluginWindowCocoa : public AudioProcessorEditor
-{
-public:
- AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& plugin_, const bool createGenericViewIfNeeded)
- : AudioProcessorEditor (&plugin_),
- plugin (plugin_)
- {
- addAndMakeVisible (&wrapper);
-
- setOpaque (true);
- setVisible (true);
- setSize (100, 100);
-
- createView (createGenericViewIfNeeded);
- }
-
- ~AudioUnitPluginWindowCocoa()
- {
- const bool wasValid = isValid();
-
- wrapper.setView (0);
-
- if (wasValid)
- plugin.editorBeingDeleted (this);
- }
-
- bool isValid() const { return wrapper.getView() != 0; }
-
- void paint (Graphics& g)
- {
- g.fillAll (Colours::white);
- }
-
- void resized()
- {
- wrapper.setSize (getWidth(), getHeight());
- }
-
-private:
- AudioUnitPluginInstance& plugin;
- NSViewComponent wrapper;
-
- bool createView (const bool createGenericViewIfNeeded)
- {
- NSView* pluginView = 0;
-
- UInt32 dataSize = 0;
- Boolean isWritable = false;
-
- if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
- 0, &dataSize, &isWritable) == noErr
- && dataSize != 0
- && AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
- 0, &dataSize, &isWritable) == noErr)
- {
- HeapBlock info;
- info.calloc (dataSize, 1);
-
- if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
- 0, info, &dataSize) == noErr)
- {
- NSString* viewClassName = (NSString*) (info->mCocoaAUViewClass[0]);
- NSString* path = (NSString*) CFURLCopyPath (info->mCocoaAUViewBundleLocation);
- NSBundle* viewBundle = [NSBundle bundleWithPath: [path autorelease]];
- Class viewClass = [viewBundle classNamed: viewClassName];
-
- if ([viewClass conformsToProtocol: @protocol (AUCocoaUIBase)]
- && [viewClass instancesRespondToSelector: @selector (interfaceVersion)]
- && [viewClass instancesRespondToSelector: @selector (uiViewForAudioUnit: withSize:)])
- {
- id factory = [[[viewClass alloc] init] autorelease];
- pluginView = [factory uiViewForAudioUnit: plugin.audioUnit
- withSize: NSMakeSize (getWidth(), getHeight())];
- }
-
- for (int i = (dataSize - sizeof (CFURLRef)) / sizeof (CFStringRef); --i >= 0;)
- {
- CFRelease (info->mCocoaAUViewClass[i]);
- CFRelease (info->mCocoaAUViewBundleLocation);
- }
- }
- }
-
- if (createGenericViewIfNeeded && (pluginView == 0))
- pluginView = [[AUGenericView alloc] initWithAudioUnit: plugin.audioUnit];
-
- wrapper.setView (pluginView);
-
- if (pluginView != 0)
- setSize ([pluginView frame].size.width,
- [pluginView frame].size.height);
-
- return pluginView != 0;
- }
-};
-
-#if JUCE_SUPPORT_CARBON
-
-//==============================================================================
-class AudioUnitPluginWindowCarbon : public AudioProcessorEditor
-{
-public:
- //==============================================================================
- AudioUnitPluginWindowCarbon (AudioUnitPluginInstance& plugin_)
- : AudioProcessorEditor (&plugin_),
- plugin (plugin_),
- viewComponent (0)
- {
- addAndMakeVisible (innerWrapper = new InnerWrapperComponent (this));
-
- setOpaque (true);
- setVisible (true);
- setSize (400, 300);
-
- ComponentDescription viewList [16];
- UInt32 viewListSize = sizeof (viewList);
- AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global,
- 0, &viewList, &viewListSize);
-
- componentRecord = FindNextComponent (0, &viewList[0]);
- }
-
- ~AudioUnitPluginWindowCarbon()
- {
- innerWrapper = 0;
-
- if (isValid())
- plugin.editorBeingDeleted (this);
- }
-
- bool isValid() const throw() { return componentRecord != 0; }
-
- //==============================================================================
- void paint (Graphics& g)
- {
- g.fillAll (Colours::black);
- }
-
- void resized()
- {
- innerWrapper->setSize (getWidth(), getHeight());
- }
-
- //==============================================================================
- bool keyStateChanged (bool)
- {
- return false;
- }
-
- bool keyPressed (const KeyPress&)
- {
- return false;
- }
-
- //==============================================================================
- AudioUnit getAudioUnit() const { return plugin.audioUnit; }
-
- AudioUnitCarbonView getViewComponent()
- {
- if (viewComponent == 0 && componentRecord != 0)
- viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord);
-
- return viewComponent;
- }
-
- void closeViewComponent()
- {
- if (viewComponent != 0)
- {
- CloseComponent (viewComponent);
- viewComponent = 0;
- }
- }
-
- //==============================================================================
- juce_UseDebuggingNewOperator
-
-private:
- AudioUnitPluginInstance& plugin;
- ComponentRecord* componentRecord;
- AudioUnitCarbonView viewComponent;
-
- //==============================================================================
- class InnerWrapperComponent : public CarbonViewWrapperComponent
- {
- public:
- InnerWrapperComponent (AudioUnitPluginWindowCarbon* const owner_)
- : owner (owner_)
- {
- }
-
- ~InnerWrapperComponent()
- {
- deleteWindow();
- }
-
- HIViewRef attachView (WindowRef windowRef, HIViewRef rootView)
- {
- log ("Opening AU GUI: " + owner->plugin.getName());
-
- AudioUnitCarbonView viewComponent = owner->getViewComponent();
-
- if (viewComponent == 0)
- return 0;
-
- Float32Point pos = { 0, 0 };
- Float32Point size = { 250, 200 };
-
- HIViewRef pluginView = 0;
-
- AudioUnitCarbonViewCreate (viewComponent,
- owner->getAudioUnit(),
- windowRef,
- rootView,
- &pos,
- &size,
- (ControlRef*) &pluginView);
-
- return pluginView;
- }
-
- void removeView (HIViewRef)
- {
- log ("Closing AU GUI: " + owner->plugin.getName());
-
- owner->closeViewComponent();
- }
-
- private:
- AudioUnitPluginWindowCarbon* const owner;
- };
-
- friend class InnerWrapperComponent;
- ScopedPointer innerWrapper;
-};
-
-#endif
-
-//==============================================================================
-AudioProcessorEditor* AudioUnitPluginInstance::createEditor()
-{
- ScopedPointer w (new AudioUnitPluginWindowCocoa (*this, false));
-
- if (! static_cast (static_cast (w))->isValid())
- w = 0;
-
-#if JUCE_SUPPORT_CARBON
- if (w == 0)
- {
- w = new AudioUnitPluginWindowCarbon (*this);
-
- if (! static_cast (static_cast (w))->isValid())
- w = 0;
- }
-#endif
-
- if (w == 0)
- w = new AudioUnitPluginWindowCocoa (*this, true); // use AUGenericView as a fallback
-
- return w.release();
-}
-
-
-//==============================================================================
-const String AudioUnitPluginInstance::getCategory() const
-{
- const char* result = 0;
-
- switch (componentDesc.componentType)
- {
- case kAudioUnitType_Effect:
- case kAudioUnitType_MusicEffect:
- result = "Effect";
- break;
- case kAudioUnitType_MusicDevice:
- result = "Synth";
- break;
- case kAudioUnitType_Generator:
- result = "Generator";
- break;
- case kAudioUnitType_Panner:
- result = "Panner";
- break;
- default:
- break;
- }
-
- return result;
-}
-
-//==============================================================================
-int AudioUnitPluginInstance::getNumParameters()
-{
- return parameterIds.size();
-}
-
-float AudioUnitPluginInstance::getParameter (int index)
-{
- const ScopedLock sl (lock);
-
- Float32 value = 0.0f;
-
- if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size())
- {
- AudioUnitGetParameter (audioUnit,
- (UInt32) parameterIds.getUnchecked (index),
- kAudioUnitScope_Global, 0,
- &value);
- }
-
- return value;
-}
-
-void AudioUnitPluginInstance::setParameter (int index, float newValue)
-{
- const ScopedLock sl (lock);
-
- if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size())
- {
- AudioUnitSetParameter (audioUnit,
- (UInt32) parameterIds.getUnchecked (index),
- kAudioUnitScope_Global, 0,
- newValue, 0);
- }
-}
-
-const String AudioUnitPluginInstance::getParameterName (int index)
-{
- AudioUnitParameterInfo info;
- zerostruct (info);
- UInt32 sz = sizeof (info);
-
- String name;
-
- if (AudioUnitGetProperty (audioUnit,
- kAudioUnitProperty_ParameterInfo,
- kAudioUnitScope_Global,
- parameterIds [index], &info, &sz) == noErr)
- {
- if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0)
- name = PlatformUtilities::cfStringToJuceString (info.cfNameString);
- else
- name = String (info.name, sizeof (info.name));
- }
-
- return name;
-}
-
-const String AudioUnitPluginInstance::getParameterText (int index)
-{
- return String (getParameter (index));
-}
-
-bool AudioUnitPluginInstance::isParameterAutomatable (int index) const
-{
- AudioUnitParameterInfo info;
- UInt32 sz = sizeof (info);
-
- if (AudioUnitGetProperty (audioUnit,
- kAudioUnitProperty_ParameterInfo,
- kAudioUnitScope_Global,
- parameterIds [index], &info, &sz) == noErr)
- {
- return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0;
- }
-
- return true;
-}
-
-//==============================================================================
-int AudioUnitPluginInstance::getNumPrograms()
-{
- CFArrayRef presets;
- UInt32 sz = sizeof (CFArrayRef);
- int num = 0;
-
- if (AudioUnitGetProperty (audioUnit,
- kAudioUnitProperty_FactoryPresets,
- kAudioUnitScope_Global,
- 0, &presets, &sz) == noErr)
- {
- num = (int) CFArrayGetCount (presets);
- CFRelease (presets);
- }
-
- return num;
-}
-
-int AudioUnitPluginInstance::getCurrentProgram()
-{
- AUPreset current;
- current.presetNumber = 0;
- UInt32 sz = sizeof (AUPreset);
-
- AudioUnitGetProperty (audioUnit,
- kAudioUnitProperty_FactoryPresets,
- kAudioUnitScope_Global,
- 0, ¤t, &sz);
-
- return current.presetNumber;
-}
-
-void AudioUnitPluginInstance::setCurrentProgram (int newIndex)
-{
- AUPreset current;
- current.presetNumber = newIndex;
- current.presetName = 0;
-
- AudioUnitSetProperty (audioUnit,
- kAudioUnitProperty_FactoryPresets,
- kAudioUnitScope_Global,
- 0, ¤t, sizeof (AUPreset));
-}
-
-const String AudioUnitPluginInstance::getProgramName (int index)
-{
- String s;
- CFArrayRef presets;
- UInt32 sz = sizeof (CFArrayRef);
-
- if (AudioUnitGetProperty (audioUnit,
- kAudioUnitProperty_FactoryPresets,
- kAudioUnitScope_Global,
- 0, &presets, &sz) == noErr)
- {
- for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i)
- {
- const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i);
-
- if (p != 0 && p->presetNumber == index)
- {
- s = PlatformUtilities::cfStringToJuceString (p->presetName);
- break;
- }
- }
-
- CFRelease (presets);
- }
-
- return s;
-}
-
-void AudioUnitPluginInstance::changeProgramName (int index, const String& newName)
-{
- jassertfalse; // xxx not implemented!
-}
-
-//==============================================================================
-const String AudioUnitPluginInstance::getInputChannelName (const int index) const
-{
- if (((unsigned int) index) < (unsigned int) getNumInputChannels())
- return "Input " + String (index + 1);
-
- return String::empty;
-}
-
-bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const
-{
- if (((unsigned int) index) >= (unsigned int) getNumInputChannels())
- return false;
-
-
- return true;
-}
-
-const String AudioUnitPluginInstance::getOutputChannelName (const int index) const
-{
- if (((unsigned int) index) < (unsigned int) getNumOutputChannels())
- return "Output " + String (index + 1);
-
- return String::empty;
-}
-
-bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const
-{
- if (((unsigned int) index) >= (unsigned int) getNumOutputChannels())
- return false;
-
- return true;
-}
-
-//==============================================================================
-void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData)
-{
- getCurrentProgramStateInformation (destData);
-}
-
-void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData)
-{
- CFPropertyListRef propertyList = 0;
- UInt32 sz = sizeof (CFPropertyListRef);
-
- if (AudioUnitGetProperty (audioUnit,
- kAudioUnitProperty_ClassInfo,
- kAudioUnitScope_Global,
- 0, &propertyList, &sz) == noErr)
- {
- CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault);
- CFWriteStreamOpen (stream);
-
- CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0);
- CFWriteStreamClose (stream);
-
- CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten);
-
- destData.setSize (bytesWritten);
- destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize());
- CFRelease (data);
-
- CFRelease (stream);
- CFRelease (propertyList);
- }
-}
-
-void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes)
-{
- setCurrentProgramStateInformation (data, sizeInBytes);
-}
-
-void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
-{
- CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault,
- (const UInt8*) data,
- sizeInBytes,
- kCFAllocatorNull);
- CFReadStreamOpen (stream);
-
- CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0;
- CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault,
- stream,
- 0,
- kCFPropertyListImmutable,
- &format,
- 0);
- CFRelease (stream);
-
- if (propertyList != 0)
- AudioUnitSetProperty (audioUnit,
- kAudioUnitProperty_ClassInfo,
- kAudioUnitScope_Global,
- 0, &propertyList, sizeof (propertyList));
-}
-
-//==============================================================================
-//==============================================================================
-AudioUnitPluginFormat::AudioUnitPluginFormat()
-{
-}
-
-AudioUnitPluginFormat::~AudioUnitPluginFormat()
-{
-}
-
-void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results,
- const String& fileOrIdentifier)
-{
- if (! fileMightContainThisPluginType (fileOrIdentifier))
- return;
-
- PluginDescription desc;
- desc.fileOrIdentifier = fileOrIdentifier;
- desc.uid = 0;
-
- try
- {
- ScopedPointer createdInstance (createInstanceFromDescription (desc));
- AudioUnitPluginInstance* const auInstance = dynamic_cast ((AudioPluginInstance*) createdInstance);
-
- if (auInstance != 0)
- {
- auInstance->fillInPluginDescription (desc);
- results.add (new PluginDescription (desc));
- }
- }
- catch (...)
- {
- // crashed while loading...
- }
-}
-
-AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc)
-{
- if (fileMightContainThisPluginType (desc.fileOrIdentifier))
- {
- ScopedPointer result (new AudioUnitPluginInstance (desc.fileOrIdentifier));
-
- if (result->audioUnit != 0)
- {
- result->initialise();
- return result.release();
- }
- }
-
- return 0;
-}
-
-const StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& /*directoriesToSearch*/,
- const bool /*recursive*/)
-{
- StringArray result;
- ComponentRecord* comp = 0;
- ComponentDescription desc;
- zerostruct (desc);
-
- for (;;)
- {
- zerostruct (desc);
- comp = FindNextComponent (comp, &desc);
-
- if (comp == 0)
- break;
-
- GetComponentInfo (comp, &desc, 0, 0, 0);
-
- if (desc.componentType == kAudioUnitType_MusicDevice
- || desc.componentType == kAudioUnitType_MusicEffect
- || desc.componentType == kAudioUnitType_Effect
- || desc.componentType == kAudioUnitType_Generator
- || desc.componentType == kAudioUnitType_Panner)
- {
- const String s (AudioUnitFormatHelpers::createAUPluginIdentifier (desc));
- DBG (s);
- result.add (s);
- }
- }
-
- return result;
-}
-
-bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
-{
- ComponentDescription desc;
-
- String name, version, manufacturer;
- if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer))
- return FindNextComponent (0, &desc) != 0;
-
- const File f (fileOrIdentifier);
-
- return f.hasFileExtension (".component")
- && f.isDirectory();
-}
-
-const String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
-{
- ComponentDescription desc;
- String name, version, manufacturer;
- AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer);
-
- if (name.isEmpty())
- name = fileOrIdentifier;
-
- return name;
-}
-
-bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc)
-{
- if (desc.fileOrIdentifier.startsWithIgnoreCase (AudioUnitFormatHelpers::auIdentifierPrefix))
- return fileMightContainThisPluginType (desc.fileOrIdentifier);
- else
- return File (desc.fileOrIdentifier).exists();
-}
-
-const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch()
-{
- return FileSearchPath ("/(Default AudioUnit locations)");
-}
-
-#endif
-
-END_JUCE_NAMESPACE
-
-#undef log
-
-#endif
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-10 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_TargetPlatform.h"
+#include "../../../../juce_Config.h"
+
+#if JUCE_PLUGINHOST_AU && ! (JUCE_LINUX || JUCE_WINDOWS)
+
+#include
+#include
+#include
+
+#if JUCE_SUPPORT_CARBON
+#include
+#include
+#endif
+
+#include "../../../core/juce_StandardHeader.h"
+
+BEGIN_JUCE_NAMESPACE
+
+#include "juce_AudioUnitPluginFormat.h"
+#include "../juce_PluginDescription.h"
+#include "../../../threads/juce_ScopedLock.h"
+#include "../../../events/juce_Timer.h"
+#include "../../../core/juce_PlatformUtilities.h"
+#include "../../../gui/components/layout/juce_ComponentMovementWatcher.h"
+#include "../../../gui/components/windows/juce_ComponentPeer.h"
+#include "../../../gui/components/special/juce_NSViewComponent.h"
+#if JUCE_MAC && JUCE_SUPPORT_CARBON
+#include "../../../native/mac/juce_mac_CarbonViewWrapperComponent.h"
+#endif
+
+#if JUCE_MAC
+
+// Change this to disable logging of various activities
+#ifndef AU_LOGGING
+ #define AU_LOGGING 1
+#endif
+
+#if AU_LOGGING
+ #define log(a) Logger::writeToLog(a);
+#else
+ #define log(a)
+#endif
+
+namespace AudioUnitFormatHelpers
+{
+ static int insideCallback = 0;
+
+ static const String osTypeToString (OSType type)
+ {
+ char s[4];
+ s[0] = (char) (((uint32) type) >> 24);
+ s[1] = (char) (((uint32) type) >> 16);
+ s[2] = (char) (((uint32) type) >> 8);
+ s[3] = (char) ((uint32) type);
+ return String (s, 4);
+ }
+
+ static OSType stringToOSType (const String& s1)
+ {
+ const String s (s1 + " ");
+
+ return (((OSType) (unsigned char) s[0]) << 24)
+ | (((OSType) (unsigned char) s[1]) << 16)
+ | (((OSType) (unsigned char) s[2]) << 8)
+ | ((OSType) (unsigned char) s[3]);
+ }
+
+ static const char* auIdentifierPrefix = "AudioUnit:";
+
+ static const String createAUPluginIdentifier (const ComponentDescription& desc)
+ {
+ jassert (osTypeToString ('abcd') == "abcd"); // agh, must have got the endianness wrong..
+ jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto
+
+ String s (auIdentifierPrefix);
+
+ if (desc.componentType == kAudioUnitType_MusicDevice)
+ s << "Synths/";
+ else if (desc.componentType == kAudioUnitType_MusicEffect
+ || desc.componentType == kAudioUnitType_Effect)
+ s << "Effects/";
+ else if (desc.componentType == kAudioUnitType_Generator)
+ s << "Generators/";
+ else if (desc.componentType == kAudioUnitType_Panner)
+ s << "Panners/";
+
+ s << osTypeToString (desc.componentType) << ","
+ << osTypeToString (desc.componentSubType) << ","
+ << osTypeToString (desc.componentManufacturer);
+
+ return s;
+ }
+
+ static void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer)
+ {
+ Handle componentNameHandle = NewHandle (sizeof (void*));
+ Handle componentInfoHandle = NewHandle (sizeof (void*));
+
+ if (componentNameHandle != 0 && componentInfoHandle != 0)
+ {
+ ComponentDescription desc;
+
+ if (GetComponentInfo (comp, &desc, componentNameHandle, componentInfoHandle, 0) == noErr)
+ {
+ ConstStr255Param nameString = (ConstStr255Param) (*componentNameHandle);
+ ConstStr255Param infoString = (ConstStr255Param) (*componentInfoHandle);
+
+ if (nameString != 0 && nameString[0] != 0)
+ {
+ const String all ((const char*) nameString + 1, nameString[0]);
+ DBG ("name: "+ all);
+
+ manufacturer = all.upToFirstOccurrenceOf (":", false, false).trim();
+ name = all.fromFirstOccurrenceOf (":", false, false).trim();
+ }
+
+ if (infoString != 0 && infoString[0] != 0)
+ {
+ DBG ("info: " + String ((const char*) infoString + 1, infoString[0]));
+ }
+
+ if (name.isEmpty())
+ name = "";
+ }
+
+ DisposeHandle (componentNameHandle);
+ DisposeHandle (componentInfoHandle);
+ }
+ }
+
+ static bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc,
+ String& name, String& version, String& manufacturer)
+ {
+ zerostruct (desc);
+
+ if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
+ {
+ String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (':'),
+ fileOrIdentifier.lastIndexOfChar ('/')) + 1));
+
+ StringArray tokens;
+ tokens.addTokens (s, ",", String::empty);
+ tokens.trim();
+ tokens.removeEmptyStrings();
+
+ if (tokens.size() == 3)
+ {
+ desc.componentType = stringToOSType (tokens[0]);
+ desc.componentSubType = stringToOSType (tokens[1]);
+ desc.componentManufacturer = stringToOSType (tokens[2]);
+
+ ComponentRecord* comp = FindNextComponent (0, &desc);
+
+ if (comp != 0)
+ {
+ getAUDetails (comp, name, manufacturer);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
+
+//==============================================================================
+class AudioUnitPluginWindowCarbon;
+class AudioUnitPluginWindowCocoa;
+
+//==============================================================================
+class AudioUnitPluginInstance : public AudioPluginInstance
+{
+public:
+ //==============================================================================
+ ~AudioUnitPluginInstance();
+
+ //==============================================================================
+ // AudioPluginInstance methods:
+
+ void fillInPluginDescription (PluginDescription& desc) const
+ {
+ desc.name = pluginName;
+ desc.fileOrIdentifier = AudioUnitFormatHelpers::createAUPluginIdentifier (componentDesc);
+ desc.uid = ((int) componentDesc.componentType)
+ ^ ((int) componentDesc.componentSubType)
+ ^ ((int) componentDesc.componentManufacturer);
+ desc.lastFileModTime = 0;
+ desc.pluginFormatName = "AudioUnit";
+ desc.category = getCategory();
+ desc.manufacturerName = manufacturer;
+ desc.version = version;
+ desc.numInputChannels = getNumInputChannels();
+ desc.numOutputChannels = getNumOutputChannels();
+ desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice);
+ }
+
+ const String getName() const { return pluginName; }
+ bool acceptsMidi() const { return wantsMidiMessages; }
+ bool producesMidi() const { return false; }
+
+ //==============================================================================
+ // AudioProcessor methods:
+
+ void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
+ void releaseResources();
+ void processBlock (AudioSampleBuffer& buffer,
+ MidiBuffer& midiMessages);
+
+ AudioProcessorEditor* createEditor();
+
+ const String getInputChannelName (const int index) const;
+ bool isInputChannelStereoPair (int index) const;
+
+ const String getOutputChannelName (const int index) const;
+ bool isOutputChannelStereoPair (int index) const;
+
+ //==============================================================================
+ int getNumParameters();
+ float getParameter (int index);
+ void setParameter (int index, float newValue);
+ const String getParameterName (int index);
+ const String getParameterText (int index);
+ bool isParameterAutomatable (int index) const;
+
+ //==============================================================================
+ int getNumPrograms();
+ int getCurrentProgram();
+ void setCurrentProgram (int index);
+ const String getProgramName (int index);
+ void changeProgramName (int index, const String& newName);
+
+ //==============================================================================
+ void getStateInformation (MemoryBlock& destData);
+ void getCurrentProgramStateInformation (MemoryBlock& destData);
+ void setStateInformation (const void* data, int sizeInBytes);
+ void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
+
+ //==============================================================================
+ juce_UseDebuggingNewOperator
+
+private:
+ friend class AudioUnitPluginWindowCarbon;
+ friend class AudioUnitPluginWindowCocoa;
+ friend class AudioUnitPluginFormat;
+
+ ComponentDescription componentDesc;
+ String pluginName, manufacturer, version;
+ String fileOrIdentifier;
+ CriticalSection lock;
+ bool initialised, wantsMidiMessages, wasPlaying;
+
+ HeapBlock outputBufferList;
+ AudioTimeStamp timeStamp;
+ AudioSampleBuffer* currentBuffer;
+
+ AudioUnit audioUnit;
+ Array parameterIds;
+
+ //==============================================================================
+ bool getComponentDescFromFile (const String& fileOrIdentifier);
+ void initialise();
+
+ //==============================================================================
+ OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
+ const AudioTimeStamp* inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList* ioData) const;
+
+ static OSStatus renderGetInputCallback (void* inRefCon,
+ AudioUnitRenderActionFlags* ioActionFlags,
+ const AudioTimeStamp* inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList* ioData)
+ {
+ return ((AudioUnitPluginInstance*) inRefCon)
+ ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
+ }
+
+ OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const;
+ OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator,
+ UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const;
+ OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged,
+ Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
+ Float64* outCycleStartBeat, Float64* outCycleEndBeat);
+
+ static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo)
+ {
+ return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo);
+ }
+
+ static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat,
+ Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator,
+ Float64* outCurrentMeasureDownBeat)
+ {
+ return ((AudioUnitPluginInstance*) inHostUserData)
+ ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator,
+ outTimeSig_Denominator, outCurrentMeasureDownBeat);
+ }
+
+ static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged,
+ Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
+ Float64* outCycleStartBeat, Float64* outCycleEndBeat)
+ {
+ return ((AudioUnitPluginInstance*) inHostUserData)
+ ->getTransportState (outIsPlaying, outTransportStateChanged,
+ outCurrentSampleInTimeLine, outIsCycling,
+ outCycleStartBeat, outCycleEndBeat);
+ }
+
+ //==============================================================================
+ void getNumChannels (int& numIns, int& numOuts)
+ {
+ numIns = 0;
+ numOuts = 0;
+
+ AUChannelInfo supportedChannels [128];
+ UInt32 supportedChannelsSize = sizeof (supportedChannels);
+
+ if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global,
+ 0, supportedChannels, &supportedChannelsSize) == noErr
+ && supportedChannelsSize > 0)
+ {
+ for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
+ {
+ numIns = jmax (numIns, (int) supportedChannels[i].inChannels);
+ numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels);
+ }
+ }
+ else
+ {
+ // (this really means the plugin will take any number of ins/outs as long
+ // as they are the same)
+ numIns = numOuts = 2;
+ }
+ }
+
+ const String getCategory() const;
+
+ //==============================================================================
+ AudioUnitPluginInstance (const String& fileOrIdentifier);
+};
+
+//==============================================================================
+AudioUnitPluginInstance::AudioUnitPluginInstance (const String& fileOrIdentifier)
+ : fileOrIdentifier (fileOrIdentifier),
+ initialised (false),
+ wantsMidiMessages (false),
+ audioUnit (0),
+ currentBuffer (0)
+{
+ using namespace AudioUnitFormatHelpers;
+
+ try
+ {
+ ++insideCallback;
+
+ log ("Opening AU: " + fileOrIdentifier);
+
+ if (getComponentDescFromFile (fileOrIdentifier))
+ {
+ ComponentRecord* const comp = FindNextComponent (0, &componentDesc);
+
+ if (comp != 0)
+ {
+ audioUnit = (AudioUnit) OpenComponent (comp);
+
+ wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice
+ || componentDesc.componentType == kAudioUnitType_MusicEffect;
+ }
+ }
+
+ --insideCallback;
+ }
+ catch (...)
+ {
+ --insideCallback;
+ }
+}
+
+AudioUnitPluginInstance::~AudioUnitPluginInstance()
+{
+ const ScopedLock sl (lock);
+
+ jassert (AudioUnitFormatHelpers::insideCallback == 0);
+
+ if (audioUnit != 0)
+ {
+ AudioUnitUninitialize (audioUnit);
+ CloseComponent (audioUnit);
+ audioUnit = 0;
+ }
+}
+
+bool AudioUnitPluginInstance::getComponentDescFromFile (const String& fileOrIdentifier)
+{
+ zerostruct (componentDesc);
+
+ if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, componentDesc, pluginName, version, manufacturer))
+ return true;
+
+ const File file (fileOrIdentifier);
+ if (! file.hasFileExtension (".component"))
+ return false;
+
+ const char* const utf8 = fileOrIdentifier.toUTF8();
+ CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8,
+ strlen (utf8), file.isDirectory());
+ if (url != 0)
+ {
+ CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
+ CFRelease (url);
+
+ if (bundleRef != 0)
+ {
+ CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"));
+
+ if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID())
+ pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name);
+
+ if (pluginName.isEmpty())
+ pluginName = file.getFileNameWithoutExtension();
+
+ CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion"));
+
+ if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID())
+ version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString);
+
+ CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString"));
+
+ if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID())
+ manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString);
+
+ short resFileId = CFBundleOpenBundleResourceMap (bundleRef);
+ UseResFile (resFileId);
+
+ for (int i = 1; i <= Count1Resources ('thng'); ++i)
+ {
+ Handle h = Get1IndResource ('thng', i);
+
+ if (h != 0)
+ {
+ HLock (h);
+ const uint32* const types = (const uint32*) *h;
+
+ if (types[0] == kAudioUnitType_MusicDevice
+ || types[0] == kAudioUnitType_MusicEffect
+ || types[0] == kAudioUnitType_Effect
+ || types[0] == kAudioUnitType_Generator
+ || types[0] == kAudioUnitType_Panner)
+ {
+ componentDesc.componentType = types[0];
+ componentDesc.componentSubType = types[1];
+ componentDesc.componentManufacturer = types[2];
+ break;
+ }
+
+ HUnlock (h);
+ ReleaseResource (h);
+ }
+ }
+
+ CFBundleCloseBundleResourceMap (bundleRef, resFileId);
+ CFRelease (bundleRef);
+ }
+ }
+
+ return componentDesc.componentType != 0 && componentDesc.componentSubType != 0;
+}
+
+//==============================================================================
+void AudioUnitPluginInstance::initialise()
+{
+ if (initialised || audioUnit == 0)
+ return;
+
+ log ("Initialising AU: " + pluginName);
+
+ parameterIds.clear();
+
+ {
+ UInt32 paramListSize = 0;
+ AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
+ 0, 0, ¶mListSize);
+
+ if (paramListSize > 0)
+ {
+ parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int));
+
+ AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
+ 0, ¶meterIds.getReference(0), ¶mListSize);
+ }
+ }
+
+ {
+ AURenderCallbackStruct info;
+ zerostruct (info);
+ info.inputProcRefCon = this;
+ info.inputProc = renderGetInputCallback;
+
+ AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
+ 0, &info, sizeof (info));
+ }
+
+ {
+ HostCallbackInfo info;
+ zerostruct (info);
+ info.hostUserData = this;
+ info.beatAndTempoProc = getBeatAndTempoCallback;
+ info.musicalTimeLocationProc = getMusicalTimeLocationCallback;
+ info.transportStateProc = getTransportStateCallback;
+
+ AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global,
+ 0, &info, sizeof (info));
+ }
+
+ int numIns, numOuts;
+ getNumChannels (numIns, numOuts);
+ setPlayConfigDetails (numIns, numOuts, 0, 0);
+
+ initialised = AudioUnitInitialize (audioUnit) == noErr;
+
+ setLatencySamples (0);
+}
+
+
+//==============================================================================
+void AudioUnitPluginInstance::prepareToPlay (double sampleRate_,
+ int samplesPerBlockExpected)
+{
+ if (audioUnit != 0)
+ {
+ Float64 sampleRateIn = 0, sampleRateOut = 0;
+ UInt32 sampleRateSize = sizeof (sampleRateIn);
+ AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRateIn, &sampleRateSize);
+ AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRateOut, &sampleRateSize);
+
+ if (sampleRateIn != sampleRate_ || sampleRateOut != sampleRate_)
+ {
+ if (initialised)
+ {
+ AudioUnitUninitialize (audioUnit);
+ initialised = false;
+ }
+
+ Float64 sr = sampleRate_;
+ AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sr, sizeof (Float64));
+ AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sr, sizeof (Float64));
+ }
+ }
+
+ initialise();
+
+ if (initialised)
+ {
+ int numIns, numOuts;
+ getNumChannels (numIns, numOuts);
+
+ setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected);
+
+ Float64 latencySecs = 0.0;
+ UInt32 latencySize = sizeof (latencySecs);
+ AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
+ 0, &latencySecs, &latencySize);
+
+ setLatencySamples (roundToInt (latencySecs * sampleRate_));
+
+ AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
+ AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
+ AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
+
+ AudioStreamBasicDescription stream;
+ zerostruct (stream);
+ stream.mSampleRate = sampleRate_;
+ stream.mFormatID = kAudioFormatLinearPCM;
+ stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
+ stream.mFramesPerPacket = 1;
+ stream.mBytesPerPacket = 4;
+ stream.mBytesPerFrame = 4;
+ stream.mBitsPerChannel = 32;
+ stream.mChannelsPerFrame = numIns;
+
+ OSStatus err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
+ 0, &stream, sizeof (stream));
+
+ stream.mChannelsPerFrame = numOuts;
+
+ err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
+ 0, &stream, sizeof (stream));
+
+ outputBufferList.calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1), 1);
+ outputBufferList->mNumberBuffers = numOuts;
+
+ for (int i = numOuts; --i >= 0;)
+ outputBufferList->mBuffers[i].mNumberChannels = 1;
+
+ zerostruct (timeStamp);
+ timeStamp.mSampleTime = 0;
+ timeStamp.mHostTime = AudioGetCurrentHostTime();
+ timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid;
+
+ currentBuffer = 0;
+ wasPlaying = false;
+ }
+}
+
+void AudioUnitPluginInstance::releaseResources()
+{
+ if (initialised)
+ {
+ AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
+ AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
+ AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
+
+ outputBufferList.free();
+ currentBuffer = 0;
+ }
+}
+
+OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
+ const AudioTimeStamp* inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList* ioData) const
+{
+ if (inBusNumber == 0
+ && currentBuffer != 0)
+ {
+ jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling
+
+ for (int i = 0; i < ioData->mNumberBuffers; ++i)
+ {
+ if (i < currentBuffer->getNumChannels())
+ {
+ memcpy (ioData->mBuffers[i].mData,
+ currentBuffer->getSampleData (i, 0),
+ sizeof (float) * inNumberFrames);
+ }
+ else
+ {
+ zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames);
+ }
+ }
+ }
+
+ return noErr;
+}
+
+void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer,
+ MidiBuffer& midiMessages)
+{
+ const int numSamples = buffer.getNumSamples();
+
+ if (initialised)
+ {
+ AudioUnitRenderActionFlags flags = 0;
+
+ timeStamp.mHostTime = AudioGetCurrentHostTime();
+
+ for (int i = getNumOutputChannels(); --i >= 0;)
+ {
+ outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples;
+ outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0);
+ }
+
+ currentBuffer = &buffer;
+
+ if (wantsMidiMessages)
+ {
+ const uint8* midiEventData;
+ int midiEventSize, midiEventPosition;
+ MidiBuffer::Iterator i (midiMessages);
+
+ while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
+ {
+ if (midiEventSize <= 3)
+ MusicDeviceMIDIEvent (audioUnit,
+ midiEventData[0], midiEventData[1], midiEventData[2],
+ midiEventPosition);
+ else
+ MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize);
+ }
+
+ midiMessages.clear();
+ }
+
+ AudioUnitRender (audioUnit, &flags, &timeStamp,
+ 0, numSamples, outputBufferList);
+
+ timeStamp.mSampleTime += numSamples;
+ }
+ else
+ {
+ // Not initialised, so just bypass..
+ for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
+ buffer.clear (i, 0, buffer.getNumSamples());
+ }
+}
+
+//==============================================================================
+OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const
+{
+ AudioPlayHead* const ph = getPlayHead();
+ AudioPlayHead::CurrentPositionInfo result;
+
+ if (ph != 0 && ph->getCurrentPosition (result))
+ {
+ if (outCurrentBeat != 0)
+ *outCurrentBeat = result.ppqPosition;
+
+ if (outCurrentTempo != 0)
+ *outCurrentTempo = result.bpm;
+ }
+ else
+ {
+ if (outCurrentBeat != 0)
+ *outCurrentBeat = 0;
+
+ if (outCurrentTempo != 0)
+ *outCurrentTempo = 120.0;
+ }
+
+ return noErr;
+}
+
+OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat,
+ Float32* outTimeSig_Numerator,
+ UInt32* outTimeSig_Denominator,
+ Float64* outCurrentMeasureDownBeat) const
+{
+ AudioPlayHead* const ph = getPlayHead();
+ AudioPlayHead::CurrentPositionInfo result;
+
+ if (ph != 0 && ph->getCurrentPosition (result))
+ {
+ if (outTimeSig_Numerator != 0)
+ *outTimeSig_Numerator = result.timeSigNumerator;
+
+ if (outTimeSig_Denominator != 0)
+ *outTimeSig_Denominator = result.timeSigDenominator;
+
+ if (outDeltaSampleOffsetToNextBeat != 0)
+ *outDeltaSampleOffsetToNextBeat = 0; //xxx
+
+ if (outCurrentMeasureDownBeat != 0)
+ *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong
+ }
+ else
+ {
+ if (outDeltaSampleOffsetToNextBeat != 0)
+ *outDeltaSampleOffsetToNextBeat = 0;
+
+ if (outTimeSig_Numerator != 0)
+ *outTimeSig_Numerator = 4;
+
+ if (outTimeSig_Denominator != 0)
+ *outTimeSig_Denominator = 4;
+
+ if (outCurrentMeasureDownBeat != 0)
+ *outCurrentMeasureDownBeat = 0;
+ }
+
+ return noErr;
+}
+
+OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying,
+ Boolean* outTransportStateChanged,
+ Float64* outCurrentSampleInTimeLine,
+ Boolean* outIsCycling,
+ Float64* outCycleStartBeat,
+ Float64* outCycleEndBeat)
+{
+ AudioPlayHead* const ph = getPlayHead();
+ AudioPlayHead::CurrentPositionInfo result;
+
+ if (ph != 0 && ph->getCurrentPosition (result))
+ {
+ if (outIsPlaying != 0)
+ *outIsPlaying = result.isPlaying;
+
+ if (outTransportStateChanged != 0)
+ {
+ *outTransportStateChanged = result.isPlaying != wasPlaying;
+ wasPlaying = result.isPlaying;
+ }
+
+ if (outCurrentSampleInTimeLine != 0)
+ *outCurrentSampleInTimeLine = roundToInt (result.timeInSeconds * getSampleRate());
+
+ if (outIsCycling != 0)
+ *outIsCycling = false;
+
+ if (outCycleStartBeat != 0)
+ *outCycleStartBeat = 0;
+
+ if (outCycleEndBeat != 0)
+ *outCycleEndBeat = 0;
+ }
+ else
+ {
+ if (outIsPlaying != 0)
+ *outIsPlaying = false;
+
+ if (outTransportStateChanged != 0)
+ *outTransportStateChanged = false;
+
+ if (outCurrentSampleInTimeLine != 0)
+ *outCurrentSampleInTimeLine = 0;
+
+ if (outIsCycling != 0)
+ *outIsCycling = false;
+
+ if (outCycleStartBeat != 0)
+ *outCycleStartBeat = 0;
+
+ if (outCycleEndBeat != 0)
+ *outCycleEndBeat = 0;
+ }
+
+ return noErr;
+}
+
+
+//==============================================================================
+class AudioUnitPluginWindowCocoa : public AudioProcessorEditor
+{
+public:
+ AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& plugin_, const bool createGenericViewIfNeeded)
+ : AudioProcessorEditor (&plugin_),
+ plugin (plugin_)
+ {
+ addAndMakeVisible (&wrapper);
+
+ setOpaque (true);
+ setVisible (true);
+ setSize (100, 100);
+
+ createView (createGenericViewIfNeeded);
+ }
+
+ ~AudioUnitPluginWindowCocoa()
+ {
+ const bool wasValid = isValid();
+
+ wrapper.setView (0);
+
+ if (wasValid)
+ plugin.editorBeingDeleted (this);
+ }
+
+ bool isValid() const { return wrapper.getView() != 0; }
+
+ void paint (Graphics& g)
+ {
+ g.fillAll (Colours::white);
+ }
+
+ void resized()
+ {
+ wrapper.setSize (getWidth(), getHeight());
+ }
+
+private:
+ AudioUnitPluginInstance& plugin;
+ NSViewComponent wrapper;
+
+ bool createView (const bool createGenericViewIfNeeded)
+ {
+ NSView* pluginView = 0;
+
+ UInt32 dataSize = 0;
+ Boolean isWritable = false;
+
+ if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
+ 0, &dataSize, &isWritable) == noErr
+ && dataSize != 0
+ && AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
+ 0, &dataSize, &isWritable) == noErr)
+ {
+ HeapBlock info;
+ info.calloc (dataSize, 1);
+
+ if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
+ 0, info, &dataSize) == noErr)
+ {
+ NSString* viewClassName = (NSString*) (info->mCocoaAUViewClass[0]);
+ NSString* path = (NSString*) CFURLCopyPath (info->mCocoaAUViewBundleLocation);
+ NSBundle* viewBundle = [NSBundle bundleWithPath: [path autorelease]];
+ Class viewClass = [viewBundle classNamed: viewClassName];
+
+ if ([viewClass conformsToProtocol: @protocol (AUCocoaUIBase)]
+ && [viewClass instancesRespondToSelector: @selector (interfaceVersion)]
+ && [viewClass instancesRespondToSelector: @selector (uiViewForAudioUnit: withSize:)])
+ {
+ id factory = [[[viewClass alloc] init] autorelease];
+ pluginView = [factory uiViewForAudioUnit: plugin.audioUnit
+ withSize: NSMakeSize (getWidth(), getHeight())];
+ }
+
+ for (int i = (dataSize - sizeof (CFURLRef)) / sizeof (CFStringRef); --i >= 0;)
+ {
+ CFRelease (info->mCocoaAUViewClass[i]);
+ CFRelease (info->mCocoaAUViewBundleLocation);
+ }
+ }
+ }
+
+ if (createGenericViewIfNeeded && (pluginView == 0))
+ pluginView = [[AUGenericView alloc] initWithAudioUnit: plugin.audioUnit];
+
+ wrapper.setView (pluginView);
+
+ if (pluginView != 0)
+ setSize ([pluginView frame].size.width,
+ [pluginView frame].size.height);
+
+ return pluginView != 0;
+ }
+};
+
+#if JUCE_SUPPORT_CARBON
+
+//==============================================================================
+class AudioUnitPluginWindowCarbon : public AudioProcessorEditor
+{
+public:
+ //==============================================================================
+ AudioUnitPluginWindowCarbon (AudioUnitPluginInstance& plugin_)
+ : AudioProcessorEditor (&plugin_),
+ plugin (plugin_),
+ viewComponent (0)
+ {
+ addAndMakeVisible (innerWrapper = new InnerWrapperComponent (this));
+
+ setOpaque (true);
+ setVisible (true);
+ setSize (400, 300);
+
+ ComponentDescription viewList [16];
+ UInt32 viewListSize = sizeof (viewList);
+ AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global,
+ 0, &viewList, &viewListSize);
+
+ componentRecord = FindNextComponent (0, &viewList[0]);
+ }
+
+ ~AudioUnitPluginWindowCarbon()
+ {
+ innerWrapper = 0;
+
+ if (isValid())
+ plugin.editorBeingDeleted (this);
+ }
+
+ bool isValid() const throw() { return componentRecord != 0; }
+
+ //==============================================================================
+ void paint (Graphics& g)
+ {
+ g.fillAll (Colours::black);
+ }
+
+ void resized()
+ {
+ innerWrapper->setSize (getWidth(), getHeight());
+ }
+
+ //==============================================================================
+ bool keyStateChanged (bool)
+ {
+ return false;
+ }
+
+ bool keyPressed (const KeyPress&)
+ {
+ return false;
+ }
+
+ //==============================================================================
+ AudioUnit getAudioUnit() const { return plugin.audioUnit; }
+
+ AudioUnitCarbonView getViewComponent()
+ {
+ if (viewComponent == 0 && componentRecord != 0)
+ viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord);
+
+ return viewComponent;
+ }
+
+ void closeViewComponent()
+ {
+ if (viewComponent != 0)
+ {
+ log ("Closing AU GUI: " + plugin.getName());
+
+ CloseComponent (viewComponent);
+ viewComponent = 0;
+ }
+ }
+
+ //==============================================================================
+ juce_UseDebuggingNewOperator
+
+private:
+ AudioUnitPluginInstance& plugin;
+ ComponentRecord* componentRecord;
+ AudioUnitCarbonView viewComponent;
+
+ //==============================================================================
+ class InnerWrapperComponent : public CarbonViewWrapperComponent
+ {
+ public:
+ InnerWrapperComponent (AudioUnitPluginWindowCarbon* const owner_)
+ : owner (owner_)
+ {
+ }
+
+ ~InnerWrapperComponent()
+ {
+ deleteWindow();
+ }
+
+ HIViewRef attachView (WindowRef windowRef, HIViewRef rootView)
+ {
+ log ("Opening AU GUI: " + owner->plugin.getName());
+
+ AudioUnitCarbonView viewComponent = owner->getViewComponent();
+
+ if (viewComponent == 0)
+ return 0;
+
+ Float32Point pos = { 0, 0 };
+ Float32Point size = { 250, 200 };
+
+ HIViewRef pluginView = 0;
+
+ AudioUnitCarbonViewCreate (viewComponent,
+ owner->getAudioUnit(),
+ windowRef,
+ rootView,
+ &pos,
+ &size,
+ (ControlRef*) &pluginView);
+
+ return pluginView;
+ }
+
+ void removeView (HIViewRef)
+ {
+ owner->closeViewComponent();
+ }
+
+ private:
+ AudioUnitPluginWindowCarbon* const owner;
+ };
+
+ friend class InnerWrapperComponent;
+ ScopedPointer innerWrapper;
+};
+
+#endif
+
+//==============================================================================
+AudioProcessorEditor* AudioUnitPluginInstance::createEditor()
+{
+ ScopedPointer w (new AudioUnitPluginWindowCocoa (*this, false));
+
+ if (! static_cast (static_cast (w))->isValid())
+ w = 0;
+
+#if JUCE_SUPPORT_CARBON
+ if (w == 0)
+ {
+ w = new AudioUnitPluginWindowCarbon (*this);
+
+ if (! static_cast (static_cast (w))->isValid())
+ w = 0;
+ }
+#endif
+
+ if (w == 0)
+ w = new AudioUnitPluginWindowCocoa (*this, true); // use AUGenericView as a fallback
+
+ return w.release();
+}
+
+
+//==============================================================================
+const String AudioUnitPluginInstance::getCategory() const
+{
+ const char* result = 0;
+
+ switch (componentDesc.componentType)
+ {
+ case kAudioUnitType_Effect:
+ case kAudioUnitType_MusicEffect:
+ result = "Effect";
+ break;
+ case kAudioUnitType_MusicDevice:
+ result = "Synth";
+ break;
+ case kAudioUnitType_Generator:
+ result = "Generator";
+ break;
+ case kAudioUnitType_Panner:
+ result = "Panner";
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+//==============================================================================
+int AudioUnitPluginInstance::getNumParameters()
+{
+ return parameterIds.size();
+}
+
+float AudioUnitPluginInstance::getParameter (int index)
+{
+ const ScopedLock sl (lock);
+
+ Float32 value = 0.0f;
+
+ if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size())
+ {
+ AudioUnitGetParameter (audioUnit,
+ (UInt32) parameterIds.getUnchecked (index),
+ kAudioUnitScope_Global, 0,
+ &value);
+ }
+
+ return value;
+}
+
+void AudioUnitPluginInstance::setParameter (int index, float newValue)
+{
+ const ScopedLock sl (lock);
+
+ if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size())
+ {
+ AudioUnitSetParameter (audioUnit,
+ (UInt32) parameterIds.getUnchecked (index),
+ kAudioUnitScope_Global, 0,
+ newValue, 0);
+ }
+}
+
+const String AudioUnitPluginInstance::getParameterName (int index)
+{
+ AudioUnitParameterInfo info;
+ zerostruct (info);
+ UInt32 sz = sizeof (info);
+
+ String name;
+
+ if (AudioUnitGetProperty (audioUnit,
+ kAudioUnitProperty_ParameterInfo,
+ kAudioUnitScope_Global,
+ parameterIds [index], &info, &sz) == noErr)
+ {
+ if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0)
+ name = PlatformUtilities::cfStringToJuceString (info.cfNameString);
+ else
+ name = String (info.name, sizeof (info.name));
+ }
+
+ return name;
+}
+
+const String AudioUnitPluginInstance::getParameterText (int index)
+{
+ return String (getParameter (index));
+}
+
+bool AudioUnitPluginInstance::isParameterAutomatable (int index) const
+{
+ AudioUnitParameterInfo info;
+ UInt32 sz = sizeof (info);
+
+ if (AudioUnitGetProperty (audioUnit,
+ kAudioUnitProperty_ParameterInfo,
+ kAudioUnitScope_Global,
+ parameterIds [index], &info, &sz) == noErr)
+ {
+ return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0;
+ }
+
+ return true;
+}
+
+//==============================================================================
+int AudioUnitPluginInstance::getNumPrograms()
+{
+ CFArrayRef presets;
+ UInt32 sz = sizeof (CFArrayRef);
+ int num = 0;
+
+ if (AudioUnitGetProperty (audioUnit,
+ kAudioUnitProperty_FactoryPresets,
+ kAudioUnitScope_Global,
+ 0, &presets, &sz) == noErr)
+ {
+ num = (int) CFArrayGetCount (presets);
+ CFRelease (presets);
+ }
+
+ return num;
+}
+
+int AudioUnitPluginInstance::getCurrentProgram()
+{
+ AUPreset current;
+ current.presetNumber = 0;
+ UInt32 sz = sizeof (AUPreset);
+
+ AudioUnitGetProperty (audioUnit,
+ kAudioUnitProperty_FactoryPresets,
+ kAudioUnitScope_Global,
+ 0, ¤t, &sz);
+
+ return current.presetNumber;
+}
+
+void AudioUnitPluginInstance::setCurrentProgram (int newIndex)
+{
+ AUPreset current;
+ current.presetNumber = newIndex;
+ current.presetName = 0;
+
+ AudioUnitSetProperty (audioUnit,
+ kAudioUnitProperty_FactoryPresets,
+ kAudioUnitScope_Global,
+ 0, ¤t, sizeof (AUPreset));
+}
+
+const String AudioUnitPluginInstance::getProgramName (int index)
+{
+ String s;
+ CFArrayRef presets;
+ UInt32 sz = sizeof (CFArrayRef);
+
+ if (AudioUnitGetProperty (audioUnit,
+ kAudioUnitProperty_FactoryPresets,
+ kAudioUnitScope_Global,
+ 0, &presets, &sz) == noErr)
+ {
+ for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i)
+ {
+ const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i);
+
+ if (p != 0 && p->presetNumber == index)
+ {
+ s = PlatformUtilities::cfStringToJuceString (p->presetName);
+ break;
+ }
+ }
+
+ CFRelease (presets);
+ }
+
+ return s;
+}
+
+void AudioUnitPluginInstance::changeProgramName (int index, const String& newName)
+{
+ jassertfalse; // xxx not implemented!
+}
+
+//==============================================================================
+const String AudioUnitPluginInstance::getInputChannelName (const int index) const
+{
+ if (((unsigned int) index) < (unsigned int) getNumInputChannels())
+ return "Input " + String (index + 1);
+
+ return String::empty;
+}
+
+bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const
+{
+ if (((unsigned int) index) >= (unsigned int) getNumInputChannels())
+ return false;
+
+
+ return true;
+}
+
+const String AudioUnitPluginInstance::getOutputChannelName (const int index) const
+{
+ if (((unsigned int) index) < (unsigned int) getNumOutputChannels())
+ return "Output " + String (index + 1);
+
+ return String::empty;
+}
+
+bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const
+{
+ if (((unsigned int) index) >= (unsigned int) getNumOutputChannels())
+ return false;
+
+ return true;
+}
+
+//==============================================================================
+void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData)
+{
+ getCurrentProgramStateInformation (destData);
+}
+
+void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData)
+{
+ CFPropertyListRef propertyList = 0;
+ UInt32 sz = sizeof (CFPropertyListRef);
+
+ if (AudioUnitGetProperty (audioUnit,
+ kAudioUnitProperty_ClassInfo,
+ kAudioUnitScope_Global,
+ 0, &propertyList, &sz) == noErr)
+ {
+ CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault);
+ CFWriteStreamOpen (stream);
+
+ CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0);
+ CFWriteStreamClose (stream);
+
+ CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten);
+
+ destData.setSize (bytesWritten);
+ destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize());
+ CFRelease (data);
+
+ CFRelease (stream);
+ CFRelease (propertyList);
+ }
+}
+
+void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes)
+{
+ setCurrentProgramStateInformation (data, sizeInBytes);
+}
+
+void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
+{
+ CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault,
+ (const UInt8*) data,
+ sizeInBytes,
+ kCFAllocatorNull);
+ CFReadStreamOpen (stream);
+
+ CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0;
+ CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault,
+ stream,
+ 0,
+ kCFPropertyListImmutable,
+ &format,
+ 0);
+ CFRelease (stream);
+
+ if (propertyList != 0)
+ AudioUnitSetProperty (audioUnit,
+ kAudioUnitProperty_ClassInfo,
+ kAudioUnitScope_Global,
+ 0, &propertyList, sizeof (propertyList));
+}
+
+//==============================================================================
+//==============================================================================
+AudioUnitPluginFormat::AudioUnitPluginFormat()
+{
+}
+
+AudioUnitPluginFormat::~AudioUnitPluginFormat()
+{
+}
+
+void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results,
+ const String& fileOrIdentifier)
+{
+ if (! fileMightContainThisPluginType (fileOrIdentifier))
+ return;
+
+ PluginDescription desc;
+ desc.fileOrIdentifier = fileOrIdentifier;
+ desc.uid = 0;
+
+ try
+ {
+ ScopedPointer createdInstance (createInstanceFromDescription (desc));
+ AudioUnitPluginInstance* const auInstance = dynamic_cast ((AudioPluginInstance*) createdInstance);
+
+ if (auInstance != 0)
+ {
+ auInstance->fillInPluginDescription (desc);
+ results.add (new PluginDescription (desc));
+ }
+ }
+ catch (...)
+ {
+ // crashed while loading...
+ }
+}
+
+AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc)
+{
+ if (fileMightContainThisPluginType (desc.fileOrIdentifier))
+ {
+ ScopedPointer result (new AudioUnitPluginInstance (desc.fileOrIdentifier));
+
+ if (result->audioUnit != 0)
+ {
+ result->initialise();
+ return result.release();
+ }
+ }
+
+ return 0;
+}
+
+const StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& /*directoriesToSearch*/,
+ const bool /*recursive*/)
+{
+ StringArray result;
+ ComponentRecord* comp = 0;
+ ComponentDescription desc;
+ zerostruct (desc);
+
+ for (;;)
+ {
+ zerostruct (desc);
+ comp = FindNextComponent (comp, &desc);
+
+ if (comp == 0)
+ break;
+
+ GetComponentInfo (comp, &desc, 0, 0, 0);
+
+ if (desc.componentType == kAudioUnitType_MusicDevice
+ || desc.componentType == kAudioUnitType_MusicEffect
+ || desc.componentType == kAudioUnitType_Effect
+ || desc.componentType == kAudioUnitType_Generator
+ || desc.componentType == kAudioUnitType_Panner)
+ {
+ const String s (AudioUnitFormatHelpers::createAUPluginIdentifier (desc));
+ DBG (s);
+ result.add (s);
+ }
+ }
+
+ return result;
+}
+
+bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
+{
+ ComponentDescription desc;
+
+ String name, version, manufacturer;
+ if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer))
+ return FindNextComponent (0, &desc) != 0;
+
+ const File f (fileOrIdentifier);
+
+ return f.hasFileExtension (".component")
+ && f.isDirectory();
+}
+
+const String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
+{
+ ComponentDescription desc;
+ String name, version, manufacturer;
+ AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer);
+
+ if (name.isEmpty())
+ name = fileOrIdentifier;
+
+ return name;
+}
+
+bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc)
+{
+ if (desc.fileOrIdentifier.startsWithIgnoreCase (AudioUnitFormatHelpers::auIdentifierPrefix))
+ return fileMightContainThisPluginType (desc.fileOrIdentifier);
+ else
+ return File (desc.fileOrIdentifier).exists();
+}
+
+const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch()
+{
+ return FileSearchPath ("/(Default AudioUnit locations)");
+}
+
+#endif
+
+END_JUCE_NAMESPACE
+
+#undef log
+
+#endif
diff --git a/src/containers/juce_Expression.cpp b/src/containers/juce_Expression.cpp
new file mode 100644
index 0000000000..3f72b84668
--- /dev/null
+++ b/src/containers/juce_Expression.cpp
@@ -0,0 +1,949 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-10 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_Expression.h"
+#include "juce_ReferenceCountedArray.h"
+
+
+//==============================================================================
+class Expression::Helpers
+{
+public:
+ //==============================================================================
+ class Constant : public Term
+ {
+ public:
+ Constant (const double value_) : value (value_), isResolutionTarget (false) {}
+
+ Term* clone() const { return new Constant (value); }
+ double evaluate (const EvaluationContext&, int) const { return value; }
+ int getNumInputs() const { return 0; }
+ Term* getInput (int) const { return 0; }
+
+ const String toString() const
+ {
+ if (isResolutionTarget)
+ return "@" + String (value);
+
+ return String (value);
+ }
+
+ double value;
+ bool isResolutionTarget;
+ };
+
+ //==============================================================================
+ class Symbol : public Term
+ {
+ public:
+ Symbol (const String& symbol_) : symbol (symbol_) {}
+
+ double evaluate (const EvaluationContext& c, int recursionDepth) const
+ {
+ if (++recursionDepth > 256)
+ throw EvaluationError ("Recursive symbol references");
+
+ return c.getSymbolValue (symbol).term->evaluate (c, recursionDepth);
+ }
+
+ Term* clone() const { return new Symbol (symbol); }
+ int getNumInputs() const { return 0; }
+ Term* getInput (int) const { return 0; }
+ const String toString() const { return symbol; }
+
+ bool referencesSymbol (const String& s, const EvaluationContext& c, int recursionDepth) const
+ {
+ if (s == symbol)
+ return true;
+
+ if (++recursionDepth > 256)
+ throw EvaluationError ("Recursive symbol references");
+
+ return c.getSymbolValue (symbol).term->referencesSymbol (s, c, recursionDepth);
+ }
+
+ String symbol;
+ };
+
+ //==============================================================================
+ class Function : public Term
+ {
+ public:
+ Function (const String& functionName_, const ReferenceCountedArray& parameters_)
+ : functionName (functionName_), parameters (parameters_)
+ {}
+
+ Term* clone() const { return new Function (functionName, parameters); }
+
+ double evaluate (const EvaluationContext& c, int recursionDepth) const
+ {
+ HeapBlock params (parameters.size());
+ for (int i = 0; i < parameters.size(); ++i)
+ params[i] = parameters.getUnchecked(i)->evaluate (c, recursionDepth);
+
+ return c.evaluateFunction (functionName, params, parameters.size());
+ }
+
+ int getInputIndexFor (const Term* possibleInput) const { return parameters.indexOf (possibleInput); }
+ int getNumInputs() const { return parameters.size(); }
+ Term* getInput (int i) const { return parameters [i]; }
+
+ bool referencesSymbol (const String& s, const EvaluationContext& c, int recursionDepth) const
+ {
+ for (int i = 0; i < parameters.size(); ++i)
+ if (parameters.getUnchecked(i)->referencesSymbol (s, c, recursionDepth))
+ return true;
+
+ return false;
+ }
+
+ const String toString() const
+ {
+ if (parameters.size() == 0)
+ return functionName + "()";
+
+ String s (functionName + " (");
+
+ for (int i = 0; i < parameters.size(); ++i)
+ {
+ s << parameters.getUnchecked(i)->toString();
+
+ if (i < parameters.size() - 1)
+ s << ", ";
+ }
+
+ s << ')';
+ return s;
+ }
+
+ const String functionName;
+ ReferenceCountedArray parameters;
+ };
+
+ //==============================================================================
+ class Negate : public Term
+ {
+ public:
+ Negate (Term* const input_) : input (input_)
+ {
+ jassert (input_ != 0);
+ }
+
+ int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; }
+ int getNumInputs() const { return 1; }
+ Term* getInput (int index) const { return index == 0 ? static_cast (input) : 0; }
+ Term* clone() const { return new Negate (input->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return -input->evaluate (c, recursionDepth); }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext&, Term* input_, double overallTarget, Term* topLevelTerm) const
+ {
+ jassert (input_ == input);
+
+ const Term* const dest = findDestinationFor (topLevelTerm, this);
+ Term* newDest;
+ if (dest == 0)
+ newDest = new Constant (overallTarget);
+ else
+ newDest = dest->clone();
+
+ return new Negate (newDest);
+ }
+
+ const String toString() const
+ {
+ if (input->getOperatorPrecedence() > 0)
+ return "-(" + input->toString() + ")";
+ else
+ return "-" + input->toString();
+ }
+
+ bool referencesSymbol (const String& s, const EvaluationContext& c, int recursionDepth) const
+ {
+ return input->referencesSymbol (s, c, recursionDepth);
+ }
+
+ private:
+ const ReferenceCountedObjectPtr input;
+ };
+
+ //==============================================================================
+ class BinaryTerm : public Term
+ {
+ public:
+ BinaryTerm (Term* const left_, Term* const right_) : left (left_), right (right_)
+ {
+ jassert (left_ != 0 && right_ != 0);
+ }
+
+ int getInputIndexFor (const Term* possibleInput) const
+ {
+ return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1);
+ }
+
+ int getNumInputs() const { return 2; }
+ Term* getInput (int index) const { return index == 0 ? static_cast (left) : (index == 1 ? static_cast (right) : 0); }
+
+ bool referencesSymbol (const String& s, const EvaluationContext& c, int recursionDepth) const
+ {
+ return left->referencesSymbol (s, c, recursionDepth)
+ || right->referencesSymbol (s, c, recursionDepth);
+ }
+
+ protected:
+ const ReferenceCountedObjectPtr left, right;
+
+ const String createString (const String& op) const
+ {
+ String s;
+
+ const int ourPrecendence = getOperatorPrecedence();
+ if (left->getOperatorPrecedence() > ourPrecendence)
+ s << '(' << left->toString() << ')';
+ else
+ s = left->toString();
+
+ s << ' ' << op << ' ';
+
+ if (right->getOperatorPrecedence() >= ourPrecendence)
+ s << '(' << right->toString() << ')';
+ else
+ s << right->toString();
+
+ return s;
+ }
+
+ Term* createDestinationTerm (const EvaluationContext&, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ jassert (input == left || input == right);
+ if (input != left && input != right)
+ return 0;
+
+ const Term* const dest = findDestinationFor (topLevelTerm, this);
+
+ if (dest == 0)
+ return new Constant (overallTarget);
+
+ return dest->clone();
+ }
+ };
+
+ //==============================================================================
+ class Add : public BinaryTerm
+ {
+ public:
+ Add (Term* const left_, Term* const right_) : BinaryTerm (left_, right_) {}
+
+ Term* clone() const { return new Add (left->clone(), right->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return left->evaluate (c, recursionDepth) + right->evaluate (c, recursionDepth); }
+ const String toString() const { return createString ("+"); }
+ int getOperatorPrecedence() const { return 2; }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext& c, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ Term* const newDest = createDestinationTerm (c, input, overallTarget, topLevelTerm);
+ if (newDest == 0)
+ return 0;
+
+ return new Subtract (newDest, (input == left ? right : left)->clone());
+ }
+ };
+
+ //==============================================================================
+ class Subtract : public BinaryTerm
+ {
+ public:
+ Subtract (Term* const left_, Term* const right_) : BinaryTerm (left_, right_) {}
+
+ Term* clone() const { return new Subtract (left->clone(), right->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return left->evaluate (c, recursionDepth) - right->evaluate (c, recursionDepth); }
+ const String toString() const { return createString ("-"); }
+ int getOperatorPrecedence() const { return 2; }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext& c, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ Term* const newDest = createDestinationTerm (c, input, overallTarget, topLevelTerm);
+ if (newDest == 0)
+ return 0;
+
+ if (input == left)
+ return new Add (newDest, right->clone());
+ else
+ return new Subtract (left->clone(), newDest);
+ }
+ };
+
+ //==============================================================================
+ class Multiply : public BinaryTerm
+ {
+ public:
+ Multiply (Term* const left_, Term* const right_) : BinaryTerm (left_, right_) {}
+
+ Term* clone() const { return new Multiply (left->clone(), right->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return left->evaluate (c, recursionDepth) * right->evaluate (c, recursionDepth); }
+ const String toString() const { return createString ("*"); }
+ int getOperatorPrecedence() const { return 1; }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext& c, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ Term* const newDest = createDestinationTerm (c, input, overallTarget, topLevelTerm);
+ if (newDest == 0)
+ return 0;
+
+ return new Divide (newDest, (input == left ? right : left)->clone());
+ }
+ };
+
+ //==============================================================================
+ class Divide : public BinaryTerm
+ {
+ public:
+ Divide (Term* const left_, Term* const right_) : BinaryTerm (left_, right_) {}
+
+ Term* clone() const { return new Divide (left->clone(), right->clone()); }
+ double evaluate (const EvaluationContext& c, int recursionDepth) const { return left->evaluate (c, recursionDepth) / right->evaluate (c, recursionDepth); }
+ const String toString() const { return createString ("/"); }
+ int getOperatorPrecedence() const { return 1; }
+
+ const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext& c, Term* input, double overallTarget, Term* topLevelTerm) const
+ {
+ Term* const newDest = createDestinationTerm (c, input, overallTarget, topLevelTerm);
+ if (newDest == 0)
+ return 0;
+
+ if (input == left)
+ return new Multiply (newDest, right->clone());
+ else
+ return new Divide (left->clone(), newDest);
+ }
+ };
+
+ //==============================================================================
+ static Term* findDestinationFor (Term* const topLevel, const Term* const inputTerm)
+ {
+ const int inputIndex = topLevel->getInputIndexFor (inputTerm);
+ if (inputIndex >= 0)
+ return topLevel;
+
+ for (int i = topLevel->getNumInputs(); --i >= 0;)
+ {
+ Term* t = findDestinationFor (topLevel->getInput (i), inputTerm);
+
+ if (t != 0)
+ return t;
+ }
+
+ return 0;
+ }
+
+ static Constant* findTermToAdjust (Term* const term, const bool mustBeFlagged)
+ {
+ Constant* c = dynamic_cast (term);
+ if (c != 0 && (c->isResolutionTarget || ! mustBeFlagged))
+ return c;
+
+ int i;
+ for (i = term->getNumInputs(); --i >= 0;)
+ {
+ Constant* c = dynamic_cast (term->getInput (i));
+ if (c != 0 && (c->isResolutionTarget || ! mustBeFlagged))
+ return c;
+ }
+
+ for (i = term->getNumInputs(); --i >= 0;)
+ {
+ Constant* c = findTermToAdjust (term->getInput (i), mustBeFlagged);
+ if (c != 0)
+ return c;
+ }
+
+ return 0;
+ }
+
+ static bool containsAnySymbols (const Term* const t)
+ {
+ if (dynamic_cast (t) != 0)
+ return true;
+
+ for (int i = t->getNumInputs(); --i >= 0;)
+ if (containsAnySymbols (t->getInput (i)))
+ return true;
+
+ return false;
+ }
+
+ static bool renameSymbol (Term* const t, const String& oldName, const String& newName)
+ {
+ Symbol* sym = dynamic_cast (t);
+
+ if (sym != 0)
+ {
+ if (sym->symbol == oldName)
+ {
+ sym->symbol = newName;
+ return true;
+ }
+ else if (sym->symbol.startsWith (oldName + "."))
+ {
+ sym->symbol = newName + "." + sym->symbol.substring (0, oldName.length() + 1);
+ return true;
+ }
+ }
+
+ bool anyChanged = false;
+
+ for (int i = t->getNumInputs(); --i >= 0;)
+ if (renameSymbol (t->getInput (i), oldName, newName))
+ anyChanged = true;
+
+ return anyChanged;
+ }
+
+ //==============================================================================
+ class Parser
+ {
+ public:
+ //==============================================================================
+ Parser (const String& stringToParse, int& textIndex_)
+ : textString (stringToParse), textIndex (textIndex_)
+ {
+ text = textString;
+ }
+
+ Term* readExpression()
+ {
+ ScopedPointer lhs (readMultiplyOrDivideExpression());
+
+ char opType;
+ while (lhs != 0 && readOperator ("+-", &opType))
+ {
+ Term* rhs = readMultiplyOrDivideExpression();
+
+ if (rhs == 0)
+ throw ParseError ("Expected expression after \"" + String::charToString (opType) + "\"");
+
+ if (opType == '+')
+ lhs = new Add (lhs.release(), rhs);
+ else
+ lhs = new Subtract (lhs.release(), rhs);
+ }
+
+ return lhs.release();
+ }
+
+ private:
+ const String textString;
+ const juce_wchar* text;
+ int& textIndex;
+
+ //==============================================================================
+ static inline bool isDecimalDigit (const juce_wchar c) throw()
+ {
+ return c >= '0' && c <= '9';
+ }
+
+ void skipWhitespace (int& i)
+ {
+ while (CharacterFunctions::isWhitespace (text [i]))
+ ++i;
+ }
+
+ bool readChar (const juce_wchar required)
+ {
+ if (text[textIndex] == required)
+ {
+ ++textIndex;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool readOperator (const char* ops, char* const opType = 0)
+ {
+ skipWhitespace (textIndex);
+
+ while (*ops != 0)
+ {
+ if (readChar (*ops))
+ {
+ if (opType != 0)
+ *opType = *ops;
+
+ return true;
+ }
+
+ ++ops;
+ }
+
+ return false;
+ }
+
+ bool readIdentifier (String& identifier)
+ {
+ skipWhitespace (textIndex);
+ int i = textIndex;
+
+ if (CharacterFunctions::isLetter (text[i]) || text[i] == '_')
+ {
+ ++i;
+
+ while (CharacterFunctions::isLetterOrDigit (text[i]) || text[i] == '_' || text[i] == '.')
+ ++i;
+ }
+
+ if (i > textIndex)
+ {
+ identifier = String (text + textIndex, i - textIndex);
+ textIndex = i;
+ return true;
+ }
+
+ return false;
+ }
+
+ Term* readNumber()
+ {
+ skipWhitespace (textIndex);
+ int i = textIndex;
+
+ const bool isResolutionTarget = (text[i] == '@');
+ if (isResolutionTarget)
+ {
+ ++i;
+ skipWhitespace (i);
+ }
+
+ int numDigits = 0;
+
+ while (isDecimalDigit (text[i]))
+ {
+ ++i;
+ ++numDigits;
+ }
+
+ const bool hasPoint = (text[i] == '.');
+
+ if (hasPoint)
+ {
+ ++i;
+
+ while (isDecimalDigit (text[i]))
+ {
+ ++i;
+ ++numDigits;
+ }
+ }
+
+ if (numDigits == 0)
+ return 0;
+
+ juce_wchar c = text[i];
+ const bool hasExponent = (c == 'e' || c == 'E');
+
+ if (hasExponent)
+ {
+ ++i;
+ c = text[i];
+ if (c == '+' || c == '-')
+ ++i;
+
+ int numExpDigits = 0;
+ while (isDecimalDigit (text[i]))
+ {
+ ++i;
+ ++numExpDigits;
+ }
+
+ if (numExpDigits == 0)
+ return 0;
+ }
+
+ Constant* t = new Constant (String (text + textIndex, i - textIndex).getDoubleValue());
+ t->isResolutionTarget = isResolutionTarget;
+ textIndex = i;
+ return t;
+ }
+
+ Term* readMultiplyOrDivideExpression()
+ {
+ ScopedPointer lhs (readUnaryExpression());
+
+ char opType;
+ while (lhs != 0 && readOperator ("*/", &opType))
+ {
+ Term* rhs = readUnaryExpression();
+
+ if (rhs == 0)
+ throw ParseError ("Expected expression after \"" + String::charToString (opType) + "\"");
+
+ if (opType == '*')
+ lhs = new Multiply (lhs.release(), rhs);
+ else
+ lhs = new Divide (lhs.release(), rhs);
+ }
+
+ return lhs.release();
+ }
+
+ Term* readUnaryExpression()
+ {
+ char opType;
+ if (readOperator ("+-", &opType))
+ {
+ Term* term = readUnaryExpression();
+
+ if (term == 0)
+ throw ParseError ("Expected expression after \"" + String::charToString (opType) + "\"");
+
+ if (opType == '-')
+ term = new Negate (term);
+
+ return term;
+ }
+
+ return readPrimaryExpression();
+ }
+
+ Term* readPrimaryExpression()
+ {
+ Term* e = readParenthesisedExpression();
+ if (e != 0)
+ return e;
+
+ e = readNumber();
+ if (e != 0)
+ return e;
+
+ String identifier;
+ if (readIdentifier (identifier))
+ {
+ if (readOperator ("(")) // method call...
+ {
+ Function* f = new Function (identifier, ReferenceCountedArray());
+ ScopedPointer func (f); // (can't use ScopedPointer in MSVC)
+
+ Term* param = readExpression();
+
+ if (param == 0)
+ {
+ if (readOperator (")"))
+ return func.release();
+
+ throw ParseError ("Expected parameters after \"" + identifier + " (\"");
+ }
+
+ f->parameters.add (param);
+
+ while (readOperator (","))
+ {
+ param = readExpression();
+
+ if (param == 0)
+ throw ParseError ("Expected expression after \",\"");
+
+ f->parameters.add (param);
+ }
+
+ if (readOperator (")"))
+ return func.release();
+
+ throw ParseError ("Expected \")\"");
+ }
+ else // just a symbol..
+ {
+ return new Symbol (identifier);
+ }
+ }
+
+ return 0;
+ }
+
+ Term* readParenthesisedExpression()
+ {
+ if (! readOperator ("("))
+ return 0;
+
+ ScopedPointer e (readExpression());
+ if (e == 0)
+ return 0;
+
+ if (! readOperator (")"))
+ e = 0;
+
+ return e.release();
+ }
+
+ Parser (const Parser&);
+ Parser& operator= (const Parser&);
+ };
+};
+
+//==============================================================================
+Expression::Expression()
+ : term (new Expression::Helpers::Constant (0))
+{
+}
+
+Expression::~Expression()
+{
+}
+
+Expression::Expression (Term* const term_)
+ : term (term_)
+{
+ jassert (term != 0);
+}
+
+Expression::Expression (const double constant)
+ : term (new Expression::Helpers::Constant (constant))
+{
+}
+
+Expression::Expression (const Expression& other)
+ : term (other.term)
+{
+}
+
+Expression& Expression::operator= (const Expression& other)
+{
+ term = other.term;
+ return *this;
+}
+
+Expression::Expression (const String& stringToParse)
+{
+ int i = 0;
+ Helpers::Parser parser (stringToParse, i);
+ term = parser.readExpression();
+
+ if (term == 0)
+ term = new Helpers::Constant (0);
+}
+
+const Expression Expression::parse (const String& stringToParse, int& textIndexToStartFrom)
+{
+ Helpers::Parser parser (stringToParse, textIndexToStartFrom);
+ Term* term = parser.readExpression();
+
+ if (term != 0)
+ return Expression (term);
+
+ return Expression();
+}
+
+double Expression::evaluate() const
+{
+ return evaluate (Expression::EvaluationContext());
+}
+
+double Expression::evaluate (const Expression::EvaluationContext& context) const
+{
+ return term->evaluate (context, 0);
+}
+
+const Expression Expression::operator+ (const Expression& other) const
+{
+ return Expression (new Helpers::Add (term, other.term));
+}
+
+const Expression Expression::operator- (const Expression& other) const
+{
+ return Expression (new Helpers::Subtract (term, other.term));
+}
+
+const Expression Expression::operator* (const Expression& other) const
+{
+ return Expression (new Helpers::Multiply (term, other.term));
+}
+
+const Expression Expression::operator/ (const Expression& other) const
+{
+ return Expression (new Helpers::Divide (term, other.term));
+}
+
+const Expression Expression::operator-() const
+{
+ return Expression (new Helpers::Negate (term));
+}
+
+const String Expression::toString() const
+{
+ return term->toString();
+}
+
+const Expression Expression::symbol (const String& symbol)
+{
+ return Expression (new Helpers::Symbol (symbol));
+}
+
+const Expression Expression::function (const String& functionName, const Array& parameters)
+{
+ ReferenceCountedArray params;
+ for (int i = 0; i < parameters.size(); ++i)
+ params.add (parameters.getReference(i).term);
+
+ return Expression (new Helpers::Function (functionName, params));
+}
+
+const Expression Expression::adjustedToGiveNewResult (const double targetValue,
+ const Expression::EvaluationContext& context) const
+{
+ ScopedPointer newTerm (term->clone());
+
+ Helpers::Constant* termToAdjust = Helpers::findTermToAdjust (newTerm, true);
+
+ if (termToAdjust == 0)
+ termToAdjust = Helpers::findTermToAdjust (newTerm, false);
+
+ if (termToAdjust == 0)
+ {
+ newTerm = new Helpers::Add (newTerm.release(), new Helpers::Constant (0));
+ termToAdjust = Helpers::findTermToAdjust (newTerm, false);
+ }
+
+ jassert (termToAdjust != 0);
+
+ const Term* parent = Helpers::findDestinationFor (newTerm, termToAdjust);
+
+ if (parent == 0)
+ {
+ termToAdjust->value = targetValue;
+ }
+ else
+ {
+ const ReferenceCountedObjectPtr reverseTerm (parent->createTermToEvaluateInput (context, termToAdjust, targetValue, newTerm));
+
+ if (reverseTerm == 0)
+ return Expression();
+
+ termToAdjust->value = reverseTerm->evaluate (context, 0);
+ }
+
+ return Expression (newTerm.release());
+}
+
+const Expression Expression::withRenamedSymbol (const String& oldSymbol, const String& newSymbol) const
+{
+ jassert (newSymbol.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
+
+ Expression newExpression (term->clone());
+ Helpers::renameSymbol (newExpression.term, oldSymbol, newSymbol);
+ return newExpression;
+}
+
+bool Expression::referencesSymbol (const String& symbol, const EvaluationContext& context) const
+{
+ return term->referencesSymbol (symbol, context, 0);
+}
+
+bool Expression::usesAnySymbols() const
+{
+ return Helpers::containsAnySymbols (term);
+}
+
+//==============================================================================
+int Expression::Term::getOperatorPrecedence() const
+{
+ return 0;
+}
+
+bool Expression::Term::referencesSymbol (const String&, const EvaluationContext&, int) const
+{
+ return false;
+}
+
+int Expression::Term::getInputIndexFor (const Term*) const
+{
+ return -1;
+}
+
+const ReferenceCountedObjectPtr Expression::Term::createTermToEvaluateInput (const EvaluationContext&, Term*, double, Term*) const
+{
+ jassertfalse;
+ return 0;
+}
+
+//==============================================================================
+Expression::ParseError::ParseError (const String& message)
+ : description (message)
+{
+}
+
+Expression::EvaluationError::EvaluationError (const String& message)
+ : description (message)
+{
+}
+
+//==============================================================================
+Expression::EvaluationContext::EvaluationContext() {}
+Expression::EvaluationContext::~EvaluationContext() {}
+
+const Expression Expression::EvaluationContext::getSymbolValue (const String& symbol) const
+{
+ throw EvaluationError ("Unknown symbol: \"" + symbol + "\"");
+}
+
+double Expression::EvaluationContext::evaluateFunction (const String& functionName, const double* parameters, int numParams) const
+{
+ if (numParams > 0)
+ {
+ if (functionName == "min")
+ {
+ double v = parameters[0];
+ for (int i = 1; i < numParams; ++i)
+ v = jmin (v, parameters[i]);
+
+ return v;
+ }
+ else if (functionName == "max")
+ {
+ double v = parameters[0];
+ for (int i = 1; i < numParams; ++i)
+ v = jmax (v, parameters[i]);
+
+ return v;
+ }
+ else if (numParams == 1)
+ {
+ if (functionName == "sin")
+ return sin (parameters[0]);
+ else if (functionName == "cos")
+ return cos (parameters[0]);
+ else if (functionName == "tan")
+ return tan (parameters[0]);
+ else if (functionName == "abs")
+ return std::abs (parameters[0]);
+ }
+ }
+
+ throw EvaluationError ("Unknown function: \"" + functionName + "\"");
+}
+
+
+END_JUCE_NAMESPACE
diff --git a/src/containers/juce_Expression.h b/src/containers/juce_Expression.h
new file mode 100644
index 0000000000..4ef269c8e8
--- /dev/null
+++ b/src/containers/juce_Expression.h
@@ -0,0 +1,216 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-10 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_EXPRESSION_JUCEHEADER__
+#define __JUCE_EXPRESSION_JUCEHEADER__
+
+#include "juce_ReferenceCountedObject.h"
+#include "juce_Array.h"
+#include "juce_ScopedPointer.h"
+
+
+//==============================================================================
+/**
+ A class for dynamically evaluating simple numeric expressions.
+
+ This class can parse a simple C-style string expression involving floating point
+ numbers, named symbols and functions. The basic arithmetic operations of +, -, *, /
+ are supported, as well as parentheses, and any alphanumeric identifiers are
+ assumed to be named symbols which will be resolved when the expression is
+ evaluated.
+
+ Expressions which use identifiers and functions require a subclass of
+ Expression::EvaluationContext to be supplied when evaluating them, and this object
+ is expected to be able to resolve the symbol names and perform the functions that
+ are used.
+*/
+class JUCE_API Expression
+{
+public:
+ //==============================================================================
+ /** Creates a simple expression with a value of 0. */
+ Expression();
+
+ /** Destructor. */
+ ~Expression();
+
+ /** Creates a simple expression with a specified constant value. */
+ explicit Expression (const double constant);
+
+ /** Creates a copy of an expression. */
+ Expression (const Expression& other);
+
+ /** Copies another expression. */
+ Expression& operator= (const Expression& other);
+
+ /** Creates an expression by parsing a string.
+ If there's a syntax error in the string, this will throw a ParseError exception.
+ @throws ParseError
+ */
+ explicit Expression (const String& stringToParse);
+
+ /** Returns a string version of the expression. */
+ const String toString() const;
+
+ /** Returns an expression which is an addtion operation of two existing expressions. */
+ const Expression operator+ (const Expression& other) const;
+ /** Returns an expression which is a subtraction operation of two existing expressions. */
+ const Expression operator- (const Expression& other) const;
+ /** Returns an expression which is a multiplication operation of two existing expressions. */
+ const Expression operator* (const Expression& other) const;
+ /** Returns an expression which is a division operation of two existing expressions. */
+ const Expression operator/ (const Expression& other) const;
+ /** Returns an expression which is a negation operation of two existing expressions. */
+ const Expression operator-() const;
+
+ /** Returns an Expression which is an identifier reference. */
+ static const Expression symbol (const String& symbol);
+
+ /** Returns an Expression which is a function call. */
+ static const Expression function (const String& functionName, const Array& parameters);
+
+ /** Returns an Expression which parses a string from a specified character index.
+
+ The index value is incremented so that on return, it indicates the character that follows
+ the end of the expression that was parsed.
+
+ If there's a syntax error in the string, this will throw a ParseError exception.
+ @throws ParseError
+ */
+ static const Expression parse (const String& stringToParse, int& textIndexToStartFrom);
+
+ //==============================================================================
+ /** When evaluating an Expression object, this class is used to resolve symbols and
+ perform functions that the expression uses.
+ */
+ class EvaluationContext
+ {
+ public:
+ EvaluationContext();
+ virtual ~EvaluationContext();
+
+ /** Returns the value of a symbol.
+ If the symbol is unknown, this can throw an Expression::EvaluationError exception.
+ @throws Expression::EvaluationError
+ */
+ virtual const Expression getSymbolValue (const String& symbol) const;
+
+ /** Executes a named function.
+ If the function name is unknown, this can throw an Expression::EvaluationError exception.
+ @throws Expression::EvaluationError
+ */
+ virtual double evaluateFunction (const String& functionName, const double* parameters, int numParams) const;
+ };
+
+ /** Evaluates this expression, without using an EvaluationContext.
+ Without an EvaluationContext, no symbols can be used, and only basic functions such as sin, cos, tan,
+ min, max are available.
+ */
+ double evaluate() const;
+
+ /** Evaluates this expression, providing a context that should be able to evaluate any symbols
+ or functions that it uses.
+ */
+ double evaluate (const EvaluationContext& context) const;
+
+ /** Attempts to return an expression which is a copy of this one, but with a constant adjusted
+ to make the expression resolve to a target value.
+
+ E.g. if the expression is "x + 10" and x is 5, then asking for a target value of 8 will return
+ the expression "x + 3". Obviously some expressions can't be reversed in this way, in which
+ case they might just be adjusted by adding a constant to them.
+ */
+ const Expression adjustedToGiveNewResult (double targetValue, const EvaluationContext& context) const;
+
+ /** Returns a copy of this expression in which all instances of a given symbol have been renamed. */
+ const Expression withRenamedSymbol (const String& oldSymbol, const String& newSymbol) const;
+
+ /** Returns true if this expression makes use of the specified symbol.
+ If a suitable context is supplied, the search will dereference and recursively check
+ all symbols, so that it can be determined whether this expression relies on the given
+ symbol at any level in its evaluation.
+ */
+ bool referencesSymbol (const String& symbol, const EvaluationContext& context) const;
+
+ /** Returns true if this expression contains any symbols. */
+ bool usesAnySymbols() const;
+
+ //==============================================================================
+ /** An exception that can be thrown by Expression::parse(). */
+ class ParseError : public std::exception
+ {
+ public:
+ ParseError (const String& message);
+
+ String description;
+ };
+
+ //==============================================================================
+ /** An exception that can be thrown by Expression::evaluate(). */
+ class EvaluationError : public std::exception
+ {
+ public:
+ EvaluationError (const String& message);
+
+ String description;
+ };
+
+ //==============================================================================
+ juce_UseDebuggingNewOperator
+
+private:
+ class Helpers;
+ friend class Helpers;
+
+ class Term : public ReferenceCountedObject
+ {
+ public:
+ Term() {}
+ virtual ~Term() {}
+
+ virtual Term* clone() const = 0;
+ virtual double evaluate (const EvaluationContext&, int recursionDepth) const = 0;
+ virtual int getNumInputs() const = 0;
+ virtual Term* getInput (int index) const = 0;
+ virtual int getInputIndexFor (const Term* possibleInput) const;
+ virtual const String toString() const = 0;
+ virtual int getOperatorPrecedence() const;
+ virtual bool referencesSymbol (const String& symbol, const EvaluationContext&, int recursionDepth) const;
+ virtual const ReferenceCountedObjectPtr createTermToEvaluateInput (const EvaluationContext&, Term* inputTerm,
+ double overallTarget, Term* topLevelTerm) const;
+ juce_UseDebuggingNewOperator
+
+ private:
+ Term (const Term& other);
+ Term& operator= (const Term&);
+ };
+
+ friend class ScopedPointer;
+ ReferenceCountedObjectPtr term;
+
+ explicit Expression (Term* term);
+};
+
+#endif // __JUCE_EXPRESSION_JUCEHEADER__
diff --git a/src/core/juce_PlatformDefs.h b/src/core/juce_PlatformDefs.h
index e676788e63..dc077fbbfe 100644
--- a/src/core/juce_PlatformDefs.h
+++ b/src/core/juce_PlatformDefs.h
@@ -68,7 +68,7 @@
@see Logger::outputDebugString
*/
- #define DBG(dbgtext) { String tempDbgBuf; tempDbgBuf << dbgtext; JUCE_NAMESPACE::Logger::outputDebugString (tempDbgBuf); }
+ #define DBG(dbgtext) { JUCE_NAMESPACE::String tempDbgBuf; tempDbgBuf << dbgtext; JUCE_NAMESPACE::Logger::outputDebugString (tempDbgBuf); }
//==============================================================================
// Assertions..
diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h
index a4f3f15222..738ae29797 100644
--- a/src/core/juce_StandardHeader.h
+++ b/src/core/juce_StandardHeader.h
@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
-#define JUCE_BUILDNUMBER 49
+#define JUCE_BUILDNUMBER 50
/** Current Juce version number.
diff --git a/src/gui/components/controls/juce_ComboBox.h b/src/gui/components/controls/juce_ComboBox.h
index 7738bce413..2659901603 100644
--- a/src/gui/components/controls/juce_ComboBox.h
+++ b/src/gui/components/controls/juce_ComboBox.h
@@ -97,7 +97,8 @@ public:
@param newItemText the text of the item to show in the list
@param newItemId an associated ID number that can be set or retrieved - see
- getSelectedId() and setSelectedId()
+ getSelectedId() and setSelectedId(). Note that this value can not
+ be 0!
@see setItemEnabled, addSeparator, addSectionHeading, removeItem, getNumItems, getItemText, getItemId
*/
void addItem (const String& newItemText,
diff --git a/src/gui/components/controls/juce_TableListBox.cpp b/src/gui/components/controls/juce_TableListBox.cpp
index 7e9fb9a5fb..12a26cff7b 100644
--- a/src/gui/components/controls/juce_TableListBox.cpp
+++ b/src/gui/components/controls/juce_TableListBox.cpp
@@ -94,6 +94,7 @@ public:
row = newRow;
isSelected = isNowSelected;
repaint();
+ deleteAllChildren();
}
if (row < owner.getNumRows())
@@ -105,13 +106,12 @@ public:
const TableHeaderComponent* const header = owner.getHeader();
const int numColumns = header->getNumColumns (true);
- int i;
columnsWithComponents.clear();
if (owner.getModel() != 0)
{
- for (i = 0; i < numColumns; ++i)
+ for (int i = 0; i < numColumns; ++i)
{
const int columnId = header->getColumnIdOfIndex (i, true);
@@ -133,7 +133,7 @@ public:
}
}
- for (i = getNumChildComponents(); --i >= 0;)
+ for (int i = getNumChildComponents(); --i >= 0;)
{
Component* const c = getChildComponent (i);
diff --git a/src/gui/components/windows/juce_CallOutBox.cpp b/src/gui/components/windows/juce_CallOutBox.cpp
index d2fc0b6e9e..1f13ed1b0e 100644
--- a/src/gui/components/windows/juce_CallOutBox.cpp
+++ b/src/gui/components/windows/juce_CallOutBox.cpp
@@ -30,6 +30,7 @@ BEGIN_JUCE_NAMESPACE
#include "juce_CallOutBox.h"
#include "juce_ComponentPeer.h"
#include "../lookandfeel/juce_LookAndFeel.h"
+#include "../../../application/juce_Application.h"
//==============================================================================
@@ -50,6 +51,9 @@ CallOutBox::CallOutBox (Component& contentComponent,
}
else
{
+ if (! JUCEApplication::isStandaloneApp())
+ setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level
+
updatePosition (componentToPointTo.getScreenBounds(),
componentToPointTo.getParentMonitorArea());
diff --git a/src/gui/graphics/drawables/juce_Drawable.cpp b/src/gui/graphics/drawables/juce_Drawable.cpp
index a5dbc224cf..fc0b670c87 100644
--- a/src/gui/graphics/drawables/juce_Drawable.cpp
+++ b/src/gui/graphics/drawables/juce_Drawable.cpp
@@ -199,7 +199,7 @@ void Drawable::ValueTreeWrapperBase::setID (const String& newID, UndoManager* co
}
const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v, RelativePoint* const gp1, RelativePoint* const gp2, RelativePoint* const gp3,
- RelativeCoordinate::NamedCoordinateFinder* const nameFinder, ImageProvider* imageProvider)
+ Expression::EvaluationContext* const nameFinder, ImageProvider* imageProvider)
{
const String newType (v[type].toString());
diff --git a/src/gui/graphics/drawables/juce_Drawable.h b/src/gui/graphics/drawables/juce_Drawable.h
index 2290e27a26..90dd302423 100644
--- a/src/gui/graphics/drawables/juce_Drawable.h
+++ b/src/gui/graphics/drawables/juce_Drawable.h
@@ -250,7 +250,7 @@ public:
static const FillType readFillType (const ValueTree& v, RelativePoint* gradientPoint1,
RelativePoint* gradientPoint2, RelativePoint* gradientPoint3,
- RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+ Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider);
static void writeFillType (ValueTree& v, const FillType& fillType,
diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.cpp b/src/gui/graphics/drawables/juce_DrawableComposite.cpp
index 0f0d7b8f81..cf6597b05e 100644
--- a/src/gui/graphics/drawables/juce_DrawableComposite.cpp
+++ b/src/gui/graphics/drawables/juce_DrawableComposite.cpp
@@ -247,33 +247,26 @@ void DrawableComposite::render (const Drawable::RenderingContext& context) const
}
}
-const RelativeCoordinate DrawableComposite::findNamedCoordinate (const String& objectName, const String& edge) const
+const Expression DrawableComposite::getSymbolValue (const String& symbol) const
{
- if (objectName == RelativeCoordinate::Strings::parent)
- {
- if (edge == RelativeCoordinate::Strings::right || edge == RelativeCoordinate::Strings::bottom)
- {
- jassertfalse; // a Drawable doesn't have a fixed right-hand or bottom edge - use a marker instead if you need a point of reference.
- return RelativeCoordinate (100.0);
- }
- }
+ jassert (! symbol.containsChar ('.')) // the only symbols available in a Drawable are markers.
int i;
for (i = 0; i < markersX.size(); ++i)
{
Marker* const m = markersX.getUnchecked(i);
- if (m->name == objectName)
- return m->position;
+ if (m->name == symbol)
+ return m->position.getExpression();
}
for (i = 0; i < markersY.size(); ++i)
{
Marker* const m = markersY.getUnchecked(i);
- if (m->name == objectName)
- return m->position;
+ if (m->name == symbol)
+ return m->position.getExpression();
}
- return RelativeCoordinate();
+ return Expression::EvaluationContext::getSymbolValue (symbol);
}
const Rectangle DrawableComposite::getUntransformedBounds (const bool includeMarkers) const
@@ -535,7 +528,7 @@ const DrawableComposite::Marker DrawableComposite::ValueTreeWrapper::getMarker (
{
jassert (containsMarker (xAxis, state));
- return Marker (state [nameProperty], RelativeCoordinate (state [posProperty].toString(), xAxis));
+ return Marker (state [nameProperty], RelativeCoordinate (state [posProperty].toString()));
}
void DrawableComposite::ValueTreeWrapper::setMarker (bool xAxis, const Marker& m, UndoManager* undoManager)
diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.h b/src/gui/graphics/drawables/juce_DrawableComposite.h
index 305a9359da..05e7248bb4 100644
--- a/src/gui/graphics/drawables/juce_DrawableComposite.h
+++ b/src/gui/graphics/drawables/juce_DrawableComposite.h
@@ -36,7 +36,7 @@
@see Drawable
*/
class JUCE_API DrawableComposite : public Drawable,
- public RelativeCoordinate::NamedCoordinateFinder
+ public Expression::EvaluationContext
{
public:
//==============================================================================
@@ -201,7 +201,7 @@ public:
/** @internal */
const Identifier getValueTreeType() const { return valueTreeType; }
/** @internal */
- const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const;
+ const Expression getSymbolValue (const String& symbol) const;
//==============================================================================
/** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */
diff --git a/src/gui/graphics/drawables/juce_DrawablePath.cpp b/src/gui/graphics/drawables/juce_DrawablePath.cpp
index 5ee175cf45..a700a0f949 100644
--- a/src/gui/graphics/drawables/juce_DrawablePath.cpp
+++ b/src/gui/graphics/drawables/juce_DrawablePath.cpp
@@ -143,6 +143,8 @@ void DrawablePath::render (const Drawable::RenderingContext& context) const
FillType f (mainFill);
if (f.isGradient())
f.gradient->multiplyOpacity (context.opacity);
+ else
+ f.setOpacity (f.getOpacity() * context.opacity);
f.transform = f.transform.followedBy (context.transform);
context.g.setFillType (f);
@@ -154,6 +156,8 @@ void DrawablePath::render (const Drawable::RenderingContext& context) const
FillType f (strokeFill);
if (f.isGradient())
f.gradient->multiplyOpacity (context.opacity);
+ else
+ f.setOpacity (f.getOpacity() * context.opacity);
f.transform = f.transform.followedBy (context.transform);
context.g.setFillType (f);
@@ -226,7 +230,7 @@ ValueTree DrawablePath::ValueTreeWrapper::getStrokeFillState()
return getStrokeFillState();
}
-const FillType DrawablePath::ValueTreeWrapper::getMainFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+const FillType DrawablePath::ValueTreeWrapper::getMainFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const
{
return readFillType (state.getChildWithName (fill), 0, 0, 0, nameFinder, imageProvider);
@@ -240,7 +244,7 @@ void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const
writeFillType (v, newFill, gp1, gp2, gp3, imageProvider, undoManager);
}
-const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const
{
return readFillType (state.getChildWithName (stroke), 0, 0, 0, nameFinder, imageProvider);
@@ -368,7 +372,7 @@ const RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const
return RelativePoint();
}
-float DrawablePath::ValueTreeWrapper::Element::getLength (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
+float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::EvaluationContext* nameFinder) const
{
const Identifier i (state.getType());
@@ -419,7 +423,7 @@ void DrawablePath::ValueTreeWrapper::Element::convertToLine (UndoManager* undoMa
}
}
-void DrawablePath::ValueTreeWrapper::Element::convertToCubic (RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager)
+void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::EvaluationContext* nameFinder, UndoManager* undoManager)
{
const Identifier i (state.getType());
@@ -473,7 +477,7 @@ static const Point findQuadraticSubdivisionPoint (float proportion, const
return mid1 + (mid2 - mid1) * proportion;
}
-float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
+float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, Expression::EvaluationContext* nameFinder) const
{
const Identifier i (state.getType());
float bestProp = 0;
@@ -529,7 +533,7 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po
return bestProp;
}
-ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager)
+ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, Expression::EvaluationContext* nameFinder, UndoManager* undoManager)
{
ValueTree newTree;
const Identifier i (state.getType());
diff --git a/src/gui/graphics/drawables/juce_DrawablePath.h b/src/gui/graphics/drawables/juce_DrawablePath.h
index e6ed14290f..21d874a603 100644
--- a/src/gui/graphics/drawables/juce_DrawablePath.h
+++ b/src/gui/graphics/drawables/juce_DrawablePath.h
@@ -129,14 +129,14 @@ public:
public:
ValueTreeWrapper (const ValueTree& state);
- const FillType getMainFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+ const FillType getMainFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const;
ValueTree getMainFillState();
void setMainFill (const FillType& newFill, const RelativePoint* gradientPoint1,
const RelativePoint* gradientPoint2, const RelativePoint* gradientPoint3,
ImageProvider* imageProvider, UndoManager* undoManager);
- const FillType getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder,
+ const FillType getStrokeFill (Expression::EvaluationContext* nameFinder,
ImageProvider* imageProvider) const;
ValueTree getStrokeFillState();
void setStrokeFill (const FillType& newFill, const RelativePoint* gradientPoint1,
@@ -163,7 +163,7 @@ public:
const RelativePoint getStartPoint() const;
const RelativePoint getEndPoint() const;
void setControlPoint (int index, const RelativePoint& point, UndoManager* undoManager);
- float getLength (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
+ float getLength (Expression::EvaluationContext* nameFinder) const;
ValueTreeWrapper getParent() const;
Element getPreviousElement() const;
@@ -172,11 +172,11 @@ public:
void setModeOfEndPoint (const String& newMode, UndoManager* undoManager);
void convertToLine (UndoManager* undoManager);
- void convertToCubic (RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager);
+ void convertToCubic (Expression::EvaluationContext* nameFinder, UndoManager* undoManager);
void convertToPathBreak (UndoManager* undoManager);
- ValueTree insertPoint (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager);
+ ValueTree insertPoint (const Point& targetPoint, Expression::EvaluationContext* nameFinder, UndoManager* undoManager);
void removePoint (UndoManager* undoManager);
- float findProportionAlongLine (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
+ float findProportionAlongLine (const Point& targetPoint, Expression::EvaluationContext* nameFinder) const;
static const Identifier mode, startSubPathElement, closeSubPathElement,
lineToElement, quadraticToElement, cubicToElement;
diff --git a/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp b/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp
index 0128d870ea..318dbdcf23 100644
--- a/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp
+++ b/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp
@@ -35,166 +35,14 @@ BEGIN_JUCE_NAMESPACE
//==============================================================================
namespace RelativeCoordinateHelpers
{
- static bool isOrigin (const String& name)
- {
- return name.isEmpty()
- || name == RelativeCoordinate::Strings::parentLeft
- || name == RelativeCoordinate::Strings::parentTop;
- }
-
- static const String getExtentAnchorName (const bool isHorizontal) throw()
- {
- return isHorizontal ? RelativeCoordinate::Strings::parentRight
- : RelativeCoordinate::Strings::parentBottom;
- }
-
- static const String getObjectName (const String& fullName)
- {
- return fullName.upToFirstOccurrenceOf (".", false, false);
- }
-
- static const String getEdgeName (const String& fullName)
- {
- return fullName.fromFirstOccurrenceOf (".", false, false);
- }
-
- static const RelativeCoordinate findCoordinate (const String& name, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
- {
- return nameFinder != 0 ? nameFinder->findNamedCoordinate (getObjectName (name), getEdgeName (name))
- : RelativeCoordinate();
- }
-
- //==============================================================================
- struct RecursionException : public std::runtime_error
- {
- RecursionException() : std::runtime_error ("Recursive RelativeCoordinate expression")
- {
- }
- };
-
- //==============================================================================
- static void skipWhitespace (const juce_wchar* const s, int& i)
+ static void skipComma (const juce_wchar* const s, int& i)
{
while (CharacterFunctions::isWhitespace (s[i]))
++i;
- }
- static void skipComma (const juce_wchar* const s, int& i)
- {
- skipWhitespace (s, i);
if (s[i] == ',')
++i;
}
-
- static const String readAnchorName (const juce_wchar* const s, int& i)
- {
- skipWhitespace (s, i);
-
- if (CharacterFunctions::isLetter (s[i]) || s[i] == '_')
- {
- int start = i;
- while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.')
- ++i;
-
- return String (s + start, i - start);
- }
-
- return String::empty;
- }
-
- static double readNumber (const juce_wchar* const s, int& i)
- {
- skipWhitespace (s, i);
-
- int start = i;
- if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-')
- ++i;
-
- while (CharacterFunctions::isDigit (s[i]) || s[i] == '.')
- ++i;
-
- if ((s[i] == 'e' || s[i] == 'E')
- && (CharacterFunctions::isDigit (s[i + 1])
- || s[i + 1] == '-'
- || s[i + 1] == '+'))
- {
- i += 2;
-
- while (CharacterFunctions::isDigit (s[i]))
- ++i;
- }
-
- const double value = String (s + start, i - start).getDoubleValue();
- while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',')
- ++i;
-
- return value;
- }
-
- static const RelativeCoordinate readNextCoordinate (const juce_wchar* const s, int& i, const bool isHorizontal)
- {
- String anchor1 (readAnchorName (s, i));
- double value = 0;
-
- if (anchor1.isNotEmpty())
- {
- skipWhitespace (s, i);
-
- if (s[i] == '+')
- value = readNumber (s, ++i);
- else if (s[i] == '-')
- value = -readNumber (s, ++i);
-
- return RelativeCoordinate (value, anchor1);
- }
- else
- {
- value = readNumber (s, i);
- skipWhitespace (s, i);
-
- if (s[i] == '%')
- {
- value /= 100.0;
- skipWhitespace (s, ++i);
- String anchor2;
-
- if (s[i] == '*')
- {
- anchor1 = readAnchorName (s, ++i);
-
- skipWhitespace (s, i);
-
- if (s[i] == '-' && s[i + 1] == '>')
- {
- i += 2;
- anchor2 = readAnchorName (s, i);
- }
- else
- {
- anchor2 = anchor1;
- anchor1 = String::empty;
- }
- }
- else
- {
- anchor1 = String::empty;
- anchor2 = getExtentAnchorName (isHorizontal);
- }
-
- return RelativeCoordinate (value, anchor1, anchor2);
- }
-
- return RelativeCoordinate (value);
- }
- }
-
- static const String limitedAccuracyString (const double n)
- {
- if (! (n < -0.001 || n > 0.001)) // to detect NaN and inf as well as for rounding
- return "0";
-
- return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd (".");
- }
}
//==============================================================================
@@ -210,33 +58,38 @@ const String RelativeCoordinate::Strings::parentBottom ("parent.bottom");
//==============================================================================
RelativeCoordinate::RelativeCoordinate()
- : value (0)
{
}
+RelativeCoordinate::RelativeCoordinate (const Expression& term_)
+ : term (term_)
+{
+}
+
+RelativeCoordinate::RelativeCoordinate (const RelativeCoordinate& other)
+ : term (other.term)
+{
+}
+
+RelativeCoordinate& RelativeCoordinate::operator= (const RelativeCoordinate& other)
+{
+ term = other.term;
+ return *this;
+}
+
RelativeCoordinate::RelativeCoordinate (const double absoluteDistanceFromOrigin)
- : value (absoluteDistanceFromOrigin)
+ : term (absoluteDistanceFromOrigin)
{
}
-RelativeCoordinate::RelativeCoordinate (const double absoluteDistance, const String& source)
- : anchor1 (source.trim()),
- value (absoluteDistance)
+RelativeCoordinate::RelativeCoordinate (const String& s)
{
-}
-
-RelativeCoordinate::RelativeCoordinate (const double relativeProportion, const String& pos1, const String& pos2)
- : anchor1 (pos1.trim()),
- anchor2 (pos2.trim()),
- value (relativeProportion)
-{
-}
-
-RelativeCoordinate::RelativeCoordinate (const String& s, const bool isHorizontal)
- : value (0)
-{
- int i = 0;
- *this = RelativeCoordinateHelpers::readNextCoordinate (s, i, isHorizontal);
+ try
+ {
+ term = Expression (s);
+ }
+ catch (...)
+ {}
}
RelativeCoordinate::~RelativeCoordinate()
@@ -245,7 +98,7 @@ RelativeCoordinate::~RelativeCoordinate()
bool RelativeCoordinate::operator== (const RelativeCoordinate& other) const throw()
{
- return value == other.value && anchor1 == other.anchor1 && anchor2 == other.anchor2;
+ return term.toString() == other.term.toString();
}
bool RelativeCoordinate::operator!= (const RelativeCoordinate& other) const throw()
@@ -253,58 +106,31 @@ bool RelativeCoordinate::operator!= (const RelativeCoordinate& other) const thro
return ! operator== (other);
}
-//==============================================================================
-const RelativeCoordinate RelativeCoordinate::getAnchorCoordinate1() const
-{
- return RelativeCoordinate (0.0, anchor1);
-}
-
-const RelativeCoordinate RelativeCoordinate::getAnchorCoordinate2() const
-{
- return RelativeCoordinate (0.0, anchor2);
-}
-
-double RelativeCoordinate::resolveAnchor (const String& anchorName, const NamedCoordinateFinder* nameFinder, int recursionCounter)
-{
- if (RelativeCoordinateHelpers::isOrigin (anchorName))
- return 0.0;
-
- return RelativeCoordinateHelpers::findCoordinate (anchorName, nameFinder).resolve (nameFinder, recursionCounter + 1);
-}
-
-double RelativeCoordinate::resolve (const NamedCoordinateFinder* nameFinder, int recursionCounter) const
-{
- if (recursionCounter > 150)
- {
- jassertfalse
- throw RelativeCoordinateHelpers::RecursionException();
- }
-
- const double pos1 = resolveAnchor (anchor1, nameFinder, recursionCounter);
-
- return isProportional() ? pos1 + (resolveAnchor (anchor2, nameFinder, recursionCounter) - pos1) * value
- : pos1 + value;
-}
-
-double RelativeCoordinate::resolve (const NamedCoordinateFinder* nameFinder) const
+double RelativeCoordinate::resolve (const Expression::EvaluationContext* context) const
{
try
{
- return resolve (nameFinder, 0);
+ if (context != 0)
+ return term.evaluate (*context);
+ else
+ return term.evaluate();
}
- catch (RelativeCoordinateHelpers::RecursionException&)
+ catch (...)
{}
return 0.0;
}
-bool RelativeCoordinate::isRecursive (const NamedCoordinateFinder* nameFinder) const
+bool RelativeCoordinate::isRecursive (const Expression::EvaluationContext* context) const
{
try
{
- (void) resolve (nameFinder, 0);
+ if (context != 0)
+ term.evaluate (*context);
+ else
+ term.evaluate();
}
- catch (RelativeCoordinateHelpers::RecursionException&)
+ catch (...)
{
return true;
}
@@ -312,153 +138,61 @@ bool RelativeCoordinate::isRecursive (const NamedCoordinateFinder* nameFinder) c
return false;
}
-void RelativeCoordinate::moveToAbsolute (double newPos, const NamedCoordinateFinder* nameFinder)
+void RelativeCoordinate::moveToAbsolute (double newPos, const Expression::EvaluationContext* context)
{
try
{
- const double pos1 = resolveAnchor (anchor1, nameFinder, 0);
-
- if (isProportional())
+ if (context != 0)
{
- const double size = resolveAnchor (anchor2, nameFinder, 0) - pos1;
-
- if (size != 0)
- value = (newPos - pos1) / size;
+ term = term.adjustedToGiveNewResult (newPos, *context);
}
else
{
- value = newPos - pos1;
+ Expression::EvaluationContext defaultContext;
+ term = term.adjustedToGiveNewResult (newPos, defaultContext);
}
}
- catch (RelativeCoordinateHelpers::RecursionException&)
+ catch (...)
{}
}
-void RelativeCoordinate::toggleProportionality (const NamedCoordinateFinder* nameFinder,
- const String& proportionalAnchor1, const String& proportionalAnchor2)
+bool RelativeCoordinate::references (const String& coordName, const Expression::EvaluationContext* context) const
{
- const double oldValue = resolve (nameFinder);
+ try
+ {
+ if (context != 0)
+ return term.referencesSymbol (coordName, *context);
- anchor1 = proportionalAnchor1;
- anchor2 = isProportional() ? String::empty : proportionalAnchor2;
+ Expression::EvaluationContext defaultContext;
+ return term.referencesSymbol (coordName, defaultContext);
+ }
+ catch (...)
+ {}
- moveToAbsolute (oldValue, nameFinder);
-}
-
-bool RelativeCoordinate::references (const String& coordName, const NamedCoordinateFinder* nameFinder) const
-{
- using namespace RelativeCoordinateHelpers;
-
- if (isOrigin (anchor1) && ! isProportional())
- return isOrigin (coordName);
-
- return anchor1 == coordName
- || anchor2 == coordName
- || findCoordinate (anchor1, nameFinder).references (coordName, nameFinder)
- || (isProportional() && findCoordinate (anchor2, nameFinder).references (coordName, nameFinder));
+ return false;
}
bool RelativeCoordinate::isDynamic() const
{
- return anchor2.isNotEmpty() || ! RelativeCoordinateHelpers::isOrigin (anchor1);
+ return term.usesAnySymbols();
}
-//==============================================================================
const String RelativeCoordinate::toString() const
{
- using namespace RelativeCoordinateHelpers;
+ return term.toString();
+}
- if (isProportional())
+void RelativeCoordinate::renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* context)
+{
+ jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
+
+ if (term.referencesSymbol (oldName, *context))
{
- const String percent (limitedAccuracyString (value * 100.0));
+ const double oldValue = resolve (context);
- if (isOrigin (anchor1))
- {
- if (anchor2 == Strings::parentRight || anchor2 == Strings::parentBottom)
- return percent + "%";
- else
- return percent + "% * " + anchor2;
- }
- else
- return percent + "% * " + anchor1 + " -> " + anchor2;
- }
- else
- {
- if (isOrigin (anchor1))
- return limitedAccuracyString (value);
- else if (value > 0)
- return anchor1 + " + " + limitedAccuracyString (value);
- else if (value < 0)
- return anchor1 + " - " + limitedAccuracyString (-value);
- else
- return anchor1;
- }
-}
-
-//==============================================================================
-const double RelativeCoordinate::getEditableNumber() const
-{
- return isProportional() ? value * 100.0 : value;
-}
-
-void RelativeCoordinate::setEditableNumber (const double newValue)
-{
- value = isProportional() ? newValue / 100.0 : newValue;
-}
-
-//==============================================================================
-const String RelativeCoordinate::getAnchorName1 (const String& returnValueIfOrigin) const
-{
- return RelativeCoordinateHelpers::isOrigin (anchor1) ? returnValueIfOrigin : anchor1;
-}
-
-const String RelativeCoordinate::getAnchorName2 (const String& returnValueIfOrigin) const
-{
- return RelativeCoordinateHelpers::isOrigin (anchor2) ? returnValueIfOrigin : anchor2;
-}
-
-void RelativeCoordinate::changeAnchor1 (const String& newAnchorName, const NamedCoordinateFinder* nameFinder)
-{
- jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
-
- const double oldValue = resolve (nameFinder);
- anchor1 = RelativeCoordinateHelpers::isOrigin (newAnchorName) ? String::empty : newAnchorName;
- moveToAbsolute (oldValue, nameFinder);
-}
-
-void RelativeCoordinate::changeAnchor2 (const String& newAnchorName, const NamedCoordinateFinder* nameFinder)
-{
- jassert (isProportional());
- jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_."));
-
- const double oldValue = resolve (nameFinder);
- anchor2 = RelativeCoordinateHelpers::isOrigin (newAnchorName) ? String::empty : newAnchorName;
- moveToAbsolute (oldValue, nameFinder);
-}
-
-void RelativeCoordinate::renameAnchorIfUsed (const String& oldName, const String& newName, const NamedCoordinateFinder* nameFinder)
-{
- using namespace RelativeCoordinateHelpers;
- jassert (oldName.isNotEmpty());
- jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_"));
-
- if (newName.isEmpty())
- {
- if (getObjectName (anchor1) == oldName
- || getObjectName (anchor2) == oldName)
- {
- value = resolve (nameFinder);
- anchor1 = String::empty;
- anchor2 = String::empty;
- }
- }
- else
- {
- if (getObjectName (anchor1) == oldName)
- anchor1 = newName + "." + getEdgeName (anchor1);
-
- if (getObjectName (anchor2) == oldName)
- anchor2 = newName + "." + getEdgeName (anchor2);
+ term = term.withRenamedSymbol (oldName, newName);
+ moveToAbsolute (oldValue, context);
}
}
@@ -485,9 +219,9 @@ RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordi
RelativePoint::RelativePoint (const String& s)
{
int i = 0;
- x = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
+ x = RelativeCoordinate (Expression::parse (s, i));
RelativeCoordinateHelpers::skipComma (s, i);
- y = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
+ y = RelativeCoordinate (Expression::parse (s, i));
}
bool RelativePoint::operator== (const RelativePoint& other) const throw()
@@ -500,16 +234,16 @@ bool RelativePoint::operator!= (const RelativePoint& other) const throw()
return ! operator== (other);
}
-const Point RelativePoint::resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
+const Point RelativePoint::resolve (const Expression::EvaluationContext* context) const
{
- return Point ((float) x.resolve (nameFinder),
- (float) y.resolve (nameFinder));
+ return Point ((float) x.resolve (context),
+ (float) y.resolve (context));
}
-void RelativePoint::moveToAbsolute (const Point& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
+void RelativePoint::moveToAbsolute (const Point& newPos, const Expression::EvaluationContext* context)
{
- x.moveToAbsolute (newPos.getX(), nameFinder);
- y.moveToAbsolute (newPos.getY(), nameFinder);
+ x.moveToAbsolute (newPos.getX(), context);
+ y.moveToAbsolute (newPos.getY(), context);
}
const String RelativePoint::toString() const
@@ -517,10 +251,10 @@ const String RelativePoint::toString() const
return x.toString() + ", " + y.toString();
}
-void RelativePoint::renameAnchorIfUsed (const String& oldName, const String& newName, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
+void RelativePoint::renameSymbolIfUsed (const String& oldName, const String& newName, const Expression::EvaluationContext* context)
{
- x.renameAnchorIfUsed (oldName, newName, nameFinder);
- y.renameAnchorIfUsed (oldName, newName, nameFinder);
+ x.renameSymbolIfUsed (oldName, newName, context);
+ y.renameSymbolIfUsed (oldName, newName, context);
}
bool RelativePoint::isDynamic() const
@@ -542,22 +276,22 @@ RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const Rel
RelativeRectangle::RelativeRectangle (const Rectangle& rect, const String& componentName)
: left (rect.getX()),
- right (rect.getWidth(), componentName + "." + RelativeCoordinate::Strings::left),
+ right (componentName + "." + RelativeCoordinate::Strings::left + " + " + String (rect.getWidth())),
top (rect.getY()),
- bottom (rect.getHeight(), componentName + "." + RelativeCoordinate::Strings::top)
+ bottom (componentName + "." + RelativeCoordinate::Strings::top + " + " + String (rect.getHeight()))
{
}
RelativeRectangle::RelativeRectangle (const String& s)
{
int i = 0;
- left = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
+ left = RelativeCoordinate (Expression::parse (s, i));
RelativeCoordinateHelpers::skipComma (s, i);
- top = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
+ top = RelativeCoordinate (Expression::parse (s, i));
RelativeCoordinateHelpers::skipComma (s, i);
- right = RelativeCoordinateHelpers::readNextCoordinate (s, i, true);
+ right = RelativeCoordinate (Expression::parse (s, i));
RelativeCoordinateHelpers::skipComma (s, i);
- bottom = RelativeCoordinateHelpers::readNextCoordinate (s, i, false);
+ bottom = RelativeCoordinate (Expression::parse (s, i));
}
bool RelativeRectangle::operator== (const RelativeRectangle& other) const throw()
@@ -570,22 +304,22 @@ bool RelativeRectangle::operator!= (const RelativeRectangle& other) const throw(
return ! operator== (other);
}
-const Rectangle RelativeRectangle::resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const
+const Rectangle RelativeRectangle::resolve (const Expression::EvaluationContext* context) const
{
- const double l = left.resolve (nameFinder);
- const double r = right.resolve (nameFinder);
- const double t = top.resolve (nameFinder);
- const double b = bottom.resolve (nameFinder);
+ const double l = left.resolve (context);
+ const double r = right.resolve (context);
+ const double t = top.resolve (context);
+ const double b = bottom.resolve (context);
return Rectangle ((float) l, (float) t, (float) (r - l), (float) (b - t));
}
-void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
+void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* context)
{
- left.moveToAbsolute (newPos.getX(), nameFinder);
- right.moveToAbsolute (newPos.getRight(), nameFinder);
- top.moveToAbsolute (newPos.getY(), nameFinder);
- bottom.moveToAbsolute (newPos.getBottom(), nameFinder);
+ left.moveToAbsolute (newPos.getX(), context);
+ right.moveToAbsolute (newPos.getRight(), context);
+ top.moveToAbsolute (newPos.getY(), context);
+ bottom.moveToAbsolute (newPos.getBottom(), context);
}
const String RelativeRectangle::toString() const
@@ -593,13 +327,13 @@ const String RelativeRectangle::toString() const
return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
}
-void RelativeRectangle::renameAnchorIfUsed (const String& oldName, const String& newName,
- const RelativeCoordinate::NamedCoordinateFinder* nameFinder)
+void RelativeRectangle::renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* context)
{
- left.renameAnchorIfUsed (oldName, newName, nameFinder);
- right.renameAnchorIfUsed (oldName, newName, nameFinder);
- top.renameAnchorIfUsed (oldName, newName, nameFinder);
- bottom.renameAnchorIfUsed (oldName, newName, nameFinder);
+ left.renameSymbolIfUsed (oldName, newName, context);
+ right.renameSymbolIfUsed (oldName, newName, context);
+ top.renameSymbolIfUsed (oldName, newName, context);
+ bottom.renameSymbolIfUsed (oldName, newName, context);
}
@@ -704,7 +438,7 @@ void RelativePointPath::swapWith (RelativePointPath& other) throw()
swapVariables (usesNonZeroWinding, other.usesNonZeroWinding);
}
-void RelativePointPath::createPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder)
+void RelativePointPath::createPath (Path& path, Expression::EvaluationContext* coordFinder)
{
for (int i = 0; i < elements.size(); ++i)
elements.getUnchecked(i)->addToPath (path, coordFinder);
@@ -733,7 +467,7 @@ const ValueTree RelativePointPath::StartSubPath::createTree() const
return v;
}
-void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
+void RelativePointPath::StartSubPath::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const
{
path.startNewSubPath (startPos.resolve (coordFinder));
}
@@ -755,7 +489,7 @@ const ValueTree RelativePointPath::CloseSubPath::createTree() const
return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement);
}
-void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const
+void RelativePointPath::CloseSubPath::addToPath (Path& path, Expression::EvaluationContext*) const
{
path.closeSubPath();
}
@@ -779,7 +513,7 @@ const ValueTree RelativePointPath::LineTo::createTree() const
return v;
}
-void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
+void RelativePointPath::LineTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const
{
path.lineTo (endPoint.resolve (coordFinder));
}
@@ -806,7 +540,7 @@ const ValueTree RelativePointPath::QuadraticTo::createTree() const
return v;
}
-void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
+void RelativePointPath::QuadraticTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const
{
path.quadraticTo (controlPoints[0].resolve (coordFinder),
controlPoints[1].resolve (coordFinder));
@@ -836,7 +570,7 @@ const ValueTree RelativePointPath::CubicTo::createTree() const
return v;
}
-void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const
+void RelativePointPath::CubicTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const
{
path.cubicTo (controlPoints[0].resolve (coordFinder),
controlPoints[1].resolve (coordFinder),
@@ -869,27 +603,27 @@ RelativeParallelogram::~RelativeParallelogram()
{
}
-void RelativeParallelogram::resolveThreePoints (Point* points, RelativeCoordinate::NamedCoordinateFinder* const coordFinder) const
+void RelativeParallelogram::resolveThreePoints (Point* points, Expression::EvaluationContext* const coordFinder) const
{
points[0] = topLeft.resolve (coordFinder);
points[1] = topRight.resolve (coordFinder);
points[2] = bottomLeft.resolve (coordFinder);
}
-void RelativeParallelogram::resolveFourCorners (Point* points, RelativeCoordinate::NamedCoordinateFinder* const coordFinder) const
+void RelativeParallelogram::resolveFourCorners (Point* points, Expression::EvaluationContext* const coordFinder) const
{
resolveThreePoints (points, coordFinder);
points[3] = points[1] + (points[2] - points[0]);
}
-const Rectangle RelativeParallelogram::getBounds (RelativeCoordinate::NamedCoordinateFinder* const coordFinder) const
+const Rectangle RelativeParallelogram::getBounds (Expression::EvaluationContext* const coordFinder) const
{
Point points[4];
resolveFourCorners (points, coordFinder);
return Rectangle::findAreaContainingPoints (points, 4);
}
-void RelativeParallelogram::getPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* const coordFinder) const
+void RelativeParallelogram::getPath (Path& path, Expression::EvaluationContext* const coordFinder) const
{
Point points[4];
resolveFourCorners (points, coordFinder);
@@ -901,7 +635,7 @@ void RelativeParallelogram::getPath (Path& path, RelativeCoordinate::NamedCoordi
path.closeSubPath();
}
-const AffineTransform RelativeParallelogram::resetToPerpendicular (RelativeCoordinate::NamedCoordinateFinder* const coordFinder)
+const AffineTransform RelativeParallelogram::resetToPerpendicular (Expression::EvaluationContext* const coordFinder)
{
Point corners[3];
resolveThreePoints (corners, coordFinder);
diff --git a/src/gui/graphics/geometry/juce_RelativeCoordinate.h b/src/gui/graphics/geometry/juce_RelativeCoordinate.h
index 062a549595..be8af3f428 100644
--- a/src/gui/graphics/geometry/juce_RelativeCoordinate.h
+++ b/src/gui/graphics/geometry/juce_RelativeCoordinate.h
@@ -28,27 +28,14 @@
#include "juce_Path.h"
#include "juce_Rectangle.h"
+#include "../../../containers/juce_Expression.h"
#include "../../../containers/juce_OwnedArray.h"
#include "../../../containers/juce_ValueTree.h"
//==============================================================================
/**
- Expresses a coordinate as an absolute or proportional distance from other
- named coordinates.
-
- A RelativeCoordinate represents a position as either:
- - an absolute distance from the origin
- - an absolute distance from another named RelativeCoordinate
- - a proportion of the distance between two other named RelativeCoordinates
-
- Of course, the coordinates that this one is relative to may themselves be relative
- to other coordinates, so complex arrangements can be built up (as long as you're careful
- not to create recursive loops!)
-
- Rather than keeping pointers to the coordinates that this one is dependent on, it
- stores their names, and when resolving this coordinate to an absolute value, a
- NamedCoordinateFinder class is required to retrieve these coordinates by name.
+ Expresses a coordinate as a dynamically evaluated expression.
@see RelativePoint, RelativeRectangle
*/
@@ -58,6 +45,9 @@ public:
//==============================================================================
/** Creates a zero coordinate. */
RelativeCoordinate();
+ RelativeCoordinate (const Expression& expression);
+ RelativeCoordinate (const RelativeCoordinate& other);
+ RelativeCoordinate& operator= (const RelativeCoordinate& other);
/** Creates an absolute position from the parent origin on either the X or Y axis.
@@ -65,53 +55,16 @@ public:
*/
RelativeCoordinate (double absoluteDistanceFromOrigin);
- /** Creates an absolute position relative to a named coordinate.
-
- @param absoluteDistanceFromAnchor the distance to add to the named anchor point
- @param anchorPoint the name of the coordinate from which this one will be relative. See the constructor
- notes for a description of the syntax for coordinate names.
- */
- RelativeCoordinate (double absoluteDistanceFromAnchor, const String& anchorPoint);
-
- /** Creates a relative position between two named points.
-
-
- @param relativeProportionBetweenAnchors a value between 0 and 1 indicating this coordinate's relative position
- between anchorPoint1 and anchorPoint2.
- @param anchorPoint1 the name of the first coordinate from which this one will be relative. See the constructor
- notes for a description of the syntax for coordinate names.
- @param anchorPoint2 the name of the first coordinate from which this one will be relative. See the constructor
- notes for a description of the syntax for coordinate names.
- */
- RelativeCoordinate (double relativeProportionBetweenAnchors, const String& anchorPoint1, const String& anchorPoint2);
-
/** Recreates a coordinate from a string description.
- The string can be in one of the following formats:
- - "123" = 123 pixels from parent origin (this is equivalent to "parent.left + 123"
- or "parent.top + 123", depending on which axis the coordinate is using)
- - "anchor" = the same position as the coordinate named "anchor"
- - "anchor + 123" = the coordinate named "anchor" + 123 pixels
- - "anchor - 123" = the coordinate named "anchor" - 123 pixels
- - "50%" = 50% of the distance between the coordinate space's top-left origin and its extent
- (this is equivalent to "50% * parent.left -> parent.right" or "50% * parent.top -> parent.bottom")
- - "50% * anchor" = 50% of the distance between the coordinate space's origin and the coordinate named "anchor"
- (this is equivalent to "50% * parent.left -> anchor" or "50% * parent.top -> anchor")
- - "50% * anchor1 -> anchor2" = 50% of the distance between the coordinate "anchor1" and the coordinate "anchor2"
+ The string will be parsed by ExpressionParser::parse().
- An anchor name can either be just a single identifier (letters, digits and underscores only - no spaces),
- e.g. "marker1", or it can be a two-part name in the form "objectName.edge". For example "parent.left" is
- the origin, or "myComponent.top" is the top edge of a component called "myComponent". The exact names that
- will be recognised are dependent on the NamedCoordinateFinder that you provide for looking them up, but
- "parent.left" and "parent.top" are always available, meaning the origin. "parent.right" and "parent.bottom"
- may also be available if the coordinate space has a fixed size.
-
- @param stringVersion the string to parse
+ @param stringVersion the expression to use
@param isHorizontal this must be true if this is an X coordinate, or false if it's on the Y axis.
@see toString
*/
- RelativeCoordinate (const String& stringVersion, bool isHorizontal);
+ RelativeCoordinate (const String& stringVersion);
/** Destructor. */
~RelativeCoordinate();
@@ -119,46 +72,21 @@ public:
bool operator== (const RelativeCoordinate& other) const throw();
bool operator!= (const RelativeCoordinate& other) const throw();
- //==============================================================================
- /**
- Provides an interface for looking up the position of a named anchor when resolving a RelativeCoordinate.
-
- When using RelativeCoordinates, to resolve their names you need to provide a subclass of this which
- can retrieve a coordinate by name.
- */
- class JUCE_API NamedCoordinateFinder
- {
- public:
- /** Destructor. */
- virtual ~NamedCoordinateFinder() {}
-
- /** Returns the coordinate for a given name.
-
- The objectName parameter will be the first section of the name, and the edge the name of the second part.
- E.g. for "parent.right", objectName would be "parent" and edge would be "right". If the name
- has no dot, the edge parameter will be an empty string.
-
- This method must be able to resolve "parent.left", "parent.top", "parent.right" and "parent.bottom", as
- well as any other objects that your application uses.
- */
- virtual const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const = 0;
- };
-
//==============================================================================
/** Calculates the absolute position of this coordinate.
- You'll need to provide a suitable NamedCoordinateFinder for looking up any coordinates that may
+ You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may
be needed to calculate the result.
*/
- double resolve (const NamedCoordinateFinder* nameFinder) const;
+ double resolve (const Expression::EvaluationContext* evaluationContext) const;
/** Returns true if this coordinate uses the specified coord name at any level in its evaluation.
This will recursively check any coordinates upon which this one depends.
*/
- bool references (const String& coordName, const NamedCoordinateFinder* nameFinder) const;
+ bool references (const String& coordName, const Expression::EvaluationContext* evaluationContext) const;
/** Returns true if there's a recursive loop when trying to resolve this coordinate's position. */
- bool isRecursive (const NamedCoordinateFinder* nameFinder) const;
+ bool isRecursive (const Expression::EvaluationContext* evaluationContext) const;
/** Returns true if this coordinate depends on any other coordinates for its position. */
bool isDynamic() const;
@@ -170,63 +98,7 @@ public:
or relative position to whatever value is necessary to make its resultant position
match the position that is provided.
*/
- void moveToAbsolute (double absoluteTargetPosition, const NamedCoordinateFinder* nameFinder);
-
- /** Returns true if the coordinate is calculated as a proportion of the distance between two other points.
- @see toggleProportionality
- */
- bool isProportional() const throw() { return anchor2.isNotEmpty(); }
-
- /** Toggles the coordinate between using a proportional or absolute position.
- Note that calling this will reset the names of any anchor points, and just make the
- coordinate relative to the parent origin and parent size.
- */
- void toggleProportionality (const NamedCoordinateFinder* nameFinder,
- const String& proportionalAnchor1, const String& proportionalAnchor2);
-
- /** Returns a value that can be edited to set this coordinate's position.
- The meaning of this number depends on the coordinate's mode. If the coordinate is
- proportional, the number will be a percentage between 0 and 100. If the
- coordinate is absolute, then it will be the number of pixels from its anchor point.
- @see setEditableNumber
- */
- const double getEditableNumber() const;
-
- /** Sets the value that controls this coordinate's position.
- The meaning of this number depends on the coordinate's mode. If the coordinate is
- proportional, the number must be a percentage between 0 and 100. If the
- coordinate is absolute, then it indicates the number of pixels from its anchor point.
- @see setEditableNumber
- */
- void setEditableNumber (const double newValue);
-
- //==============================================================================
- /** Returns the name of the first anchor point from which this coordinate is relative.
- */
- const String getAnchorName1 (const String& returnValueIfOrigin) const;
-
- /** Returns the name of the second anchor point from which this coordinate is relative.
- The second anchor is only valid if the coordinate is in proportional mode.
- */
- const String getAnchorName2 (const String& returnValueIfOrigin) const;
-
- /** Returns the first anchor point as a coordinate. */
- const RelativeCoordinate getAnchorCoordinate1() const;
-
- /** Returns the first anchor point as a coordinate.
- The second anchor is only valid if the coordinate is in proportional mode.
- */
- const RelativeCoordinate getAnchorCoordinate2() const;
-
- /** Changes the first anchor point, keeping the resultant position of this coordinate in
- the same place it was previously.
- */
- void changeAnchor1 (const String& newAnchor, const NamedCoordinateFinder* nameFinder);
-
- /** Changes the second anchor point, keeping the resultant position of this coordinate in
- the same place it was previously.
- */
- void changeAnchor2 (const String& newAnchor, const NamedCoordinateFinder* nameFinder);
+ void moveToAbsolute (double absoluteTargetPosition, const Expression::EvaluationContext* evaluationContext);
/** Tells the coordinate that an object is changing its name or being deleted.
@@ -235,8 +107,12 @@ public:
this coordinate was using it, the coordinate is changed to be relative to the origin
instead.
*/
- void renameAnchorIfUsed (const String& oldName, const String& newName,
- const NamedCoordinateFinder* nameFinder);
+ void renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* evaluationContext);
+
+ /** Returns the expression that defines this coordinate. */
+ const Expression& getExpression() const { return term; }
+
//==============================================================================
/** Returns a string which represents this coordinate.
@@ -267,11 +143,10 @@ public:
private:
//==============================================================================
- String anchor1, anchor2;
- double value;
+ Expression term;
- double resolve (const NamedCoordinateFinder* nameFinder, int recursionCounter) const;
- static double resolveAnchor (const String& anchorName, const NamedCoordinateFinder* nameFinder, int recursionCounter);
+// double resolve (const Expression::EvaluationContext* evaluationContext, int recursionCounter) const;
+ // static double resolveAnchor (const String& anchorName, const Expression::EvaluationContext* evaluationContext, int recursionCounter);
};
@@ -308,10 +183,10 @@ public:
/** Calculates the absolute position of this point.
- You'll need to provide a suitable NamedCoordinateFinder for looking up any coordinates that may
+ You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may
be needed to calculate the result.
*/
- const Point resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
+ const Point resolve (const Expression::EvaluationContext* evaluationContext) const;
/** Changes the values of this point's coordinates to make it resolve to the specified position.
@@ -319,7 +194,7 @@ public:
or relative positions to whatever values are necessary to make the resultant position
match the position that is provided.
*/
- void moveToAbsolute (const Point& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder);
+ void moveToAbsolute (const Point& newPos, const Expression::EvaluationContext* evaluationContext);
/** Returns a string which represents this point.
This returns a comma-separated pair of coordinates. For details of the string syntax used by the
@@ -331,8 +206,8 @@ public:
/** Tells the point that an object is changing its name or being deleted.
This calls RelativeCoordinate::renameAnchorIfUsed() on its X and Y coordinates.
*/
- void renameAnchorIfUsed (const String& oldName, const String& newName,
- const RelativeCoordinate::NamedCoordinateFinder* nameFinder);
+ void renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* evaluationContext);
/** Returns true if this point depends on any other coordinates for its position. */
bool isDynamic() const;
@@ -378,10 +253,10 @@ public:
//==============================================================================
/** Calculates the absolute position of this rectangle.
- You'll need to provide a suitable NamedCoordinateFinder for looking up any coordinates that may
+ You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may
be needed to calculate the result.
*/
- const Rectangle resolve (const RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
+ const Rectangle resolve (const Expression::EvaluationContext* evaluationContext) const;
/** Changes the values of this rectangle's coordinates to make it resolve to the specified position.
@@ -389,7 +264,7 @@ public:
or relative positions to whatever values are necessary to make the resultant position
match the position that is provided.
*/
- void moveToAbsolute (const Rectangle& newPos, const RelativeCoordinate::NamedCoordinateFinder* nameFinder);
+ void moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* evaluationContext);
/** Returns a string which represents this point.
This returns a comma-separated list of coordinates, in the order left, top, right, bottom. For details of
@@ -399,10 +274,10 @@ public:
const String toString() const;
/** Tells the rectangle that an object is changing its name or being deleted.
- This calls RelativeCoordinate::renameAnchorIfUsed() on the rectangle's coordinates.
+ This calls RelativeCoordinate::renameSymbolIfUsed() on the rectangle's coordinates.
*/
- void renameAnchorIfUsed (const String& oldName, const String& newName,
- const RelativeCoordinate::NamedCoordinateFinder* nameFinder);
+ void renameSymbolIfUsed (const String& oldName, const String& newName,
+ const Expression::EvaluationContext* evaluationContext);
// The actual rectangle coords...
RelativeCoordinate left, right, top, bottom;
@@ -430,7 +305,7 @@ public:
//==============================================================================
/** Resolves this points in this path and adds them to a normal Path object. */
- void createPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder);
+ void createPath (Path& path, Expression::EvaluationContext* coordFinder);
/** Returns true if the path contains any non-fixed points. */
bool containsAnyDynamicPoints() const;
@@ -464,7 +339,7 @@ public:
ElementBase (ElementType type);
virtual ~ElementBase() {}
virtual const ValueTree createTree() const = 0;
- virtual void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const = 0;
+ virtual void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const = 0;
virtual RelativePoint* getControlPoints (int& numPoints) = 0;
const ElementType type;
@@ -480,7 +355,7 @@ public:
StartSubPath (const RelativePoint& pos);
~StartSubPath() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
RelativePoint startPos;
@@ -496,7 +371,7 @@ public:
CloseSubPath();
~CloseSubPath() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
private:
@@ -510,7 +385,7 @@ public:
LineTo (const RelativePoint& endPoint);
~LineTo() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
RelativePoint endPoint;
@@ -526,7 +401,7 @@ public:
QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint);
~QuadraticTo() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
RelativePoint controlPoints[2];
@@ -542,7 +417,7 @@ public:
CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint);
~CubicTo() {}
const ValueTree createTree() const;
- void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
+ void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const;
RelativePoint* getControlPoints (int& numPoints);
RelativePoint controlPoints[3];
@@ -580,11 +455,11 @@ public:
~RelativeParallelogram();
//==============================================================================
- void resolveThreePoints (Point* points, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
- void resolveFourCorners (Point* points, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
- const Rectangle getBounds (RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
- void getPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
- const AffineTransform resetToPerpendicular (RelativeCoordinate::NamedCoordinateFinder* coordFinder);
+ void resolveThreePoints (Point* points, Expression::EvaluationContext* coordFinder) const;
+ void resolveFourCorners (Point* points, Expression::EvaluationContext* coordFinder) const;
+ const Rectangle getBounds (Expression::EvaluationContext* coordFinder) const;
+ void getPath (Path& path, Expression::EvaluationContext* coordFinder) const;
+ const AffineTransform resetToPerpendicular (Expression::EvaluationContext* coordFinder);
bool operator== (const RelativeParallelogram& other) const throw();
bool operator!= (const RelativeParallelogram& other) const throw();
diff --git a/src/juce_core_includes.h b/src/juce_core_includes.h
index 3865fa2db5..71368e5d44 100644
--- a/src/juce_core_includes.h
+++ b/src/juce_core_includes.h
@@ -41,6 +41,9 @@
#ifndef __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__
#include "containers/juce_ElementComparator.h"
#endif
+#ifndef __JUCE_EXPRESSION_JUCEHEADER__
+ #include "containers/juce_Expression.h"
+#endif
#ifndef __JUCE_HEAPBLOCK_JUCEHEADER__
#include "containers/juce_HeapBlock.h"
#endif
diff --git a/src/native/mac/juce_mac_OpenGLComponent.mm b/src/native/mac/juce_mac_OpenGLComponent.mm
index 8b3f5c8671..59276a8869 100644
--- a/src/native/mac/juce_mac_OpenGLComponent.mm
+++ b/src/native/mac/juce_mac_OpenGLComponent.mm
@@ -167,8 +167,6 @@ public:
[renderContext setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
[view setOpenGLContext: renderContext];
- [renderContext setView: view];
-
[format release];
viewHolder = new NSViewComponentInternal (view, component);
@@ -192,6 +190,10 @@ public:
bool makeActive() const throw()
{
jassert (renderContext != 0);
+
+ if ([renderContext view] != view)
+ [renderContext setView: view];
+
[view makeActive];
return isActive();
}
diff --git a/src/text/juce_XmlDocument.cpp b/src/text/juce_XmlDocument.cpp
index 935608a0eb..cbb724f7cc 100644
--- a/src/text/juce_XmlDocument.cpp
+++ b/src/text/juce_XmlDocument.cpp
@@ -479,8 +479,7 @@ void XmlDocument::readChildElements (XmlElement* parent)
++len;
}
- XmlElement* const e = new XmlElement ((int) 0);
- e->setText (String (inputStart, len));
+ XmlElement* const e = XmlElement::createTextElement (String (inputStart, len));
if (lastChildNode != 0)
lastChildNode->nextElement = e;