Audio plugin host https://kx.studio/carla
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.

juce_DrawableComposite.cpp 13KB

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