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;