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.

460 lines
17KB

  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. DrawableShape::DrawableShape()
  21. : strokeType (0.0f),
  22. mainFill (Colours::black),
  23. strokeFill (Colours::black)
  24. {
  25. }
  26. DrawableShape::DrawableShape (const DrawableShape& other)
  27. : strokeType (other.strokeType),
  28. mainFill (other.mainFill),
  29. strokeFill (other.strokeFill)
  30. {
  31. }
  32. DrawableShape::~DrawableShape()
  33. {
  34. }
  35. //==============================================================================
  36. class DrawableShape::RelativePositioner : public RelativeCoordinatePositionerBase
  37. {
  38. public:
  39. RelativePositioner (DrawableShape& component_, const DrawableShape::RelativeFillType& fill_, bool isMainFill_)
  40. : RelativeCoordinatePositionerBase (component_),
  41. owner (component_),
  42. fill (fill_),
  43. isMainFill (isMainFill_)
  44. {
  45. }
  46. bool registerCoordinates()
  47. {
  48. bool ok = addPoint (fill.gradientPoint1);
  49. ok = addPoint (fill.gradientPoint2) && ok;
  50. return addPoint (fill.gradientPoint3) && ok;
  51. }
  52. void applyToComponentBounds()
  53. {
  54. ComponentScope scope (owner);
  55. if (isMainFill ? owner.mainFill.recalculateCoords (&scope)
  56. : owner.strokeFill.recalculateCoords (&scope))
  57. owner.repaint();
  58. }
  59. void applyNewBounds (const Rectangle<int>&)
  60. {
  61. jassertfalse; // drawables can't be resized directly!
  62. }
  63. private:
  64. DrawableShape& owner;
  65. const DrawableShape::RelativeFillType fill;
  66. const bool isMainFill;
  67. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner);
  68. };
  69. void DrawableShape::setFill (const FillType& newFill)
  70. {
  71. setFill (RelativeFillType (newFill));
  72. }
  73. void DrawableShape::setStrokeFill (const FillType& newFill)
  74. {
  75. setStrokeFill (RelativeFillType (newFill));
  76. }
  77. void DrawableShape::setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill,
  78. ScopedPointer<RelativeCoordinatePositionerBase>& pos)
  79. {
  80. if (fill != newFill)
  81. {
  82. fill = newFill;
  83. pos = nullptr;
  84. if (fill.isDynamic())
  85. {
  86. pos = new RelativePositioner (*this, fill, true);
  87. pos->apply();
  88. }
  89. else
  90. {
  91. fill.recalculateCoords (nullptr);
  92. }
  93. repaint();
  94. }
  95. }
  96. void DrawableShape::setFill (const RelativeFillType& newFill)
  97. {
  98. setFillInternal (mainFill, newFill, mainFillPositioner);
  99. }
  100. void DrawableShape::setStrokeFill (const RelativeFillType& newFill)
  101. {
  102. setFillInternal (strokeFill, newFill, strokeFillPositioner);
  103. }
  104. void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType)
  105. {
  106. if (strokeType != newStrokeType)
  107. {
  108. strokeType = newStrokeType;
  109. strokeChanged();
  110. }
  111. }
  112. void DrawableShape::setStrokeThickness (const float newThickness)
  113. {
  114. setStrokeType (PathStrokeType (newThickness, strokeType.getJointStyle(), strokeType.getEndStyle()));
  115. }
  116. bool DrawableShape::isStrokeVisible() const noexcept
  117. {
  118. return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.fill.isInvisible();
  119. }
  120. void DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, ComponentBuilder::ImageProvider* imageProvider)
  121. {
  122. setFill (newState.getFill (FillAndStrokeState::fill, imageProvider));
  123. setStrokeFill (newState.getFill (FillAndStrokeState::stroke, imageProvider));
  124. }
  125. void DrawableShape::writeTo (FillAndStrokeState& state, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const
  126. {
  127. state.setFill (FillAndStrokeState::fill, mainFill, imageProvider, undoManager);
  128. state.setFill (FillAndStrokeState::stroke, strokeFill, imageProvider, undoManager);
  129. state.setStrokeType (strokeType, undoManager);
  130. }
  131. //==============================================================================
  132. void DrawableShape::paint (Graphics& g)
  133. {
  134. transformContextToCorrectOrigin (g);
  135. g.setFillType (mainFill.fill);
  136. g.fillPath (path);
  137. if (isStrokeVisible())
  138. {
  139. g.setFillType (strokeFill.fill);
  140. g.fillPath (strokePath);
  141. }
  142. }
  143. void DrawableShape::pathChanged()
  144. {
  145. strokeChanged();
  146. }
  147. void DrawableShape::strokeChanged()
  148. {
  149. strokePath.clear();
  150. strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f);
  151. setBoundsToEnclose (getDrawableBounds());
  152. repaint();
  153. }
  154. Rectangle<float> DrawableShape::getDrawableBounds() const
  155. {
  156. if (isStrokeVisible())
  157. return strokePath.getBounds();
  158. else
  159. return path.getBounds();
  160. }
  161. bool DrawableShape::hitTest (int x, int y)
  162. {
  163. bool allowsClicksOnThisComponent, allowsClicksOnChildComponents;
  164. getInterceptsMouseClicks (allowsClicksOnThisComponent, allowsClicksOnChildComponents);
  165. if (! allowsClicksOnThisComponent)
  166. return false;
  167. const float globalX = (float) (x - originRelativeToComponent.getX());
  168. const float globalY = (float) (y - originRelativeToComponent.getY());
  169. return path.contains (globalX, globalY)
  170. || (isStrokeVisible() && strokePath.contains (globalX, globalY));
  171. }
  172. //==============================================================================
  173. DrawableShape::RelativeFillType::RelativeFillType()
  174. {
  175. }
  176. DrawableShape::RelativeFillType::RelativeFillType (const FillType& fill_)
  177. : fill (fill_)
  178. {
  179. if (fill.isGradient())
  180. {
  181. const ColourGradient& g = *fill.gradient;
  182. gradientPoint1 = g.point1.transformedBy (fill.transform);
  183. gradientPoint2 = g.point2.transformedBy (fill.transform);
  184. gradientPoint3 = Point<float> (g.point1.getX() + g.point2.getY() - g.point1.getY(),
  185. g.point1.getY() + g.point1.getX() - g.point2.getX())
  186. .transformedBy (fill.transform);
  187. fill.transform = AffineTransform::identity;
  188. }
  189. }
  190. DrawableShape::RelativeFillType::RelativeFillType (const RelativeFillType& other)
  191. : fill (other.fill),
  192. gradientPoint1 (other.gradientPoint1),
  193. gradientPoint2 (other.gradientPoint2),
  194. gradientPoint3 (other.gradientPoint3)
  195. {
  196. }
  197. DrawableShape::RelativeFillType& DrawableShape::RelativeFillType::operator= (const RelativeFillType& other)
  198. {
  199. fill = other.fill;
  200. gradientPoint1 = other.gradientPoint1;
  201. gradientPoint2 = other.gradientPoint2;
  202. gradientPoint3 = other.gradientPoint3;
  203. return *this;
  204. }
  205. bool DrawableShape::RelativeFillType::operator== (const RelativeFillType& other) const
  206. {
  207. return fill == other.fill
  208. && ((! fill.isGradient())
  209. || (gradientPoint1 == other.gradientPoint1
  210. && gradientPoint2 == other.gradientPoint2
  211. && gradientPoint3 == other.gradientPoint3));
  212. }
  213. bool DrawableShape::RelativeFillType::operator!= (const RelativeFillType& other) const
  214. {
  215. return ! operator== (other);
  216. }
  217. bool DrawableShape::RelativeFillType::recalculateCoords (Expression::Scope* scope)
  218. {
  219. if (fill.isGradient())
  220. {
  221. const Point<float> g1 (gradientPoint1.resolve (scope));
  222. const Point<float> g2 (gradientPoint2.resolve (scope));
  223. AffineTransform t;
  224. ColourGradient& g = *fill.gradient;
  225. if (g.isRadial)
  226. {
  227. const Point<float> g3 (gradientPoint3.resolve (scope));
  228. const Point<float> g3Source (g1.getX() + g2.getY() - g1.getY(),
  229. g1.getY() + g1.getX() - g2.getX());
  230. t = AffineTransform::fromTargetPoints (g1.getX(), g1.getY(), g1.getX(), g1.getY(),
  231. g2.getX(), g2.getY(), g2.getX(), g2.getY(),
  232. g3Source.getX(), g3Source.getY(), g3.getX(), g3.getY());
  233. }
  234. if (g.point1 != g1 || g.point2 != g2 || fill.transform != t)
  235. {
  236. g.point1 = g1;
  237. g.point2 = g2;
  238. fill.transform = t;
  239. return true;
  240. }
  241. }
  242. return false;
  243. }
  244. bool DrawableShape::RelativeFillType::isDynamic() const
  245. {
  246. return gradientPoint1.isDynamic() || gradientPoint2.isDynamic() || gradientPoint3.isDynamic();
  247. }
  248. void DrawableShape::RelativeFillType::writeTo (ValueTree& v, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const
  249. {
  250. if (fill.isColour())
  251. {
  252. v.setProperty (FillAndStrokeState::type, "solid", undoManager);
  253. v.setProperty (FillAndStrokeState::colour, String::toHexString ((int) fill.colour.getARGB()), undoManager);
  254. }
  255. else if (fill.isGradient())
  256. {
  257. v.setProperty (FillAndStrokeState::type, "gradient", undoManager);
  258. v.setProperty (FillAndStrokeState::gradientPoint1, gradientPoint1.toString(), undoManager);
  259. v.setProperty (FillAndStrokeState::gradientPoint2, gradientPoint2.toString(), undoManager);
  260. v.setProperty (FillAndStrokeState::gradientPoint3, gradientPoint3.toString(), undoManager);
  261. const ColourGradient& cg = *fill.gradient;
  262. v.setProperty (FillAndStrokeState::radial, cg.isRadial, undoManager);
  263. String s;
  264. for (int i = 0; i < cg.getNumColours(); ++i)
  265. s << ' ' << cg.getColourPosition (i)
  266. << ' ' << String::toHexString ((int) cg.getColour(i).getARGB());
  267. v.setProperty (FillAndStrokeState::colours, s.trimStart(), undoManager);
  268. }
  269. else if (fill.isTiledImage())
  270. {
  271. v.setProperty (FillAndStrokeState::type, "image", undoManager);
  272. if (imageProvider != nullptr)
  273. v.setProperty (FillAndStrokeState::imageId, imageProvider->getIdentifierForImage (fill.image), undoManager);
  274. if (fill.getOpacity() < 1.0f)
  275. v.setProperty (FillAndStrokeState::imageOpacity, fill.getOpacity(), undoManager);
  276. else
  277. v.removeProperty (FillAndStrokeState::imageOpacity, undoManager);
  278. }
  279. else
  280. {
  281. jassertfalse;
  282. }
  283. }
  284. bool DrawableShape::RelativeFillType::readFrom (const ValueTree& v, ComponentBuilder::ImageProvider* imageProvider)
  285. {
  286. const String newType (v [FillAndStrokeState::type].toString());
  287. if (newType == "solid")
  288. {
  289. const String colourString (v [FillAndStrokeState::colour].toString());
  290. fill.setColour (Colour (colourString.isEmpty() ? (uint32) 0xff000000
  291. : (uint32) colourString.getHexValue32()));
  292. return true;
  293. }
  294. else if (newType == "gradient")
  295. {
  296. ColourGradient g;
  297. g.isRadial = v [FillAndStrokeState::radial];
  298. StringArray colourSteps;
  299. colourSteps.addTokens (v [FillAndStrokeState::colours].toString(), false);
  300. for (int i = 0; i < colourSteps.size() / 2; ++i)
  301. g.addColour (colourSteps[i * 2].getDoubleValue(),
  302. Colour ((uint32) colourSteps[i * 2 + 1].getHexValue32()));
  303. fill.setGradient (g);
  304. gradientPoint1 = RelativePoint (v [FillAndStrokeState::gradientPoint1]);
  305. gradientPoint2 = RelativePoint (v [FillAndStrokeState::gradientPoint2]);
  306. gradientPoint3 = RelativePoint (v [FillAndStrokeState::gradientPoint3]);
  307. return true;
  308. }
  309. else if (newType == "image")
  310. {
  311. Image im;
  312. if (imageProvider != nullptr)
  313. im = imageProvider->getImageForIdentifier (v [FillAndStrokeState::imageId]);
  314. fill.setTiledImage (im, AffineTransform::identity);
  315. fill.setOpacity ((float) v.getProperty (FillAndStrokeState::imageOpacity, 1.0f));
  316. return true;
  317. }
  318. jassertfalse;
  319. return false;
  320. }
  321. //==============================================================================
  322. const Identifier DrawableShape::FillAndStrokeState::type ("type");
  323. const Identifier DrawableShape::FillAndStrokeState::colour ("colour");
  324. const Identifier DrawableShape::FillAndStrokeState::colours ("colours");
  325. const Identifier DrawableShape::FillAndStrokeState::fill ("Fill");
  326. const Identifier DrawableShape::FillAndStrokeState::stroke ("Stroke");
  327. const Identifier DrawableShape::FillAndStrokeState::path ("Path");
  328. const Identifier DrawableShape::FillAndStrokeState::jointStyle ("jointStyle");
  329. const Identifier DrawableShape::FillAndStrokeState::capStyle ("capStyle");
  330. const Identifier DrawableShape::FillAndStrokeState::strokeWidth ("strokeWidth");
  331. const Identifier DrawableShape::FillAndStrokeState::gradientPoint1 ("point1");
  332. const Identifier DrawableShape::FillAndStrokeState::gradientPoint2 ("point2");
  333. const Identifier DrawableShape::FillAndStrokeState::gradientPoint3 ("point3");
  334. const Identifier DrawableShape::FillAndStrokeState::radial ("radial");
  335. const Identifier DrawableShape::FillAndStrokeState::imageId ("imageId");
  336. const Identifier DrawableShape::FillAndStrokeState::imageOpacity ("imageOpacity");
  337. DrawableShape::FillAndStrokeState::FillAndStrokeState (const ValueTree& state_)
  338. : Drawable::ValueTreeWrapperBase (state_)
  339. {
  340. }
  341. DrawableShape::RelativeFillType DrawableShape::FillAndStrokeState::getFill (const Identifier& fillOrStrokeType, ComponentBuilder::ImageProvider* imageProvider) const
  342. {
  343. DrawableShape::RelativeFillType f;
  344. f.readFrom (state.getChildWithName (fillOrStrokeType), imageProvider);
  345. return f;
  346. }
  347. ValueTree DrawableShape::FillAndStrokeState::getFillState (const Identifier& fillOrStrokeType)
  348. {
  349. ValueTree v (state.getChildWithName (fillOrStrokeType));
  350. if (v.isValid())
  351. return v;
  352. setFill (fillOrStrokeType, FillType (Colours::black), nullptr, nullptr);
  353. return getFillState (fillOrStrokeType);
  354. }
  355. void DrawableShape::FillAndStrokeState::setFill (const Identifier& fillOrStrokeType, const RelativeFillType& newFill,
  356. ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager)
  357. {
  358. ValueTree v (state.getOrCreateChildWithName (fillOrStrokeType, undoManager));
  359. newFill.writeTo (v, imageProvider, undoManager);
  360. }
  361. PathStrokeType DrawableShape::FillAndStrokeState::getStrokeType() const
  362. {
  363. const String jointStyleString (state [jointStyle].toString());
  364. const String capStyleString (state [capStyle].toString());
  365. return PathStrokeType (state [strokeWidth],
  366. jointStyleString == "curved" ? PathStrokeType::curved
  367. : (jointStyleString == "bevel" ? PathStrokeType::beveled
  368. : PathStrokeType::mitered),
  369. capStyleString == "square" ? PathStrokeType::square
  370. : (capStyleString == "round" ? PathStrokeType::rounded
  371. : PathStrokeType::butt));
  372. }
  373. void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager)
  374. {
  375. state.setProperty (strokeWidth, (double) newStrokeType.getStrokeThickness(), undoManager);
  376. state.setProperty (jointStyle, newStrokeType.getJointStyle() == PathStrokeType::mitered
  377. ? "miter" : (newStrokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), undoManager);
  378. state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt
  379. ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
  380. }
  381. END_JUCE_NAMESPACE