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.

495 lines
16KB

  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. #pragma once
  20. #include "../jucer_PaintRoutine.h"
  21. #include "../properties/jucer_FilePropertyComponent.h"
  22. #include "jucer_ImageResourceProperty.h"
  23. #include "jucer_PaintElementUndoableAction.h"
  24. //==============================================================================
  25. class PaintElementImage : public PaintElement
  26. {
  27. public:
  28. PaintElementImage (PaintRoutine* pr)
  29. : PaintElement (pr, "Image"),
  30. opacity (1.0),
  31. mode (stretched)
  32. {
  33. }
  34. enum StretchMode
  35. {
  36. stretched = 0,
  37. proportional = 1,
  38. proportionalReducingOnly = 2
  39. };
  40. const Drawable* getDrawable()
  41. {
  42. if (JucerDocument* const document = getDocument())
  43. return document->getResources().getDrawable (resourceName);
  44. return nullptr;
  45. }
  46. void draw (Graphics& g, const ComponentLayout* layout, const Rectangle<int>& parentArea)
  47. {
  48. const Rectangle<int> r (position.getRectangle (parentArea, layout));
  49. if (const Drawable* const image = getDrawable())
  50. {
  51. image->drawWithin (g, r.toFloat(),
  52. mode == stretched ? RectanglePlacement::stretchToFit
  53. : (mode == proportionalReducingOnly ? (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize)
  54. : RectanglePlacement::centred),
  55. (float) opacity);
  56. }
  57. else
  58. {
  59. g.setColour (Colours::grey.withAlpha (0.5f));
  60. g.fillRect (r);
  61. g.setColour (Colours::black);
  62. g.drawText ("(image missing)",
  63. r.getX(), r.getY(), r.getWidth(), r.getHeight(),
  64. Justification::centred, true);
  65. }
  66. }
  67. //==============================================================================
  68. void getEditableProperties (Array <PropertyComponent*>& props)
  69. {
  70. PaintElement::getEditableProperties (props);
  71. props.add (new ImageElementResourceProperty (this));
  72. props.add (new StretchModeProperty (this));
  73. props.add (new OpacityProperty (this));
  74. props.add (new ResetSizeProperty (this));
  75. }
  76. void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode)
  77. {
  78. if (opacity > 0)
  79. {
  80. String x, y, w, h, r;
  81. positionToCode (position, getDocument()->getComponentLayout(), x, y, w, h);
  82. r << "{\n"
  83. << " int x = " << x << ", y = " << y << ", width = " << w << ", height = " << h << ";\n"
  84. << " //[UserPaintCustomArguments] Customize the painting arguments here..\n"
  85. << customPaintCode
  86. << " //[/UserPaintCustomArguments]\n";
  87. if (dynamic_cast<const DrawableImage*> (getDrawable()) != 0)
  88. {
  89. const String imageVariable ("cachedImage_" + resourceName.replace ("::", "_") + "_" + String (code.getUniqueSuffix()));
  90. code.addImageResourceLoader (imageVariable, resourceName);
  91. if (opacity >= 254.0 / 255.0)
  92. r << " g.setColour (Colours::black);\n";
  93. else
  94. r << " g.setColour (Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n";
  95. if (mode == stretched)
  96. {
  97. r << " g.drawImage (" << imageVariable << ",\n"
  98. << " x, y, width, height,\n"
  99. << " 0, 0, " << imageVariable << ".getWidth(), " << imageVariable << ".getHeight());\n";
  100. }
  101. else
  102. {
  103. r << " g.drawImageWithin (" << imageVariable << ",\n"
  104. << " x, y, width, height,\n"
  105. << " ";
  106. if (mode == proportionalReducingOnly)
  107. r << "RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize";
  108. else
  109. r << "RectanglePlacement::centred";
  110. r << ",\n"
  111. << " false);\n";
  112. }
  113. }
  114. else
  115. {
  116. if (resourceName.isNotEmpty())
  117. {
  118. const String imageVariable ("drawable" + String (code.getUniqueSuffix()));
  119. code.privateMemberDeclarations
  120. << "ScopedPointer<Drawable> " << imageVariable << ";\n";
  121. code.constructorCode
  122. << imageVariable << " = Drawable::createFromImageData ("
  123. << resourceName << ", " << resourceName << "Size);\n";
  124. code.destructorCode
  125. << imageVariable << " = nullptr;\n";
  126. if (opacity >= 254.0 / 255.0)
  127. r << " g.setColour (Colours::black);\n";
  128. else
  129. r << " g.setColour (Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n";
  130. r << " jassert (" << imageVariable << " != 0);\n"
  131. << " if (" << imageVariable << " != 0)\n"
  132. << " " << imageVariable << "->drawWithin (g, Rectangle<float> (x, y, width, height),\n"
  133. << " " << String::repeatedString (" ", imageVariable.length() + 18)
  134. << (mode == stretched ? "RectanglePlacement::stretchToFit"
  135. : (mode == proportionalReducingOnly ? "RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize"
  136. : "RectanglePlacement::centred"))
  137. << ", " << CodeHelpers::floatLiteral (opacity, 3) << ");\n";
  138. }
  139. }
  140. r << "}\n\n";
  141. paintMethodCode += r;
  142. }
  143. }
  144. void applyCustomPaintSnippets (StringArray& snippets)
  145. {
  146. customPaintCode.clear();
  147. if (! snippets.isEmpty() && opacity > 0)
  148. {
  149. customPaintCode = snippets[0];
  150. snippets.remove (0);
  151. }
  152. }
  153. //==============================================================================
  154. class SetResourceAction : public PaintElementUndoableAction <PaintElementImage>
  155. {
  156. public:
  157. SetResourceAction (PaintElementImage* const element, const String& newResource_)
  158. : PaintElementUndoableAction <PaintElementImage> (element),
  159. newResource (newResource_)
  160. {
  161. oldResource = element->getResource();
  162. }
  163. bool perform()
  164. {
  165. showCorrectTab();
  166. getElement()->setResource (newResource, false);
  167. return true;
  168. }
  169. bool undo()
  170. {
  171. showCorrectTab();
  172. getElement()->setResource (oldResource, false);
  173. return true;
  174. }
  175. private:
  176. String newResource, oldResource;
  177. };
  178. void setResource (const String& newName, const bool undoable)
  179. {
  180. if (resourceName != newName)
  181. {
  182. if (undoable)
  183. {
  184. perform (new SetResourceAction (this, newName),
  185. "Change image resource");
  186. }
  187. else
  188. {
  189. resourceName = newName;
  190. changed();
  191. }
  192. }
  193. repaint();
  194. }
  195. String getResource() const
  196. {
  197. return resourceName;
  198. }
  199. //==============================================================================
  200. class SetOpacityAction : public PaintElementUndoableAction <PaintElementImage>
  201. {
  202. public:
  203. SetOpacityAction (PaintElementImage* const element, const double newOpacity_)
  204. : PaintElementUndoableAction <PaintElementImage> (element),
  205. newOpacity (newOpacity_)
  206. {
  207. oldOpacity = element->getOpacity();
  208. }
  209. bool perform()
  210. {
  211. showCorrectTab();
  212. getElement()->setOpacity (newOpacity, false);
  213. return true;
  214. }
  215. bool undo()
  216. {
  217. showCorrectTab();
  218. getElement()->setOpacity (oldOpacity, false);
  219. return true;
  220. }
  221. private:
  222. double newOpacity, oldOpacity;
  223. };
  224. void setOpacity (double newOpacity, const bool undoable)
  225. {
  226. newOpacity = jlimit (0.0, 1.0, newOpacity);
  227. if (opacity != newOpacity)
  228. {
  229. if (undoable)
  230. {
  231. perform (new SetOpacityAction (this, newOpacity),
  232. "Change image opacity");
  233. }
  234. else
  235. {
  236. opacity = newOpacity;
  237. changed();
  238. }
  239. }
  240. }
  241. double getOpacity() const noexcept { return opacity; }
  242. //==============================================================================
  243. static const char* getTagName() noexcept { return "IMAGE"; }
  244. void resetToImageSize()
  245. {
  246. if (const Drawable* const image = getDrawable())
  247. {
  248. if (PaintRoutineEditor* ed = dynamic_cast<PaintRoutineEditor*> (getParentComponent()))
  249. {
  250. const Rectangle<int> parentArea (ed->getComponentArea());
  251. Rectangle<int> r (getCurrentBounds (parentArea));
  252. Rectangle<float> b (image->getDrawableBounds());
  253. r.setSize ((int) (b.getWidth() + 0.999f),
  254. (int) (b.getHeight() + 0.999f));
  255. setCurrentBounds (r, parentArea, true);
  256. }
  257. }
  258. }
  259. //==============================================================================
  260. class SetStretchModeAction : public PaintElementUndoableAction <PaintElementImage>
  261. {
  262. public:
  263. SetStretchModeAction (PaintElementImage* const element, const StretchMode newValue_)
  264. : PaintElementUndoableAction <PaintElementImage> (element),
  265. newValue (newValue_)
  266. {
  267. oldValue = element->getStretchMode();
  268. }
  269. bool perform()
  270. {
  271. showCorrectTab();
  272. getElement()->setStretchMode (newValue, false);
  273. return true;
  274. }
  275. bool undo()
  276. {
  277. showCorrectTab();
  278. getElement()->setStretchMode (oldValue, false);
  279. return true;
  280. }
  281. private:
  282. StretchMode newValue, oldValue;
  283. };
  284. StretchMode getStretchMode() const noexcept { return mode; }
  285. void setStretchMode (const StretchMode newMode, const bool undoable)
  286. {
  287. if (mode != newMode)
  288. {
  289. if (undoable)
  290. {
  291. perform (new SetStretchModeAction (this, newMode),
  292. "Change image mode");
  293. }
  294. else
  295. {
  296. mode = newMode;
  297. changed();
  298. }
  299. }
  300. }
  301. //==============================================================================
  302. XmlElement* createXml() const
  303. {
  304. XmlElement* e = new XmlElement (getTagName());
  305. position.applyToXml (*e);
  306. e->setAttribute ("resource", resourceName);
  307. e->setAttribute ("opacity", opacity);
  308. e->setAttribute ("mode", (int) mode);
  309. return e;
  310. }
  311. bool loadFromXml (const XmlElement& xml)
  312. {
  313. if (xml.hasTagName (getTagName()))
  314. {
  315. position.restoreFromXml (xml, position);
  316. resourceName = xml.getStringAttribute ("resource", String());
  317. opacity = xml.getDoubleAttribute ("opacity", 1.0);
  318. mode = (StretchMode) xml.getIntAttribute ("mode", (int) stretched);
  319. repaint();
  320. return true;
  321. }
  322. jassertfalse;
  323. return false;
  324. }
  325. private:
  326. String resourceName;
  327. double opacity;
  328. StretchMode mode;
  329. String customPaintCode;
  330. //==============================================================================
  331. class ImageElementResourceProperty : public ImageResourceProperty <PaintElementImage>
  332. {
  333. public:
  334. ImageElementResourceProperty (PaintElementImage* const e)
  335. : ImageResourceProperty <PaintElementImage> (e, "image source")
  336. {
  337. }
  338. void setResource (const String& newName)
  339. {
  340. if (element != nullptr)
  341. element->setResource (newName, true);
  342. }
  343. String getResource() const
  344. {
  345. if (element != nullptr)
  346. return element->getResource();
  347. return {};
  348. }
  349. };
  350. //==============================================================================
  351. class OpacityProperty : public SliderPropertyComponent
  352. {
  353. public:
  354. OpacityProperty (PaintElementImage* const e)
  355. : SliderPropertyComponent ("opacity", 0.0, 1.0, 0.001),
  356. listener (e)
  357. {
  358. listener.setPropertyToRefresh (*this);
  359. }
  360. void setValue (double newValue)
  361. {
  362. listener.owner->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  363. listener.owner->setOpacity (newValue, true);
  364. }
  365. double getValue() const
  366. {
  367. return listener.owner->getOpacity();
  368. }
  369. ElementListener<PaintElementImage> listener;
  370. };
  371. class StretchModeProperty : public ChoicePropertyComponent
  372. {
  373. public:
  374. StretchModeProperty (PaintElementImage* const e)
  375. : ChoicePropertyComponent ("stretch mode"),
  376. listener (e)
  377. {
  378. listener.setPropertyToRefresh (*this);
  379. choices.add ("Stretched to fit");
  380. choices.add ("Maintain aspect ratio");
  381. choices.add ("Maintain aspect ratio, only reduce in size");
  382. }
  383. void setIndex (int newIndex)
  384. {
  385. listener.owner->setStretchMode ((StretchMode) newIndex, true);
  386. }
  387. int getIndex() const
  388. {
  389. return (int) listener.owner->getStretchMode();
  390. }
  391. ElementListener<PaintElementImage> listener;
  392. };
  393. class ResetSizeProperty : public ButtonPropertyComponent
  394. {
  395. public:
  396. ResetSizeProperty (PaintElementImage* const e)
  397. : ButtonPropertyComponent ("reset", false),
  398. element (e)
  399. {
  400. }
  401. void buttonClicked()
  402. {
  403. element->resetToImageSize();
  404. }
  405. String getButtonText() const { return "reset to image size"; }
  406. private:
  407. PaintElementImage* const element;
  408. };
  409. };