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.

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