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.

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