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.

340 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software 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. {
  35. const Drawable* const d = dynamic_cast <const Drawable*> (other.getChildComponent(i));
  36. if (d != nullptr)
  37. addAndMakeVisible (d->createCopy());
  38. }
  39. }
  40. DrawableComposite::~DrawableComposite()
  41. {
  42. deleteAllChildren();
  43. }
  44. Drawable* DrawableComposite::createCopy() const
  45. {
  46. return new DrawableComposite (*this);
  47. }
  48. //==============================================================================
  49. Rectangle<float> DrawableComposite::getDrawableBounds() const
  50. {
  51. Rectangle<float> r;
  52. for (int i = getNumChildComponents(); --i >= 0;)
  53. {
  54. const Drawable* const d = dynamic_cast <const Drawable*> (getChildComponent(i));
  55. if (d != nullptr)
  56. r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformed (d->getTransform())
  57. : d->getDrawableBounds());
  58. }
  59. return r;
  60. }
  61. MarkerList* DrawableComposite::getMarkers (bool xAxis)
  62. {
  63. return xAxis ? &markersX : &markersY;
  64. }
  65. RelativeRectangle DrawableComposite::getContentArea() const
  66. {
  67. jassert (markersX.getNumMarkers() >= 2 && markersX.getMarker (0)->name == contentLeftMarkerName && markersX.getMarker (1)->name == contentRightMarkerName);
  68. jassert (markersY.getNumMarkers() >= 2 && markersY.getMarker (0)->name == contentTopMarkerName && markersY.getMarker (1)->name == contentBottomMarkerName);
  69. return RelativeRectangle (markersX.getMarker(0)->position, markersX.getMarker(1)->position,
  70. markersY.getMarker(0)->position, markersY.getMarker(1)->position);
  71. }
  72. void DrawableComposite::setContentArea (const RelativeRectangle& newArea)
  73. {
  74. markersX.setMarker (contentLeftMarkerName, newArea.left);
  75. markersX.setMarker (contentRightMarkerName, newArea.right);
  76. markersY.setMarker (contentTopMarkerName, newArea.top);
  77. markersY.setMarker (contentBottomMarkerName, newArea.bottom);
  78. }
  79. void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBounds)
  80. {
  81. if (bounds != newBounds)
  82. {
  83. bounds = newBounds;
  84. if (bounds.isDynamic())
  85. {
  86. Drawable::Positioner<DrawableComposite>* const p = new Drawable::Positioner<DrawableComposite> (*this);
  87. setPositioner (p);
  88. p->apply();
  89. }
  90. else
  91. {
  92. setPositioner (nullptr);
  93. recalculateCoordinates (nullptr);
  94. }
  95. }
  96. }
  97. void DrawableComposite::resetBoundingBoxToContentArea()
  98. {
  99. const RelativeRectangle content (getContentArea());
  100. setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
  101. RelativePoint (content.right, content.top),
  102. RelativePoint (content.left, content.bottom)));
  103. }
  104. void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
  105. {
  106. const Rectangle<float> activeArea (getDrawableBounds());
  107. setContentArea (RelativeRectangle (RelativeCoordinate (activeArea.getX()),
  108. RelativeCoordinate (activeArea.getRight()),
  109. RelativeCoordinate (activeArea.getY()),
  110. RelativeCoordinate (activeArea.getBottom())));
  111. resetBoundingBoxToContentArea();
  112. }
  113. bool DrawableComposite::registerCoordinates (RelativeCoordinatePositionerBase& pos)
  114. {
  115. bool ok = pos.addPoint (bounds.topLeft);
  116. ok = pos.addPoint (bounds.topRight) && ok;
  117. return pos.addPoint (bounds.bottomLeft) && ok;
  118. }
  119. void DrawableComposite::recalculateCoordinates (Expression::Scope* scope)
  120. {
  121. Point<float> resolved[3];
  122. bounds.resolveThreePoints (resolved, scope);
  123. const Rectangle<float> content (getContentArea().resolve (scope));
  124. AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].x, resolved[0].y,
  125. content.getRight(), content.getY(), resolved[1].x, resolved[1].y,
  126. content.getX(), content.getBottom(), resolved[2].x, resolved[2].y));
  127. if (t.isSingularity())
  128. t = AffineTransform::identity;
  129. setTransform (t);
  130. }
  131. void DrawableComposite::parentHierarchyChanged()
  132. {
  133. DrawableComposite* parent = getParent();
  134. if (parent != nullptr)
  135. originRelativeToComponent = parent->originRelativeToComponent - getPosition();
  136. }
  137. void DrawableComposite::childBoundsChanged (Component*)
  138. {
  139. updateBoundsToFitChildren();
  140. }
  141. void DrawableComposite::childrenChanged()
  142. {
  143. updateBoundsToFitChildren();
  144. }
  145. void DrawableComposite::updateBoundsToFitChildren()
  146. {
  147. if (! updateBoundsReentrant)
  148. {
  149. const ScopedValueSetter<bool> setter (updateBoundsReentrant, true, false);
  150. Rectangle<int> childArea;
  151. for (int i = getNumChildComponents(); --i >= 0;)
  152. childArea = childArea.getUnion (getChildComponent(i)->getBoundsInParent());
  153. const Point<int> delta (childArea.getPosition());
  154. childArea += getPosition();
  155. if (childArea != getBounds())
  156. {
  157. if (! delta.isOrigin())
  158. {
  159. originRelativeToComponent -= delta;
  160. for (int i = getNumChildComponents(); --i >= 0;)
  161. {
  162. Component* const c = getChildComponent(i);
  163. if (c != nullptr)
  164. c->setBounds (c->getBounds() - delta);
  165. }
  166. }
  167. setBounds (childArea);
  168. }
  169. }
  170. }
  171. //==============================================================================
  172. const char* const DrawableComposite::contentLeftMarkerName = "left";
  173. const char* const DrawableComposite::contentRightMarkerName = "right";
  174. const char* const DrawableComposite::contentTopMarkerName = "top";
  175. const char* const DrawableComposite::contentBottomMarkerName = "bottom";
  176. //==============================================================================
  177. const Identifier DrawableComposite::valueTreeType ("Group");
  178. const Identifier DrawableComposite::ValueTreeWrapper::topLeft ("topLeft");
  179. const Identifier DrawableComposite::ValueTreeWrapper::topRight ("topRight");
  180. const Identifier DrawableComposite::ValueTreeWrapper::bottomLeft ("bottomLeft");
  181. const Identifier DrawableComposite::ValueTreeWrapper::childGroupTag ("Drawables");
  182. const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagX ("MarkersX");
  183. const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagY ("MarkersY");
  184. //==============================================================================
  185. DrawableComposite::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
  186. : ValueTreeWrapperBase (state_)
  187. {
  188. jassert (state.hasType (valueTreeType));
  189. }
  190. ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const
  191. {
  192. return state.getChildWithName (childGroupTag);
  193. }
  194. ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager)
  195. {
  196. return state.getOrCreateChildWithName (childGroupTag, undoManager);
  197. }
  198. RelativeParallelogram DrawableComposite::ValueTreeWrapper::getBoundingBox() const
  199. {
  200. return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
  201. state.getProperty (topRight, "100, 0"),
  202. state.getProperty (bottomLeft, "0, 100"));
  203. }
  204. void DrawableComposite::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
  205. {
  206. state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
  207. state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
  208. state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
  209. }
  210. void DrawableComposite::ValueTreeWrapper::resetBoundingBoxToContentArea (UndoManager* undoManager)
  211. {
  212. const RelativeRectangle content (getContentArea());
  213. setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
  214. RelativePoint (content.right, content.top),
  215. RelativePoint (content.left, content.bottom)), undoManager);
  216. }
  217. RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const
  218. {
  219. MarkerList::ValueTreeWrapper marksX (getMarkerList (true));
  220. MarkerList::ValueTreeWrapper marksY (getMarkerList (false));
  221. return RelativeRectangle (marksX.getMarker (marksX.getMarkerState (0)).position,
  222. marksX.getMarker (marksX.getMarkerState (1)).position,
  223. marksY.getMarker (marksY.getMarkerState (0)).position,
  224. marksY.getMarker (marksY.getMarkerState (1)).position);
  225. }
  226. void DrawableComposite::ValueTreeWrapper::setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager)
  227. {
  228. MarkerList::ValueTreeWrapper marksX (getMarkerListCreating (true, nullptr));
  229. MarkerList::ValueTreeWrapper marksY (getMarkerListCreating (false, nullptr));
  230. marksX.setMarker (MarkerList::Marker (contentLeftMarkerName, newArea.left), undoManager);
  231. marksX.setMarker (MarkerList::Marker (contentRightMarkerName, newArea.right), undoManager);
  232. marksY.setMarker (MarkerList::Marker (contentTopMarkerName, newArea.top), undoManager);
  233. marksY.setMarker (MarkerList::Marker (contentBottomMarkerName, newArea.bottom), undoManager);
  234. }
  235. MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const
  236. {
  237. return state.getChildWithName (xAxis ? markerGroupTagX : markerGroupTagY);
  238. }
  239. MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager)
  240. {
  241. return state.getOrCreateChildWithName (xAxis ? markerGroupTagX : markerGroupTagY, undoManager);
  242. }
  243. //==============================================================================
  244. void DrawableComposite::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
  245. {
  246. const ValueTreeWrapper wrapper (tree);
  247. setComponentID (wrapper.getID());
  248. wrapper.getMarkerList (true).applyTo (markersX);
  249. wrapper.getMarkerList (false).applyTo (markersY);
  250. setBoundingBox (wrapper.getBoundingBox());
  251. builder.updateChildComponents (*this, wrapper.getChildList());
  252. }
  253. ValueTree DrawableComposite::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
  254. {
  255. ValueTree tree (valueTreeType);
  256. ValueTreeWrapper v (tree);
  257. v.setID (getComponentID());
  258. v.setBoundingBox (bounds, nullptr);
  259. ValueTree childList (v.getChildListCreating (nullptr));
  260. for (int i = 0; i < getNumChildComponents(); ++i)
  261. {
  262. const Drawable* const d = dynamic_cast <const Drawable*> (getChildComponent(i));
  263. jassert (d != nullptr); // You can't save a mix of Drawables and normal components!
  264. childList.addChild (d->createValueTree (imageProvider), -1, nullptr);
  265. }
  266. v.getMarkerListCreating (true, nullptr).readFrom (markersX, nullptr);
  267. v.getMarkerListCreating (false, nullptr).readFrom (markersY, nullptr);
  268. return tree;
  269. }