The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
9.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. namespace RelativeRectangleHelpers
  21. {
  22. inline void skipComma (String::CharPointerType& s)
  23. {
  24. s = s.findEndOfWhitespace();
  25. if (*s == ',')
  26. ++s;
  27. }
  28. bool dependsOnSymbolsOtherThanThis (const Expression& e)
  29. {
  30. if (e.getType() == Expression::operatorType && e.getSymbolOrFunction() == ".")
  31. return true;
  32. if (e.getType() == Expression::symbolType)
  33. {
  34. switch (RelativeCoordinate::StandardStrings::getTypeOf (e.getSymbolOrFunction()))
  35. {
  36. case RelativeCoordinate::StandardStrings::x:
  37. case RelativeCoordinate::StandardStrings::y:
  38. case RelativeCoordinate::StandardStrings::left:
  39. case RelativeCoordinate::StandardStrings::right:
  40. case RelativeCoordinate::StandardStrings::top:
  41. case RelativeCoordinate::StandardStrings::bottom: return false;
  42. default: break;
  43. }
  44. return true;
  45. }
  46. else
  47. {
  48. for (int i = e.getNumInputs(); --i >= 0;)
  49. if (dependsOnSymbolsOtherThanThis (e.getInput(i)))
  50. return true;
  51. }
  52. return false;
  53. }
  54. }
  55. //==============================================================================
  56. RelativeRectangle::RelativeRectangle()
  57. {
  58. }
  59. RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const RelativeCoordinate& right_,
  60. const RelativeCoordinate& top_, const RelativeCoordinate& bottom_)
  61. : left (left_), right (right_), top (top_), bottom (bottom_)
  62. {
  63. }
  64. RelativeRectangle::RelativeRectangle (const Rectangle<float>& rect)
  65. : left (rect.getX()),
  66. right (Expression::symbol (RelativeCoordinate::Strings::left) + Expression ((double) rect.getWidth())),
  67. top (rect.getY()),
  68. bottom (Expression::symbol (RelativeCoordinate::Strings::top) + Expression ((double) rect.getHeight()))
  69. {
  70. }
  71. RelativeRectangle::RelativeRectangle (const String& s)
  72. {
  73. String::CharPointerType text (s.getCharPointer());
  74. left = RelativeCoordinate (Expression::parse (text));
  75. RelativeRectangleHelpers::skipComma (text);
  76. top = RelativeCoordinate (Expression::parse (text));
  77. RelativeRectangleHelpers::skipComma (text);
  78. right = RelativeCoordinate (Expression::parse (text));
  79. RelativeRectangleHelpers::skipComma (text);
  80. bottom = RelativeCoordinate (Expression::parse (text));
  81. }
  82. bool RelativeRectangle::operator== (const RelativeRectangle& other) const noexcept
  83. {
  84. return left == other.left && top == other.top && right == other.right && bottom == other.bottom;
  85. }
  86. bool RelativeRectangle::operator!= (const RelativeRectangle& other) const noexcept
  87. {
  88. return ! operator== (other);
  89. }
  90. //==============================================================================
  91. // An expression context that can evaluate expressions using "this"
  92. class RelativeRectangleLocalScope : public Expression::Scope
  93. {
  94. public:
  95. RelativeRectangleLocalScope (const RelativeRectangle& rect_) : rect (rect_) {}
  96. Expression getSymbolValue (const String& symbol) const
  97. {
  98. switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol))
  99. {
  100. case RelativeCoordinate::StandardStrings::x:
  101. case RelativeCoordinate::StandardStrings::left: return rect.left.getExpression();
  102. case RelativeCoordinate::StandardStrings::y:
  103. case RelativeCoordinate::StandardStrings::top: return rect.top.getExpression();
  104. case RelativeCoordinate::StandardStrings::right: return rect.right.getExpression();
  105. case RelativeCoordinate::StandardStrings::bottom: return rect.bottom.getExpression();
  106. default: break;
  107. }
  108. return Expression::Scope::getSymbolValue (symbol);
  109. }
  110. private:
  111. const RelativeRectangle& rect;
  112. JUCE_DECLARE_NON_COPYABLE (RelativeRectangleLocalScope);
  113. };
  114. const Rectangle<float> RelativeRectangle::resolve (const Expression::Scope* scope) const
  115. {
  116. if (scope == nullptr)
  117. {
  118. RelativeRectangleLocalScope defaultScope (*this);
  119. return resolve (&defaultScope);
  120. }
  121. else
  122. {
  123. const double l = left.resolve (scope);
  124. const double r = right.resolve (scope);
  125. const double t = top.resolve (scope);
  126. const double b = bottom.resolve (scope);
  127. return Rectangle<float> ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t));
  128. }
  129. }
  130. void RelativeRectangle::moveToAbsolute (const Rectangle<float>& newPos, const Expression::Scope* scope)
  131. {
  132. left.moveToAbsolute (newPos.getX(), scope);
  133. right.moveToAbsolute (newPos.getRight(), scope);
  134. top.moveToAbsolute (newPos.getY(), scope);
  135. bottom.moveToAbsolute (newPos.getBottom(), scope);
  136. }
  137. bool RelativeRectangle::isDynamic() const
  138. {
  139. using namespace RelativeRectangleHelpers;
  140. return dependsOnSymbolsOtherThanThis (left.getExpression())
  141. || dependsOnSymbolsOtherThanThis (right.getExpression())
  142. || dependsOnSymbolsOtherThanThis (top.getExpression())
  143. || dependsOnSymbolsOtherThanThis (bottom.getExpression());
  144. }
  145. String RelativeRectangle::toString() const
  146. {
  147. return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
  148. }
  149. void RelativeRectangle::renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope)
  150. {
  151. left = left.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  152. right = right.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  153. top = top.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  154. bottom = bottom.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  155. }
  156. //==============================================================================
  157. class RelativeRectangleComponentPositioner : public RelativeCoordinatePositionerBase
  158. {
  159. public:
  160. RelativeRectangleComponentPositioner (Component& component_, const RelativeRectangle& rectangle_)
  161. : RelativeCoordinatePositionerBase (component_),
  162. rectangle (rectangle_)
  163. {
  164. }
  165. bool registerCoordinates()
  166. {
  167. bool ok = addCoordinate (rectangle.left);
  168. ok = addCoordinate (rectangle.right) && ok;
  169. ok = addCoordinate (rectangle.top) && ok;
  170. ok = addCoordinate (rectangle.bottom) && ok;
  171. return ok;
  172. }
  173. bool isUsingRectangle (const RelativeRectangle& other) const noexcept
  174. {
  175. return rectangle == other;
  176. }
  177. void applyToComponentBounds()
  178. {
  179. for (int i = 4; --i >= 0;)
  180. {
  181. ComponentScope scope (getComponent());
  182. const Rectangle<int> newBounds (rectangle.resolve (&scope).getSmallestIntegerContainer());
  183. if (newBounds == getComponent().getBounds())
  184. return;
  185. getComponent().setBounds (newBounds);
  186. }
  187. jassertfalse; // must be a recursive reference!
  188. }
  189. void applyNewBounds (const Rectangle<int>& newBounds)
  190. {
  191. if (newBounds != getComponent().getBounds())
  192. {
  193. ComponentScope scope (getComponent());
  194. rectangle.moveToAbsolute (newBounds.toFloat(), &scope);
  195. applyToComponentBounds();
  196. }
  197. }
  198. private:
  199. RelativeRectangle rectangle;
  200. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner);
  201. };
  202. void RelativeRectangle::applyToComponent (Component& component) const
  203. {
  204. if (isDynamic())
  205. {
  206. RelativeRectangleComponentPositioner* current = dynamic_cast <RelativeRectangleComponentPositioner*> (component.getPositioner());
  207. if (current == nullptr || ! current->isUsingRectangle (*this))
  208. {
  209. RelativeRectangleComponentPositioner* p = new RelativeRectangleComponentPositioner (component, *this);
  210. component.setPositioner (p);
  211. p->apply();
  212. }
  213. }
  214. else
  215. {
  216. component.setPositioner (nullptr);
  217. component.setBounds (resolve (nullptr).getSmallestIntegerContainer());
  218. }
  219. }
  220. END_JUCE_NAMESPACE