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) 2013 - Raw Material Software 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_JUCEHEADER__
  18. #define __JUCER_PAINTELEMENTIMAGE_JUCEHEADER__
  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* owner)
  28. : PaintElement (owner, "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*>& properties)
  68. {
  69. PaintElement::getEditableProperties (properties);
  70. properties.add (new ImageElementResourceProperty (this));
  71. properties.add (new StretchModeProperty (this));
  72. properties.add (new OpacityProperty (this));
  73. properties.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);
  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. else
  143. {
  144. jassertfalse; // this resource isn't valid!
  145. }
  146. }
  147. }
  148. }
  149. //==============================================================================
  150. class SetResourceAction : public PaintElementUndoableAction <PaintElementImage>
  151. {
  152. public:
  153. SetResourceAction (PaintElementImage* const element, const String& newResource_)
  154. : PaintElementUndoableAction <PaintElementImage> (element),
  155. newResource (newResource_)
  156. {
  157. oldResource = element->getResource();
  158. }
  159. bool perform()
  160. {
  161. showCorrectTab();
  162. getElement()->setResource (newResource, false);
  163. return true;
  164. }
  165. bool undo()
  166. {
  167. showCorrectTab();
  168. getElement()->setResource (oldResource, false);
  169. return true;
  170. }
  171. private:
  172. String newResource, oldResource;
  173. };
  174. void setResource (const String& newName, const bool undoable)
  175. {
  176. if (resourceName != newName)
  177. {
  178. if (undoable)
  179. {
  180. perform (new SetResourceAction (this, newName),
  181. "Change image resource");
  182. }
  183. else
  184. {
  185. resourceName = newName;
  186. changed();
  187. }
  188. }
  189. repaint();
  190. }
  191. String getResource() const
  192. {
  193. return resourceName;
  194. }
  195. //==============================================================================
  196. class SetOpacityAction : public PaintElementUndoableAction <PaintElementImage>
  197. {
  198. public:
  199. SetOpacityAction (PaintElementImage* const element, const double newOpacity_)
  200. : PaintElementUndoableAction <PaintElementImage> (element),
  201. newOpacity (newOpacity_)
  202. {
  203. oldOpacity = element->getOpacity();
  204. }
  205. bool perform()
  206. {
  207. showCorrectTab();
  208. getElement()->setOpacity (newOpacity, false);
  209. return true;
  210. }
  211. bool undo()
  212. {
  213. showCorrectTab();
  214. getElement()->setOpacity (oldOpacity, false);
  215. return true;
  216. }
  217. private:
  218. double newOpacity, oldOpacity;
  219. };
  220. void setOpacity (double newOpacity, const bool undoable)
  221. {
  222. newOpacity = jlimit (0.0, 1.0, newOpacity);
  223. if (opacity != newOpacity)
  224. {
  225. if (undoable)
  226. {
  227. perform (new SetOpacityAction (this, newOpacity),
  228. "Change image opacity");
  229. }
  230. else
  231. {
  232. opacity = newOpacity;
  233. changed();
  234. }
  235. }
  236. }
  237. double getOpacity() const noexcept { return opacity; }
  238. //==============================================================================
  239. static const char* getTagName() noexcept { return "IMAGE"; }
  240. void resetToImageSize()
  241. {
  242. if (const Drawable* const image = getDrawable())
  243. {
  244. if (PaintRoutineEditor* ed = dynamic_cast <PaintRoutineEditor*> (getParentComponent()))
  245. {
  246. const Rectangle<int> parentArea (ed->getComponentArea());
  247. Rectangle<int> r (getCurrentBounds (parentArea));
  248. Rectangle<float> bounds (image->getDrawableBounds());
  249. r.setSize ((int) (bounds.getWidth() + 0.999f), (int) (bounds.getHeight() + 0.999f));
  250. setCurrentBounds (r, parentArea, true);
  251. }
  252. }
  253. }
  254. //==============================================================================
  255. class SetStretchModeAction : public PaintElementUndoableAction <PaintElementImage>
  256. {
  257. public:
  258. SetStretchModeAction (PaintElementImage* const element, const StretchMode newValue_)
  259. : PaintElementUndoableAction <PaintElementImage> (element),
  260. newValue (newValue_)
  261. {
  262. oldValue = element->getStretchMode();
  263. }
  264. bool perform()
  265. {
  266. showCorrectTab();
  267. getElement()->setStretchMode (newValue, false);
  268. return true;
  269. }
  270. bool undo()
  271. {
  272. showCorrectTab();
  273. getElement()->setStretchMode (oldValue, false);
  274. return true;
  275. }
  276. private:
  277. StretchMode newValue, oldValue;
  278. };
  279. StretchMode getStretchMode() const noexcept { return mode; }
  280. void setStretchMode (const StretchMode newMode, const bool undoable)
  281. {
  282. if (mode != newMode)
  283. {
  284. if (undoable)
  285. {
  286. perform (new SetStretchModeAction (this, newMode),
  287. "Change image mode");
  288. }
  289. else
  290. {
  291. mode = newMode;
  292. changed();
  293. }
  294. }
  295. }
  296. //==============================================================================
  297. XmlElement* createXml() const
  298. {
  299. XmlElement* e = new XmlElement (getTagName());
  300. position.applyToXml (*e);
  301. e->setAttribute ("resource", resourceName);
  302. e->setAttribute ("opacity", opacity);
  303. e->setAttribute ("mode", (int) mode);
  304. return e;
  305. }
  306. bool loadFromXml (const XmlElement& xml)
  307. {
  308. if (xml.hasTagName (getTagName()))
  309. {
  310. position.restoreFromXml (xml, position);
  311. resourceName = xml.getStringAttribute ("resource", String::empty);
  312. opacity = xml.getDoubleAttribute ("opacity", 1.0);
  313. mode = (StretchMode) xml.getIntAttribute ("mode", (int) stretched);
  314. repaint();
  315. return true;
  316. }
  317. jassertfalse;
  318. return false;
  319. }
  320. private:
  321. String resourceName;
  322. double opacity;
  323. StretchMode mode;
  324. //==============================================================================
  325. class ImageElementResourceProperty : public ImageResourceProperty <PaintElementImage>
  326. {
  327. public:
  328. ImageElementResourceProperty (PaintElementImage* const e)
  329. : ImageResourceProperty <PaintElementImage> (e, "image source")
  330. {
  331. }
  332. void setResource (const String& newName)
  333. {
  334. if (element != nullptr)
  335. element->setResource (newName, true);
  336. }
  337. String getResource() const
  338. {
  339. if (element != nullptr)
  340. return element->getResource();
  341. return String::empty;
  342. }
  343. };
  344. //==============================================================================
  345. class OpacityProperty : public SliderPropertyComponent,
  346. private ElementListenerBase <PaintElementImage>
  347. {
  348. public:
  349. OpacityProperty (PaintElementImage* const e)
  350. : SliderPropertyComponent ("opacity", 0.0, 1.0, 0.001),
  351. ElementListenerBase <PaintElementImage> (e)
  352. {
  353. }
  354. void setValue (double newValue)
  355. {
  356. owner->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  357. owner->setOpacity (newValue, true);
  358. }
  359. double getValue() const
  360. {
  361. return owner->getOpacity();
  362. }
  363. };
  364. class StretchModeProperty : public ChoicePropertyComponent,
  365. private ElementListenerBase <PaintElementImage>
  366. {
  367. public:
  368. StretchModeProperty (PaintElementImage* const e)
  369. : ChoicePropertyComponent ("stretch mode"),
  370. ElementListenerBase <PaintElementImage> (e)
  371. {
  372. choices.add ("Stretched to fit");
  373. choices.add ("Maintain aspect ratio");
  374. choices.add ("Maintain aspect ratio, only reduce in size");
  375. }
  376. void setIndex (int newIndex)
  377. {
  378. owner->setStretchMode ((StretchMode) newIndex, true);
  379. }
  380. int getIndex() const
  381. {
  382. return (int) owner->getStretchMode();
  383. }
  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. };
  402. #endif // __JUCER_PAINTELEMENTIMAGE_JUCEHEADER__