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.

256 lines
7.8KB

  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. Drawable::Drawable()
  20. {
  21. setInterceptsMouseClicks (false, false);
  22. setPaintingIsUnclipped (true);
  23. }
  24. Drawable::Drawable (const Drawable& other)
  25. : Component (other.getName())
  26. {
  27. setInterceptsMouseClicks (false, false);
  28. setPaintingIsUnclipped (true);
  29. setComponentID (other.getComponentID());
  30. setTransform (other.getTransform());
  31. }
  32. Drawable::~Drawable()
  33. {
  34. }
  35. //==============================================================================
  36. void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const
  37. {
  38. const_cast<Drawable*> (this)->nonConstDraw (g, opacity, transform);
  39. }
  40. void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform)
  41. {
  42. Graphics::ScopedSaveState ss (g);
  43. g.addTransform (AffineTransform::translation ((float) -(originRelativeToComponent.x),
  44. (float) -(originRelativeToComponent.y))
  45. .followedBy (getTransform())
  46. .followedBy (transform));
  47. if (! g.isClipEmpty())
  48. {
  49. if (opacity < 1.0f)
  50. {
  51. g.beginTransparencyLayer (opacity);
  52. paintEntireComponent (g, true);
  53. g.endTransparencyLayer();
  54. }
  55. else
  56. {
  57. paintEntireComponent (g, true);
  58. }
  59. }
  60. }
  61. void Drawable::drawAt (Graphics& g, float x, float y, float opacity) const
  62. {
  63. draw (g, opacity, AffineTransform::translation (x, y));
  64. }
  65. void Drawable::drawWithin (Graphics& g, Rectangle<float> destArea,
  66. RectanglePlacement placement, float opacity) const
  67. {
  68. draw (g, opacity, placement.getTransformToFit (getDrawableBounds(), destArea));
  69. }
  70. //==============================================================================
  71. DrawableComposite* Drawable::getParent() const
  72. {
  73. return dynamic_cast<DrawableComposite*> (getParentComponent());
  74. }
  75. void Drawable::transformContextToCorrectOrigin (Graphics& g)
  76. {
  77. g.setOrigin (originRelativeToComponent);
  78. }
  79. void Drawable::parentHierarchyChanged()
  80. {
  81. setBoundsToEnclose (getDrawableBounds());
  82. }
  83. void Drawable::setBoundsToEnclose (Rectangle<float> area)
  84. {
  85. Point<int> parentOrigin;
  86. if (auto* parent = getParent())
  87. parentOrigin = parent->originRelativeToComponent;
  88. auto newBounds = area.getSmallestIntegerContainer() + parentOrigin;
  89. originRelativeToComponent = parentOrigin - newBounds.getPosition();
  90. setBounds (newBounds);
  91. }
  92. //==============================================================================
  93. bool Drawable::replaceColour (Colour original, Colour replacement)
  94. {
  95. bool changed = false;
  96. for (int i = getNumChildComponents(); --i >= 0;)
  97. if (auto* d = dynamic_cast<Drawable*> (getChildComponent(i)))
  98. changed = d->replaceColour (original, replacement) || changed;
  99. return changed;
  100. }
  101. //==============================================================================
  102. void Drawable::setOriginWithOriginalSize (Point<float> originWithinParent)
  103. {
  104. setTransform (AffineTransform::translation (originWithinParent.x, originWithinParent.y));
  105. }
  106. void Drawable::setTransformToFit (const Rectangle<float>& area, RectanglePlacement placement)
  107. {
  108. if (! area.isEmpty())
  109. setTransform (placement.getTransformToFit (getDrawableBounds(), area));
  110. }
  111. //==============================================================================
  112. Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes)
  113. {
  114. Drawable* result = nullptr;
  115. auto image = ImageFileFormat::loadFrom (data, numBytes);
  116. if (image.isValid())
  117. {
  118. auto* di = new DrawableImage();
  119. di->setImage (image);
  120. result = di;
  121. }
  122. else
  123. {
  124. auto asString = String::createStringFromData (data, (int) numBytes);
  125. XmlDocument doc (asString);
  126. ScopedPointer<XmlElement> outer (doc.getDocumentElement (true));
  127. if (outer != nullptr && outer->hasTagName ("svg"))
  128. {
  129. ScopedPointer<XmlElement> svg (doc.getDocumentElement());
  130. if (svg != nullptr)
  131. result = Drawable::createFromSVG (*svg);
  132. }
  133. }
  134. return result;
  135. }
  136. Drawable* Drawable::createFromImageDataStream (InputStream& dataSource)
  137. {
  138. MemoryOutputStream mo;
  139. mo << dataSource;
  140. return createFromImageData (mo.getData(), mo.getDataSize());
  141. }
  142. Drawable* Drawable::createFromImageFile (const File& file)
  143. {
  144. FileInputStream fin (file);
  145. return fin.openedOk() ? createFromImageDataStream (fin) : nullptr;
  146. }
  147. //==============================================================================
  148. template <class DrawableClass>
  149. struct DrawableTypeHandler : public ComponentBuilder::TypeHandler
  150. {
  151. DrawableTypeHandler() : ComponentBuilder::TypeHandler (DrawableClass::valueTreeType)
  152. {
  153. }
  154. Component* addNewComponentFromState (const ValueTree& state, Component* parent)
  155. {
  156. auto* d = new DrawableClass();
  157. if (parent != nullptr)
  158. parent->addAndMakeVisible (d);
  159. updateComponentFromState (d, state);
  160. return d;
  161. }
  162. void updateComponentFromState (Component* component, const ValueTree& state)
  163. {
  164. if (auto* d = dynamic_cast<DrawableClass*> (component))
  165. d->refreshFromValueTree (state, *this->getBuilder());
  166. else
  167. jassertfalse;
  168. }
  169. };
  170. void Drawable::registerDrawableTypeHandlers (ComponentBuilder& builder)
  171. {
  172. builder.registerTypeHandler (new DrawableTypeHandler<DrawablePath>());
  173. builder.registerTypeHandler (new DrawableTypeHandler<DrawableComposite>());
  174. builder.registerTypeHandler (new DrawableTypeHandler<DrawableRectangle>());
  175. builder.registerTypeHandler (new DrawableTypeHandler<DrawableImage>());
  176. builder.registerTypeHandler (new DrawableTypeHandler<DrawableText>());
  177. }
  178. Drawable* Drawable::createFromValueTree (const ValueTree& tree, ComponentBuilder::ImageProvider* imageProvider)
  179. {
  180. ComponentBuilder builder (tree);
  181. builder.setImageProvider (imageProvider);
  182. registerDrawableTypeHandlers (builder);
  183. ScopedPointer<Component> comp (builder.createComponent());
  184. auto* d = dynamic_cast<Drawable*> (static_cast<Component*> (comp));
  185. if (d != nullptr)
  186. comp.release();
  187. return d;
  188. }
  189. //==============================================================================
  190. Drawable::ValueTreeWrapperBase::ValueTreeWrapperBase (const ValueTree& s) : state (s)
  191. {
  192. }
  193. String Drawable::ValueTreeWrapperBase::getID() const
  194. {
  195. return state [ComponentBuilder::idProperty];
  196. }
  197. void Drawable::ValueTreeWrapperBase::setID (const String& newID)
  198. {
  199. if (newID.isEmpty())
  200. state.removeProperty (ComponentBuilder::idProperty, nullptr);
  201. else
  202. state.setProperty (ComponentBuilder::idProperty, newID, nullptr);
  203. }