diff --git a/extras/Jucer (experimental)/Source/Project/jucer_ProjectInformationComponent.cpp b/extras/Jucer (experimental)/Source/Project/jucer_ProjectInformationComponent.cpp index 45da13c89f..a32e94be02 100644 --- a/extras/Jucer (experimental)/Source/Project/jucer_ProjectInformationComponent.cpp +++ b/extras/Jucer (experimental)/Source/Project/jucer_ProjectInformationComponent.cpp @@ -104,21 +104,21 @@ ProjectInformationComponent::ProjectInformationComponent (Project& project_) configTabBox (TabbedButtonBar::TabsAtTop) { addAndMakeVisible (&configTabBox); - configTabBox.setBounds (RelativeRectangle ("8, 0, this.left + parent.right - 16, this.top + parent.bottom - 36")); + configTabBox.setBounds (RelativeRectangle ("8, 0, this.left + parent.width - 16, this.top + parent.height - 36")); addAndMakeVisible (&editConfigsButton); - editConfigsButton.setBounds (RelativeRectangle ("8, parent.bottom - 30, this.left + 192, this.top + 22")); + editConfigsButton.setBounds (RelativeRectangle ("8, parent.height - 30, this.left + 192, this.top + 22")); editConfigsButton.setButtonText ("Add/Remove Configurations..."); editConfigsButton.addListener (this); addAndMakeVisible (&openProjectButton); - openProjectButton.setBounds (RelativeRectangle ("608, parent.bottom - 30, this.left + 208, this.top + 22")); + openProjectButton.setBounds (RelativeRectangle ("608, parent.height - 30, this.left + 208, this.top + 22")); openProjectButton.setButtonText ("Open Project in "); openProjectButton.addListener (this); addAndMakeVisible (&editExportersButton); - editExportersButton.setBounds (RelativeRectangle ("208, parent.bottom - 30, this.left + 160, this.top + 22")); + editExportersButton.setBounds (RelativeRectangle ("208, parent.height - 30, this.left + 160, this.top + 22")); editExportersButton.setButtonText ("Add/Remove Exporters..."); editExportersButton.addListener (this); addAndMakeVisible (&saveAndOpenButton); - saveAndOpenButton.setBounds (RelativeRectangle ("391, parent.bottom - 30, this.left + 208, this.top + 22")); + saveAndOpenButton.setBounds (RelativeRectangle ("391, parent.height - 30, this.left + 208, this.top + 22")); saveAndOpenButton.setButtonText ("Save And Open in"); saveAndOpenButton.addListener (this); @@ -354,22 +354,22 @@ JUCER_COMPONENT_METADATA_START constructorParams="Project& project_" memberInitialisers="project (project_)"> + position="8, 0, this.left + parent.width - 16, this.top + parent.height - 36"/> + position="8, parent.height - 30, this.left + 192, this.top + 22"/> + textColour="" backgroundColourOn="" textColourOn="" position="608, parent.height - 30, this.left + 208, this.top + 22"/> - + diff --git a/extras/Jucer (experimental)/Source/Utility/jucer_PresetIDs.h b/extras/Jucer (experimental)/Source/Utility/jucer_PresetIDs.h index c3786ac218..8976eac34c 100644 --- a/extras/Jucer (experimental)/Source/Utility/jucer_PresetIDs.h +++ b/extras/Jucer (experimental)/Source/Utility/jucer_PresetIDs.h @@ -112,6 +112,7 @@ namespace Ids DECLARE_ID (tooltip); DECLARE_ID (memberName); DECLARE_ID (focusOrder); + DECLARE_ID (hidden); const Identifier class_ ("class"); const Identifier id_ ("id"); diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 1cb815bb0d..4611b70c17 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -4664,138 +4664,252 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_Expression.cpp ***/ BEGIN_JUCE_NAMESPACE +class Expression::Term : public ReferenceCountedObject +{ +public: + Term() {} + virtual ~Term() {} + + virtual Type getType() const throw() = 0; + virtual Term* clone() const = 0; + virtual const ReferenceCountedObjectPtr resolve (const Scope&, int recursionDepth) = 0; + virtual const String toString() const = 0; + virtual double toDouble() const { return 0; } + virtual int getInputIndexFor (const Term*) const { return -1; } + virtual int getOperatorPrecedence() const { return 0; } + virtual int getNumInputs() const { return 0; } + virtual Term* getInput (int) const { return 0; } + virtual const ReferenceCountedObjectPtr negated(); + + virtual const ReferenceCountedObjectPtr createTermToEvaluateInput (const Scope&, const Term* /*inputTerm*/, + double /*overallTarget*/, Term* /*topLevelTerm*/) const + { + jassertfalse; + return 0; + } + + virtual const String getName() const + { + jassertfalse; // You shouldn't call this for an expression that's not actually a function! + return String::empty; + } + + virtual void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) + { + for (int i = getNumInputs(); --i >= 0;) + getInput (i)->renameSymbol (oldSymbol, newName, scope, recursionDepth); + } + + class SymbolVisitor + { + public: + virtual ~SymbolVisitor() {} + virtual void useSymbol (const Symbol&) = 0; + }; + + virtual void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) + { + for (int i = getNumInputs(); --i >= 0;) + getInput(i)->visitAllSymbols (visitor, scope, recursionDepth); + } + +private: + JUCE_DECLARE_NON_COPYABLE (Term); +}; + class Expression::Helpers { public: typedef ReferenceCountedObjectPtr TermPtr; // This helper function is needed to work around VC6 scoping bugs - static const TermPtr& getTermFor (const Expression& exp) throw() { return exp.term; } + static inline const TermPtr& getTermFor (const Expression& exp) throw() { return exp.term; } + + static void checkRecursionDepth (const int depth) + { + if (depth > 256) + throw EvaluationError ("Recursive symbol references"); + } friend class Expression::Term; // (also only needed as a VC6 workaround) + /** An exception that can be thrown by Expression::evaluate(). */ + class EvaluationError : public std::exception + { + public: + EvaluationError (const String& description_) + : description (description_) + { + DBG ("Expression::EvaluationError: " + description); + } + + String description; + }; + class Constant : public Term { public: - Constant (const double value_, bool isResolutionTarget_) + Constant (const double value_, const bool isResolutionTarget_) : value (value_), isResolutionTarget (isResolutionTarget_) {} - Type getType() const throw() { return constantType; } - Term* clone() const { return new Constant (value, isResolutionTarget); } - double evaluate (const EvaluationContext&, int) const { return value; } - int getNumInputs() const { return 0; } - Term* getInput (int) const { return 0; } - - const TermPtr negated() - { - return new Constant (-value, isResolutionTarget); - } + Type getType() const throw() { return constantType; } + Term* clone() const { return new Constant (value, isResolutionTarget); } + const TermPtr resolve (const Scope&, int) { return this; } + double toDouble() const { return value; } + const TermPtr negated() { return new Constant (-value, isResolutionTarget); } const String toString() const { + String s (value); if (isResolutionTarget) - return "@" + String (value); + s = "@" + s; - return String (value); + return s; } double value; bool isResolutionTarget; }; - class Symbol : public Term + class BinaryTerm : public Term { public: - explicit Symbol (const String& symbol_) - : mainSymbol (symbol_.upToFirstOccurrenceOf (".", false, false).trim()), - member (symbol_.fromFirstOccurrenceOf (".", false, false).trim()) - {} + BinaryTerm (Term* const left_, Term* const right_) : left (left_), right (right_) + { + jassert (left_ != 0 && right_ != 0); + } - Symbol (const String& symbol_, const String& member_) - : mainSymbol (symbol_), - member (member_) - {} + int getInputIndexFor (const Term* possibleInput) const + { + return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1); + } + + Type getType() const throw() { return operatorType; } + int getNumInputs() const { return 2; } + Term* getInput (int index) const { return index == 0 ? left.getObject() : (index == 1 ? right.getObject() : 0); } + + virtual double performFunction (double left, double right) const = 0; + virtual void writeOperator (String& dest) const = 0; - double evaluate (const EvaluationContext& c, int recursionDepth) const + const TermPtr resolve (const Scope& scope, int recursionDepth) { - if (++recursionDepth > 256) - throw EvaluationError ("Recursive symbol references"); + return new Constant (performFunction (left->resolve (scope, recursionDepth)->toDouble(), + right->resolve (scope, recursionDepth)->toDouble()), false); + } - try - { - return getTermFor (c.getSymbolValue (mainSymbol, member))->evaluate (c, recursionDepth); - } - catch (...) - {} + const String toString() const + { + String s; - return 0; + const int ourPrecendence = getOperatorPrecedence(); + if (left->getOperatorPrecedence() > ourPrecendence) + s << '(' << left->toString() << ')'; + else + s = left->toString(); + + writeOperator (s); + + if (right->getOperatorPrecedence() >= ourPrecendence) + s << '(' << right->toString() << ')'; + else + s << right->toString(); + + return s; } - Type getType() const throw() { return symbolType; } - Term* clone() const { return new Symbol (mainSymbol, member); } - int getNumInputs() const { return 0; } - Term* getInput (int) const { return 0; } - const String toString() const { return joinParts (mainSymbol, member); } - void getSymbolParts (String& objectName, String& memberName) const { objectName = mainSymbol; memberName = member; } + protected: + const TermPtr left, right; - static const String joinParts (const String& mainSymbol, const String& member) + const TermPtr createDestinationTerm (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - return member.isEmpty() ? mainSymbol - : mainSymbol + "." + member; + 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, false); + + return dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm); } + }; - bool referencesSymbol (const String& s, const EvaluationContext* c, int recursionDepth) const + class SymbolTerm : public Term + { + public: + explicit SymbolTerm (const String& symbol_) : symbol (symbol_) {} + + const TermPtr resolve (const Scope& scope, int recursionDepth) { - if (s == mainSymbol || (s.containsChar ('.') && s == toString())) - return true; + checkRecursionDepth (recursionDepth); + return getTermFor (scope.getSymbolValue (symbol))->resolve (scope, recursionDepth + 1); + } - if (++recursionDepth > 256) - throw EvaluationError ("Recursive symbol references"); + Type getType() const throw() { return symbolType; } + Term* clone() const { return new SymbolTerm (symbol); } + const String toString() const { return symbol; } + const String getName() const { return symbol; } - try - { - return c != 0 && getTermFor (c->getSymbolValue (mainSymbol, member))->referencesSymbol (s, c, recursionDepth); - } - catch (EvaluationError&) - { - return false; - } + void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) + { + checkRecursionDepth (recursionDepth); + visitor.useSymbol (Symbol (scope.getScopeUID(), symbol)); + getTermFor (scope.getSymbolValue (symbol))->visitAllSymbols (visitor, scope, recursionDepth + 1); } - String mainSymbol, member; + void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int /*recursionDepth*/) + { + if (oldSymbol.symbolName == symbol && scope.getScopeUID() == oldSymbol.scopeUID) + symbol = newName; + } + + String symbol; }; class Function : public Term { public: - Function (const String& functionName_, const ReferenceCountedArray& parameters_) + explicit Function (const String& functionName_) : functionName (functionName_) {} + + Function (const String& functionName_, const Array& parameters_) : functionName (functionName_), parameters (parameters_) {} - Term* clone() const { return new Function (functionName, parameters); } + Type getType() const throw() { return functionType; } + Term* clone() const { return new Function (functionName, parameters); } + int getNumInputs() const { return parameters.size(); } + Term* getInput (int i) const { return getTermFor (parameters [i]); } + const String getName() const { return functionName; } - double evaluate (const EvaluationContext& c, int recursionDepth) const + const TermPtr resolve (const Scope& scope, int recursionDepth) { - HeapBlock params (parameters.size()); - for (int i = 0; i < parameters.size(); ++i) - params[i] = parameters.getUnchecked(i)->evaluate (c, recursionDepth); + checkRecursionDepth (recursionDepth); + double result = 0; + const int numParams = parameters.size(); + if (numParams > 0) + { + HeapBlock params (numParams); + for (int i = 0; i < numParams; ++i) + params[i] = getTermFor (parameters.getReference(i))->resolve (scope, recursionDepth + 1)->toDouble(); - return c.evaluateFunction (functionName, params, parameters.size()); - } + result = scope.evaluateFunction (functionName, params, numParams); + } + else + { + result = scope.evaluateFunction (functionName, 0, 0); + } - Type getType() const throw() { return functionType; } - int getInputIndexFor (const Term* possibleInput) const { return parameters.indexOf (possibleInput); } - int getNumInputs() const { return parameters.size(); } - Term* getInput (int i) const { return parameters [i]; } - const String getFunctionName() const { return functionName; } + return new Constant (result, false); + } - bool referencesSymbol (const String& s, const EvaluationContext* c, int recursionDepth) const + int getInputIndexFor (const Term* possibleInput) const { for (int i = 0; i < parameters.size(); ++i) - if (parameters.getUnchecked(i)->referencesSymbol (s, c, recursionDepth)) - return true; + if (getTermFor (parameters.getReference(i)) == possibleInput) + return i; - return false; + return -1; } const String toString() const @@ -4807,7 +4921,7 @@ public: for (int i = 0; i < parameters.size(); ++i) { - s << parameters.getUnchecked(i)->toString(); + s << getTermFor (parameters.getReference(i))->toString(); if (i < parameters.size() - 1) s << ", "; @@ -4818,117 +4932,146 @@ public: } const String functionName; - ReferenceCountedArray parameters; + Array parameters; }; - class Negate : public Term + class DotOperator : public BinaryTerm { public: - explicit Negate (const TermPtr& input_) : input (input_) - { - jassert (input_ != 0); - } - - Type getType() const throw() { return operatorType; } - 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 String getFunctionName() const { return "-"; } + DotOperator (SymbolTerm* const left_, Term* const right_) : BinaryTerm (left_, right_) {} - const TermPtr negated() + const TermPtr resolve (const Scope& scope, int recursionDepth) { - return input; + checkRecursionDepth (recursionDepth); + + EvaluationVisitor visitor (right, recursionDepth + 1); + scope.visitRelativeScope (getSymbol()->symbol, visitor); + return visitor.output; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& context, const Term* input_, double overallTarget, Term* topLevelTerm) const - { - (void) input_; - jassert (input_ == input); + Term* clone() const { return new DotOperator (getSymbol(), right); } + const String getName() const { return "."; } + int getOperatorPrecedence() const { return 1; } + void writeOperator (String& dest) const { dest << '.'; } + double performFunction (double, double) const { return 0.0; } - const Term* const dest = findDestinationFor (topLevelTerm, this); + void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) + { + checkRecursionDepth (recursionDepth); + visitor.useSymbol (Symbol (scope.getScopeUID(), getSymbol()->symbol)); - return new Negate (dest == 0 ? new Constant (overallTarget, false) - : dest->createTermToEvaluateInput (context, this, overallTarget, topLevelTerm)); + SymbolVisitingVisitor v (right, visitor, recursionDepth + 1); + scope.visitRelativeScope (getSymbol()->symbol, v); } - const String toString() const + void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) { - if (input->getOperatorPrecedence() > 0) - return "-(" + input->toString() + ")"; - else - return "-" + input->toString(); - } + checkRecursionDepth (recursionDepth); + getSymbol()->renameSymbol (oldSymbol, newName, scope, recursionDepth); - bool referencesSymbol (const String& s, const EvaluationContext* c, int recursionDepth) const - { - return input->referencesSymbol (s, c, recursionDepth); + SymbolRenamingVisitor visitor (right, oldSymbol, newName, recursionDepth + 1); + scope.visitRelativeScope (getSymbol()->symbol, visitor); } private: - const TermPtr input; - }; - class BinaryTerm : public Term - { - public: - BinaryTerm (Term* const left_, Term* const right_) : left (left_), right (right_) + class EvaluationVisitor : public Scope::Visitor { - jassert (left_ != 0 && right_ != 0); - } + public: + EvaluationVisitor (const TermPtr& input_, const int recursionCount_) + : input (input_), output (input_), recursionCount (recursionCount_) {} - int getInputIndexFor (const Term* possibleInput) const - { - return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1); - } + void visit (const Scope& scope) { output = input->resolve (scope, recursionCount); } - Type getType() const throw() { return operatorType; } - int getNumInputs() const { return 2; } - Term* getInput (int index) const { return index == 0 ? static_cast (left) : (index == 1 ? static_cast (right) : 0); } + const TermPtr input; + TermPtr output; + const int recursionCount; - bool referencesSymbol (const String& s, const EvaluationContext* c, int recursionDepth) const + private: + JUCE_DECLARE_NON_COPYABLE (EvaluationVisitor); + }; + + class SymbolVisitingVisitor : public Scope::Visitor { - return left->referencesSymbol (s, c, recursionDepth) - || right->referencesSymbol (s, c, recursionDepth); - } + public: + SymbolVisitingVisitor (const TermPtr& input_, SymbolVisitor& visitor_, const int recursionCount_) + : input (input_), visitor (visitor_), recursionCount (recursionCount_) {} - const String toString() const + void visit (const Scope& scope) { input->visitAllSymbols (visitor, scope, recursionCount); } + + private: + const TermPtr input; + SymbolVisitor& visitor; + const int recursionCount; + + JUCE_DECLARE_NON_COPYABLE (SymbolVisitingVisitor); + }; + + class SymbolRenamingVisitor : public Scope::Visitor { - String s; + public: + SymbolRenamingVisitor (const TermPtr& input_, const Expression::Symbol& symbol_, const String& newName_, const int recursionCount_) + : input (input_), symbol (symbol_), newName (newName_), recursionCount (recursionCount_) {} - const int ourPrecendence = getOperatorPrecedence(); - if (left->getOperatorPrecedence() > ourPrecendence) - s << '(' << left->toString() << ')'; - else - s = left->toString(); + void visit (const Scope& scope) { input->renameSymbol (symbol, newName, scope, recursionCount); } - s << ' ' << getFunctionName() << ' '; + private: + const TermPtr input; + const Symbol& symbol; + const String newName; + const int recursionCount; - if (right->getOperatorPrecedence() >= ourPrecendence) - s << '(' << right->toString() << ')'; - else - s << right->toString(); + JUCE_DECLARE_NON_COPYABLE (SymbolRenamingVisitor); + }; - return s; + SymbolTerm* getSymbol() const { return static_cast (left.getObject()); } + + JUCE_DECLARE_NON_COPYABLE (DotOperator); + }; + + class Negate : public Term + { + public: + explicit Negate (const TermPtr& input_) : input (input_) + { + jassert (input_ != 0); } - protected: - const TermPtr left, right; + Type getType() const throw() { return operatorType; } + int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; } + int getNumInputs() const { return 1; } + Term* getInput (int index) const { return index == 0 ? input.getObject() : 0; } + Term* clone() const { return new Negate (input->clone()); } - const TermPtr createDestinationTerm (const EvaluationContext& context, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr resolve (const Scope& scope, int recursionDepth) { - jassert (input == left || input == right); - if (input != left && input != right) - return 0; + return new Constant (-input->resolve (scope, recursionDepth)->toDouble(), false); + } + + const String getName() const { return "-"; } + const TermPtr negated() { return input; } + + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input_, double overallTarget, Term* topLevelTerm) const + { + (void) input_; + jassert (input_ == input); const Term* const dest = findDestinationFor (topLevelTerm, this); - if (dest == 0) - return new Constant (overallTarget, false); + return new Negate (dest == 0 ? new Constant (overallTarget, false) + : dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm)); + } - return dest->createTermToEvaluateInput (context, this, overallTarget, topLevelTerm); + const String toString() const + { + if (input->getOperatorPrecedence() > 0) + return "-(" + input->toString() + ")"; + else + return "-" + input->toString(); } + + private: + const TermPtr input; }; class Add : public BinaryTerm @@ -4937,13 +5080,14 @@ 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); } - int getOperatorPrecedence() const { return 2; } - const String getFunctionName() const { return "+"; } + double performFunction (double lhs, double rhs) const { return lhs + rhs; } + int getOperatorPrecedence() const { return 3; } + const String getName() const { return "+"; } + void writeOperator (String& dest) const { dest << " + "; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& c, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - const TermPtr newDest (createDestinationTerm (c, input, overallTarget, topLevelTerm)); + const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == 0) return 0; @@ -4960,13 +5104,14 @@ 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); } - int getOperatorPrecedence() const { return 2; } - const String getFunctionName() const { return "-"; } + double performFunction (double lhs, double rhs) const { return lhs - rhs; } + int getOperatorPrecedence() const { return 3; } + const String getName() const { return "-"; } + void writeOperator (String& dest) const { dest << " - "; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& c, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - const TermPtr newDest (createDestinationTerm (c, input, overallTarget, topLevelTerm)); + const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == 0) return 0; @@ -4986,13 +5131,14 @@ 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 getFunctionName() const { return "*"; } - int getOperatorPrecedence() const { return 1; } + double performFunction (double lhs, double rhs) const { return lhs * rhs; } + const String getName() const { return "*"; } + void writeOperator (String& dest) const { dest << " * "; } + int getOperatorPrecedence() const { return 2; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& c, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - const TermPtr newDest (createDestinationTerm (c, input, overallTarget, topLevelTerm)); + const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == 0) return 0; @@ -5009,13 +5155,14 @@ 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 getFunctionName() const { return "/"; } - int getOperatorPrecedence() const { return 1; } + double performFunction (double lhs, double rhs) const { return lhs / rhs; } + const String getName() const { return "/"; } + void writeOperator (String& dest) const { dest << " / "; } + int getOperatorPrecedence() const { return 2; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& c, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - const TermPtr newDest (createDestinationTerm (c, input, overallTarget, topLevelTerm)); + const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == 0) return 0; @@ -5088,24 +5235,31 @@ public: return false; } - static bool renameSymbol (Term* const t, const String& oldName, const String& newName) + class SymbolCheckVisitor : public Term::SymbolVisitor { - Symbol* const sym = dynamic_cast (t); + public: + SymbolCheckVisitor (const Symbol& symbol_) : wasFound (false), symbol (symbol_) {} + void useSymbol (const Symbol& s) { wasFound = wasFound || s == symbol; } - if (sym != 0 && sym->mainSymbol == oldName) - { - sym->mainSymbol = newName; - return true; - } + bool wasFound; - bool anyChanged = false; + private: + const Symbol& symbol; - for (int i = t->getNumInputs(); --i >= 0;) - if (renameSymbol (t->getInput (i), oldName, newName)) - anyChanged = true; + JUCE_DECLARE_NON_COPYABLE (SymbolCheckVisitor); + }; - return anyChanged; - } + class SymbolListVisitor : public Term::SymbolVisitor + { + public: + SymbolListVisitor (Array& list_) : list (list_) {} + void useSymbol (const Symbol& s) { list.addIfNotAlreadyThere (s); } + + private: + Array& list; + + JUCE_DECLARE_NON_COPYABLE (SymbolListVisitor); + }; class Parser { @@ -5194,7 +5348,7 @@ public: { ++i; - while (CharacterFunctions::isLetterOrDigit (text[i]) || text[i] == '_' || text[i] == '.') + while (CharacterFunctions::isLetterOrDigit (text[i]) || text[i] == '_') ++i; } @@ -5327,12 +5481,17 @@ public: if (e != 0) return e; + return readSymbolOrFunction(); + } + + const TermPtr readSymbolOrFunction() + { String identifier; if (readIdentifier (identifier)) { if (readOperator ("(")) // method call... { - Function* const f = new Function (identifier, ReferenceCountedArray()); + Function* const f = new Function (identifier); ScopedPointer func (f); // (can't use ScopedPointer in MSVC) TermPtr param (readExpression()); @@ -5345,7 +5504,7 @@ public: throw ParseError ("Expected parameters after \"" + identifier + " (\""); } - f->parameters.add (param); + f->parameters.add (Expression (param)); while (readOperator (",")) { @@ -5354,7 +5513,7 @@ public: if (param == 0) throw ParseError ("Expected expression after \",\""); - f->parameters.add (param); + f->parameters.add (Expression (param)); } if (readOperator (")")) @@ -5362,9 +5521,22 @@ public: throw ParseError ("Expected \")\""); } + else if (readOperator (".")) + { + TermPtr rhs (readSymbolOrFunction()); + + if (rhs == 0) + throw ParseError ("Expected symbol or function after \".\""); + + if (identifier == "this") + return rhs; + + return new DotOperator (new SymbolTerm (identifier), rhs); + } else // just a symbol.. { - return new Symbol (identifier); + jassert (identifier.trim() == identifier); + return new SymbolTerm (identifier); } } @@ -5441,12 +5613,33 @@ const Expression Expression::parse (const String& stringToParse, int& textIndexT double Expression::evaluate() const { - return evaluate (Expression::EvaluationContext()); + return evaluate (Expression::Scope()); +} + +double Expression::evaluate (const Expression::Scope& scope) const +{ + try + { + return term->resolve (scope, 0)->toDouble(); + } + catch (Helpers::EvaluationError&) + {} + + return 0; } -double Expression::evaluate (const Expression::EvaluationContext& context) const +double Expression::evaluate (const Scope& scope, String& evaluationError) const { - return term->evaluate (context, 0); + try + { + return term->resolve (scope, 0)->toDouble(); + } + catch (Helpers::EvaluationError& e) + { + evaluationError = e.description; + } + + return 0; } const Expression Expression::operator+ (const Expression& other) const { return Expression (new Helpers::Add (term, other.term)); } @@ -5454,28 +5647,14 @@ const Expression Expression::operator- (const Expression& other) const { return 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 (term->negated()); } - -const String Expression::toString() const -{ - return term->toString(); -} - -const Expression Expression::symbol (const String& symbol) -{ - return Expression (new Helpers::Symbol (symbol)); -} +const Expression Expression::symbol (const String& symbol) { return Expression (new Helpers::SymbolTerm (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)); + return Expression (new Helpers::Function (functionName, parameters)); } -const Expression Expression::adjustedToGiveNewResult (const double targetValue, - const Expression::EvaluationContext& context) const +const Expression Expression::adjustedToGiveNewResult (const double targetValue, const Expression::Scope& scope) const { ScopedPointer newTerm (term->clone()); @@ -5500,140 +5679,96 @@ const Expression Expression::adjustedToGiveNewResult (const double targetValue, } else { - const Helpers::TermPtr reverseTerm (parent->createTermToEvaluateInput (context, termToAdjust, targetValue, newTerm)); + const Helpers::TermPtr reverseTerm (parent->createTermToEvaluateInput (scope, termToAdjust, targetValue, newTerm)); if (reverseTerm == 0) return Expression (targetValue); - termToAdjust->value = reverseTerm->evaluate (context, 0); + termToAdjust->value = reverseTerm->resolve (scope, 0)->toDouble(); } return Expression (newTerm.release()); } -const Expression Expression::withRenamedSymbol (const String& oldSymbol, const String& newSymbol) const +const Expression Expression::withRenamedSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Scope& scope) const { - jassert (newSymbol.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); + jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); - if (oldSymbol == newSymbol) + if (oldSymbol.symbolName == newName) return *this; - 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); -} - -Expression::Type Expression::getType() const throw() -{ - return term->getType(); -} - -const String Expression::getSymbol() const -{ - String objectName, memberName; - term->getSymbolParts (objectName, memberName); - return Expression::Helpers::Symbol::joinParts (objectName, memberName); -} - -void Expression::getSymbolParts (String& objectName, String& memberName) const -{ - term->getSymbolParts (objectName, memberName); -} - -const String Expression::getFunction() const -{ - return term->getFunctionName(); -} - -const String Expression::getOperator() const -{ - return term->getFunctionName(); -} - -int Expression::getNumInputs() const -{ - return term->getNumInputs(); + Expression e (term->clone()); + e.term->renameSymbol (oldSymbol, newName, scope, 0); + return e; } -const Expression Expression::getInput (int index) const +bool Expression::referencesSymbol (const Expression::Symbol& symbol, const Scope& scope) const { - return Expression (term->getInput (index)); -} + Helpers::SymbolCheckVisitor visitor (symbol); -int Expression::Term::getOperatorPrecedence() const -{ - return 0; -} + try + { + term->visitAllSymbols (visitor, scope, 0); + } + catch (Helpers::EvaluationError&) + {} -bool Expression::Term::referencesSymbol (const String&, const EvaluationContext*, int) const -{ - return false; + return visitor.wasFound; } -int Expression::Term::getInputIndexFor (const Term*) const +void Expression::findReferencedSymbols (Array& results, const Scope& scope) const { - return -1; + try + { + Helpers::SymbolListVisitor visitor (results); + term->visitAllSymbols (visitor, scope, 0); + } + catch (Helpers::EvaluationError&) + {} } -const ReferenceCountedObjectPtr Expression::Term::createTermToEvaluateInput (const EvaluationContext&, const Term*, double, Term*) const -{ - jassertfalse; - return 0; -} +const String Expression::toString() const { return term->toString(); } +bool Expression::usesAnySymbols() const { return Helpers::containsAnySymbols (term); } +Expression::Type Expression::getType() const throw() { return term->getType(); } +const String Expression::getSymbolOrFunction() const { return term->getName(); } +int Expression::getNumInputs() const { return term->getNumInputs(); } +const Expression Expression::getInput (int index) const { return Expression (term->getInput (index)); } const ReferenceCountedObjectPtr Expression::Term::negated() { return new Helpers::Negate (this); } -void Expression::Term::getSymbolParts (String&, String&) const -{ - jassertfalse; // You should only call getSymbol() on an expression that's actually a symbol! -} - -const String Expression::Term::getFunctionName() const -{ - jassertfalse; // You shouldn't call this for an expression that's not actually a function! - return String::empty; -} - Expression::ParseError::ParseError (const String& message) : description (message) { DBG ("Expression::ParseError: " + message); } -Expression::EvaluationError::EvaluationError (const String& message) - : description (message) +Expression::Symbol::Symbol (const String& scopeUID_, const String& symbolName_) + : scopeUID (scopeUID_), symbolName (symbolName_) { - DBG ("Expression::EvaluationError: " + description); } -Expression::EvaluationError::EvaluationError (const String& symbol, const String& member) - : description ("Unknown symbol: \"" + symbol + (member.isEmpty() ? "\"" : ("." + member + "\""))) +bool Expression::Symbol::operator== (const Symbol& other) const throw() { - DBG ("Expression::EvaluationError: " + description); + return symbolName == other.symbolName && scopeUID == other.scopeUID; } -Expression::EvaluationContext::EvaluationContext() {} -Expression::EvaluationContext::~EvaluationContext() {} +bool Expression::Symbol::operator!= (const Symbol& other) const throw() +{ + return ! operator== (other); +} + +Expression::Scope::Scope() {} +Expression::Scope::~Scope() {} -const Expression Expression::EvaluationContext::getSymbolValue (const String& symbol, const String& member) const +const Expression Expression::Scope::getSymbolValue (const String& symbol) const { - throw EvaluationError (symbol, member); + throw Helpers::EvaluationError ("Unknown symbol: " + symbol); } -double Expression::EvaluationContext::evaluateFunction (const String& functionName, const double* parameters, int numParams) const +double Expression::Scope::evaluateFunction (const String& functionName, const double* parameters, int numParams) const { if (numParams > 0) { @@ -5662,7 +5797,16 @@ double Expression::EvaluationContext::evaluateFunction (const String& functionNa } } - throw EvaluationError ("Unknown function: \"" + functionName + "\""); + throw Helpers::EvaluationError ("Unknown function: \"" + functionName + "\""); +} + +void Expression::Scope::visitRelativeScope (const String&, Visitor&) const +{ +} + +const String Expression::Scope::getScopeUID() const +{ + return String::empty; } END_JUCE_NAMESPACE @@ -23229,10 +23373,6 @@ public: } } - ~WavAudioFormatReader() - { - } - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { @@ -23388,7 +23528,8 @@ private: const int bytesPerFrame = numChannels * bitsPerSample / 8; output->writeInt (chunkName ("RIFF")); output->writeInt ((int) (lengthInSamples * bytesPerFrame - + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36))); + + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36) + + (smplChunk.getSize() > 0 ? smplChunk.getSize() + 8 : 0))); output->writeInt (chunkName ("WAVE")); output->writeInt (chunkName ("fmt ")); @@ -35989,10 +36130,6 @@ AudioProcessorGraph::Node::Node (const uint32 id_, AudioProcessor* const process jassert (processor_ != 0); } -AudioProcessorGraph::Node::~Node() -{ -} - void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize, AudioProcessorGraph* const graph) { @@ -42908,6 +43045,8 @@ public: void componentBeingDeleted (Component& comp) { + ComponentMovementWatcher::componentBeingDeleted (comp); + if (component == &comp || comp.isParentOf (component)) cancel(); } @@ -43041,9 +43180,14 @@ void ModalComponentManager::handleAsyncUpdate() if (! item->isActive) { for (int j = item->callbacks.size(); --j >= 0;) + { item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue); - stack.remove (i); + if (! stack.contains (item)) + break; + } + + stack.removeObject (item); } } } @@ -49185,8 +49329,13 @@ const Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) Graphics g (snapshot); g.setOrigin (pos.getX() - imageX, pos.getY() - imageY); + if (g.reduceClipRegion (rowComp->getLocalBounds())) + { + g.beginTransparencyLayer (0.6f); rowComp->paintEntireComponent (g, false); + g.endTransparencyLayer(); + } } } @@ -49202,7 +49351,6 @@ void ListBox::startDragAndDrop (const MouseEvent& e, const String& dragDescripti { int x, y; Image dragImage (createSnapshotOfSelectedRows (x, y)); - dragImage.multiplyAllAlphas (0.6f); MouseEvent e2 (e.getEventRelativeTo (this)); const Point p (x - e2.x, y - e2.y); @@ -79205,43 +79353,17 @@ void MarkerList::ValueTreeWrapper::removeMarker (const ValueTree& marker, UndoMa state.removeChild (marker, undoManager); } -class MarkerListEvaluator : public Expression::EvaluationContext +double MarkerList::getMarkerPosition (const Marker& marker, Component* parentComponent) const { -public: - MarkerListEvaluator (const MarkerList& markerList_, Component* const parentComponent_) - : markerList (markerList_), parentComponent (parentComponent_) + if (parentComponent != 0) { + RelativeCoordinatePositionerBase::ComponentScope scope (*parentComponent); + return marker.position.resolve (&scope); } - - const Expression getSymbolValue (const String& objectName, const String& member) const + else { - if (member.isEmpty()) - { - const MarkerList::Marker* const marker = markerList.getMarker (objectName); - - if (marker != 0) - return Expression (marker->position.resolve (this)); - } - else if (parentComponent != 0 && objectName == RelativeCoordinate::Strings::parent) - { - if (member == RelativeCoordinate::Strings::right) return Expression ((double) parentComponent->getWidth()); - if (member == RelativeCoordinate::Strings::bottom) return Expression ((double) parentComponent->getHeight()); - } - - return Expression::EvaluationContext::getSymbolValue (objectName, member); + return marker.position.resolve (0); } - -private: - const MarkerList& markerList; - Component* parentComponent; - - JUCE_DECLARE_NON_COPYABLE (MarkerListEvaluator); -}; - -double MarkerList::getMarkerPosition (const Marker& marker, Component* const parentComponent) const -{ - MarkerListEvaluator context (*this, parentComponent); - return marker.position.resolve (&context); } void MarkerList::ValueTreeWrapper::applyTo (MarkerList& markerList) @@ -79280,15 +79402,28 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE const String RelativeCoordinate::Strings::parent ("parent"); -const String RelativeCoordinate::Strings::this_ ("this"); const String RelativeCoordinate::Strings::left ("left"); const String RelativeCoordinate::Strings::right ("right"); const String RelativeCoordinate::Strings::top ("top"); const String RelativeCoordinate::Strings::bottom ("bottom"); -const String RelativeCoordinate::Strings::parentLeft ("parent.left"); -const String RelativeCoordinate::Strings::parentTop ("parent.top"); -const String RelativeCoordinate::Strings::parentRight ("parent.right"); -const String RelativeCoordinate::Strings::parentBottom ("parent.bottom"); +const String RelativeCoordinate::Strings::x ("x"); +const String RelativeCoordinate::Strings::y ("y"); +const String RelativeCoordinate::Strings::width ("width"); +const String RelativeCoordinate::Strings::height ("height"); + +RelativeCoordinate::StandardStrings::Type RelativeCoordinate::StandardStrings::getTypeOf (const String& s) throw() +{ + if (s == Strings::left) return left; + if (s == Strings::right) return right; + if (s == Strings::top) return top; + if (s == Strings::bottom) return bottom; + if (s == Strings::x) return x; + if (s == Strings::y) return y; + if (s == Strings::width) return width; + if (s == Strings::height) return height; + if (s == Strings::parent) return parent; + return unknown; +} RelativeCoordinate::RelativeCoordinate() { @@ -79339,12 +79474,12 @@ bool RelativeCoordinate::operator!= (const RelativeCoordinate& other) const thro return ! operator== (other); } -double RelativeCoordinate::resolve (const Expression::EvaluationContext* context) const +double RelativeCoordinate::resolve (const Expression::Scope* scope) const { try { - if (context != 0) - return term.evaluate (*context); + if (scope != 0) + return term.evaluate (*scope); else return term.evaluate(); } @@ -79354,12 +79489,12 @@ double RelativeCoordinate::resolve (const Expression::EvaluationContext* context return 0.0; } -bool RelativeCoordinate::isRecursive (const Expression::EvaluationContext* context) const +bool RelativeCoordinate::isRecursive (const Expression::Scope* scope) const { try { - if (context != 0) - term.evaluate (*context); + if (scope != 0) + term.evaluate (*scope); else term.evaluate(); } @@ -79371,36 +79506,24 @@ bool RelativeCoordinate::isRecursive (const Expression::EvaluationContext* conte return false; } -void RelativeCoordinate::moveToAbsolute (double newPos, const Expression::EvaluationContext* context) +void RelativeCoordinate::moveToAbsolute (double newPos, const Expression::Scope* scope) { try { - if (context != 0) + if (scope != 0) { - term = term.adjustedToGiveNewResult (newPos, *context); + term = term.adjustedToGiveNewResult (newPos, *scope); } else { - Expression::EvaluationContext defaultContext; - term = term.adjustedToGiveNewResult (newPos, defaultContext); + Expression::Scope defaultScope; + term = term.adjustedToGiveNewResult (newPos, defaultScope); } } catch (...) {} } -bool RelativeCoordinate::references (const String& coordName, const Expression::EvaluationContext* context) const -{ - try - { - return term.referencesSymbol (coordName, context); - } - catch (...) - {} - - return false; -} - bool RelativeCoordinate::isDynamic() const { return term.usesAnySymbols(); @@ -79411,14 +79534,6 @@ const String RelativeCoordinate::toString() const return term.toString(); } -void RelativeCoordinate::renameSymbolIfUsed (const String& oldName, const String& newName) -{ - jassert (newName.isNotEmpty() && newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); - - if (term.referencesSymbol (oldName, 0)) - term = term.withRenamedSymbol (oldName, newName); -} - END_JUCE_NAMESPACE /*** End of inlined file: juce_RelativeCoordinate.cpp ***/ @@ -79475,16 +79590,16 @@ bool RelativePoint::operator!= (const RelativePoint& other) const throw() return ! operator== (other); } -const Point RelativePoint::resolve (const Expression::EvaluationContext* context) const +const Point RelativePoint::resolve (const Expression::Scope* scope) const { - return Point ((float) x.resolve (context), - (float) y.resolve (context)); + return Point ((float) x.resolve (scope), + (float) y.resolve (scope)); } -void RelativePoint::moveToAbsolute (const Point& newPos, const Expression::EvaluationContext* context) +void RelativePoint::moveToAbsolute (const Point& newPos, const Expression::Scope* scope) { - x.moveToAbsolute (newPos.getX(), context); - y.moveToAbsolute (newPos.getY(), context); + x.moveToAbsolute (newPos.getX(), scope); + y.moveToAbsolute (newPos.getY(), scope); } const String RelativePoint::toString() const @@ -79492,12 +79607,6 @@ const String RelativePoint::toString() const return x.toString() + ", " + y.toString(); } -void RelativePoint::renameSymbolIfUsed (const String& oldName, const String& newName) -{ - x.renameSymbolIfUsed (oldName, newName); - y.renameSymbolIfUsed (oldName, newName); -} - bool RelativePoint::isDynamic() const { return x.isDynamic() || y.isDynamic(); @@ -79523,13 +79632,23 @@ namespace RelativeRectangleHelpers bool dependsOnSymbolsOtherThanThis (const Expression& e) { + if (e.getType() == Expression::operatorType && e.getSymbolOrFunction() == ".") + return true; + if (e.getType() == Expression::symbolType) { - String objectName, memberName; - e.getSymbolParts (objectName, memberName); + switch (RelativeCoordinate::StandardStrings::getTypeOf (e.getSymbolOrFunction())) + { + case RelativeCoordinate::StandardStrings::x: + case RelativeCoordinate::StandardStrings::y: + case RelativeCoordinate::StandardStrings::left: + case RelativeCoordinate::StandardStrings::right: + case RelativeCoordinate::StandardStrings::top: + case RelativeCoordinate::StandardStrings::bottom: return false; + default: break; + } - if (objectName != RelativeCoordinate::Strings::this_) - return true; + return true; } else { @@ -79554,11 +79673,9 @@ RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const Rel RelativeRectangle::RelativeRectangle (const Rectangle& rect) : left (rect.getX()), - right (Expression::symbol (RelativeCoordinate::Strings::this_ + "." + RelativeCoordinate::Strings::left) - + Expression ((double) rect.getWidth())), + right (Expression::symbol (RelativeCoordinate::Strings::left) + Expression ((double) rect.getWidth())), top (rect.getY()), - bottom (Expression::symbol (RelativeCoordinate::Strings::this_ + "." + RelativeCoordinate::Strings::top) - + Expression ((double) rect.getHeight())) + bottom (Expression::symbol (RelativeCoordinate::Strings::top) + Expression ((double) rect.getHeight())) { } @@ -79584,22 +79701,58 @@ bool RelativeRectangle::operator!= (const RelativeRectangle& other) const throw( return ! operator== (other); } -const Rectangle RelativeRectangle::resolve (const Expression::EvaluationContext* context) const +// An expression context that can evaluate expressions using "this" +class RelativeRectangleLocalScope : public Expression::Scope { - const double l = left.resolve (context); - const double r = right.resolve (context); - const double t = top.resolve (context); - const double b = bottom.resolve (context); +public: + RelativeRectangleLocalScope (const RelativeRectangle& rect_) : rect (rect_) {} + + const Expression getSymbolValue (const String& symbol) const + { + switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol)) + { + case RelativeCoordinate::StandardStrings::x: + case RelativeCoordinate::StandardStrings::left: return rect.left.getExpression(); + case RelativeCoordinate::StandardStrings::y: + case RelativeCoordinate::StandardStrings::top: return rect.top.getExpression(); + case RelativeCoordinate::StandardStrings::right: return rect.right.getExpression(); + case RelativeCoordinate::StandardStrings::bottom: return rect.bottom.getExpression(); + default: break; + } + + return Expression::Scope::getSymbolValue (symbol); + } - return Rectangle ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t)); +private: + const RelativeRectangle& rect; + + JUCE_DECLARE_NON_COPYABLE (RelativeRectangleLocalScope); +}; + +const Rectangle RelativeRectangle::resolve (const Expression::Scope* scope) const +{ + if (scope == 0) + { + RelativeRectangleLocalScope scope (*this); + return resolve (&scope); + } + else + { + const double l = left.resolve (scope); + const double r = right.resolve (scope); + const double t = top.resolve (scope); + const double b = bottom.resolve (scope); + + return Rectangle ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t)); + } } -void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* context) +void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Expression::Scope* scope) { - left.moveToAbsolute (newPos.getX(), context); - right.moveToAbsolute (newPos.getRight(), context); - top.moveToAbsolute (newPos.getY(), context); - bottom.moveToAbsolute (newPos.getBottom(), context); + left.moveToAbsolute (newPos.getX(), scope); + right.moveToAbsolute (newPos.getRight(), scope); + top.moveToAbsolute (newPos.getY(), scope); + bottom.moveToAbsolute (newPos.getBottom(), scope); } bool RelativeRectangle::isDynamic() const @@ -79617,12 +79770,12 @@ const String RelativeRectangle::toString() const return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString(); } -void RelativeRectangle::renameSymbolIfUsed (const String& oldName, const String& newName) +void RelativeRectangle::renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope) { - left.renameSymbolIfUsed (oldName, newName); - right.renameSymbolIfUsed (oldName, newName); - top.renameSymbolIfUsed (oldName, newName); - bottom.renameSymbolIfUsed (oldName, newName); + left = left.getExpression().withRenamedSymbol (oldSymbol, newName, scope); + right = right.getExpression().withRenamedSymbol (oldSymbol, newName, scope); + top = top.getExpression().withRenamedSymbol (oldSymbol, newName, scope); + bottom = bottom.getExpression().withRenamedSymbol (oldSymbol, newName, scope); } class RelativeRectangleComponentPositioner : public RelativeCoordinatePositionerBase @@ -79652,7 +79805,8 @@ public: { for (int i = 4; --i >= 0;) { - const Rectangle newBounds (rectangle.resolve (this).getSmallestIntegerContainer()); + ComponentScope scope (getComponent()); + const Rectangle newBounds (rectangle.resolve (&scope).getSmallestIntegerContainer()); if (newBounds == getComponent().getBounds()) return; @@ -79669,31 +79823,6 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner); }; -// An expression context that can evaluate expressions using "this" -class TemporaryRectangleContext : public Expression::EvaluationContext -{ -public: - TemporaryRectangleContext (const RelativeRectangle& rect_) : rect (rect_) {} - - const Expression getSymbolValue (const String& objectName, const String& edge) const - { - if (objectName == RelativeCoordinate::Strings::this_) - { - if (edge == RelativeCoordinate::Strings::left) return rect.left.getExpression(); - if (edge == RelativeCoordinate::Strings::right) return rect.right.getExpression(); - if (edge == RelativeCoordinate::Strings::top) return rect.top.getExpression(); - if (edge == RelativeCoordinate::Strings::bottom) return rect.bottom.getExpression(); - } - - return Expression::EvaluationContext::getSymbolValue (objectName, edge); - } - -private: - const RelativeRectangle& rect; - - JUCE_DECLARE_NON_COPYABLE (TemporaryRectangleContext); -}; - void RelativeRectangle::applyToComponent (Component& component) const { if (isDynamic()) @@ -79711,9 +79840,7 @@ void RelativeRectangle::applyToComponent (Component& component) const else { component.setPositioner (0); - - TemporaryRectangleContext context (*this); - component.setBounds (resolve (&context).getSmallestIntegerContainer()); + component.setBounds (resolve (0).getSmallestIntegerContainer()); } } @@ -79801,10 +79928,10 @@ void RelativePointPath::swapWith (RelativePointPath& other) throw() swapVariables (containsDynamicPoints, other.containsDynamicPoints); } -void RelativePointPath::createPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::createPath (Path& path, Expression::Scope* scope) const { for (int i = 0; i < elements.size(); ++i) - elements.getUnchecked(i)->addToPath (path, coordFinder); + elements.getUnchecked(i)->addToPath (path, scope); } bool RelativePointPath::containsAnyDynamicPoints() const @@ -79849,9 +79976,9 @@ const ValueTree RelativePointPath::StartSubPath::createTree() const return v; } -void RelativePointPath::StartSubPath::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::StartSubPath::addToPath (Path& path, Expression::Scope* scope) const { - path.startNewSubPath (startPos.resolve (coordFinder)); + path.startNewSubPath (startPos.resolve (scope)); } RelativePoint* RelativePointPath::StartSubPath::getControlPoints (int& numPoints) @@ -79875,7 +80002,7 @@ const ValueTree RelativePointPath::CloseSubPath::createTree() const return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement); } -void RelativePointPath::CloseSubPath::addToPath (Path& path, Expression::EvaluationContext*) const +void RelativePointPath::CloseSubPath::addToPath (Path& path, Expression::Scope*) const { path.closeSubPath(); } @@ -79903,9 +80030,9 @@ const ValueTree RelativePointPath::LineTo::createTree() const return v; } -void RelativePointPath::LineTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::LineTo::addToPath (Path& path, Expression::Scope* scope) const { - path.lineTo (endPoint.resolve (coordFinder)); + path.lineTo (endPoint.resolve (scope)); } RelativePoint* RelativePointPath::LineTo::getControlPoints (int& numPoints) @@ -79934,10 +80061,10 @@ const ValueTree RelativePointPath::QuadraticTo::createTree() const return v; } -void RelativePointPath::QuadraticTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::QuadraticTo::addToPath (Path& path, Expression::Scope* scope) const { - path.quadraticTo (controlPoints[0].resolve (coordFinder), - controlPoints[1].resolve (coordFinder)); + path.quadraticTo (controlPoints[0].resolve (scope), + controlPoints[1].resolve (scope)); } RelativePoint* RelativePointPath::QuadraticTo::getControlPoints (int& numPoints) @@ -79968,11 +80095,11 @@ const ValueTree RelativePointPath::CubicTo::createTree() const return v; } -void RelativePointPath::CubicTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::CubicTo::addToPath (Path& path, Expression::Scope* scope) const { - path.cubicTo (controlPoints[0].resolve (coordFinder), - controlPoints[1].resolve (coordFinder), - controlPoints[2].resolve (coordFinder)); + path.cubicTo (controlPoints[0].resolve (scope), + controlPoints[1].resolve (scope), + controlPoints[2].resolve (scope)); } RelativePoint* RelativePointPath::CubicTo::getControlPoints (int& numPoints) @@ -80016,30 +80143,30 @@ RelativeParallelogram::~RelativeParallelogram() { } -void RelativeParallelogram::resolveThreePoints (Point* points, Expression::EvaluationContext* const coordFinder) const +void RelativeParallelogram::resolveThreePoints (Point* points, Expression::Scope* const scope) const { - points[0] = topLeft.resolve (coordFinder); - points[1] = topRight.resolve (coordFinder); - points[2] = bottomLeft.resolve (coordFinder); + points[0] = topLeft.resolve (scope); + points[1] = topRight.resolve (scope); + points[2] = bottomLeft.resolve (scope); } -void RelativeParallelogram::resolveFourCorners (Point* points, Expression::EvaluationContext* const coordFinder) const +void RelativeParallelogram::resolveFourCorners (Point* points, Expression::Scope* const scope) const { - resolveThreePoints (points, coordFinder); + resolveThreePoints (points, scope); points[3] = points[1] + (points[2] - points[0]); } -const Rectangle RelativeParallelogram::getBounds (Expression::EvaluationContext* const coordFinder) const +const Rectangle RelativeParallelogram::getBounds (Expression::Scope* const scope) const { Point points[4]; - resolveFourCorners (points, coordFinder); + resolveFourCorners (points, scope); return Rectangle::findAreaContainingPoints (points, 4); } -void RelativeParallelogram::getPath (Path& path, Expression::EvaluationContext* const coordFinder) const +void RelativeParallelogram::getPath (Path& path, Expression::Scope* const scope) const { Point points[4]; - resolveFourCorners (points, coordFinder); + resolveFourCorners (points, scope); path.startNewSubPath (points[0]); path.lineTo (points[1]); @@ -80048,18 +80175,18 @@ void RelativeParallelogram::getPath (Path& path, Expression::EvaluationContext* path.closeSubPath(); } -const AffineTransform RelativeParallelogram::resetToPerpendicular (Expression::EvaluationContext* const coordFinder) +const AffineTransform RelativeParallelogram::resetToPerpendicular (Expression::Scope* const scope) { Point corners[3]; - resolveThreePoints (corners, coordFinder); + resolveThreePoints (corners, scope); const Line top (corners[0], corners[1]); const Line left (corners[0], corners[2]); const Point newTopRight (corners[0] + Point (top.getLength(), 0.0f)); const Point newBottomLeft (corners[0] + Point (0.0f, left.getLength())); - topRight.moveToAbsolute (newTopRight, coordFinder); - bottomLeft.moveToAbsolute (newBottomLeft, coordFinder); + topRight.moveToAbsolute (newTopRight, scope); + bottomLeft.moveToAbsolute (newBottomLeft, scope); return AffineTransform::fromTargetPoints (corners[0].getX(), corners[0].getY(), corners[0].getX(), corners[0].getY(), corners[1].getX(), corners[1].getY(), newTopRight.getX(), newTopRight.getY(), @@ -80111,51 +80238,169 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_RelativeCoordinatePositioner.cpp ***/ BEGIN_JUCE_NAMESPACE -RelativeCoordinatePositionerBase::RelativeCoordinatePositionerBase (Component& component_) - : Component::Positioner (component_), registeredOk (false) +RelativeCoordinatePositionerBase::ComponentScope::ComponentScope (Component& component_) + : component (component_) { } -RelativeCoordinatePositionerBase::~RelativeCoordinatePositionerBase() +const Expression RelativeCoordinatePositionerBase::ComponentScope::getSymbolValue (const String& symbol) const { - unregisterListeners(); + switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol)) + { + case RelativeCoordinate::StandardStrings::x: + case RelativeCoordinate::StandardStrings::left: return Expression ((double) component.getX()); + case RelativeCoordinate::StandardStrings::y: + case RelativeCoordinate::StandardStrings::top: return Expression ((double) component.getY()); + case RelativeCoordinate::StandardStrings::width: return Expression ((double) component.getWidth()); + case RelativeCoordinate::StandardStrings::height: return Expression ((double) component.getHeight()); + case RelativeCoordinate::StandardStrings::right: return Expression ((double) component.getRight()); + case RelativeCoordinate::StandardStrings::bottom: return Expression ((double) component.getBottom()); + default: break; + } + + MarkerList* list; + const MarkerList::Marker* const marker = findMarker (symbol, list); + + if (marker != 0) + return marker->position.getExpression(); + + return Expression::Scope::getSymbolValue (symbol); +} + +void RelativeCoordinatePositionerBase::ComponentScope::visitRelativeScope (const String& scopeName, Visitor& visitor) const +{ + Component* targetComp = 0; + + if (scopeName == RelativeCoordinate::Strings::parent) + targetComp = component.getParentComponent(); + else + targetComp = findSiblingComponent (scopeName); + + if (targetComp != 0) + visitor.visit (ComponentScope (*targetComp)); +} + +const String RelativeCoordinatePositionerBase::ComponentScope::getScopeUID() const +{ + return String::toHexString ((pointer_sized_int) (void*) &component); +} + +Component* RelativeCoordinatePositionerBase::ComponentScope::findSiblingComponent (const String& componentID) const +{ + Component* const parent = component.getParentComponent(); + + if (parent != 0) + { + for (int i = parent->getNumChildComponents(); --i >= 0;) + { + Component* const c = parent->getChildComponent(i); + + if (c->getComponentID() == componentID) + return c; + } + } + + return 0; } -const Expression RelativeCoordinatePositionerBase::getSymbolValue (const String& objectName, const String& member) const +const MarkerList::Marker* RelativeCoordinatePositionerBase::ComponentScope::findMarker (const String& name, MarkerList*& list) const { - jassert (objectName.isNotEmpty()); + const MarkerList::Marker* marker = 0; - if (member.isNotEmpty()) + Component* const parent = component.getParentComponent(); + + if (parent != 0) { - const Component* comp = getSourceComponent (objectName); + list = parent->getMarkers (true); + if (list != 0) + marker = list->getMarker (name); - if (comp == 0) + if (marker == 0) { - if (objectName == RelativeCoordinate::Strings::parent) - comp = getComponent().getParentComponent(); - else if (objectName == RelativeCoordinate::Strings::this_ || objectName == getComponent().getComponentID()) - comp = &getComponent(); + list = parent->getMarkers (false); + + if (list != 0) + marker = list->getMarker (name); } + } + + return marker; +} + +class RelativeCoordinatePositionerBase::DependencyFinderScope : public ComponentScope +{ +public: + DependencyFinderScope (Component& component_, RelativeCoordinatePositionerBase& positioner_, bool& ok_) + : ComponentScope (component_), positioner (positioner_), ok (ok_) + { + } - if (comp != 0) + const Expression getSymbolValue (const String& symbol) const + { + if (symbol == RelativeCoordinate::Strings::left || symbol == RelativeCoordinate::Strings::x + || symbol == RelativeCoordinate::Strings::width || symbol == RelativeCoordinate::Strings::right + || symbol == RelativeCoordinate::Strings::top || symbol == RelativeCoordinate::Strings::y + || symbol == RelativeCoordinate::Strings::height || symbol == RelativeCoordinate::Strings::bottom) + { + positioner.registerComponentListener (component); + } + else { - if (member == RelativeCoordinate::Strings::left) return xToExpression (comp, 0); - if (member == RelativeCoordinate::Strings::right) return xToExpression (comp, comp->getWidth()); - if (member == RelativeCoordinate::Strings::top) return yToExpression (comp, 0); - if (member == RelativeCoordinate::Strings::bottom) return yToExpression (comp, comp->getHeight()); + MarkerList* list; + const MarkerList::Marker* const marker = findMarker (symbol, list); + + if (marker != 0) + { + positioner.registerMarkerListListener (list); + } + else + { + // The marker we want doesn't exist, so watch all lists in case they change and the marker appears later.. + positioner.registerMarkerListListener (component.getMarkers (true)); + positioner.registerMarkerListListener (component.getMarkers (false)); + ok = false; + } } + + return ComponentScope::getSymbolValue (symbol); } - for (int i = sourceMarkerLists.size(); --i >= 0;) + void visitRelativeScope (const String& scopeName, Visitor& visitor) const { - MarkerList* const markerList = sourceMarkerLists.getUnchecked(i); - const MarkerList::Marker* const marker = markerList->getMarker (objectName); + Component* targetComp = 0; + + if (scopeName == RelativeCoordinate::Strings::parent) + targetComp = component.getParentComponent(); + else + targetComp = findSiblingComponent (scopeName); - if (marker != 0) - return Expression (markerList->getMarkerPosition (*marker, getComponent().getParentComponent())); + if (targetComp != 0) + { + visitor.visit (DependencyFinderScope (*targetComp, positioner, ok)); + } + else + { + // The named component doesn't exist, so we'll watch the parent for changes in case it appears later.. + positioner.registerComponentListener (component); + ok = false; + } } - return Expression::EvaluationContext::getSymbolValue (objectName, member); +private: + RelativeCoordinatePositionerBase& positioner; + bool& ok; + + JUCE_DECLARE_NON_COPYABLE (DependencyFinderScope); +}; + +RelativeCoordinatePositionerBase::RelativeCoordinatePositionerBase (Component& component_) + : Component::Positioner (component_), registeredOk (false) +{ +} + +RelativeCoordinatePositionerBase::~RelativeCoordinatePositionerBase() +{ + unregisterListeners(); } void RelativeCoordinatePositionerBase::componentMovedOrResized (Component&, bool /*wasMoved*/, bool /*wasResized*/) @@ -80198,7 +80443,10 @@ void RelativeCoordinatePositionerBase::apply() bool RelativeCoordinatePositionerBase::addCoordinate (const RelativeCoordinate& coord) { - return registerListeners (coord.getExpression()); + bool ok = true; + DependencyFinderScope finderScope (getComponent(), *this, ok); + coord.getExpression().evaluate (finderScope); + return ok; } bool RelativeCoordinatePositionerBase::addPoint (const RelativePoint& point) @@ -80207,95 +80455,12 @@ bool RelativeCoordinatePositionerBase::addPoint (const RelativePoint& point) return addCoordinate (point.y) && ok; } -bool RelativeCoordinatePositionerBase::registerListeners (const Expression& e) -{ - bool ok = true; - - if (e.getType() == Expression::symbolType) - { - String objectName, memberName; - e.getSymbolParts (objectName, memberName); - - if (memberName.isNotEmpty()) - ok = registerComponent (objectName) && ok; - else - ok = registerMarker (objectName) && ok; - } - else - { - for (int i = e.getNumInputs(); --i >= 0;) - ok = registerListeners (e.getInput (i)) && ok; - } - - return ok; -} - -bool RelativeCoordinatePositionerBase::registerComponent (const String& componentID) -{ - Component* comp = findComponent (componentID); - - if (comp == 0) - { - if (componentID == RelativeCoordinate::Strings::parent) - comp = getComponent().getParentComponent(); - else if (componentID == RelativeCoordinate::Strings::this_ || componentID == getComponent().getComponentID()) - comp = &getComponent(); - } - - if (comp != 0) - { - if (comp != &getComponent()) - registerComponentListener (comp); - - return true; - } - else - { - // The component we want doesn't exist, so watch the parent in case the hierarchy changes and it appears later.. - Component* const parent = getComponent().getParentComponent(); - - if (parent != 0) - registerComponentListener (parent); - else - registerComponentListener (&getComponent()); - - return false; - } -} - -bool RelativeCoordinatePositionerBase::registerMarker (const String markerName) -{ - Component* const parent = getComponent().getParentComponent(); - - if (parent != 0) - { - MarkerList* list = parent->getMarkers (true); - - if (list == 0 || list->getMarker (markerName) == 0) - list = parent->getMarkers (false); - - if (list != 0 && list->getMarker (markerName) != 0) - { - registerMarkerListListener (list); - return true; - } - else - { - // The marker we want doesn't exist, so watch all lists in case they change and the marker appears later.. - registerMarkerListListener (parent->getMarkers (true)); - registerMarkerListListener (parent->getMarkers (false)); - } - } - - return false; -} - -void RelativeCoordinatePositionerBase::registerComponentListener (Component* const comp) +void RelativeCoordinatePositionerBase::registerComponentListener (Component& comp) { - if (comp != 0 && ! sourceComponents.contains (comp)) + if (! sourceComponents.contains (&comp)) { - comp->addComponentListener (this); - sourceComponents.add (comp); + comp.addComponentListener (this); + sourceComponents.add (&comp); } } @@ -80321,47 +80486,6 @@ void RelativeCoordinatePositionerBase::unregisterListeners() sourceMarkerLists.clear(); } -Component* RelativeCoordinatePositionerBase::findComponent (const String& componentID) const -{ - Component* const parent = getComponent().getParentComponent(); - - if (parent != 0) - { - for (int i = parent->getNumChildComponents(); --i >= 0;) - { - Component* const c = parent->getChildComponent(i); - - if (c->getComponentID() == componentID) - return c; - } - } - - return 0; -} - -Component* RelativeCoordinatePositionerBase::getSourceComponent (const String& objectName) const -{ - for (int i = sourceComponents.size(); --i >= 0;) - { - Component* const comp = sourceComponents.getUnchecked(i); - - if (comp->getComponentID() == objectName) - return comp; - } - - return 0; -} - -const Expression RelativeCoordinatePositionerBase::xToExpression (const Component* const source, const int x) const -{ - return Expression ((double) (getComponent().getLocalPoint (source, Point (x, 0)).getX() + getComponent().getX())); -} - -const Expression RelativeCoordinatePositionerBase::yToExpression (const Component* const source, const int y) const -{ - return Expression ((double) (getComponent().getLocalPoint (source, Point (0, y)).getY() + getComponent().getY())); -} - END_JUCE_NAMESPACE /*** End of inlined file: juce_RelativeCoordinatePositioner.cpp ***/ @@ -86252,8 +86376,9 @@ public: void applyToComponentBounds() { - if (isMainFill ? owner.mainFill.recalculateCoords (this) - : owner.strokeFill.recalculateCoords (this)) + ComponentScope scope (owner); + if (isMainFill ? owner.mainFill.recalculateCoords (&scope) + : owner.strokeFill.recalculateCoords (&scope)) owner.repaint(); } @@ -86435,19 +86560,19 @@ bool DrawableShape::RelativeFillType::operator!= (const RelativeFillType& other) return ! operator== (other); } -bool DrawableShape::RelativeFillType::recalculateCoords (Expression::EvaluationContext* context) +bool DrawableShape::RelativeFillType::recalculateCoords (Expression::Scope* scope) { if (fill.isGradient()) { - const Point g1 (gradientPoint1.resolve (context)); - const Point g2 (gradientPoint2.resolve (context)); + const Point g1 (gradientPoint1.resolve (scope)); + const Point g2 (gradientPoint2.resolve (scope)); AffineTransform t; ColourGradient& g = *fill.gradient; if (g.isRadial) { - const Point g3 (gradientPoint3.resolve (context)); + const Point g3 (gradientPoint3.resolve (scope)); const Point g3Source (g1.getX() + g2.getY() - g1.getY(), g1.getY() + g1.getX() - g2.getX()); @@ -86755,12 +86880,12 @@ bool DrawableComposite::registerCoordinates (RelativeCoordinatePositionerBase& p return positioner.addPoint (bounds.bottomLeft) && ok; } -void DrawableComposite::recalculateCoordinates (Expression::EvaluationContext* context) +void DrawableComposite::recalculateCoordinates (Expression::Scope* scope) { Point resolved[3]; - bounds.resolveThreePoints (resolved, context); + bounds.resolveThreePoints (resolved, scope); - const Rectangle content (getContentArea().resolve (context)); + const Rectangle content (getContentArea().resolve (scope)); AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].getX(), resolved[0].getY(), content.getRight(), content.getY(), resolved[1].getX(), resolved[1].getY(), @@ -87023,12 +87148,12 @@ bool DrawableImage::registerCoordinates (RelativeCoordinatePositionerBase& posit return positioner.addPoint (bounds.bottomLeft) && ok; } -void DrawableImage::recalculateCoordinates (Expression::EvaluationContext* context) +void DrawableImage::recalculateCoordinates (Expression::Scope* scope) { if (image.isValid()) { Point resolved[3]; - bounds.resolveThreePoints (resolved, context); + bounds.resolveThreePoints (resolved, scope); const Point tr (resolved[0] + (resolved[1] - resolved[0]) / (float) image.getWidth()); const Point bl (resolved[0] + (resolved[2] - resolved[0]) / (float) image.getHeight()); @@ -87256,10 +87381,10 @@ const Path& DrawablePath::getStrokePath() const return strokePath; } -void DrawablePath::applyRelativePath (const RelativePointPath& newRelativePath, Expression::EvaluationContext* context) +void DrawablePath::applyRelativePath (const RelativePointPath& newRelativePath, Expression::Scope* scope) { Path newPath; - newRelativePath.createPath (newPath, context); + newRelativePath.createPath (newPath, scope); if (path != newPath) { @@ -87301,7 +87426,9 @@ public: void applyToComponentBounds() { jassert (owner.relativePath != 0); - owner.applyRelativePath (*owner.relativePath, this); + + ComponentScope scope (getComponent()); + owner.applyRelativePath (*owner.relativePath, &scope); } private: @@ -87479,26 +87606,26 @@ const RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const return RelativePoint(); } -float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::EvaluationContext* context) const +float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::Scope* scope) const { const Identifier i (state.getType()); if (i == lineToElement || i == closeSubPathElement) - return getEndPoint().resolve (context).getDistanceFrom (getStartPoint().resolve (context)); + return getEndPoint().resolve (scope).getDistanceFrom (getStartPoint().resolve (scope)); if (i == cubicToElement) { Path p; - p.startNewSubPath (getStartPoint().resolve (context)); - p.cubicTo (getControlPoint (0).resolve (context), getControlPoint (1).resolve (context), getControlPoint (2).resolve (context)); + p.startNewSubPath (getStartPoint().resolve (scope)); + p.cubicTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope), getControlPoint (2).resolve (scope)); return p.getLength(); } if (i == quadraticToElement) { Path p; - p.startNewSubPath (getStartPoint().resolve (context)); - p.quadraticTo (getControlPoint (0).resolve (context), getControlPoint (1).resolve (context)); + p.startNewSubPath (getStartPoint().resolve (scope)); + p.quadraticTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope)); return p.getLength(); } @@ -87530,7 +87657,7 @@ void DrawablePath::ValueTreeWrapper::Element::convertToLine (UndoManager* undoMa } } -void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::EvaluationContext* context, UndoManager* undoManager) +void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::Scope* scope, UndoManager* undoManager) { const Identifier i (state.getType()); @@ -87541,8 +87668,8 @@ void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::Evalua const RelativePoint start (getStartPoint()); const RelativePoint end (getEndPoint()); - const Point startResolved (start.resolve (context)); - const Point endResolved (end.resolve (context)); + const Point startResolved (start.resolve (scope)); + const Point endResolved (end.resolve (scope)); e.setControlPoint (0, startResolved + (endResolved - startResolved) * 0.3f, undoManager); e.setControlPoint (1, startResolved + (endResolved - startResolved) * 0.7f, undoManager); e.setControlPoint (2, end, undoManager); @@ -87587,7 +87714,7 @@ namespace DrawablePathHelpers } } -float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, Expression::EvaluationContext* context) const +float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, Expression::Scope* scope) const { using namespace DrawablePathHelpers; const Identifier type (state.getType()); @@ -87597,7 +87724,7 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po { RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); - const Point points[] = { rp1.resolve (context), rp2.resolve (context), rp3.resolve (context), rp4.resolve (context) }; + const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) }; float bestDistance = std::numeric_limits::max(); @@ -87617,7 +87744,7 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po else if (type == quadraticToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); - const Point points[] = { rp1.resolve (context), rp2.resolve (context), rp3.resolve (context) }; + const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) }; float bestDistance = std::numeric_limits::max(); @@ -87637,24 +87764,24 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po else if (type == lineToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); - const Line line (rp1.resolve (context), rp2.resolve (context)); + const Line line (rp1.resolve (scope), rp2.resolve (scope)); bestProp = line.findNearestProportionalPositionTo (targetPoint); } return bestProp; } -ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, Expression::EvaluationContext* context, UndoManager* undoManager) +ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, Expression::Scope* scope, UndoManager* undoManager) { ValueTree newTree; const Identifier type (state.getType()); if (type == cubicToElement) { - float bestProp = findProportionAlongLine (targetPoint, context); + float bestProp = findProportionAlongLine (targetPoint, scope); RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); - const Point points[] = { rp1.resolve (context), rp2.resolve (context), rp3.resolve (context), rp4.resolve (context) }; + const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) }; const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), mid2 (points[1] + (points[2] - points[1]) * bestProp), @@ -87679,10 +87806,10 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point points[] = { rp1.resolve (context), rp2.resolve (context), rp3.resolve (context) }; + const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) }; const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), mid2 (points[1] + (points[2] - points[1]) * bestProp); @@ -87702,7 +87829,7 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point line (rp1.resolve (context), rp2.resolve (context)); + const Line line (rp1.resolve (scope), rp2.resolve (scope)); const Point newPoint (line.findNearestPointTo (targetPoint)); setControlPoint (0, newPoint, undoManager); @@ -87821,13 +87948,13 @@ bool DrawableRectangle::registerCoordinates (RelativeCoordinatePositionerBase& p return positioner.addPoint (cornerSize) && ok; } -void DrawableRectangle::recalculateCoordinates (Expression::EvaluationContext* context) +void DrawableRectangle::recalculateCoordinates (Expression::Scope* scope) { Point points[3]; - bounds.resolveThreePoints (points, context); + bounds.resolveThreePoints (points, scope); - const float cornerSizeX = (float) cornerSize.x.resolve (context); - const float cornerSizeY = (float) cornerSize.y.resolve (context); + const float cornerSizeX = (float) cornerSize.x.resolve (scope); + const float cornerSizeY = (float) cornerSize.y.resolve (scope); const float w = Line (points[0], points[1]).getLength(); const float h = Line (points[0], points[2]).getLength(); @@ -88027,14 +88154,14 @@ bool DrawableText::registerCoordinates (RelativeCoordinatePositionerBase& positi return positioner.addPoint (fontSizeControlPoint) && ok; } -void DrawableText::recalculateCoordinates (Expression::EvaluationContext* context) +void DrawableText::recalculateCoordinates (Expression::Scope* scope) { - bounds.resolveThreePoints (resolvedPoints, context); + bounds.resolveThreePoints (resolvedPoints, scope); const float w = Line (resolvedPoints[0], resolvedPoints[1]).getLength(); const float h = Line (resolvedPoints[0], resolvedPoints[2]).getLength(); - const Point fontCoords (RelativeParallelogram::getInternalCoordForPoint (resolvedPoints, fontSizeControlPoint.resolve (context))); + const Point fontCoords (RelativeParallelogram::getInternalCoordForPoint (resolvedPoints, fontSizeControlPoint.resolve (scope))); const float fontHeight = jlimit (0.01f, jmax (0.01f, h), fontCoords.getY()); const float fontWidth = jlimit (0.01f, jmax (0.01f, w), fontCoords.getX()); diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 47c84cadc6..97c02561b6 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -73,7 +73,7 @@ namespace JuceDummyNamespace {} */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 14 +#define JUCE_BUILDNUMBER 15 /** Current Juce version number. @@ -17817,7 +17817,7 @@ private: evaluated. Expressions which use identifiers and functions require a subclass of - Expression::EvaluationContext to be supplied when evaluating them, and this object + Expression::Scope 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. */ @@ -17879,11 +17879,14 @@ public: /** When evaluating an Expression object, this class is used to resolve symbols and perform functions that the expression uses. */ - class JUCE_API EvaluationContext + class JUCE_API Scope { public: - EvaluationContext(); - virtual ~EvaluationContext(); + Scope(); + virtual ~Scope(); + + /** Returns some kind of globally unique ID that identifies this scope. */ + virtual const String getScopeUID() const; /** Returns the value of a symbol. If the symbol is unknown, this can throw an Expression::EvaluationError exception. @@ -17891,55 +17894,97 @@ public: one, e.g. for "foo.bar", symbol = "foo" and member = "bar". @throws Expression::EvaluationError */ - virtual const Expression getSymbolValue (const String& symbol, const String& member) const; + 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; + virtual double evaluateFunction (const String& functionName, + const double* parameters, int numParameters) const; + + /** Used as a callback by the Scope::visitRelativeScope() method. + You should never create an instance of this class yourself, it's used by the + expression evaluation code. + */ + class Visitor + { + public: + virtual ~Visitor() {} + virtual void visit (const Scope&) = 0; + }; + + /** Creates a Scope object for a named scope, and then calls a visitor + to do some kind of processing with this new scope. + + If the name is valid, this method must create a suitable (temporary) Scope + object to represent it, and must call the Visitor::visit() method with this + new scope. + */ + virtual void visitRelativeScope (const String& scopeName, Visitor& visitor) 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, + /** Evaluates this expression, without using a Scope. + Without a Scope, no symbols can be used, and only basic functions such as sin, cos, tan, min, max are available. - @throws Expression::EvaluationError + To find out about any errors during evaluation, use the other version of this method which + takes a String parameter. */ double evaluate() const; - /** Evaluates this expression, providing a context that should be able to evaluate any symbols + /** Evaluates this expression, providing a scope that should be able to evaluate any symbols + or functions that it uses. + To find out about any errors during evaluation, use the other version of this method which + takes a String parameter. + */ + double evaluate (const Scope& scope) const; + + /** Evaluates this expression, providing a scope that should be able to evaluate any symbols or functions that it uses. - @throws Expression::EvaluationError */ - double evaluate (const EvaluationContext& context) const; + double evaluate (const Scope& scope, String& evaluationError) 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. + case they might just be adjusted by adding a constant to the original expression. @throws Expression::EvaluationError */ - const Expression adjustedToGiveNewResult (double targetValue, const EvaluationContext& context) const; + const Expression adjustedToGiveNewResult (double targetValue, const Scope& scope) const; + + /** Represents a symbol that is used in an Expression. */ + struct Symbol + { + Symbol (const String& scopeUID, const String& symbolName); + bool operator== (const Symbol&) const throw(); + bool operator!= (const Symbol&) const throw(); + + String scopeUID; /**< The unique ID of the Scope that contains this symbol. */ + String symbolName; /**< The name of the symbol. */ + }; /** 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; + const Expression withRenamedSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope) 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 + If a suitable scope 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. If the context parameter is null, this just checks + symbol at any level in its evaluation. If the scope parameter is null, this just checks whether the expression contains any direct references to the symbol. @throws Expression::EvaluationError */ - bool referencesSymbol (const String& symbol, const EvaluationContext* context) const; + bool referencesSymbol (const Symbol& symbol, const Scope& scope) const; /** Returns true if this expression contains any symbols. */ bool usesAnySymbols() const; + /** Returns a list of all symbols that may be needed to resolve this expression in the given scope. */ + void findReferencedSymbols (Array& results, const Scope& scope) const; + /** An exception that can be thrown by Expression::parse(). */ class ParseError : public std::exception { @@ -17949,16 +17994,6 @@ public: String description; }; - /** An exception that can be thrown by Expression::evaluate(). */ - class EvaluationError : public std::exception - { - public: - EvaluationError (const String& message); - EvaluationError (const String& symbolName, const String& memberName); - - String description; - }; - /** Expression type. @see Expression::getType() */ @@ -17973,19 +18008,8 @@ public: /** Returns the type of this expression. */ Type getType() const throw(); - /** If this expression is a symbol, this returns its full name. */ - const String getSymbol() const; - - /** For a symbol that contains a dot, this returns the two */ - void getSymbolParts (String& objectName, String& memberName) const; - - /** If this expression is a function, this returns its name. */ - const String getFunction() const; - - /** If this expression is an operator, this returns its name. - E.g. "+", "-", "*", "/", etc. - */ - const String getOperator() const; + /** If this expression is a symbol, function or operator, this returns its identifier. */ + const String getSymbolOrFunction() const; /** Returns the number of inputs to this expression. @see getInput @@ -17999,35 +18023,12 @@ public: private: + class Term; class Helpers; + friend class Term; 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&, const Term* inputTerm, - double overallTarget, Term* topLevelTerm) const; - virtual const ReferenceCountedObjectPtr negated(); - virtual Type getType() const throw() = 0; - virtual void getSymbolParts (String& objectName, String& memberName) const; - virtual const String getFunctionName() const; - - private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Term); - }; - friend class ScopedPointer; + friend class ReferenceCountedObjectPtr; ReferenceCountedObjectPtr term; explicit Expression (Term* term); @@ -27477,35 +27478,35 @@ public: for more details. When using relative expressions, the following symbols are available: - - "this.left", "this.right", "this.top", "this.bottom" refer to the position of those - edges in this component, so e.g. for a component whose width is always 100, you might - set the right edge to the "this.left + 100". - - "parent.left", "parent.right", "parent.top", "parent.bottom" refer to the parent component's - positions, in its own coordinate space, so "parent.left", "parent.right" are always 0, and - "parent.top", "parent.bottom" will actually be the width and height of the parent. So - for example to make your component's right-hand edge always 10 pixels away from its parent's - right-hand edge, you could set it to "parent.right - 10" - - "[id].left", "[id].right", "[id].top", "[id].bottom", where [id] is the identifier of one of - this component's siblings. A component's identifier is set with Component::setComponentID(). - So for example if you want your component to always be 50 pixels to the right of the one - called "xyz", you could set your left edge to be "xyz.right + 50" - - The name of a marker that is defined in the parent component. For markers to be used, the parent - component must implement its Component::getMarkers() method, and return at least one + - "left", "right", "top", "bottom" refer to the position of those edges in this component, so + e.g. for a component whose width is always 100, you might set the right edge to the "left + 100". + - "[id].left", "[id].right", "[id].top", "[id].bottom", "[id].width", "[id].height", where [id] is + the identifier of one of this component's siblings. A component's identifier is set with + Component::setComponentID(). So for example if you want your component to always be 50 pixels to the + right of the one called "xyz", you could set your left edge to be "xyz.right + 50". + - Instead of an [id], you can use the name "parent" to refer to this component's parent. Like + any other component, these values are relative to their component's parent, so "parent.right" won't be + very useful for positioning a component because it refers to a position with the parent's parent.. but + "parent.width" can be used for setting positions relative to the parent's size. E.g. to make a 10x10 + component which remains 1 pixel away from its parent's bottom-right, you could use + "right - 10, bottom - 10, parent.width - 1, parent.height - 1". + - The name of one of the parent component's markers can also be used as a symbol. For markers to be + used, the parent component must implement its Component::getMarkers() method, and return at least one valid MarkerList. So if you want your component's top edge to be 10 pixels below the marker called "foobar", you'd set it to "foobar + 10". See the Expression class for details about the operators that are supported, but for example if you wanted to make your component remain centred within its parent with a size of 100, 100, you could express it as: - @code myComp.setBounds (RelativeBounds ("parent.right / 2 - 50, parent.bottom / 2 - 50, this.left + 100, this.top + 100")); + @code myComp.setBounds (RelativeBounds ("parent.width / 2 - 50, parent.height / 2 - 50, left + 100, top + 100")); @endcode ..or an alternative way to achieve the same thing: - @code myComp.setBounds (RelativeBounds ("this.right - 100, this.bottom - 100, parent.right / 2 + 50, parent.bottom / 2 + 50")); + @code myComp.setBounds (RelativeBounds ("right - 100, bottom - 100, parent.width / 2 + 50, parent.height / 2 + 50")); @endcode Or if you wanted a 100x100 component whose top edge is lined up to a marker called "topMarker" and which is positioned 50 pixels to the right of another component called "otherComp", you could write: - @code myComp.setBounds (RelativeBounds ("otherComp.right + 50, topMarker, this.left + 100, this.top + 100")); + @code myComp.setBounds (RelativeBounds ("otherComp.right + 50, topMarker, left + 100, top + 100")); @endcode Be careful not to make your coordinate expressions recursive, though, or exceptions and assertions will @@ -43481,7 +43482,6 @@ public: AudioProcessorGraph(); /** Destructor. - Any processor objects that have been added to the graph will also be deleted. */ ~AudioProcessorGraph(); @@ -43493,9 +43493,6 @@ public: class JUCE_API Node : public ReferenceCountedObject { public: - /** Destructor. - */ - ~Node(); /** The ID number assigned to this node. @@ -45643,18 +45640,18 @@ public: /** Calculates the absolute position of this coordinate. - You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may + You'll need to provide a suitable Expression::Scope for looking up any coordinates that may be needed to calculate the result. */ - double resolve (const Expression::EvaluationContext* evaluationContext) const; + double resolve (const Expression::Scope* evaluationScope) 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 Expression::EvaluationContext* evaluationContext) const; + bool references (const String& coordName, const Expression::Scope* evaluationScope) const; /** Returns true if there's a recursive loop when trying to resolve this coordinate's position. */ - bool isRecursive (const Expression::EvaluationContext* evaluationContext) const; + bool isRecursive (const Expression::Scope* evaluationScope) const; /** Returns true if this coordinate depends on any other coordinates for its position. */ bool isDynamic() const; @@ -45665,10 +45662,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 Expression::EvaluationContext* evaluationContext); - - /** Changes the name of a symbol if it is used as part of the coordinate's expression. */ - void renameSymbolIfUsed (const String& oldName, const String& newName); + void moveToAbsolute (double absoluteTargetPosition, const Expression::Scope* evaluationScope); /** Returns the expression that defines this coordinate. */ const Expression& getExpression() const { return term; } @@ -45688,15 +45682,27 @@ public: struct Strings { static const String parent; /**< "parent" */ - static const String this_; /**< "this" */ static const String left; /**< "left" */ static const String right; /**< "right" */ static const String top; /**< "top" */ static const String bottom; /**< "bottom" */ - static const String parentLeft; /**< "parent.left" */ - static const String parentTop; /**< "parent.top" */ - static const String parentRight; /**< "parent.right" */ - static const String parentBottom; /**< "parent.bottom" */ + static const String x; /**< "x" */ + static const String y; /**< "y" */ + static const String width; /**< "width" */ + static const String height; /**< "height" */ + }; + + struct StandardStrings + { + enum Type + { + left, right, top, bottom, + x, y, width, height, + parent, + unknown + }; + + static Type getTypeOf (const String& s) throw(); }; private: @@ -45749,10 +45755,10 @@ public: /** Calculates the absolute position of this point. - You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may + You'll need to provide a suitable Expression::Scope for looking up any coordinates that may be needed to calculate the result. */ - const Point resolve (const Expression::EvaluationContext* evaluationContext) const; + const Point resolve (const Expression::Scope* evaluationContext) const; /** Changes the values of this point's coordinates to make it resolve to the specified position. @@ -45760,7 +45766,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 Expression::EvaluationContext* evaluationContext); + void moveToAbsolute (const Point& newPos, const Expression::Scope* 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 @@ -45769,11 +45775,6 @@ public: */ const String toString() const; - /** Renames a symbol if it is used by any of the coordinates. - This calls RelativeCoordinate::renameAnchorIfUsed() on its X and Y coordinates. - */ - void renameSymbolIfUsed (const String& oldName, const String& newName); - /** Returns true if this point depends on any other coordinates for its position. */ bool isDynamic() const; @@ -45946,15 +45947,12 @@ private: */ class JUCE_API RelativeCoordinatePositionerBase : public Component::Positioner, public ComponentListener, - public MarkerList::Listener, - public Expression::EvaluationContext + public MarkerList::Listener { public: RelativeCoordinatePositionerBase (Component& component_); ~RelativeCoordinatePositionerBase(); - const Expression getSymbolValue (const String& objectName, const String& member) const; - void componentMovedOrResized (Component&, bool, bool); void componentParentHierarchyChanged (Component&); void componentBeingDeleted (Component& component); @@ -45966,25 +45964,40 @@ public: bool addCoordinate (const RelativeCoordinate& coord); bool addPoint (const RelativePoint& point); + /** Used for resolving a RelativeCoordinate expression in the context of a component. */ + class ComponentScope : public Expression::Scope + { + public: + ComponentScope (Component& component_); + + const Expression getSymbolValue (const String& symbol) const; + void visitRelativeScope (const String& scopeName, Visitor& visitor) const; + const String getScopeUID() const; + + protected: + Component& component; + + Component* findSiblingComponent (const String& componentID) const; + const MarkerList::Marker* findMarker (const String& name, MarkerList*& list) const; + + private: + JUCE_DECLARE_NON_COPYABLE (ComponentScope); + }; + protected: virtual bool registerCoordinates() = 0; virtual void applyToComponentBounds() = 0; private: + class DependencyFinderScope; + friend class DependencyFinderScope; Array sourceComponents; Array sourceMarkerLists; bool registeredOk; - bool registerListeners (const Expression& e); - bool registerComponent (const String& componentID); - bool registerMarker (const String markerName); - void registerComponentListener (Component* const comp); + void registerComponentListener (Component& comp); void registerMarkerListListener (MarkerList* const list); void unregisterListeners(); - Component* findComponent (const String& componentID) const; - Component* getSourceComponent (const String& objectName) const; - const Expression xToExpression (const Component* const source, const int x) const; - const Expression yToExpression (const Component* const source, const int y) const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeCoordinatePositionerBase); }; @@ -46395,7 +46408,11 @@ protected: {} bool registerCoordinates() { return owner.registerCoordinates (*this); } - void applyToComponentBounds() { owner.recalculateCoordinates (this); } + void applyToComponentBounds() + { + ComponentScope scope (getComponent()); + owner.recalculateCoordinates (&scope); + } private: DrawableType& owner; @@ -57857,11 +57874,11 @@ public: RelativeParallelogram (const String& topLeft, const String& topRight, const String& bottomLeft); ~RelativeParallelogram(); - 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); + void resolveThreePoints (Point* points, Expression::Scope* scope) const; + void resolveFourCorners (Point* points, Expression::Scope* scope) const; + const Rectangle getBounds (Expression::Scope* scope) const; + void getPath (Path& path, Expression::Scope* scope) const; + const AffineTransform resetToPerpendicular (Expression::Scope* scope); bool isDynamic() const; bool operator== (const RelativeParallelogram& other) const throw(); @@ -57911,7 +57928,7 @@ public: bool operator!= (const RelativePointPath& other) const throw(); /** Resolves this points in this path and adds them to a normal Path object. */ - void createPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void createPath (Path& path, Expression::Scope* scope) const; /** Returns true if the path contains any non-fixed points. */ bool containsAnyDynamicPoints() const; @@ -57940,7 +57957,7 @@ public: ElementBase (ElementType type); virtual ~ElementBase() {} virtual const ValueTree createTree() const = 0; - virtual void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const = 0; + virtual void addToPath (Path& path, Expression::Scope*) const = 0; virtual RelativePoint* getControlPoints (int& numPoints) = 0; virtual ElementBase* clone() const = 0; bool isDynamic(); @@ -57956,7 +57973,7 @@ public: public: StartSubPath (const RelativePoint& pos); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -57971,7 +57988,7 @@ public: public: CloseSubPath(); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -57984,7 +58001,7 @@ public: public: LineTo (const RelativePoint& endPoint); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -57999,7 +58016,7 @@ public: public: QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -58014,7 +58031,7 @@ public: public: CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -58087,10 +58104,10 @@ public: /** Calculates the absolute position of this rectangle. - You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may + You'll need to provide a suitable Expression::Scope for looking up any coordinates that may be needed to calculate the result. */ - const Rectangle resolve (const Expression::EvaluationContext* evaluationContext) const; + const Rectangle resolve (const Expression::Scope* scope) const; /** Changes the values of this rectangle's coordinates to make it resolve to the specified position. @@ -58098,7 +58115,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 Expression::EvaluationContext* evaluationContext); + void moveToAbsolute (const Rectangle& newPos, const Expression::Scope* scope); /** Returns true if this rectangle depends on any external symbols for its position. Coordinates that refer to symbols based on "this" are assumed not to be dynamic. @@ -58113,9 +58130,9 @@ public: const String toString() const; /** Renames a symbol if it is used by any of the coordinates. - This calls RelativeCoordinate::renameSymbolIfUsed() on the rectangle's coordinates. + This calls Expression::withRenamedSymbol() on the rectangle's coordinates. */ - void renameSymbolIfUsed (const String& oldName, const String& newName); + void renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope); /** Creates and sets an appropriate Component::Positioner object for the given component, which will keep it positioned with this rectangle. @@ -61907,7 +61924,7 @@ private: friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); - void recalculateCoordinates (Expression::EvaluationContext*); + void recalculateCoordinates (Expression::Scope*); void updateBoundsToFitChildren(); @@ -62024,7 +62041,7 @@ private: friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); - void recalculateCoordinates (Expression::EvaluationContext*); + void recalculateCoordinates (Expression::Scope*); DrawableImage& operator= (const DrawableImage&); JUCE_LEAK_DETECTOR (DrawableImage); @@ -62077,7 +62094,7 @@ public: bool operator!= (const RelativeFillType&) const; bool isDynamic() const; - bool recalculateCoords (Expression::EvaluationContext* context); + bool recalculateCoords (Expression::Scope* scope); void writeTo (ValueTree& v, ComponentBuilder::ImageProvider*, UndoManager*) const; bool readFrom (const ValueTree& v, ComponentBuilder::ImageProvider*); @@ -62260,7 +62277,7 @@ public: const RelativePoint getStartPoint() const; const RelativePoint getEndPoint() const; void setControlPoint (int index, const RelativePoint& point, UndoManager*); - float getLength (Expression::EvaluationContext*) const; + float getLength (Expression::Scope*) const; ValueTreeWrapper getParent() const; Element getPreviousElement() const; @@ -62269,11 +62286,11 @@ public: void setModeOfEndPoint (const String& newMode, UndoManager*); void convertToLine (UndoManager*); - void convertToCubic (Expression::EvaluationContext*, UndoManager*); + void convertToCubic (Expression::Scope*, UndoManager*); void convertToPathBreak (UndoManager* undoManager); - ValueTree insertPoint (const Point& targetPoint, Expression::EvaluationContext*, UndoManager*); + ValueTree insertPoint (const Point& targetPoint, Expression::Scope*, UndoManager*); void removePoint (UndoManager* undoManager); - float findProportionAlongLine (const Point& targetPoint, Expression::EvaluationContext*) const; + float findProportionAlongLine (const Point& targetPoint, Expression::Scope*) const; static const Identifier mode, startSubPathElement, closeSubPathElement, lineToElement, quadraticToElement, cubicToElement; @@ -62298,7 +62315,7 @@ private: class RelativePositioner; friend class RelativePositioner; - void applyRelativePath (const RelativePointPath&, Expression::EvaluationContext*); + void applyRelativePath (const RelativePointPath&, Expression::Scope*); DrawablePath& operator= (const DrawablePath&); JUCE_LEAK_DETECTOR (DrawablePath); @@ -62377,7 +62394,7 @@ private: void rebuildPath(); bool registerCoordinates (RelativeCoordinatePositionerBase&); - void recalculateCoordinates (Expression::EvaluationContext*); + void recalculateCoordinates (Expression::Scope*); DrawableRectangle& operator= (const DrawableRectangle&); JUCE_LEAK_DETECTOR (DrawableRectangle); @@ -62503,7 +62520,7 @@ private: friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); - void recalculateCoordinates (Expression::EvaluationContext*); + void recalculateCoordinates (Expression::Scope*); void refreshBounds(); const AffineTransform getArrangementAndTransform (GlyphArrangement& glyphs) const; diff --git a/src/audio/audio_file_formats/juce_WavAudioFormat.cpp b/src/audio/audio_file_formats/juce_WavAudioFormat.cpp index d9d4d3da8f..f1aafce43a 100644 --- a/src/audio/audio_file_formats/juce_WavAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_WavAudioFormat.cpp @@ -370,10 +370,6 @@ public: } } - ~WavAudioFormatReader() - { - } - //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) @@ -532,7 +528,8 @@ private: const int bytesPerFrame = numChannels * bitsPerSample / 8; output->writeInt (chunkName ("RIFF")); output->writeInt ((int) (lengthInSamples * bytesPerFrame - + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36))); + + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36) + + (smplChunk.getSize() > 0 ? smplChunk.getSize() + 8 : 0))); output->writeInt (chunkName ("WAVE")); output->writeInt (chunkName ("fmt ")); diff --git a/src/audio/processors/juce_AudioProcessorGraph.cpp b/src/audio/processors/juce_AudioProcessorGraph.cpp index 2199190cb8..95389a1589 100644 --- a/src/audio/processors/juce_AudioProcessorGraph.cpp +++ b/src/audio/processors/juce_AudioProcessorGraph.cpp @@ -42,10 +42,6 @@ AudioProcessorGraph::Node::Node (const uint32 id_, AudioProcessor* const process jassert (processor_ != 0); } -AudioProcessorGraph::Node::~Node() -{ -} - void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize, AudioProcessorGraph* const graph) { diff --git a/src/audio/processors/juce_AudioProcessorGraph.h b/src/audio/processors/juce_AudioProcessorGraph.h index 994bd901a7..ef60ac184d 100644 --- a/src/audio/processors/juce_AudioProcessorGraph.h +++ b/src/audio/processors/juce_AudioProcessorGraph.h @@ -57,7 +57,6 @@ public: AudioProcessorGraph(); /** Destructor. - Any processor objects that have been added to the graph will also be deleted. */ ~AudioProcessorGraph(); @@ -70,10 +69,6 @@ public: class JUCE_API Node : public ReferenceCountedObject { public: - /** Destructor. - */ - ~Node(); - //============================================================================== /** The ID number assigned to this node. diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 132b4ea3f0..a65aaa57f2 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 53 -#define JUCE_BUILDNUMBER 14 +#define JUCE_BUILDNUMBER 15 /** Current Juce version number. diff --git a/src/gui/components/controls/juce_ListBox.cpp b/src/gui/components/controls/juce_ListBox.cpp index 94769be410..836710a462 100644 --- a/src/gui/components/controls/juce_ListBox.cpp +++ b/src/gui/components/controls/juce_ListBox.cpp @@ -888,8 +888,13 @@ const Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) Graphics g (snapshot); g.setOrigin (pos.getX() - imageX, pos.getY() - imageY); + if (g.reduceClipRegion (rowComp->getLocalBounds())) + { + g.beginTransparencyLayer (0.6f); rowComp->paintEntireComponent (g, false); + g.endTransparencyLayer(); + } } } @@ -905,7 +910,6 @@ void ListBox::startDragAndDrop (const MouseEvent& e, const String& dragDescripti { int x, y; Image dragImage (createSnapshotOfSelectedRows (x, y)); - dragImage.multiplyAllAlphas (0.6f); MouseEvent e2 (e.getEventRelativeTo (this)); const Point p (x - e2.x, y - e2.y); diff --git a/src/gui/components/juce_Component.h b/src/gui/components/juce_Component.h index 910f57c9c0..6b5e31c720 100644 --- a/src/gui/components/juce_Component.h +++ b/src/gui/components/juce_Component.h @@ -488,35 +488,35 @@ public: for more details. When using relative expressions, the following symbols are available: - - "this.left", "this.right", "this.top", "this.bottom" refer to the position of those - edges in this component, so e.g. for a component whose width is always 100, you might - set the right edge to the "this.left + 100". - - "parent.left", "parent.right", "parent.top", "parent.bottom" refer to the parent component's - positions, in its own coordinate space, so "parent.left", "parent.right" are always 0, and - "parent.top", "parent.bottom" will actually be the width and height of the parent. So - for example to make your component's right-hand edge always 10 pixels away from its parent's - right-hand edge, you could set it to "parent.right - 10" - - "[id].left", "[id].right", "[id].top", "[id].bottom", where [id] is the identifier of one of - this component's siblings. A component's identifier is set with Component::setComponentID(). - So for example if you want your component to always be 50 pixels to the right of the one - called "xyz", you could set your left edge to be "xyz.right + 50" - - The name of a marker that is defined in the parent component. For markers to be used, the parent - component must implement its Component::getMarkers() method, and return at least one + - "left", "right", "top", "bottom" refer to the position of those edges in this component, so + e.g. for a component whose width is always 100, you might set the right edge to the "left + 100". + - "[id].left", "[id].right", "[id].top", "[id].bottom", "[id].width", "[id].height", where [id] is + the identifier of one of this component's siblings. A component's identifier is set with + Component::setComponentID(). So for example if you want your component to always be 50 pixels to the + right of the one called "xyz", you could set your left edge to be "xyz.right + 50". + - Instead of an [id], you can use the name "parent" to refer to this component's parent. Like + any other component, these values are relative to their component's parent, so "parent.right" won't be + very useful for positioning a component because it refers to a position with the parent's parent.. but + "parent.width" can be used for setting positions relative to the parent's size. E.g. to make a 10x10 + component which remains 1 pixel away from its parent's bottom-right, you could use + "right - 10, bottom - 10, parent.width - 1, parent.height - 1". + - The name of one of the parent component's markers can also be used as a symbol. For markers to be + used, the parent component must implement its Component::getMarkers() method, and return at least one valid MarkerList. So if you want your component's top edge to be 10 pixels below the marker called "foobar", you'd set it to "foobar + 10". See the Expression class for details about the operators that are supported, but for example if you wanted to make your component remain centred within its parent with a size of 100, 100, you could express it as: - @code myComp.setBounds (RelativeBounds ("parent.right / 2 - 50, parent.bottom / 2 - 50, this.left + 100, this.top + 100")); + @code myComp.setBounds (RelativeBounds ("parent.width / 2 - 50, parent.height / 2 - 50, left + 100, top + 100")); @endcode ..or an alternative way to achieve the same thing: - @code myComp.setBounds (RelativeBounds ("this.right - 100, this.bottom - 100, parent.right / 2 + 50, parent.bottom / 2 + 50")); + @code myComp.setBounds (RelativeBounds ("right - 100, bottom - 100, parent.width / 2 + 50, parent.height / 2 + 50")); @endcode Or if you wanted a 100x100 component whose top edge is lined up to a marker called "topMarker" and which is positioned 50 pixels to the right of another component called "otherComp", you could write: - @code myComp.setBounds (RelativeBounds ("otherComp.right + 50, topMarker, this.left + 100, this.top + 100")); + @code myComp.setBounds (RelativeBounds ("otherComp.right + 50, topMarker, left + 100, top + 100")); @endcode Be careful not to make your coordinate expressions recursive, though, or exceptions and assertions will diff --git a/src/gui/components/juce_ModalComponentManager.cpp b/src/gui/components/juce_ModalComponentManager.cpp index c5cf2b4c02..6d58bdd957 100644 --- a/src/gui/components/juce_ModalComponentManager.cpp +++ b/src/gui/components/juce_ModalComponentManager.cpp @@ -65,6 +65,8 @@ public: void componentBeingDeleted (Component& comp) { + ComponentMovementWatcher::componentBeingDeleted (comp); + if (component == &comp || comp.isParentOf (component)) cancel(); } @@ -201,9 +203,14 @@ void ModalComponentManager::handleAsyncUpdate() if (! item->isActive) { for (int j = item->callbacks.size(); --j >= 0;) + { item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue); - stack.remove (i); + if (! stack.contains (item)) + break; + } + + stack.removeObject (item); } } } diff --git a/src/gui/components/positioning/juce_MarkerList.cpp b/src/gui/components/positioning/juce_MarkerList.cpp index 12cf0f2417..de82a67a59 100644 --- a/src/gui/components/positioning/juce_MarkerList.cpp +++ b/src/gui/components/positioning/juce_MarkerList.cpp @@ -28,8 +28,10 @@ BEGIN_JUCE_NAMESPACE #include "juce_MarkerList.h" +#include "juce_RelativeCoordinatePositioner.h" #include "../juce_Component.h" + //============================================================================== MarkerList::MarkerList() { @@ -246,44 +248,17 @@ void MarkerList::ValueTreeWrapper::removeMarker (const ValueTree& marker, UndoMa state.removeChild (marker, undoManager); } -//============================================================================== -class MarkerListEvaluator : public Expression::EvaluationContext +double MarkerList::getMarkerPosition (const Marker& marker, Component* parentComponent) const { -public: - MarkerListEvaluator (const MarkerList& markerList_, Component* const parentComponent_) - : markerList (markerList_), parentComponent (parentComponent_) + if (parentComponent != 0) { + RelativeCoordinatePositionerBase::ComponentScope scope (*parentComponent); + return marker.position.resolve (&scope); } - - const Expression getSymbolValue (const String& objectName, const String& member) const + else { - if (member.isEmpty()) - { - const MarkerList::Marker* const marker = markerList.getMarker (objectName); - - if (marker != 0) - return Expression (marker->position.resolve (this)); - } - else if (parentComponent != 0 && objectName == RelativeCoordinate::Strings::parent) - { - if (member == RelativeCoordinate::Strings::right) return Expression ((double) parentComponent->getWidth()); - if (member == RelativeCoordinate::Strings::bottom) return Expression ((double) parentComponent->getHeight()); - } - - return Expression::EvaluationContext::getSymbolValue (objectName, member); + return marker.position.resolve (0); } - -private: - const MarkerList& markerList; - Component* parentComponent; - - JUCE_DECLARE_NON_COPYABLE (MarkerListEvaluator); -}; - -double MarkerList::getMarkerPosition (const Marker& marker, Component* const parentComponent) const -{ - MarkerListEvaluator context (*this, parentComponent); - return marker.position.resolve (&context); } //============================================================================== diff --git a/src/gui/components/positioning/juce_RelativeCoordinate.cpp b/src/gui/components/positioning/juce_RelativeCoordinate.cpp index 9861000402..6389d360d1 100644 --- a/src/gui/components/positioning/juce_RelativeCoordinate.cpp +++ b/src/gui/components/positioning/juce_RelativeCoordinate.cpp @@ -32,15 +32,28 @@ BEGIN_JUCE_NAMESPACE //============================================================================== const String RelativeCoordinate::Strings::parent ("parent"); -const String RelativeCoordinate::Strings::this_ ("this"); const String RelativeCoordinate::Strings::left ("left"); const String RelativeCoordinate::Strings::right ("right"); const String RelativeCoordinate::Strings::top ("top"); const String RelativeCoordinate::Strings::bottom ("bottom"); -const String RelativeCoordinate::Strings::parentLeft ("parent.left"); -const String RelativeCoordinate::Strings::parentTop ("parent.top"); -const String RelativeCoordinate::Strings::parentRight ("parent.right"); -const String RelativeCoordinate::Strings::parentBottom ("parent.bottom"); +const String RelativeCoordinate::Strings::x ("x"); +const String RelativeCoordinate::Strings::y ("y"); +const String RelativeCoordinate::Strings::width ("width"); +const String RelativeCoordinate::Strings::height ("height"); + +RelativeCoordinate::StandardStrings::Type RelativeCoordinate::StandardStrings::getTypeOf (const String& s) throw() +{ + if (s == Strings::left) return left; + if (s == Strings::right) return right; + if (s == Strings::top) return top; + if (s == Strings::bottom) return bottom; + if (s == Strings::x) return x; + if (s == Strings::y) return y; + if (s == Strings::width) return width; + if (s == Strings::height) return height; + if (s == Strings::parent) return parent; + return unknown; +} //============================================================================== RelativeCoordinate::RelativeCoordinate() @@ -92,12 +105,12 @@ bool RelativeCoordinate::operator!= (const RelativeCoordinate& other) const thro return ! operator== (other); } -double RelativeCoordinate::resolve (const Expression::EvaluationContext* context) const +double RelativeCoordinate::resolve (const Expression::Scope* scope) const { try { - if (context != 0) - return term.evaluate (*context); + if (scope != 0) + return term.evaluate (*scope); else return term.evaluate(); } @@ -107,12 +120,12 @@ double RelativeCoordinate::resolve (const Expression::EvaluationContext* context return 0.0; } -bool RelativeCoordinate::isRecursive (const Expression::EvaluationContext* context) const +bool RelativeCoordinate::isRecursive (const Expression::Scope* scope) const { try { - if (context != 0) - term.evaluate (*context); + if (scope != 0) + term.evaluate (*scope); else term.evaluate(); } @@ -124,36 +137,24 @@ bool RelativeCoordinate::isRecursive (const Expression::EvaluationContext* conte return false; } -void RelativeCoordinate::moveToAbsolute (double newPos, const Expression::EvaluationContext* context) +void RelativeCoordinate::moveToAbsolute (double newPos, const Expression::Scope* scope) { try { - if (context != 0) + if (scope != 0) { - term = term.adjustedToGiveNewResult (newPos, *context); + term = term.adjustedToGiveNewResult (newPos, *scope); } else { - Expression::EvaluationContext defaultContext; - term = term.adjustedToGiveNewResult (newPos, defaultContext); + Expression::Scope defaultScope; + term = term.adjustedToGiveNewResult (newPos, defaultScope); } } catch (...) {} } -bool RelativeCoordinate::references (const String& coordName, const Expression::EvaluationContext* context) const -{ - try - { - return term.referencesSymbol (coordName, context); - } - catch (...) - {} - - return false; -} - bool RelativeCoordinate::isDynamic() const { return term.usesAnySymbols(); @@ -164,13 +165,6 @@ const String RelativeCoordinate::toString() const return term.toString(); } -void RelativeCoordinate::renameSymbolIfUsed (const String& oldName, const String& newName) -{ - jassert (newName.isNotEmpty() && newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); - - if (term.referencesSymbol (oldName, 0)) - term = term.withRenamedSymbol (oldName, newName); -} END_JUCE_NAMESPACE diff --git a/src/gui/components/positioning/juce_RelativeCoordinate.h b/src/gui/components/positioning/juce_RelativeCoordinate.h index d9dceb6e70..ce8d68cc83 100644 --- a/src/gui/components/positioning/juce_RelativeCoordinate.h +++ b/src/gui/components/positioning/juce_RelativeCoordinate.h @@ -69,18 +69,18 @@ public: //============================================================================== /** Calculates the absolute position of this coordinate. - You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may + You'll need to provide a suitable Expression::Scope for looking up any coordinates that may be needed to calculate the result. */ - double resolve (const Expression::EvaluationContext* evaluationContext) const; + double resolve (const Expression::Scope* evaluationScope) 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 Expression::EvaluationContext* evaluationContext) const; + bool references (const String& coordName, const Expression::Scope* evaluationScope) const; /** Returns true if there's a recursive loop when trying to resolve this coordinate's position. */ - bool isRecursive (const Expression::EvaluationContext* evaluationContext) const; + bool isRecursive (const Expression::Scope* evaluationScope) const; /** Returns true if this coordinate depends on any other coordinates for its position. */ bool isDynamic() const; @@ -92,10 +92,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 Expression::EvaluationContext* evaluationContext); - - /** Changes the name of a symbol if it is used as part of the coordinate's expression. */ - void renameSymbolIfUsed (const String& oldName, const String& newName); + void moveToAbsolute (double absoluteTargetPosition, const Expression::Scope* evaluationScope); /** Returns the expression that defines this coordinate. */ const Expression& getExpression() const { return term; } @@ -118,15 +115,27 @@ public: struct Strings { static const String parent; /**< "parent" */ - static const String this_; /**< "this" */ static const String left; /**< "left" */ static const String right; /**< "right" */ static const String top; /**< "top" */ static const String bottom; /**< "bottom" */ - static const String parentLeft; /**< "parent.left" */ - static const String parentTop; /**< "parent.top" */ - static const String parentRight; /**< "parent.right" */ - static const String parentBottom; /**< "parent.bottom" */ + static const String x; /**< "x" */ + static const String y; /**< "y" */ + static const String width; /**< "width" */ + static const String height; /**< "height" */ + }; + + struct StandardStrings + { + enum Type + { + left, right, top, bottom, + x, y, width, height, + parent, + unknown + }; + + static Type getTypeOf (const String& s) throw(); }; private: diff --git a/src/gui/components/positioning/juce_RelativeCoordinatePositioner.cpp b/src/gui/components/positioning/juce_RelativeCoordinatePositioner.cpp index bd87084daf..3de66a14eb 100644 --- a/src/gui/components/positioning/juce_RelativeCoordinatePositioner.cpp +++ b/src/gui/components/positioning/juce_RelativeCoordinatePositioner.cpp @@ -31,51 +31,171 @@ BEGIN_JUCE_NAMESPACE //============================================================================== -RelativeCoordinatePositionerBase::RelativeCoordinatePositionerBase (Component& component_) - : Component::Positioner (component_), registeredOk (false) +RelativeCoordinatePositionerBase::ComponentScope::ComponentScope (Component& component_) + : component (component_) { } -RelativeCoordinatePositionerBase::~RelativeCoordinatePositionerBase() +const Expression RelativeCoordinatePositionerBase::ComponentScope::getSymbolValue (const String& symbol) const { - unregisterListeners(); + switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol)) + { + case RelativeCoordinate::StandardStrings::x: + case RelativeCoordinate::StandardStrings::left: return Expression ((double) component.getX()); + case RelativeCoordinate::StandardStrings::y: + case RelativeCoordinate::StandardStrings::top: return Expression ((double) component.getY()); + case RelativeCoordinate::StandardStrings::width: return Expression ((double) component.getWidth()); + case RelativeCoordinate::StandardStrings::height: return Expression ((double) component.getHeight()); + case RelativeCoordinate::StandardStrings::right: return Expression ((double) component.getRight()); + case RelativeCoordinate::StandardStrings::bottom: return Expression ((double) component.getBottom()); + default: break; + } + + MarkerList* list; + const MarkerList::Marker* const marker = findMarker (symbol, list); + + if (marker != 0) + return marker->position.getExpression(); + + return Expression::Scope::getSymbolValue (symbol); } -const Expression RelativeCoordinatePositionerBase::getSymbolValue (const String& objectName, const String& member) const +void RelativeCoordinatePositionerBase::ComponentScope::visitRelativeScope (const String& scopeName, Visitor& visitor) const { - jassert (objectName.isNotEmpty()); + Component* targetComp = 0; - if (member.isNotEmpty()) + if (scopeName == RelativeCoordinate::Strings::parent) + targetComp = component.getParentComponent(); + else + targetComp = findSiblingComponent (scopeName); + + if (targetComp != 0) + visitor.visit (ComponentScope (*targetComp)); +} + +const String RelativeCoordinatePositionerBase::ComponentScope::getScopeUID() const +{ + return String::toHexString ((pointer_sized_int) (void*) &component); +} + +Component* RelativeCoordinatePositionerBase::ComponentScope::findSiblingComponent (const String& componentID) const +{ + Component* const parent = component.getParentComponent(); + + if (parent != 0) { - const Component* comp = getSourceComponent (objectName); + for (int i = parent->getNumChildComponents(); --i >= 0;) + { + Component* const c = parent->getChildComponent(i); + + if (c->getComponentID() == componentID) + return c; + } + } + + return 0; +} + +const MarkerList::Marker* RelativeCoordinatePositionerBase::ComponentScope::findMarker (const String& name, MarkerList*& list) const +{ + const MarkerList::Marker* marker = 0; + + Component* const parent = component.getParentComponent(); - if (comp == 0) + if (parent != 0) + { + list = parent->getMarkers (true); + if (list != 0) + marker = list->getMarker (name); + + if (marker == 0) { - if (objectName == RelativeCoordinate::Strings::parent) - comp = getComponent().getParentComponent(); - else if (objectName == RelativeCoordinate::Strings::this_ || objectName == getComponent().getComponentID()) - comp = &getComponent(); + list = parent->getMarkers (false); + + if (list != 0) + marker = list->getMarker (name); } + } + + return marker; +} + +//============================================================================== +class RelativeCoordinatePositionerBase::DependencyFinderScope : public ComponentScope +{ +public: + DependencyFinderScope (Component& component_, RelativeCoordinatePositionerBase& positioner_, bool& ok_) + : ComponentScope (component_), positioner (positioner_), ok (ok_) + { + } - if (comp != 0) + const Expression getSymbolValue (const String& symbol) const + { + if (symbol == RelativeCoordinate::Strings::left || symbol == RelativeCoordinate::Strings::x + || symbol == RelativeCoordinate::Strings::width || symbol == RelativeCoordinate::Strings::right + || symbol == RelativeCoordinate::Strings::top || symbol == RelativeCoordinate::Strings::y + || symbol == RelativeCoordinate::Strings::height || symbol == RelativeCoordinate::Strings::bottom) { - if (member == RelativeCoordinate::Strings::left) return xToExpression (comp, 0); - if (member == RelativeCoordinate::Strings::right) return xToExpression (comp, comp->getWidth()); - if (member == RelativeCoordinate::Strings::top) return yToExpression (comp, 0); - if (member == RelativeCoordinate::Strings::bottom) return yToExpression (comp, comp->getHeight()); + positioner.registerComponentListener (component); } + else + { + MarkerList* list; + const MarkerList::Marker* const marker = findMarker (symbol, list); + + if (marker != 0) + { + positioner.registerMarkerListListener (list); + } + else + { + // The marker we want doesn't exist, so watch all lists in case they change and the marker appears later.. + positioner.registerMarkerListListener (component.getMarkers (true)); + positioner.registerMarkerListListener (component.getMarkers (false)); + ok = false; + } + } + + return ComponentScope::getSymbolValue (symbol); } - for (int i = sourceMarkerLists.size(); --i >= 0;) + void visitRelativeScope (const String& scopeName, Visitor& visitor) const { - MarkerList* const markerList = sourceMarkerLists.getUnchecked(i); - const MarkerList::Marker* const marker = markerList->getMarker (objectName); + Component* targetComp = 0; - if (marker != 0) - return Expression (markerList->getMarkerPosition (*marker, getComponent().getParentComponent())); + if (scopeName == RelativeCoordinate::Strings::parent) + targetComp = component.getParentComponent(); + else + targetComp = findSiblingComponent (scopeName); + + if (targetComp != 0) + { + visitor.visit (DependencyFinderScope (*targetComp, positioner, ok)); + } + else + { + // The named component doesn't exist, so we'll watch the parent for changes in case it appears later.. + positioner.registerComponentListener (component); + ok = false; + } } - return Expression::EvaluationContext::getSymbolValue (objectName, member); +private: + RelativeCoordinatePositionerBase& positioner; + bool& ok; + + JUCE_DECLARE_NON_COPYABLE (DependencyFinderScope); +}; + +//============================================================================== +RelativeCoordinatePositionerBase::RelativeCoordinatePositionerBase (Component& component_) + : Component::Positioner (component_), registeredOk (false) +{ +} + +RelativeCoordinatePositionerBase::~RelativeCoordinatePositionerBase() +{ + unregisterListeners(); } void RelativeCoordinatePositionerBase::componentMovedOrResized (Component&, bool /*wasMoved*/, bool /*wasResized*/) @@ -118,7 +238,10 @@ void RelativeCoordinatePositionerBase::apply() bool RelativeCoordinatePositionerBase::addCoordinate (const RelativeCoordinate& coord) { - return registerListeners (coord.getExpression()); + bool ok = true; + DependencyFinderScope finderScope (getComponent(), *this, ok); + coord.getExpression().evaluate (finderScope); + return ok; } bool RelativeCoordinatePositionerBase::addPoint (const RelativePoint& point) @@ -127,95 +250,12 @@ bool RelativeCoordinatePositionerBase::addPoint (const RelativePoint& point) return addCoordinate (point.y) && ok; } -bool RelativeCoordinatePositionerBase::registerListeners (const Expression& e) -{ - bool ok = true; - - if (e.getType() == Expression::symbolType) - { - String objectName, memberName; - e.getSymbolParts (objectName, memberName); - - if (memberName.isNotEmpty()) - ok = registerComponent (objectName) && ok; - else - ok = registerMarker (objectName) && ok; - } - else - { - for (int i = e.getNumInputs(); --i >= 0;) - ok = registerListeners (e.getInput (i)) && ok; - } - - return ok; -} - -bool RelativeCoordinatePositionerBase::registerComponent (const String& componentID) +void RelativeCoordinatePositionerBase::registerComponentListener (Component& comp) { - Component* comp = findComponent (componentID); - - if (comp == 0) + if (! sourceComponents.contains (&comp)) { - if (componentID == RelativeCoordinate::Strings::parent) - comp = getComponent().getParentComponent(); - else if (componentID == RelativeCoordinate::Strings::this_ || componentID == getComponent().getComponentID()) - comp = &getComponent(); - } - - if (comp != 0) - { - if (comp != &getComponent()) - registerComponentListener (comp); - - return true; - } - else - { - // The component we want doesn't exist, so watch the parent in case the hierarchy changes and it appears later.. - Component* const parent = getComponent().getParentComponent(); - - if (parent != 0) - registerComponentListener (parent); - else - registerComponentListener (&getComponent()); - - return false; - } -} - -bool RelativeCoordinatePositionerBase::registerMarker (const String markerName) -{ - Component* const parent = getComponent().getParentComponent(); - - if (parent != 0) - { - MarkerList* list = parent->getMarkers (true); - - if (list == 0 || list->getMarker (markerName) == 0) - list = parent->getMarkers (false); - - if (list != 0 && list->getMarker (markerName) != 0) - { - registerMarkerListListener (list); - return true; - } - else - { - // The marker we want doesn't exist, so watch all lists in case they change and the marker appears later.. - registerMarkerListListener (parent->getMarkers (true)); - registerMarkerListListener (parent->getMarkers (false)); - } - } - - return false; -} - -void RelativeCoordinatePositionerBase::registerComponentListener (Component* const comp) -{ - if (comp != 0 && ! sourceComponents.contains (comp)) - { - comp->addComponentListener (this); - sourceComponents.add (comp); + comp.addComponentListener (this); + sourceComponents.add (&comp); } } @@ -241,45 +281,5 @@ void RelativeCoordinatePositionerBase::unregisterListeners() sourceMarkerLists.clear(); } -Component* RelativeCoordinatePositionerBase::findComponent (const String& componentID) const -{ - Component* const parent = getComponent().getParentComponent(); - - if (parent != 0) - { - for (int i = parent->getNumChildComponents(); --i >= 0;) - { - Component* const c = parent->getChildComponent(i); - - if (c->getComponentID() == componentID) - return c; - } - } - - return 0; -} - -Component* RelativeCoordinatePositionerBase::getSourceComponent (const String& objectName) const -{ - for (int i = sourceComponents.size(); --i >= 0;) - { - Component* const comp = sourceComponents.getUnchecked(i); - - if (comp->getComponentID() == objectName) - return comp; - } - - return 0; -} - -const Expression RelativeCoordinatePositionerBase::xToExpression (const Component* const source, const int x) const -{ - return Expression ((double) (getComponent().getLocalPoint (source, Point (x, 0)).getX() + getComponent().getX())); -} - -const Expression RelativeCoordinatePositionerBase::yToExpression (const Component* const source, const int y) const -{ - return Expression ((double) (getComponent().getLocalPoint (source, Point (0, y)).getY() + getComponent().getY())); -} END_JUCE_NAMESPACE diff --git a/src/gui/components/positioning/juce_RelativeCoordinatePositioner.h b/src/gui/components/positioning/juce_RelativeCoordinatePositioner.h index 84a689467f..a7bc77f2f1 100644 --- a/src/gui/components/positioning/juce_RelativeCoordinatePositioner.h +++ b/src/gui/components/positioning/juce_RelativeCoordinatePositioner.h @@ -37,15 +37,12 @@ */ class JUCE_API RelativeCoordinatePositionerBase : public Component::Positioner, public ComponentListener, - public MarkerList::Listener, - public Expression::EvaluationContext + public MarkerList::Listener { public: RelativeCoordinatePositionerBase (Component& component_); ~RelativeCoordinatePositionerBase(); - const Expression getSymbolValue (const String& objectName, const String& member) const; - void componentMovedOrResized (Component&, bool, bool); void componentParentHierarchyChanged (Component&); void componentBeingDeleted (Component& component); @@ -57,25 +54,41 @@ public: bool addCoordinate (const RelativeCoordinate& coord); bool addPoint (const RelativePoint& point); + //============================================================================== + /** Used for resolving a RelativeCoordinate expression in the context of a component. */ + class ComponentScope : public Expression::Scope + { + public: + ComponentScope (Component& component_); + + const Expression getSymbolValue (const String& symbol) const; + void visitRelativeScope (const String& scopeName, Visitor& visitor) const; + const String getScopeUID() const; + + protected: + Component& component; + + Component* findSiblingComponent (const String& componentID) const; + const MarkerList::Marker* findMarker (const String& name, MarkerList*& list) const; + + private: + JUCE_DECLARE_NON_COPYABLE (ComponentScope); + }; + protected: virtual bool registerCoordinates() = 0; virtual void applyToComponentBounds() = 0; private: + class DependencyFinderScope; + friend class DependencyFinderScope; Array sourceComponents; Array sourceMarkerLists; bool registeredOk; - bool registerListeners (const Expression& e); - bool registerComponent (const String& componentID); - bool registerMarker (const String markerName); - void registerComponentListener (Component* const comp); + void registerComponentListener (Component& comp); void registerMarkerListListener (MarkerList* const list); void unregisterListeners(); - Component* findComponent (const String& componentID) const; - Component* getSourceComponent (const String& objectName) const; - const Expression xToExpression (const Component* const source, const int x) const; - const Expression yToExpression (const Component* const source, const int y) const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeCoordinatePositionerBase); }; diff --git a/src/gui/components/positioning/juce_RelativeParallelogram.cpp b/src/gui/components/positioning/juce_RelativeParallelogram.cpp index d451b5a4f8..c24ae4ef45 100644 --- a/src/gui/components/positioning/juce_RelativeParallelogram.cpp +++ b/src/gui/components/positioning/juce_RelativeParallelogram.cpp @@ -53,30 +53,30 @@ RelativeParallelogram::~RelativeParallelogram() { } -void RelativeParallelogram::resolveThreePoints (Point* points, Expression::EvaluationContext* const coordFinder) const +void RelativeParallelogram::resolveThreePoints (Point* points, Expression::Scope* const scope) const { - points[0] = topLeft.resolve (coordFinder); - points[1] = topRight.resolve (coordFinder); - points[2] = bottomLeft.resolve (coordFinder); + points[0] = topLeft.resolve (scope); + points[1] = topRight.resolve (scope); + points[2] = bottomLeft.resolve (scope); } -void RelativeParallelogram::resolveFourCorners (Point* points, Expression::EvaluationContext* const coordFinder) const +void RelativeParallelogram::resolveFourCorners (Point* points, Expression::Scope* const scope) const { - resolveThreePoints (points, coordFinder); + resolveThreePoints (points, scope); points[3] = points[1] + (points[2] - points[0]); } -const Rectangle RelativeParallelogram::getBounds (Expression::EvaluationContext* const coordFinder) const +const Rectangle RelativeParallelogram::getBounds (Expression::Scope* const scope) const { Point points[4]; - resolveFourCorners (points, coordFinder); + resolveFourCorners (points, scope); return Rectangle::findAreaContainingPoints (points, 4); } -void RelativeParallelogram::getPath (Path& path, Expression::EvaluationContext* const coordFinder) const +void RelativeParallelogram::getPath (Path& path, Expression::Scope* const scope) const { Point points[4]; - resolveFourCorners (points, coordFinder); + resolveFourCorners (points, scope); path.startNewSubPath (points[0]); path.lineTo (points[1]); @@ -85,18 +85,18 @@ void RelativeParallelogram::getPath (Path& path, Expression::EvaluationContext* path.closeSubPath(); } -const AffineTransform RelativeParallelogram::resetToPerpendicular (Expression::EvaluationContext* const coordFinder) +const AffineTransform RelativeParallelogram::resetToPerpendicular (Expression::Scope* const scope) { Point corners[3]; - resolveThreePoints (corners, coordFinder); + resolveThreePoints (corners, scope); const Line top (corners[0], corners[1]); const Line left (corners[0], corners[2]); const Point newTopRight (corners[0] + Point (top.getLength(), 0.0f)); const Point newBottomLeft (corners[0] + Point (0.0f, left.getLength())); - topRight.moveToAbsolute (newTopRight, coordFinder); - bottomLeft.moveToAbsolute (newBottomLeft, coordFinder); + topRight.moveToAbsolute (newTopRight, scope); + bottomLeft.moveToAbsolute (newBottomLeft, scope); return AffineTransform::fromTargetPoints (corners[0].getX(), corners[0].getY(), corners[0].getX(), corners[0].getY(), corners[1].getX(), corners[1].getY(), newTopRight.getX(), newTopRight.getY(), diff --git a/src/gui/components/positioning/juce_RelativeParallelogram.h b/src/gui/components/positioning/juce_RelativeParallelogram.h index a0dbe94bff..e378882cce 100644 --- a/src/gui/components/positioning/juce_RelativeParallelogram.h +++ b/src/gui/components/positioning/juce_RelativeParallelogram.h @@ -46,11 +46,11 @@ public: ~RelativeParallelogram(); //============================================================================== - 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); + void resolveThreePoints (Point* points, Expression::Scope* scope) const; + void resolveFourCorners (Point* points, Expression::Scope* scope) const; + const Rectangle getBounds (Expression::Scope* scope) const; + void getPath (Path& path, Expression::Scope* scope) const; + const AffineTransform resetToPerpendicular (Expression::Scope* scope); bool isDynamic() const; bool operator== (const RelativeParallelogram& other) const throw(); diff --git a/src/gui/components/positioning/juce_RelativePoint.cpp b/src/gui/components/positioning/juce_RelativePoint.cpp index 1b5ec9f36c..3458b9a592 100644 --- a/src/gui/components/positioning/juce_RelativePoint.cpp +++ b/src/gui/components/positioning/juce_RelativePoint.cpp @@ -79,16 +79,16 @@ bool RelativePoint::operator!= (const RelativePoint& other) const throw() return ! operator== (other); } -const Point RelativePoint::resolve (const Expression::EvaluationContext* context) const +const Point RelativePoint::resolve (const Expression::Scope* scope) const { - return Point ((float) x.resolve (context), - (float) y.resolve (context)); + return Point ((float) x.resolve (scope), + (float) y.resolve (scope)); } -void RelativePoint::moveToAbsolute (const Point& newPos, const Expression::EvaluationContext* context) +void RelativePoint::moveToAbsolute (const Point& newPos, const Expression::Scope* scope) { - x.moveToAbsolute (newPos.getX(), context); - y.moveToAbsolute (newPos.getY(), context); + x.moveToAbsolute (newPos.getX(), scope); + y.moveToAbsolute (newPos.getY(), scope); } const String RelativePoint::toString() const @@ -96,12 +96,6 @@ const String RelativePoint::toString() const return x.toString() + ", " + y.toString(); } -void RelativePoint::renameSymbolIfUsed (const String& oldName, const String& newName) -{ - x.renameSymbolIfUsed (oldName, newName); - y.renameSymbolIfUsed (oldName, newName); -} - bool RelativePoint::isDynamic() const { return x.isDynamic() || y.isDynamic(); diff --git a/src/gui/components/positioning/juce_RelativePoint.h b/src/gui/components/positioning/juce_RelativePoint.h index 1d614e7ce8..93f54f338c 100644 --- a/src/gui/components/positioning/juce_RelativePoint.h +++ b/src/gui/components/positioning/juce_RelativePoint.h @@ -62,10 +62,10 @@ public: /** Calculates the absolute position of this point. - You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may + You'll need to provide a suitable Expression::Scope for looking up any coordinates that may be needed to calculate the result. */ - const Point resolve (const Expression::EvaluationContext* evaluationContext) const; + const Point resolve (const Expression::Scope* evaluationContext) const; /** Changes the values of this point's coordinates to make it resolve to the specified position. @@ -73,7 +73,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 Expression::EvaluationContext* evaluationContext); + void moveToAbsolute (const Point& newPos, const Expression::Scope* 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 @@ -82,11 +82,6 @@ public: */ const String toString() const; - /** Renames a symbol if it is used by any of the coordinates. - This calls RelativeCoordinate::renameAnchorIfUsed() on its X and Y coordinates. - */ - void renameSymbolIfUsed (const String& oldName, const String& newName); - /** Returns true if this point depends on any other coordinates for its position. */ bool isDynamic() const; diff --git a/src/gui/components/positioning/juce_RelativePointPath.cpp b/src/gui/components/positioning/juce_RelativePointPath.cpp index 629fafc58b..b19b4da0ca 100644 --- a/src/gui/components/positioning/juce_RelativePointPath.cpp +++ b/src/gui/components/positioning/juce_RelativePointPath.cpp @@ -109,10 +109,10 @@ void RelativePointPath::swapWith (RelativePointPath& other) throw() swapVariables (containsDynamicPoints, other.containsDynamicPoints); } -void RelativePointPath::createPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::createPath (Path& path, Expression::Scope* scope) const { for (int i = 0; i < elements.size(); ++i) - elements.getUnchecked(i)->addToPath (path, coordFinder); + elements.getUnchecked(i)->addToPath (path, scope); } bool RelativePointPath::containsAnyDynamicPoints() const @@ -159,9 +159,9 @@ const ValueTree RelativePointPath::StartSubPath::createTree() const return v; } -void RelativePointPath::StartSubPath::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::StartSubPath::addToPath (Path& path, Expression::Scope* scope) const { - path.startNewSubPath (startPos.resolve (coordFinder)); + path.startNewSubPath (startPos.resolve (scope)); } RelativePoint* RelativePointPath::StartSubPath::getControlPoints (int& numPoints) @@ -186,7 +186,7 @@ const ValueTree RelativePointPath::CloseSubPath::createTree() const return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement); } -void RelativePointPath::CloseSubPath::addToPath (Path& path, Expression::EvaluationContext*) const +void RelativePointPath::CloseSubPath::addToPath (Path& path, Expression::Scope*) const { path.closeSubPath(); } @@ -215,9 +215,9 @@ const ValueTree RelativePointPath::LineTo::createTree() const return v; } -void RelativePointPath::LineTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::LineTo::addToPath (Path& path, Expression::Scope* scope) const { - path.lineTo (endPoint.resolve (coordFinder)); + path.lineTo (endPoint.resolve (scope)); } RelativePoint* RelativePointPath::LineTo::getControlPoints (int& numPoints) @@ -247,10 +247,10 @@ const ValueTree RelativePointPath::QuadraticTo::createTree() const return v; } -void RelativePointPath::QuadraticTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::QuadraticTo::addToPath (Path& path, Expression::Scope* scope) const { - path.quadraticTo (controlPoints[0].resolve (coordFinder), - controlPoints[1].resolve (coordFinder)); + path.quadraticTo (controlPoints[0].resolve (scope), + controlPoints[1].resolve (scope)); } RelativePoint* RelativePointPath::QuadraticTo::getControlPoints (int& numPoints) @@ -283,11 +283,11 @@ const ValueTree RelativePointPath::CubicTo::createTree() const return v; } -void RelativePointPath::CubicTo::addToPath (Path& path, Expression::EvaluationContext* coordFinder) const +void RelativePointPath::CubicTo::addToPath (Path& path, Expression::Scope* scope) const { - path.cubicTo (controlPoints[0].resolve (coordFinder), - controlPoints[1].resolve (coordFinder), - controlPoints[2].resolve (coordFinder)); + path.cubicTo (controlPoints[0].resolve (scope), + controlPoints[1].resolve (scope), + controlPoints[2].resolve (scope)); } RelativePoint* RelativePointPath::CubicTo::getControlPoints (int& numPoints) diff --git a/src/gui/components/positioning/juce_RelativePointPath.h b/src/gui/components/positioning/juce_RelativePointPath.h index ff9ba1c8b9..c686dce4bb 100644 --- a/src/gui/components/positioning/juce_RelativePointPath.h +++ b/src/gui/components/positioning/juce_RelativePointPath.h @@ -54,7 +54,7 @@ public: //============================================================================== /** Resolves this points in this path and adds them to a normal Path object. */ - void createPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void createPath (Path& path, Expression::Scope* scope) const; /** Returns true if the path contains any non-fixed points. */ bool containsAnyDynamicPoints() const; @@ -85,7 +85,7 @@ public: ElementBase (ElementType type); virtual ~ElementBase() {} virtual const ValueTree createTree() const = 0; - virtual void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const = 0; + virtual void addToPath (Path& path, Expression::Scope*) const = 0; virtual RelativePoint* getControlPoints (int& numPoints) = 0; virtual ElementBase* clone() const = 0; bool isDynamic(); @@ -102,7 +102,7 @@ public: public: StartSubPath (const RelativePoint& pos); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -118,7 +118,7 @@ public: public: CloseSubPath(); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -132,7 +132,7 @@ public: public: LineTo (const RelativePoint& endPoint); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -148,7 +148,7 @@ public: public: QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; @@ -164,7 +164,7 @@ public: public: CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint); const ValueTree createTree() const; - void addToPath (Path& path, Expression::EvaluationContext* coordFinder) const; + void addToPath (Path& path, Expression::Scope*) const; RelativePoint* getControlPoints (int& numPoints); ElementBase* clone() const; diff --git a/src/gui/components/positioning/juce_RelativeRectangle.cpp b/src/gui/components/positioning/juce_RelativeRectangle.cpp index 6fc8aed9b6..308a6fb1ec 100644 --- a/src/gui/components/positioning/juce_RelativeRectangle.cpp +++ b/src/gui/components/positioning/juce_RelativeRectangle.cpp @@ -30,6 +30,8 @@ BEGIN_JUCE_NAMESPACE #include "juce_RelativeRectangle.h" #include "juce_RelativeCoordinatePositioner.h" + +//============================================================================== namespace RelativeRectangleHelpers { inline void skipComma (const juce_wchar* const s, int& i) @@ -43,13 +45,23 @@ namespace RelativeRectangleHelpers bool dependsOnSymbolsOtherThanThis (const Expression& e) { + if (e.getType() == Expression::operatorType && e.getSymbolOrFunction() == ".") + return true; + if (e.getType() == Expression::symbolType) { - String objectName, memberName; - e.getSymbolParts (objectName, memberName); - - if (objectName != RelativeCoordinate::Strings::this_) - return true; + switch (RelativeCoordinate::StandardStrings::getTypeOf (e.getSymbolOrFunction())) + { + case RelativeCoordinate::StandardStrings::x: + case RelativeCoordinate::StandardStrings::y: + case RelativeCoordinate::StandardStrings::left: + case RelativeCoordinate::StandardStrings::right: + case RelativeCoordinate::StandardStrings::top: + case RelativeCoordinate::StandardStrings::bottom: return false; + default: break; + } + + return true; } else { @@ -75,11 +87,9 @@ RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const Rel RelativeRectangle::RelativeRectangle (const Rectangle& rect) : left (rect.getX()), - right (Expression::symbol (RelativeCoordinate::Strings::this_ + "." + RelativeCoordinate::Strings::left) - + Expression ((double) rect.getWidth())), + right (Expression::symbol (RelativeCoordinate::Strings::left) + Expression ((double) rect.getWidth())), top (rect.getY()), - bottom (Expression::symbol (RelativeCoordinate::Strings::this_ + "." + RelativeCoordinate::Strings::top) - + Expression ((double) rect.getHeight())) + bottom (Expression::symbol (RelativeCoordinate::Strings::top) + Expression ((double) rect.getHeight())) { } @@ -105,22 +115,59 @@ bool RelativeRectangle::operator!= (const RelativeRectangle& other) const throw( return ! operator== (other); } -const Rectangle RelativeRectangle::resolve (const Expression::EvaluationContext* context) const +//============================================================================== +// An expression context that can evaluate expressions using "this" +class RelativeRectangleLocalScope : public Expression::Scope { - const double l = left.resolve (context); - const double r = right.resolve (context); - const double t = top.resolve (context); - const double b = bottom.resolve (context); +public: + RelativeRectangleLocalScope (const RelativeRectangle& rect_) : rect (rect_) {} + + const Expression getSymbolValue (const String& symbol) const + { + switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol)) + { + case RelativeCoordinate::StandardStrings::x: + case RelativeCoordinate::StandardStrings::left: return rect.left.getExpression(); + case RelativeCoordinate::StandardStrings::y: + case RelativeCoordinate::StandardStrings::top: return rect.top.getExpression(); + case RelativeCoordinate::StandardStrings::right: return rect.right.getExpression(); + case RelativeCoordinate::StandardStrings::bottom: return rect.bottom.getExpression(); + default: break; + } - return Rectangle ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t)); + return Expression::Scope::getSymbolValue (symbol); + } + +private: + const RelativeRectangle& rect; + + JUCE_DECLARE_NON_COPYABLE (RelativeRectangleLocalScope); +}; + +const Rectangle RelativeRectangle::resolve (const Expression::Scope* scope) const +{ + if (scope == 0) + { + RelativeRectangleLocalScope scope (*this); + return resolve (&scope); + } + else + { + const double l = left.resolve (scope); + const double r = right.resolve (scope); + const double t = top.resolve (scope); + const double b = bottom.resolve (scope); + + return Rectangle ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t)); + } } -void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* context) +void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Expression::Scope* scope) { - left.moveToAbsolute (newPos.getX(), context); - right.moveToAbsolute (newPos.getRight(), context); - top.moveToAbsolute (newPos.getY(), context); - bottom.moveToAbsolute (newPos.getBottom(), context); + left.moveToAbsolute (newPos.getX(), scope); + right.moveToAbsolute (newPos.getRight(), scope); + top.moveToAbsolute (newPos.getY(), scope); + bottom.moveToAbsolute (newPos.getBottom(), scope); } bool RelativeRectangle::isDynamic() const @@ -138,12 +185,12 @@ const String RelativeRectangle::toString() const return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString(); } -void RelativeRectangle::renameSymbolIfUsed (const String& oldName, const String& newName) +void RelativeRectangle::renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope) { - left.renameSymbolIfUsed (oldName, newName); - right.renameSymbolIfUsed (oldName, newName); - top.renameSymbolIfUsed (oldName, newName); - bottom.renameSymbolIfUsed (oldName, newName); + left = left.getExpression().withRenamedSymbol (oldSymbol, newName, scope); + right = right.getExpression().withRenamedSymbol (oldSymbol, newName, scope); + top = top.getExpression().withRenamedSymbol (oldSymbol, newName, scope); + bottom = bottom.getExpression().withRenamedSymbol (oldSymbol, newName, scope); } //============================================================================== @@ -174,7 +221,8 @@ public: { for (int i = 4; --i >= 0;) { - const Rectangle newBounds (rectangle.resolve (this).getSmallestIntegerContainer()); + ComponentScope scope (getComponent()); + const Rectangle newBounds (rectangle.resolve (&scope).getSmallestIntegerContainer()); if (newBounds == getComponent().getBounds()) return; @@ -191,31 +239,6 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner); }; -// An expression context that can evaluate expressions using "this" -class TemporaryRectangleContext : public Expression::EvaluationContext -{ -public: - TemporaryRectangleContext (const RelativeRectangle& rect_) : rect (rect_) {} - - const Expression getSymbolValue (const String& objectName, const String& edge) const - { - if (objectName == RelativeCoordinate::Strings::this_) - { - if (edge == RelativeCoordinate::Strings::left) return rect.left.getExpression(); - if (edge == RelativeCoordinate::Strings::right) return rect.right.getExpression(); - if (edge == RelativeCoordinate::Strings::top) return rect.top.getExpression(); - if (edge == RelativeCoordinate::Strings::bottom) return rect.bottom.getExpression(); - } - - return Expression::EvaluationContext::getSymbolValue (objectName, edge); - } - -private: - const RelativeRectangle& rect; - - JUCE_DECLARE_NON_COPYABLE (TemporaryRectangleContext); -}; - void RelativeRectangle::applyToComponent (Component& component) const { if (isDynamic()) @@ -233,9 +256,7 @@ void RelativeRectangle::applyToComponent (Component& component) const else { component.setPositioner (0); - - TemporaryRectangleContext context (*this); - component.setBounds (resolve (&context).getSmallestIntegerContainer()); + component.setBounds (resolve (0).getSmallestIntegerContainer()); } } diff --git a/src/gui/components/positioning/juce_RelativeRectangle.h b/src/gui/components/positioning/juce_RelativeRectangle.h index 7b77461c08..24f0775d02 100644 --- a/src/gui/components/positioning/juce_RelativeRectangle.h +++ b/src/gui/components/positioning/juce_RelativeRectangle.h @@ -65,10 +65,10 @@ public: //============================================================================== /** Calculates the absolute position of this rectangle. - You'll need to provide a suitable Expression::EvaluationContext for looking up any coordinates that may + You'll need to provide a suitable Expression::Scope for looking up any coordinates that may be needed to calculate the result. */ - const Rectangle resolve (const Expression::EvaluationContext* evaluationContext) const; + const Rectangle resolve (const Expression::Scope* scope) const; /** Changes the values of this rectangle's coordinates to make it resolve to the specified position. @@ -76,7 +76,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 Expression::EvaluationContext* evaluationContext); + void moveToAbsolute (const Rectangle& newPos, const Expression::Scope* scope); /** Returns true if this rectangle depends on any external symbols for its position. Coordinates that refer to symbols based on "this" are assumed not to be dynamic. @@ -91,9 +91,9 @@ public: const String toString() const; /** Renames a symbol if it is used by any of the coordinates. - This calls RelativeCoordinate::renameSymbolIfUsed() on the rectangle's coordinates. + This calls Expression::withRenamedSymbol() on the rectangle's coordinates. */ - void renameSymbolIfUsed (const String& oldName, const String& newName); + void renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope); /** Creates and sets an appropriate Component::Positioner object for the given component, which will keep it positioned with this rectangle. diff --git a/src/gui/graphics/drawables/juce_Drawable.h b/src/gui/graphics/drawables/juce_Drawable.h index 883cd291ac..00644e28eb 100644 --- a/src/gui/graphics/drawables/juce_Drawable.h +++ b/src/gui/graphics/drawables/juce_Drawable.h @@ -228,7 +228,11 @@ protected: {} bool registerCoordinates() { return owner.registerCoordinates (*this); } - void applyToComponentBounds() { owner.recalculateCoordinates (this); } + void applyToComponentBounds() + { + ComponentScope scope (getComponent()); + owner.recalculateCoordinates (&scope); + } private: DrawableType& owner; diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.cpp b/src/gui/graphics/drawables/juce_DrawableComposite.cpp index bf5d77c195..873074d62d 100644 --- a/src/gui/graphics/drawables/juce_DrawableComposite.cpp +++ b/src/gui/graphics/drawables/juce_DrawableComposite.cpp @@ -153,12 +153,12 @@ bool DrawableComposite::registerCoordinates (RelativeCoordinatePositionerBase& p return positioner.addPoint (bounds.bottomLeft) && ok; } -void DrawableComposite::recalculateCoordinates (Expression::EvaluationContext* context) +void DrawableComposite::recalculateCoordinates (Expression::Scope* scope) { Point resolved[3]; - bounds.resolveThreePoints (resolved, context); + bounds.resolveThreePoints (resolved, scope); - const Rectangle content (getContentArea().resolve (context)); + const Rectangle content (getContentArea().resolve (scope)); AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].getX(), resolved[0].getY(), content.getRight(), content.getY(), resolved[1].getX(), resolved[1].getY(), diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.h b/src/gui/graphics/drawables/juce_DrawableComposite.h index 8421c33090..cf0273e3d1 100644 --- a/src/gui/graphics/drawables/juce_DrawableComposite.h +++ b/src/gui/graphics/drawables/juce_DrawableComposite.h @@ -150,7 +150,7 @@ private: friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); - void recalculateCoordinates (Expression::EvaluationContext*); + void recalculateCoordinates (Expression::Scope*); void updateBoundsToFitChildren(); diff --git a/src/gui/graphics/drawables/juce_DrawableImage.cpp b/src/gui/graphics/drawables/juce_DrawableImage.cpp index 419b0e3f31..cef4dacdda 100644 --- a/src/gui/graphics/drawables/juce_DrawableImage.cpp +++ b/src/gui/graphics/drawables/juce_DrawableImage.cpp @@ -106,12 +106,12 @@ bool DrawableImage::registerCoordinates (RelativeCoordinatePositionerBase& posit return positioner.addPoint (bounds.bottomLeft) && ok; } -void DrawableImage::recalculateCoordinates (Expression::EvaluationContext* context) +void DrawableImage::recalculateCoordinates (Expression::Scope* scope) { if (image.isValid()) { Point resolved[3]; - bounds.resolveThreePoints (resolved, context); + bounds.resolveThreePoints (resolved, scope); const Point tr (resolved[0] + (resolved[1] - resolved[0]) / (float) image.getWidth()); const Point bl (resolved[0] + (resolved[2] - resolved[0]) / (float) image.getHeight()); diff --git a/src/gui/graphics/drawables/juce_DrawableImage.h b/src/gui/graphics/drawables/juce_DrawableImage.h index 4537b90181..32251c772e 100644 --- a/src/gui/graphics/drawables/juce_DrawableImage.h +++ b/src/gui/graphics/drawables/juce_DrawableImage.h @@ -133,7 +133,7 @@ private: friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); - void recalculateCoordinates (Expression::EvaluationContext*); + void recalculateCoordinates (Expression::Scope*); DrawableImage& operator= (const DrawableImage&); JUCE_LEAK_DETECTOR (DrawableImage); diff --git a/src/gui/graphics/drawables/juce_DrawablePath.cpp b/src/gui/graphics/drawables/juce_DrawablePath.cpp index 477c6c4dff..ac803e2351 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.cpp +++ b/src/gui/graphics/drawables/juce_DrawablePath.cpp @@ -72,10 +72,10 @@ const Path& DrawablePath::getStrokePath() const return strokePath; } -void DrawablePath::applyRelativePath (const RelativePointPath& newRelativePath, Expression::EvaluationContext* context) +void DrawablePath::applyRelativePath (const RelativePointPath& newRelativePath, Expression::Scope* scope) { Path newPath; - newRelativePath.createPath (newPath, context); + newRelativePath.createPath (newPath, scope); if (path != newPath) { @@ -118,7 +118,9 @@ public: void applyToComponentBounds() { jassert (owner.relativePath != 0); - owner.applyRelativePath (*owner.relativePath, this); + + ComponentScope scope (getComponent()); + owner.applyRelativePath (*owner.relativePath, &scope); } private: @@ -299,26 +301,26 @@ const RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const return RelativePoint(); } -float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::EvaluationContext* context) const +float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::Scope* scope) const { const Identifier i (state.getType()); if (i == lineToElement || i == closeSubPathElement) - return getEndPoint().resolve (context).getDistanceFrom (getStartPoint().resolve (context)); + return getEndPoint().resolve (scope).getDistanceFrom (getStartPoint().resolve (scope)); if (i == cubicToElement) { Path p; - p.startNewSubPath (getStartPoint().resolve (context)); - p.cubicTo (getControlPoint (0).resolve (context), getControlPoint (1).resolve (context), getControlPoint (2).resolve (context)); + p.startNewSubPath (getStartPoint().resolve (scope)); + p.cubicTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope), getControlPoint (2).resolve (scope)); return p.getLength(); } if (i == quadraticToElement) { Path p; - p.startNewSubPath (getStartPoint().resolve (context)); - p.quadraticTo (getControlPoint (0).resolve (context), getControlPoint (1).resolve (context)); + p.startNewSubPath (getStartPoint().resolve (scope)); + p.quadraticTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope)); return p.getLength(); } @@ -350,7 +352,7 @@ void DrawablePath::ValueTreeWrapper::Element::convertToLine (UndoManager* undoMa } } -void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::EvaluationContext* context, UndoManager* undoManager) +void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::Scope* scope, UndoManager* undoManager) { const Identifier i (state.getType()); @@ -361,8 +363,8 @@ void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::Evalua const RelativePoint start (getStartPoint()); const RelativePoint end (getEndPoint()); - const Point startResolved (start.resolve (context)); - const Point endResolved (end.resolve (context)); + const Point startResolved (start.resolve (scope)); + const Point endResolved (end.resolve (scope)); e.setControlPoint (0, startResolved + (endResolved - startResolved) * 0.3f, undoManager); e.setControlPoint (1, startResolved + (endResolved - startResolved) * 0.7f, undoManager); e.setControlPoint (2, end, undoManager); @@ -407,7 +409,7 @@ namespace DrawablePathHelpers } } -float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, Expression::EvaluationContext* context) const +float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, Expression::Scope* scope) const { using namespace DrawablePathHelpers; const Identifier type (state.getType()); @@ -417,7 +419,7 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po { RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); - const Point points[] = { rp1.resolve (context), rp2.resolve (context), rp3.resolve (context), rp4.resolve (context) }; + const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) }; float bestDistance = std::numeric_limits::max(); @@ -437,7 +439,7 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po else if (type == quadraticToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); - const Point points[] = { rp1.resolve (context), rp2.resolve (context), rp3.resolve (context) }; + const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) }; float bestDistance = std::numeric_limits::max(); @@ -457,24 +459,24 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po else if (type == lineToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); - const Line line (rp1.resolve (context), rp2.resolve (context)); + const Line line (rp1.resolve (scope), rp2.resolve (scope)); bestProp = line.findNearestProportionalPositionTo (targetPoint); } return bestProp; } -ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, Expression::EvaluationContext* context, UndoManager* undoManager) +ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, Expression::Scope* scope, UndoManager* undoManager) { ValueTree newTree; const Identifier type (state.getType()); if (type == cubicToElement) { - float bestProp = findProportionAlongLine (targetPoint, context); + float bestProp = findProportionAlongLine (targetPoint, scope); RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); - const Point points[] = { rp1.resolve (context), rp2.resolve (context), rp3.resolve (context), rp4.resolve (context) }; + const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) }; const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), mid2 (points[1] + (points[2] - points[1]) * bestProp), @@ -499,10 +501,10 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point points[] = { rp1.resolve (context), rp2.resolve (context), rp3.resolve (context) }; + const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) }; const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), mid2 (points[1] + (points[2] - points[1]) * bestProp); @@ -522,7 +524,7 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point line (rp1.resolve (context), rp2.resolve (context)); + const Line line (rp1.resolve (scope), rp2.resolve (scope)); const Point newPoint (line.findNearestPointTo (targetPoint)); setControlPoint (0, newPoint, undoManager); diff --git a/src/gui/graphics/drawables/juce_DrawablePath.h b/src/gui/graphics/drawables/juce_DrawablePath.h index b7a223709b..15bc04c484 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.h +++ b/src/gui/graphics/drawables/juce_DrawablePath.h @@ -101,7 +101,7 @@ public: const RelativePoint getStartPoint() const; const RelativePoint getEndPoint() const; void setControlPoint (int index, const RelativePoint& point, UndoManager*); - float getLength (Expression::EvaluationContext*) const; + float getLength (Expression::Scope*) const; ValueTreeWrapper getParent() const; Element getPreviousElement() const; @@ -110,11 +110,11 @@ public: void setModeOfEndPoint (const String& newMode, UndoManager*); void convertToLine (UndoManager*); - void convertToCubic (Expression::EvaluationContext*, UndoManager*); + void convertToCubic (Expression::Scope*, UndoManager*); void convertToPathBreak (UndoManager* undoManager); - ValueTree insertPoint (const Point& targetPoint, Expression::EvaluationContext*, UndoManager*); + ValueTree insertPoint (const Point& targetPoint, Expression::Scope*, UndoManager*); void removePoint (UndoManager* undoManager); - float findProportionAlongLine (const Point& targetPoint, Expression::EvaluationContext*) const; + float findProportionAlongLine (const Point& targetPoint, Expression::Scope*) const; static const Identifier mode, startSubPathElement, closeSubPathElement, lineToElement, quadraticToElement, cubicToElement; @@ -139,7 +139,7 @@ private: class RelativePositioner; friend class RelativePositioner; - void applyRelativePath (const RelativePointPath&, Expression::EvaluationContext*); + void applyRelativePath (const RelativePointPath&, Expression::Scope*); DrawablePath& operator= (const DrawablePath&); JUCE_LEAK_DETECTOR (DrawablePath); diff --git a/src/gui/graphics/drawables/juce_DrawableRectangle.cpp b/src/gui/graphics/drawables/juce_DrawableRectangle.cpp index 3752ab5eea..29932606c5 100644 --- a/src/gui/graphics/drawables/juce_DrawableRectangle.cpp +++ b/src/gui/graphics/drawables/juce_DrawableRectangle.cpp @@ -95,13 +95,13 @@ bool DrawableRectangle::registerCoordinates (RelativeCoordinatePositionerBase& p return positioner.addPoint (cornerSize) && ok; } -void DrawableRectangle::recalculateCoordinates (Expression::EvaluationContext* context) +void DrawableRectangle::recalculateCoordinates (Expression::Scope* scope) { Point points[3]; - bounds.resolveThreePoints (points, context); + bounds.resolveThreePoints (points, scope); - const float cornerSizeX = (float) cornerSize.x.resolve (context); - const float cornerSizeY = (float) cornerSize.y.resolve (context); + const float cornerSizeX = (float) cornerSize.x.resolve (scope); + const float cornerSizeY = (float) cornerSize.y.resolve (scope); const float w = Line (points[0], points[1]).getLength(); const float h = Line (points[0], points[2]).getLength(); diff --git a/src/gui/graphics/drawables/juce_DrawableRectangle.h b/src/gui/graphics/drawables/juce_DrawableRectangle.h index 0b5a55869c..300308dc67 100644 --- a/src/gui/graphics/drawables/juce_DrawableRectangle.h +++ b/src/gui/graphics/drawables/juce_DrawableRectangle.h @@ -97,7 +97,7 @@ private: void rebuildPath(); bool registerCoordinates (RelativeCoordinatePositionerBase&); - void recalculateCoordinates (Expression::EvaluationContext*); + void recalculateCoordinates (Expression::Scope*); DrawableRectangle& operator= (const DrawableRectangle&); JUCE_LEAK_DETECTOR (DrawableRectangle); diff --git a/src/gui/graphics/drawables/juce_DrawableShape.cpp b/src/gui/graphics/drawables/juce_DrawableShape.cpp index 82d8300ed7..0eed3fc833 100644 --- a/src/gui/graphics/drawables/juce_DrawableShape.cpp +++ b/src/gui/graphics/drawables/juce_DrawableShape.cpp @@ -71,8 +71,9 @@ public: void applyToComponentBounds() { - if (isMainFill ? owner.mainFill.recalculateCoords (this) - : owner.strokeFill.recalculateCoords (this)) + ComponentScope scope (owner); + if (isMainFill ? owner.mainFill.recalculateCoords (&scope) + : owner.strokeFill.recalculateCoords (&scope)) owner.repaint(); } @@ -256,19 +257,19 @@ bool DrawableShape::RelativeFillType::operator!= (const RelativeFillType& other) return ! operator== (other); } -bool DrawableShape::RelativeFillType::recalculateCoords (Expression::EvaluationContext* context) +bool DrawableShape::RelativeFillType::recalculateCoords (Expression::Scope* scope) { if (fill.isGradient()) { - const Point g1 (gradientPoint1.resolve (context)); - const Point g2 (gradientPoint2.resolve (context)); + const Point g1 (gradientPoint1.resolve (scope)); + const Point g2 (gradientPoint2.resolve (scope)); AffineTransform t; ColourGradient& g = *fill.gradient; if (g.isRadial) { - const Point g3 (gradientPoint3.resolve (context)); + const Point g3 (gradientPoint3.resolve (scope)); const Point g3Source (g1.getX() + g2.getY() - g1.getY(), g1.getY() + g1.getX() - g2.getX()); diff --git a/src/gui/graphics/drawables/juce_DrawableShape.h b/src/gui/graphics/drawables/juce_DrawableShape.h index 89193b199a..421e9dde52 100644 --- a/src/gui/graphics/drawables/juce_DrawableShape.h +++ b/src/gui/graphics/drawables/juce_DrawableShape.h @@ -65,7 +65,7 @@ public: bool operator!= (const RelativeFillType&) const; bool isDynamic() const; - bool recalculateCoords (Expression::EvaluationContext* context); + bool recalculateCoords (Expression::Scope* scope); void writeTo (ValueTree& v, ComponentBuilder::ImageProvider*, UndoManager*) const; bool readFrom (const ValueTree& v, ComponentBuilder::ImageProvider*); diff --git a/src/gui/graphics/drawables/juce_DrawableText.cpp b/src/gui/graphics/drawables/juce_DrawableText.cpp index 3b3ba6fbf9..5674a9b2d0 100644 --- a/src/gui/graphics/drawables/juce_DrawableText.cpp +++ b/src/gui/graphics/drawables/juce_DrawableText.cpp @@ -138,14 +138,14 @@ bool DrawableText::registerCoordinates (RelativeCoordinatePositionerBase& positi return positioner.addPoint (fontSizeControlPoint) && ok; } -void DrawableText::recalculateCoordinates (Expression::EvaluationContext* context) +void DrawableText::recalculateCoordinates (Expression::Scope* scope) { - bounds.resolveThreePoints (resolvedPoints, context); + bounds.resolveThreePoints (resolvedPoints, scope); const float w = Line (resolvedPoints[0], resolvedPoints[1]).getLength(); const float h = Line (resolvedPoints[0], resolvedPoints[2]).getLength(); - const Point fontCoords (RelativeParallelogram::getInternalCoordForPoint (resolvedPoints, fontSizeControlPoint.resolve (context))); + const Point fontCoords (RelativeParallelogram::getInternalCoordForPoint (resolvedPoints, fontSizeControlPoint.resolve (scope))); const float fontHeight = jlimit (0.01f, jmax (0.01f, h), fontCoords.getY()); const float fontWidth = jlimit (0.01f, jmax (0.01f, w), fontCoords.getX()); diff --git a/src/gui/graphics/drawables/juce_DrawableText.h b/src/gui/graphics/drawables/juce_DrawableText.h index 0d8e0112c2..704436ad2e 100644 --- a/src/gui/graphics/drawables/juce_DrawableText.h +++ b/src/gui/graphics/drawables/juce_DrawableText.h @@ -142,7 +142,7 @@ private: friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); - void recalculateCoordinates (Expression::EvaluationContext*); + void recalculateCoordinates (Expression::Scope*); void refreshBounds(); const AffineTransform getArrangementAndTransform (GlyphArrangement& glyphs) const; diff --git a/src/maths/juce_Expression.cpp b/src/maths/juce_Expression.cpp index cf1fef9829..a294430ce8 100644 --- a/src/maths/juce_Expression.cpp +++ b/src/maths/juce_Expression.cpp @@ -31,6 +31,61 @@ BEGIN_JUCE_NAMESPACE #include "../containers/juce_ReferenceCountedArray.h" +//============================================================================== +class Expression::Term : public ReferenceCountedObject +{ +public: + Term() {} + virtual ~Term() {} + + virtual Type getType() const throw() = 0; + virtual Term* clone() const = 0; + virtual const ReferenceCountedObjectPtr resolve (const Scope&, int recursionDepth) = 0; + virtual const String toString() const = 0; + virtual double toDouble() const { return 0; } + virtual int getInputIndexFor (const Term*) const { return -1; } + virtual int getOperatorPrecedence() const { return 0; } + virtual int getNumInputs() const { return 0; } + virtual Term* getInput (int) const { return 0; } + virtual const ReferenceCountedObjectPtr negated(); + + virtual const ReferenceCountedObjectPtr createTermToEvaluateInput (const Scope&, const Term* /*inputTerm*/, + double /*overallTarget*/, Term* /*topLevelTerm*/) const + { + jassertfalse; + return 0; + } + + virtual const String getName() const + { + jassertfalse; // You shouldn't call this for an expression that's not actually a function! + return String::empty; + } + + virtual void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) + { + for (int i = getNumInputs(); --i >= 0;) + getInput (i)->renameSymbol (oldSymbol, newName, scope, recursionDepth); + } + + class SymbolVisitor + { + public: + virtual ~SymbolVisitor() {} + virtual void useSymbol (const Symbol&) = 0; + }; + + virtual void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) + { + for (int i = getNumInputs(); --i >= 0;) + getInput(i)->visitAllSymbols (visitor, scope, recursionDepth); + } + +private: + JUCE_DECLARE_NON_COPYABLE (Term); +}; + + //============================================================================== class Expression::Helpers { @@ -38,34 +93,50 @@ public: typedef ReferenceCountedObjectPtr TermPtr; // This helper function is needed to work around VC6 scoping bugs - static const TermPtr& getTermFor (const Expression& exp) throw() { return exp.term; } + static inline const TermPtr& getTermFor (const Expression& exp) throw() { return exp.term; } + + static void checkRecursionDepth (const int depth) + { + if (depth > 256) + throw EvaluationError ("Recursive symbol references"); + } friend class Expression::Term; // (also only needed as a VC6 workaround) + //============================================================================== + /** An exception that can be thrown by Expression::evaluate(). */ + class EvaluationError : public std::exception + { + public: + EvaluationError (const String& description_) + : description (description_) + { + DBG ("Expression::EvaluationError: " + description); + } + + String description; + }; + //============================================================================== class Constant : public Term { public: - Constant (const double value_, bool isResolutionTarget_) + Constant (const double value_, const bool isResolutionTarget_) : value (value_), isResolutionTarget (isResolutionTarget_) {} - Type getType() const throw() { return constantType; } - Term* clone() const { return new Constant (value, isResolutionTarget); } - double evaluate (const EvaluationContext&, int) const { return value; } - int getNumInputs() const { return 0; } - Term* getInput (int) const { return 0; } - - const TermPtr negated() - { - return new Constant (-value, isResolutionTarget); - } + Type getType() const throw() { return constantType; } + Term* clone() const { return new Constant (value, isResolutionTarget); } + const TermPtr resolve (const Scope&, int) { return this; } + double toDouble() const { return value; } + const TermPtr negated() { return new Constant (-value, isResolutionTarget); } const String toString() const { + String s (value); if (isResolutionTarget) - return "@" + String (value); + s = "@" + s; - return String (value); + return s; } double value; @@ -73,100 +144,147 @@ public: }; //============================================================================== - class Symbol : public Term + class BinaryTerm : public Term { public: - explicit Symbol (const String& symbol_) - : mainSymbol (symbol_.upToFirstOccurrenceOf (".", false, false).trim()), - member (symbol_.fromFirstOccurrenceOf (".", false, false).trim()) - {} + BinaryTerm (Term* const left_, Term* const right_) : left (left_), right (right_) + { + jassert (left_ != 0 && right_ != 0); + } - Symbol (const String& symbol_, const String& member_) - : mainSymbol (symbol_), - member (member_) - {} + int getInputIndexFor (const Term* possibleInput) const + { + return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1); + } - double evaluate (const EvaluationContext& c, int recursionDepth) const + Type getType() const throw() { return operatorType; } + int getNumInputs() const { return 2; } + Term* getInput (int index) const { return index == 0 ? left.getObject() : (index == 1 ? right.getObject() : 0); } + + virtual double performFunction (double left, double right) const = 0; + virtual void writeOperator (String& dest) const = 0; + + const TermPtr resolve (const Scope& scope, int recursionDepth) { - if (++recursionDepth > 256) - throw EvaluationError ("Recursive symbol references"); + return new Constant (performFunction (left->resolve (scope, recursionDepth)->toDouble(), + right->resolve (scope, recursionDepth)->toDouble()), false); + } - try - { - return getTermFor (c.getSymbolValue (mainSymbol, member))->evaluate (c, recursionDepth); - } - catch (...) - {} + const String toString() const + { + String s; - return 0; + const int ourPrecendence = getOperatorPrecedence(); + if (left->getOperatorPrecedence() > ourPrecendence) + s << '(' << left->toString() << ')'; + else + s = left->toString(); + + writeOperator (s); + + if (right->getOperatorPrecedence() >= ourPrecendence) + s << '(' << right->toString() << ')'; + else + s << right->toString(); + + return s; } - Type getType() const throw() { return symbolType; } - Term* clone() const { return new Symbol (mainSymbol, member); } - int getNumInputs() const { return 0; } - Term* getInput (int) const { return 0; } - const String toString() const { return joinParts (mainSymbol, member); } - void getSymbolParts (String& objectName, String& memberName) const { objectName = mainSymbol; memberName = member; } + protected: + const TermPtr left, right; - static const String joinParts (const String& mainSymbol, const String& member) + const TermPtr createDestinationTerm (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - return member.isEmpty() ? mainSymbol - : mainSymbol + "." + member; + 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, false); + + return dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm); } + }; - bool referencesSymbol (const String& s, const EvaluationContext* c, int recursionDepth) const + //============================================================================== + class SymbolTerm : public Term + { + public: + explicit SymbolTerm (const String& symbol_) : symbol (symbol_) {} + + const TermPtr resolve (const Scope& scope, int recursionDepth) { - if (s == mainSymbol || (s.containsChar ('.') && s == toString())) - return true; + checkRecursionDepth (recursionDepth); + return getTermFor (scope.getSymbolValue (symbol))->resolve (scope, recursionDepth + 1); + } - if (++recursionDepth > 256) - throw EvaluationError ("Recursive symbol references"); + Type getType() const throw() { return symbolType; } + Term* clone() const { return new SymbolTerm (symbol); } + const String toString() const { return symbol; } + const String getName() const { return symbol; } - try - { - return c != 0 && getTermFor (c->getSymbolValue (mainSymbol, member))->referencesSymbol (s, c, recursionDepth); - } - catch (EvaluationError&) - { - return false; - } + void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) + { + checkRecursionDepth (recursionDepth); + visitor.useSymbol (Symbol (scope.getScopeUID(), symbol)); + getTermFor (scope.getSymbolValue (symbol))->visitAllSymbols (visitor, scope, recursionDepth + 1); } - String mainSymbol, member; + void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int /*recursionDepth*/) + { + if (oldSymbol.symbolName == symbol && scope.getScopeUID() == oldSymbol.scopeUID) + symbol = newName; + } + + String symbol; }; //============================================================================== class Function : public Term { public: - Function (const String& functionName_, const ReferenceCountedArray& parameters_) + explicit Function (const String& functionName_) : functionName (functionName_) {} + + Function (const String& functionName_, const Array& parameters_) : functionName (functionName_), parameters (parameters_) {} - Term* clone() const { return new Function (functionName, parameters); } + Type getType() const throw() { return functionType; } + Term* clone() const { return new Function (functionName, parameters); } + int getNumInputs() const { return parameters.size(); } + Term* getInput (int i) const { return getTermFor (parameters [i]); } + const String getName() const { return functionName; } - double evaluate (const EvaluationContext& c, int recursionDepth) const + const TermPtr resolve (const Scope& scope, int recursionDepth) { - HeapBlock params (parameters.size()); - for (int i = 0; i < parameters.size(); ++i) - params[i] = parameters.getUnchecked(i)->evaluate (c, recursionDepth); + checkRecursionDepth (recursionDepth); + double result = 0; + const int numParams = parameters.size(); + if (numParams > 0) + { + HeapBlock params (numParams); + for (int i = 0; i < numParams; ++i) + params[i] = getTermFor (parameters.getReference(i))->resolve (scope, recursionDepth + 1)->toDouble(); - return c.evaluateFunction (functionName, params, parameters.size()); - } + result = scope.evaluateFunction (functionName, params, numParams); + } + else + { + result = scope.evaluateFunction (functionName, 0, 0); + } - Type getType() const throw() { return functionType; } - int getInputIndexFor (const Term* possibleInput) const { return parameters.indexOf (possibleInput); } - int getNumInputs() const { return parameters.size(); } - Term* getInput (int i) const { return parameters [i]; } - const String getFunctionName() const { return functionName; } + return new Constant (result, false); + } - bool referencesSymbol (const String& s, const EvaluationContext* c, int recursionDepth) const + int getInputIndexFor (const Term* possibleInput) const { for (int i = 0; i < parameters.size(); ++i) - if (parameters.getUnchecked(i)->referencesSymbol (s, c, recursionDepth)) - return true; + if (getTermFor (parameters.getReference(i)) == possibleInput) + return i; - return false; + return -1; } const String toString() const @@ -178,7 +296,7 @@ public: for (int i = 0; i < parameters.size(); ++i) { - s << parameters.getUnchecked(i)->toString(); + s << getTermFor (parameters.getReference(i))->toString(); if (i < parameters.size() - 1) s << ", "; @@ -189,119 +307,148 @@ public: } const String functionName; - ReferenceCountedArray parameters; + Array parameters; }; //============================================================================== - class Negate : public Term + class DotOperator : public BinaryTerm { public: - explicit Negate (const TermPtr& input_) : input (input_) + DotOperator (SymbolTerm* const left_, Term* const right_) : BinaryTerm (left_, right_) {} + + const TermPtr resolve (const Scope& scope, int recursionDepth) { - jassert (input_ != 0); + checkRecursionDepth (recursionDepth); + + EvaluationVisitor visitor (right, recursionDepth + 1); + scope.visitRelativeScope (getSymbol()->symbol, visitor); + return visitor.output; } - Type getType() const throw() { return operatorType; } - 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 String getFunctionName() const { return "-"; } + Term* clone() const { return new DotOperator (getSymbol(), right); } + const String getName() const { return "."; } + int getOperatorPrecedence() const { return 1; } + void writeOperator (String& dest) const { dest << '.'; } + double performFunction (double, double) const { return 0.0; } - const TermPtr negated() + void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) { - return input; + checkRecursionDepth (recursionDepth); + visitor.useSymbol (Symbol (scope.getScopeUID(), getSymbol()->symbol)); + + SymbolVisitingVisitor v (right, visitor, recursionDepth + 1); + scope.visitRelativeScope (getSymbol()->symbol, v); } - const TermPtr createTermToEvaluateInput (const EvaluationContext& context, const Term* input_, double overallTarget, Term* topLevelTerm) const + void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) { - (void) input_; - jassert (input_ == input); - - const Term* const dest = findDestinationFor (topLevelTerm, this); + checkRecursionDepth (recursionDepth); + getSymbol()->renameSymbol (oldSymbol, newName, scope, recursionDepth); - return new Negate (dest == 0 ? new Constant (overallTarget, false) - : dest->createTermToEvaluateInput (context, this, overallTarget, topLevelTerm)); + SymbolRenamingVisitor visitor (right, oldSymbol, newName, recursionDepth + 1); + scope.visitRelativeScope (getSymbol()->symbol, visitor); } - const String toString() const + private: + //============================================================================== + class EvaluationVisitor : public Scope::Visitor { - if (input->getOperatorPrecedence() > 0) - return "-(" + input->toString() + ")"; - else - return "-" + input->toString(); - } + public: + EvaluationVisitor (const TermPtr& input_, const int recursionCount_) + : input (input_), output (input_), recursionCount (recursionCount_) {} - bool referencesSymbol (const String& s, const EvaluationContext* c, int recursionDepth) const + void visit (const Scope& scope) { output = input->resolve (scope, recursionCount); } + + const TermPtr input; + TermPtr output; + const int recursionCount; + + private: + JUCE_DECLARE_NON_COPYABLE (EvaluationVisitor); + }; + + class SymbolVisitingVisitor : public Scope::Visitor { - return input->referencesSymbol (s, c, recursionDepth); - } + public: + SymbolVisitingVisitor (const TermPtr& input_, SymbolVisitor& visitor_, const int recursionCount_) + : input (input_), visitor (visitor_), recursionCount (recursionCount_) {} - private: - const TermPtr input; + void visit (const Scope& scope) { input->visitAllSymbols (visitor, scope, recursionCount); } + + private: + const TermPtr input; + SymbolVisitor& visitor; + const int recursionCount; + + JUCE_DECLARE_NON_COPYABLE (SymbolVisitingVisitor); + }; + + class SymbolRenamingVisitor : public Scope::Visitor + { + public: + SymbolRenamingVisitor (const TermPtr& input_, const Expression::Symbol& symbol_, const String& newName_, const int recursionCount_) + : input (input_), symbol (symbol_), newName (newName_), recursionCount (recursionCount_) {} + + void visit (const Scope& scope) { input->renameSymbol (symbol, newName, scope, recursionCount); } + + private: + const TermPtr input; + const Symbol& symbol; + const String newName; + const int recursionCount; + + JUCE_DECLARE_NON_COPYABLE (SymbolRenamingVisitor); + }; + + SymbolTerm* getSymbol() const { return static_cast (left.getObject()); } + + JUCE_DECLARE_NON_COPYABLE (DotOperator); }; //============================================================================== - class BinaryTerm : public Term + class Negate : public Term { public: - BinaryTerm (Term* const left_, Term* const right_) : left (left_), right (right_) + explicit Negate (const TermPtr& input_) : input (input_) { - jassert (left_ != 0 && right_ != 0); + jassert (input_ != 0); } - int getInputIndexFor (const Term* possibleInput) const + Type getType() const throw() { return operatorType; } + int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; } + int getNumInputs() const { return 1; } + Term* getInput (int index) const { return index == 0 ? input.getObject() : 0; } + Term* clone() const { return new Negate (input->clone()); } + + const TermPtr resolve (const Scope& scope, int recursionDepth) { - return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1); + return new Constant (-input->resolve (scope, recursionDepth)->toDouble(), false); } - Type getType() const throw() { return operatorType; } - int getNumInputs() const { return 2; } - Term* getInput (int index) const { return index == 0 ? static_cast (left) : (index == 1 ? static_cast (right) : 0); } + const String getName() const { return "-"; } + const TermPtr negated() { return input; } - bool referencesSymbol (const String& s, const EvaluationContext* c, int recursionDepth) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input_, double overallTarget, Term* topLevelTerm) const { - return left->referencesSymbol (s, c, recursionDepth) - || right->referencesSymbol (s, c, recursionDepth); + (void) input_; + jassert (input_ == input); + + const Term* const dest = findDestinationFor (topLevelTerm, this); + + return new Negate (dest == 0 ? new Constant (overallTarget, false) + : dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm)); } const String toString() const { - String s; - - const int ourPrecendence = getOperatorPrecedence(); - if (left->getOperatorPrecedence() > ourPrecendence) - s << '(' << left->toString() << ')'; - else - s = left->toString(); - - s << ' ' << getFunctionName() << ' '; - - if (right->getOperatorPrecedence() >= ourPrecendence) - s << '(' << right->toString() << ')'; + if (input->getOperatorPrecedence() > 0) + return "-(" + input->toString() + ")"; else - s << right->toString(); - - return s; + return "-" + input->toString(); } - protected: - const TermPtr left, right; - - const TermPtr createDestinationTerm (const EvaluationContext& context, const 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, false); - - return dest->createTermToEvaluateInput (context, this, overallTarget, topLevelTerm); - } + private: + const TermPtr input; }; //============================================================================== @@ -311,13 +458,14 @@ 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); } - int getOperatorPrecedence() const { return 2; } - const String getFunctionName() const { return "+"; } + double performFunction (double lhs, double rhs) const { return lhs + rhs; } + int getOperatorPrecedence() const { return 3; } + const String getName() const { return "+"; } + void writeOperator (String& dest) const { dest << " + "; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& c, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - const TermPtr newDest (createDestinationTerm (c, input, overallTarget, topLevelTerm)); + const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == 0) return 0; @@ -335,13 +483,14 @@ 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); } - int getOperatorPrecedence() const { return 2; } - const String getFunctionName() const { return "-"; } + double performFunction (double lhs, double rhs) const { return lhs - rhs; } + int getOperatorPrecedence() const { return 3; } + const String getName() const { return "-"; } + void writeOperator (String& dest) const { dest << " - "; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& c, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - const TermPtr newDest (createDestinationTerm (c, input, overallTarget, topLevelTerm)); + const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == 0) return 0; @@ -362,13 +511,14 @@ 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 getFunctionName() const { return "*"; } - int getOperatorPrecedence() const { return 1; } + double performFunction (double lhs, double rhs) const { return lhs * rhs; } + const String getName() const { return "*"; } + void writeOperator (String& dest) const { dest << " * "; } + int getOperatorPrecedence() const { return 2; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& c, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - const TermPtr newDest (createDestinationTerm (c, input, overallTarget, topLevelTerm)); + const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == 0) return 0; @@ -386,13 +536,14 @@ 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 getFunctionName() const { return "/"; } - int getOperatorPrecedence() const { return 1; } + double performFunction (double lhs, double rhs) const { return lhs / rhs; } + const String getName() const { return "/"; } + void writeOperator (String& dest) const { dest << " / "; } + int getOperatorPrecedence() const { return 2; } - const TermPtr createTermToEvaluateInput (const EvaluationContext& c, const Term* input, double overallTarget, Term* topLevelTerm) const + const TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { - const TermPtr newDest (createDestinationTerm (c, input, overallTarget, topLevelTerm)); + const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == 0) return 0; @@ -466,24 +617,33 @@ public: return false; } - static bool renameSymbol (Term* const t, const String& oldName, const String& newName) + //============================================================================== + class SymbolCheckVisitor : public Term::SymbolVisitor { - Symbol* const sym = dynamic_cast (t); + public: + SymbolCheckVisitor (const Symbol& symbol_) : wasFound (false), symbol (symbol_) {} + void useSymbol (const Symbol& s) { wasFound = wasFound || s == symbol; } - if (sym != 0 && sym->mainSymbol == oldName) - { - sym->mainSymbol = newName; - return true; - } + bool wasFound; - bool anyChanged = false; + private: + const Symbol& symbol; - for (int i = t->getNumInputs(); --i >= 0;) - if (renameSymbol (t->getInput (i), oldName, newName)) - anyChanged = true; + JUCE_DECLARE_NON_COPYABLE (SymbolCheckVisitor); + }; - return anyChanged; - } + //============================================================================== + class SymbolListVisitor : public Term::SymbolVisitor + { + public: + SymbolListVisitor (Array& list_) : list (list_) {} + void useSymbol (const Symbol& s) { list.addIfNotAlreadyThere (s); } + + private: + Array& list; + + JUCE_DECLARE_NON_COPYABLE (SymbolListVisitor); + }; //============================================================================== class Parser @@ -574,7 +734,7 @@ public: { ++i; - while (CharacterFunctions::isLetterOrDigit (text[i]) || text[i] == '_' || text[i] == '.') + while (CharacterFunctions::isLetterOrDigit (text[i]) || text[i] == '_') ++i; } @@ -707,12 +867,17 @@ public: if (e != 0) return e; + return readSymbolOrFunction(); + } + + const TermPtr readSymbolOrFunction() + { String identifier; if (readIdentifier (identifier)) { if (readOperator ("(")) // method call... { - Function* const f = new Function (identifier, ReferenceCountedArray()); + Function* const f = new Function (identifier); ScopedPointer func (f); // (can't use ScopedPointer in MSVC) TermPtr param (readExpression()); @@ -725,7 +890,7 @@ public: throw ParseError ("Expected parameters after \"" + identifier + " (\""); } - f->parameters.add (param); + f->parameters.add (Expression (param)); while (readOperator (",")) { @@ -734,7 +899,7 @@ public: if (param == 0) throw ParseError ("Expected expression after \",\""); - f->parameters.add (param); + f->parameters.add (Expression (param)); } if (readOperator (")")) @@ -742,9 +907,22 @@ public: throw ParseError ("Expected \")\""); } + else if (readOperator (".")) + { + TermPtr rhs (readSymbolOrFunction()); + + if (rhs == 0) + throw ParseError ("Expected symbol or function after \".\""); + + if (identifier == "this") + return rhs; + + return new DotOperator (new SymbolTerm (identifier), rhs); + } else // just a symbol.. { - return new Symbol (identifier); + jassert (identifier.trim() == identifier); + return new SymbolTerm (identifier); } } @@ -822,12 +1000,33 @@ const Expression Expression::parse (const String& stringToParse, int& textIndexT double Expression::evaluate() const { - return evaluate (Expression::EvaluationContext()); + return evaluate (Expression::Scope()); } -double Expression::evaluate (const Expression::EvaluationContext& context) const +double Expression::evaluate (const Expression::Scope& scope) const { - return term->evaluate (context, 0); + try + { + return term->resolve (scope, 0)->toDouble(); + } + catch (Helpers::EvaluationError&) + {} + + return 0; +} + +double Expression::evaluate (const Scope& scope, String& evaluationError) const +{ + try + { + return term->resolve (scope, 0)->toDouble(); + } + catch (Helpers::EvaluationError& e) + { + evaluationError = e.description; + } + + return 0; } const Expression Expression::operator+ (const Expression& other) const { return Expression (new Helpers::Add (term, other.term)); } @@ -835,28 +1034,14 @@ const Expression Expression::operator- (const Expression& other) const { return 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 (term->negated()); } - -const String Expression::toString() const -{ - return term->toString(); -} - -const Expression Expression::symbol (const String& symbol) -{ - return Expression (new Helpers::Symbol (symbol)); -} +const Expression Expression::symbol (const String& symbol) { return Expression (new Helpers::SymbolTerm (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)); + return Expression (new Helpers::Function (functionName, parameters)); } -const Expression Expression::adjustedToGiveNewResult (const double targetValue, - const Expression::EvaluationContext& context) const +const Expression Expression::adjustedToGiveNewResult (const double targetValue, const Expression::Scope& scope) const { ScopedPointer newTerm (term->clone()); @@ -881,114 +1066,67 @@ const Expression Expression::adjustedToGiveNewResult (const double targetValue, } else { - const Helpers::TermPtr reverseTerm (parent->createTermToEvaluateInput (context, termToAdjust, targetValue, newTerm)); + const Helpers::TermPtr reverseTerm (parent->createTermToEvaluateInput (scope, termToAdjust, targetValue, newTerm)); if (reverseTerm == 0) return Expression (targetValue); - termToAdjust->value = reverseTerm->evaluate (context, 0); + termToAdjust->value = reverseTerm->resolve (scope, 0)->toDouble(); } return Expression (newTerm.release()); } -const Expression Expression::withRenamedSymbol (const String& oldSymbol, const String& newSymbol) const +const Expression Expression::withRenamedSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Scope& scope) const { - jassert (newSymbol.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); + jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); - if (oldSymbol == newSymbol) + if (oldSymbol.symbolName == newName) return *this; - 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); + Expression e (term->clone()); + e.term->renameSymbol (oldSymbol, newName, scope, 0); + return e; } -Expression::Type Expression::getType() const throw() +bool Expression::referencesSymbol (const Expression::Symbol& symbol, const Scope& scope) const { - return term->getType(); -} + Helpers::SymbolCheckVisitor visitor (symbol); -const String Expression::getSymbol() const -{ - String objectName, memberName; - term->getSymbolParts (objectName, memberName); - return Expression::Helpers::Symbol::joinParts (objectName, memberName); -} - -void Expression::getSymbolParts (String& objectName, String& memberName) const -{ - term->getSymbolParts (objectName, memberName); -} - -const String Expression::getFunction() const -{ - return term->getFunctionName(); -} + try + { + term->visitAllSymbols (visitor, scope, 0); + } + catch (Helpers::EvaluationError&) + {} -const String Expression::getOperator() const -{ - return term->getFunctionName(); + return visitor.wasFound; } -int Expression::getNumInputs() const +void Expression::findReferencedSymbols (Array& results, const Scope& scope) const { - return term->getNumInputs(); + try + { + Helpers::SymbolListVisitor visitor (results); + term->visitAllSymbols (visitor, scope, 0); + } + catch (Helpers::EvaluationError&) + {} } -const Expression Expression::getInput (int index) const -{ - return Expression (term->getInput (index)); -} +const String Expression::toString() const { return term->toString(); } +bool Expression::usesAnySymbols() const { return Helpers::containsAnySymbols (term); } +Expression::Type Expression::getType() const throw() { return term->getType(); } +const String Expression::getSymbolOrFunction() const { return term->getName(); } +int Expression::getNumInputs() const { return term->getNumInputs(); } +const Expression Expression::getInput (int index) const { return Expression (term->getInput (index)); } //============================================================================== -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&, const Term*, double, Term*) const -{ - jassertfalse; - return 0; -} - const ReferenceCountedObjectPtr Expression::Term::negated() { return new Helpers::Negate (this); } -void Expression::Term::getSymbolParts (String&, String&) const -{ - jassertfalse; // You should only call getSymbol() on an expression that's actually a symbol! -} - -const String Expression::Term::getFunctionName() const -{ - jassertfalse; // You shouldn't call this for an expression that's not actually a function! - return String::empty; -} - //============================================================================== Expression::ParseError::ParseError (const String& message) : description (message) @@ -996,28 +1134,32 @@ Expression::ParseError::ParseError (const String& message) DBG ("Expression::ParseError: " + message); } -Expression::EvaluationError::EvaluationError (const String& message) - : description (message) +//============================================================================== +Expression::Symbol::Symbol (const String& scopeUID_, const String& symbolName_) + : scopeUID (scopeUID_), symbolName (symbolName_) { - DBG ("Expression::EvaluationError: " + description); } -Expression::EvaluationError::EvaluationError (const String& symbol, const String& member) - : description ("Unknown symbol: \"" + symbol + (member.isEmpty() ? "\"" : ("." + member + "\""))) +bool Expression::Symbol::operator== (const Symbol& other) const throw() { - DBG ("Expression::EvaluationError: " + description); + return symbolName == other.symbolName && scopeUID == other.scopeUID; +} + +bool Expression::Symbol::operator!= (const Symbol& other) const throw() +{ + return ! operator== (other); } //============================================================================== -Expression::EvaluationContext::EvaluationContext() {} -Expression::EvaluationContext::~EvaluationContext() {} +Expression::Scope::Scope() {} +Expression::Scope::~Scope() {} -const Expression Expression::EvaluationContext::getSymbolValue (const String& symbol, const String& member) const +const Expression Expression::Scope::getSymbolValue (const String& symbol) const { - throw EvaluationError (symbol, member); + throw Helpers::EvaluationError ("Unknown symbol: " + symbol); } -double Expression::EvaluationContext::evaluateFunction (const String& functionName, const double* parameters, int numParams) const +double Expression::Scope::evaluateFunction (const String& functionName, const double* parameters, int numParams) const { if (numParams > 0) { @@ -1046,7 +1188,16 @@ double Expression::EvaluationContext::evaluateFunction (const String& functionNa } } - throw EvaluationError ("Unknown function: \"" + functionName + "\""); + throw Helpers::EvaluationError ("Unknown function: \"" + functionName + "\""); +} + +void Expression::Scope::visitRelativeScope (const String&, Visitor&) const +{ +} + +const String Expression::Scope::getScopeUID() const +{ + return String::empty; } diff --git a/src/maths/juce_Expression.h b/src/maths/juce_Expression.h index 9a4d205f9c..4eea8dc1a6 100644 --- a/src/maths/juce_Expression.h +++ b/src/maths/juce_Expression.h @@ -42,7 +42,7 @@ evaluated. Expressions which use identifiers and functions require a subclass of - Expression::EvaluationContext to be supplied when evaluating them, and this object + Expression::Scope 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. */ @@ -105,11 +105,14 @@ public: /** When evaluating an Expression object, this class is used to resolve symbols and perform functions that the expression uses. */ - class JUCE_API EvaluationContext + class JUCE_API Scope { public: - EvaluationContext(); - virtual ~EvaluationContext(); + Scope(); + virtual ~Scope(); + + /** Returns some kind of globally unique ID that identifies this scope. */ + virtual const String getScopeUID() const; /** Returns the value of a symbol. If the symbol is unknown, this can throw an Expression::EvaluationError exception. @@ -117,55 +120,97 @@ public: one, e.g. for "foo.bar", symbol = "foo" and member = "bar". @throws Expression::EvaluationError */ - virtual const Expression getSymbolValue (const String& symbol, const String& member) const; + 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; + virtual double evaluateFunction (const String& functionName, + const double* parameters, int numParameters) const; + + /** Used as a callback by the Scope::visitRelativeScope() method. + You should never create an instance of this class yourself, it's used by the + expression evaluation code. + */ + class Visitor + { + public: + virtual ~Visitor() {} + virtual void visit (const Scope&) = 0; + }; + + /** Creates a Scope object for a named scope, and then calls a visitor + to do some kind of processing with this new scope. + + If the name is valid, this method must create a suitable (temporary) Scope + object to represent it, and must call the Visitor::visit() method with this + new scope. + */ + virtual void visitRelativeScope (const String& scopeName, Visitor& visitor) 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, + /** Evaluates this expression, without using a Scope. + Without a Scope, no symbols can be used, and only basic functions such as sin, cos, tan, min, max are available. - @throws Expression::EvaluationError + To find out about any errors during evaluation, use the other version of this method which + takes a String parameter. */ double evaluate() const; - /** Evaluates this expression, providing a context that should be able to evaluate any symbols + /** Evaluates this expression, providing a scope that should be able to evaluate any symbols or functions that it uses. - @throws Expression::EvaluationError + To find out about any errors during evaluation, use the other version of this method which + takes a String parameter. */ - double evaluate (const EvaluationContext& context) const; + double evaluate (const Scope& scope) const; + + /** Evaluates this expression, providing a scope that should be able to evaluate any symbols + or functions that it uses. + */ + double evaluate (const Scope& scope, String& evaluationError) 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. + case they might just be adjusted by adding a constant to the original expression. @throws Expression::EvaluationError */ - const Expression adjustedToGiveNewResult (double targetValue, const EvaluationContext& context) const; + const Expression adjustedToGiveNewResult (double targetValue, const Scope& scope) const; + + /** Represents a symbol that is used in an Expression. */ + struct Symbol + { + Symbol (const String& scopeUID, const String& symbolName); + bool operator== (const Symbol&) const throw(); + bool operator!= (const Symbol&) const throw(); + + String scopeUID; /**< The unique ID of the Scope that contains this symbol. */ + String symbolName; /**< The name of the symbol. */ + }; /** 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; + const Expression withRenamedSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope) 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 + If a suitable scope 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. If the context parameter is null, this just checks + symbol at any level in its evaluation. If the scope parameter is null, this just checks whether the expression contains any direct references to the symbol. @throws Expression::EvaluationError */ - bool referencesSymbol (const String& symbol, const EvaluationContext* context) const; + bool referencesSymbol (const Symbol& symbol, const Scope& scope) const; /** Returns true if this expression contains any symbols. */ bool usesAnySymbols() const; + /** Returns a list of all symbols that may be needed to resolve this expression in the given scope. */ + void findReferencedSymbols (Array& results, const Scope& scope) const; + //============================================================================== /** An exception that can be thrown by Expression::parse(). */ class ParseError : public std::exception @@ -176,17 +221,6 @@ public: String description; }; - //============================================================================== - /** An exception that can be thrown by Expression::evaluate(). */ - class EvaluationError : public std::exception - { - public: - EvaluationError (const String& message); - EvaluationError (const String& symbolName, const String& memberName); - - String description; - }; - //============================================================================== /** Expression type. @see Expression::getType() @@ -202,19 +236,8 @@ public: /** Returns the type of this expression. */ Type getType() const throw(); - /** If this expression is a symbol, this returns its full name. */ - const String getSymbol() const; - - /** For a symbol that contains a dot, this returns the two */ - void getSymbolParts (String& objectName, String& memberName) const; - - /** If this expression is a function, this returns its name. */ - const String getFunction() const; - - /** If this expression is an operator, this returns its name. - E.g. "+", "-", "*", "/", etc. - */ - const String getOperator() const; + /** If this expression is a symbol, function or operator, this returns its identifier. */ + const String getSymbolOrFunction() const; /** Returns the number of inputs to this expression. @see getInput @@ -228,35 +251,12 @@ public: private: //============================================================================== + class Term; class Helpers; + friend class Term; 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&, const Term* inputTerm, - double overallTarget, Term* topLevelTerm) const; - virtual const ReferenceCountedObjectPtr negated(); - virtual Type getType() const throw() = 0; - virtual void getSymbolParts (String& objectName, String& memberName) const; - virtual const String getFunctionName() const; - - private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Term); - }; - friend class ScopedPointer; + friend class ReferenceCountedObjectPtr; ReferenceCountedObjectPtr term; explicit Expression (Term* term);