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.

254 lines
7.8KB

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