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.

346 lines
13KB

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