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.

483 lines
16KB

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