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.

322 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. DrawableComposite::DrawableComposite()
  20. : bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f)),
  21. updateBoundsReentrant (false)
  22. {
  23. setContentArea (RelativeRectangle (Rectangle<float> (0.0f, 0.0f, 100.0f, 100.0f)));
  24. }
  25. DrawableComposite::DrawableComposite (const DrawableComposite& other)
  26. : Drawable (other),
  27. bounds (other.bounds),
  28. markersX (other.markersX),
  29. markersY (other.markersY),
  30. updateBoundsReentrant (false)
  31. {
  32. for (int i = 0; i < other.getNumChildComponents(); ++i)
  33. if (const Drawable* const d = dynamic_cast<const Drawable*> (other.getChildComponent(i)))
  34. addAndMakeVisible (d->createCopy());
  35. }
  36. DrawableComposite::~DrawableComposite()
  37. {
  38. deleteAllChildren();
  39. }
  40. Drawable* DrawableComposite::createCopy() const
  41. {
  42. return new DrawableComposite (*this);
  43. }
  44. //==============================================================================
  45. Rectangle<float> DrawableComposite::getDrawableBounds() const
  46. {
  47. Rectangle<float> r;
  48. for (int i = getNumChildComponents(); --i >= 0;)
  49. if (const Drawable* const d = dynamic_cast<const Drawable*> (getChildComponent(i)))
  50. r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformedBy (d->getTransform())
  51. : d->getDrawableBounds());
  52. return r;
  53. }
  54. MarkerList* DrawableComposite::getMarkers (bool xAxis)
  55. {
  56. return xAxis ? &markersX : &markersY;
  57. }
  58. RelativeRectangle DrawableComposite::getContentArea() const
  59. {
  60. jassert (markersX.getNumMarkers() >= 2 && markersX.getMarker (0)->name == contentLeftMarkerName && markersX.getMarker (1)->name == contentRightMarkerName);
  61. jassert (markersY.getNumMarkers() >= 2 && markersY.getMarker (0)->name == contentTopMarkerName && markersY.getMarker (1)->name == contentBottomMarkerName);
  62. return RelativeRectangle (markersX.getMarker(0)->position, markersX.getMarker(1)->position,
  63. markersY.getMarker(0)->position, markersY.getMarker(1)->position);
  64. }
  65. void DrawableComposite::setContentArea (const RelativeRectangle& newArea)
  66. {
  67. markersX.setMarker (contentLeftMarkerName, newArea.left);
  68. markersX.setMarker (contentRightMarkerName, newArea.right);
  69. markersY.setMarker (contentTopMarkerName, newArea.top);
  70. markersY.setMarker (contentBottomMarkerName, newArea.bottom);
  71. }
  72. void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBounds)
  73. {
  74. if (bounds != newBounds)
  75. {
  76. bounds = newBounds;
  77. if (bounds.isDynamic())
  78. {
  79. Drawable::Positioner<DrawableComposite>* const p = new Drawable::Positioner<DrawableComposite> (*this);
  80. setPositioner (p);
  81. p->apply();
  82. }
  83. else
  84. {
  85. setPositioner (nullptr);
  86. recalculateCoordinates (nullptr);
  87. }
  88. }
  89. }
  90. void DrawableComposite::resetBoundingBoxToContentArea()
  91. {
  92. const RelativeRectangle content (getContentArea());
  93. setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
  94. RelativePoint (content.right, content.top),
  95. RelativePoint (content.left, content.bottom)));
  96. }
  97. void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
  98. {
  99. setContentArea (RelativeRectangle (getDrawableBounds()));
  100. resetBoundingBoxToContentArea();
  101. }
  102. bool DrawableComposite::registerCoordinates (RelativeCoordinatePositionerBase& pos)
  103. {
  104. bool ok = pos.addPoint (bounds.topLeft);
  105. ok = pos.addPoint (bounds.topRight) && ok;
  106. return pos.addPoint (bounds.bottomLeft) && ok;
  107. }
  108. void DrawableComposite::recalculateCoordinates (Expression::Scope* scope)
  109. {
  110. Point<float> resolved[3];
  111. bounds.resolveThreePoints (resolved, scope);
  112. const Rectangle<float> content (getContentArea().resolve (scope));
  113. AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].x, resolved[0].y,
  114. content.getRight(), content.getY(), resolved[1].x, resolved[1].y,
  115. content.getX(), content.getBottom(), resolved[2].x, resolved[2].y));
  116. if (t.isSingularity())
  117. t = AffineTransform();
  118. setTransform (t);
  119. }
  120. void DrawableComposite::parentHierarchyChanged()
  121. {
  122. DrawableComposite* parent = getParent();
  123. if (parent != nullptr)
  124. originRelativeToComponent = parent->originRelativeToComponent - getPosition();
  125. }
  126. void DrawableComposite::childBoundsChanged (Component*)
  127. {
  128. updateBoundsToFitChildren();
  129. }
  130. void DrawableComposite::childrenChanged()
  131. {
  132. updateBoundsToFitChildren();
  133. }
  134. void DrawableComposite::updateBoundsToFitChildren()
  135. {
  136. if (! updateBoundsReentrant)
  137. {
  138. const ScopedValueSetter<bool> setter (updateBoundsReentrant, true, false);
  139. Rectangle<int> childArea;
  140. for (int i = getNumChildComponents(); --i >= 0;)
  141. childArea = childArea.getUnion (getChildComponent(i)->getBoundsInParent());
  142. const Point<int> delta (childArea.getPosition());
  143. childArea += getPosition();
  144. if (childArea != getBounds())
  145. {
  146. if (! delta.isOrigin())
  147. {
  148. originRelativeToComponent -= delta;
  149. for (int i = getNumChildComponents(); --i >= 0;)
  150. if (Component* const c = getChildComponent(i))
  151. c->setBounds (c->getBounds() - delta);
  152. }
  153. setBounds (childArea);
  154. }
  155. }
  156. }
  157. //==============================================================================
  158. const char* const DrawableComposite::contentLeftMarkerName = "left";
  159. const char* const DrawableComposite::contentRightMarkerName = "right";
  160. const char* const DrawableComposite::contentTopMarkerName = "top";
  161. const char* const DrawableComposite::contentBottomMarkerName = "bottom";
  162. //==============================================================================
  163. const Identifier DrawableComposite::valueTreeType ("Group");
  164. const Identifier DrawableComposite::ValueTreeWrapper::topLeft ("topLeft");
  165. const Identifier DrawableComposite::ValueTreeWrapper::topRight ("topRight");
  166. const Identifier DrawableComposite::ValueTreeWrapper::bottomLeft ("bottomLeft");
  167. const Identifier DrawableComposite::ValueTreeWrapper::childGroupTag ("Drawables");
  168. const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagX ("MarkersX");
  169. const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagY ("MarkersY");
  170. //==============================================================================
  171. DrawableComposite::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
  172. : ValueTreeWrapperBase (state_)
  173. {
  174. jassert (state.hasType (valueTreeType));
  175. }
  176. ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const
  177. {
  178. return state.getChildWithName (childGroupTag);
  179. }
  180. ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager)
  181. {
  182. return state.getOrCreateChildWithName (childGroupTag, undoManager);
  183. }
  184. RelativeParallelogram DrawableComposite::ValueTreeWrapper::getBoundingBox() const
  185. {
  186. return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
  187. state.getProperty (topRight, "100, 0"),
  188. state.getProperty (bottomLeft, "0, 100"));
  189. }
  190. void DrawableComposite::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
  191. {
  192. state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
  193. state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
  194. state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
  195. }
  196. void DrawableComposite::ValueTreeWrapper::resetBoundingBoxToContentArea (UndoManager* undoManager)
  197. {
  198. const RelativeRectangle content (getContentArea());
  199. setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
  200. RelativePoint (content.right, content.top),
  201. RelativePoint (content.left, content.bottom)), undoManager);
  202. }
  203. RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const
  204. {
  205. MarkerList::ValueTreeWrapper marksX (getMarkerList (true));
  206. MarkerList::ValueTreeWrapper marksY (getMarkerList (false));
  207. return RelativeRectangle (marksX.getMarker (marksX.getMarkerState (0)).position,
  208. marksX.getMarker (marksX.getMarkerState (1)).position,
  209. marksY.getMarker (marksY.getMarkerState (0)).position,
  210. marksY.getMarker (marksY.getMarkerState (1)).position);
  211. }
  212. void DrawableComposite::ValueTreeWrapper::setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager)
  213. {
  214. MarkerList::ValueTreeWrapper marksX (getMarkerListCreating (true, nullptr));
  215. MarkerList::ValueTreeWrapper marksY (getMarkerListCreating (false, nullptr));
  216. marksX.setMarker (MarkerList::Marker (contentLeftMarkerName, newArea.left), undoManager);
  217. marksX.setMarker (MarkerList::Marker (contentRightMarkerName, newArea.right), undoManager);
  218. marksY.setMarker (MarkerList::Marker (contentTopMarkerName, newArea.top), undoManager);
  219. marksY.setMarker (MarkerList::Marker (contentBottomMarkerName, newArea.bottom), undoManager);
  220. }
  221. MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const
  222. {
  223. return state.getChildWithName (xAxis ? markerGroupTagX : markerGroupTagY);
  224. }
  225. MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager)
  226. {
  227. return state.getOrCreateChildWithName (xAxis ? markerGroupTagX : markerGroupTagY, undoManager);
  228. }
  229. //==============================================================================
  230. void DrawableComposite::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
  231. {
  232. const ValueTreeWrapper wrapper (tree);
  233. setComponentID (wrapper.getID());
  234. wrapper.getMarkerList (true).applyTo (markersX);
  235. wrapper.getMarkerList (false).applyTo (markersY);
  236. setBoundingBox (wrapper.getBoundingBox());
  237. builder.updateChildComponents (*this, wrapper.getChildList());
  238. }
  239. ValueTree DrawableComposite::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
  240. {
  241. ValueTree tree (valueTreeType);
  242. ValueTreeWrapper v (tree);
  243. v.setID (getComponentID());
  244. v.setBoundingBox (bounds, nullptr);
  245. ValueTree childList (v.getChildListCreating (nullptr));
  246. for (int i = 0; i < getNumChildComponents(); ++i)
  247. {
  248. const Drawable* const d = dynamic_cast<const Drawable*> (getChildComponent(i));
  249. jassert (d != nullptr); // You can't save a mix of Drawables and normal components!
  250. childList.addChild (d->createValueTree (imageProvider), -1, nullptr);
  251. }
  252. v.getMarkerListCreating (true, nullptr).readFrom (markersX, nullptr);
  253. v.getMarkerListCreating (false, nullptr).readFrom (markersY, nullptr);
  254. return tree;
  255. }