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.

512 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifndef __JUCER_PAINTELEMENTIMAGE_JUCEHEADER__
  19. #define __JUCER_PAINTELEMENTIMAGE_JUCEHEADER__
  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. //==============================================================================
  29. PaintElementImage (PaintRoutine* owner)
  30. : PaintElement (owner, "Image"),
  31. opacity (1.0),
  32. mode (stretched)
  33. {
  34. }
  35. enum StretchMode
  36. {
  37. stretched = 0,
  38. proportional = 1,
  39. proportionalReducingOnly = 2
  40. };
  41. const Drawable* getDrawable()
  42. {
  43. if (JucerDocument* const document = getDocument())
  44. return document->getResources().getDrawable (resourceName);
  45. return nullptr;
  46. }
  47. void draw (Graphics& g, const ComponentLayout* layout, const Rectangle<int>& parentArea)
  48. {
  49. const Rectangle<int> r (position.getRectangle (parentArea, layout));
  50. if (const Drawable* const image = getDrawable())
  51. {
  52. image->drawWithin (g, r.toFloat(),
  53. mode == stretched ? RectanglePlacement::stretchToFit
  54. : (mode == proportionalReducingOnly ? (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize)
  55. : RectanglePlacement::centred),
  56. (float) opacity);
  57. }
  58. else
  59. {
  60. g.setColour (Colours::grey.withAlpha (0.5f));
  61. g.fillRect (r);
  62. g.setColour (Colours::black);
  63. g.drawText ("(image missing)",
  64. r.getX(), r.getY(), r.getWidth(), r.getHeight(),
  65. Justification::centred, true);
  66. }
  67. }
  68. //==============================================================================
  69. void getEditableProperties (Array <PropertyComponent*>& properties)
  70. {
  71. PaintElement::getEditableProperties (properties);
  72. properties.add (new ImageElementResourceProperty (this));
  73. properties.add (new StretchModeProperty (this));
  74. properties.add (new OpacityProperty (this));
  75. properties.add (new ResetSizeProperty (this));
  76. }
  77. void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode)
  78. {
  79. String r;
  80. if (opacity > 0)
  81. {
  82. if (dynamic_cast <const DrawableImage*> (getDrawable()) != 0)
  83. {
  84. const String imageVariable ("cachedImage_" + resourceName);
  85. code.addImageResourceLoader (imageVariable, resourceName);
  86. if (opacity >= 254.0 / 255.0)
  87. r << "g.setColour (Colours::black);\n";
  88. else
  89. r << "g.setColour (Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n";
  90. String x, y, w, h;
  91. positionToCode (position, getDocument()->getComponentLayout(), x, y, w, h);
  92. if (mode == stretched)
  93. {
  94. r << "g.drawImage (" << imageVariable << ",\n "
  95. << x << ", " << y << ", " << w << ", " << h
  96. << ",\n 0, 0, "
  97. << imageVariable << ".getWidth(), "
  98. << imageVariable << ".getHeight());\n\n";
  99. }
  100. else
  101. {
  102. r << "g.drawImageWithin (" << imageVariable << ",\n "
  103. << x << ", " << y << ", " << w << ", " << h
  104. << ",\n ";
  105. if (mode == proportionalReducingOnly)
  106. r << "RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize";
  107. else
  108. r << "RectanglePlacement::centred";
  109. r << ",\n false);\n\n";
  110. }
  111. paintMethodCode += r;
  112. }
  113. else
  114. {
  115. if (resourceName.isNotEmpty())
  116. {
  117. const String imageVariable ("drawable" + String (code.getUniqueSuffix()));
  118. code.privateMemberDeclarations
  119. << "Drawable* " << imageVariable << ";\n";
  120. code.constructorCode
  121. << imageVariable << " = Drawable::createFromImageData ("
  122. << resourceName << ", " << resourceName << "Size);\n";
  123. code.destructorCode
  124. << "deleteAndZero (" << imageVariable << ");\n";
  125. if (opacity >= 254.0 / 255.0)
  126. r << "g.setColour (Colours::black);\n";
  127. else
  128. r << "g.setColour (Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n";
  129. String x, y, w, h;
  130. positionToCode (position, code.document->getComponentLayout(), x, y, w, h);
  131. r << "jassert (" << imageVariable << " != 0);\n"
  132. << "if (" << imageVariable << " != 0)\n "
  133. << imageVariable << "->drawWithin (g, Rectangle<float> ("
  134. << x << ", " << y << ", " << w << ", " << h
  135. << "),\n"
  136. << String::repeatedString (" ", imageVariable.length() + 18)
  137. << (mode == stretched ? "RectanglePlacement::stretchToFit"
  138. : (mode == proportionalReducingOnly ? "RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize"
  139. : "RectanglePlacement::centred"))
  140. << ", " << CodeHelpers::floatLiteral (opacity, 3)
  141. << ");\n\n";
  142. paintMethodCode += r;
  143. }
  144. else
  145. {
  146. jassertfalse; // this resource isn't valid!
  147. }
  148. }
  149. }
  150. }
  151. //==============================================================================
  152. class SetResourceAction : public PaintElementUndoableAction <PaintElementImage>
  153. {
  154. public:
  155. SetResourceAction (PaintElementImage* const element, const String& newResource_)
  156. : PaintElementUndoableAction <PaintElementImage> (element),
  157. newResource (newResource_)
  158. {
  159. oldResource = element->getResource();
  160. }
  161. bool perform()
  162. {
  163. showCorrectTab();
  164. getElement()->setResource (newResource, false);
  165. return true;
  166. }
  167. bool undo()
  168. {
  169. showCorrectTab();
  170. getElement()->setResource (oldResource, false);
  171. return true;
  172. }
  173. private:
  174. String newResource, oldResource;
  175. };
  176. void setResource (const String& newName, const bool undoable)
  177. {
  178. if (resourceName != newName)
  179. {
  180. if (undoable)
  181. {
  182. perform (new SetResourceAction (this, newName),
  183. "Change image resource");
  184. }
  185. else
  186. {
  187. resourceName = newName;
  188. changed();
  189. }
  190. }
  191. repaint();
  192. }
  193. String getResource() const
  194. {
  195. return resourceName;
  196. }
  197. //==============================================================================
  198. class SetOpacityAction : public PaintElementUndoableAction <PaintElementImage>
  199. {
  200. public:
  201. SetOpacityAction (PaintElementImage* const element, const double newOpacity_)
  202. : PaintElementUndoableAction <PaintElementImage> (element),
  203. newOpacity (newOpacity_)
  204. {
  205. oldOpacity = element->getOpacity();
  206. }
  207. bool perform()
  208. {
  209. showCorrectTab();
  210. getElement()->setOpacity (newOpacity, false);
  211. return true;
  212. }
  213. bool undo()
  214. {
  215. showCorrectTab();
  216. getElement()->setOpacity (oldOpacity, false);
  217. return true;
  218. }
  219. private:
  220. double newOpacity, oldOpacity;
  221. };
  222. void setOpacity (double newOpacity, const bool undoable)
  223. {
  224. newOpacity = jlimit (0.0, 1.0, newOpacity);
  225. if (opacity != newOpacity)
  226. {
  227. if (undoable)
  228. {
  229. perform (new SetOpacityAction (this, newOpacity),
  230. "Change image opacity");
  231. }
  232. else
  233. {
  234. opacity = newOpacity;
  235. changed();
  236. }
  237. }
  238. }
  239. double getOpacity() const noexcept { return opacity; }
  240. //==============================================================================
  241. static const char* getTagName() noexcept { return "IMAGE"; }
  242. void resetToImageSize()
  243. {
  244. const Drawable* const image = getDrawable();
  245. if (image != nullptr && getParentComponent() != nullptr)
  246. {
  247. const Rectangle<int> parentArea (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  248. Rectangle<int> r (getCurrentBounds (parentArea));
  249. Rectangle<float> bounds (image->getDrawableBounds());
  250. r.setSize ((int) (bounds.getWidth() + 0.999f), (int) (bounds.getHeight() + 0.999f));
  251. setCurrentBounds (r, parentArea, true);
  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. element->setResource (newName, true);
  335. }
  336. String getResource() const
  337. {
  338. return element->getResource();
  339. }
  340. };
  341. //==============================================================================
  342. class OpacityProperty : public SliderPropertyComponent,
  343. private ChangeListener
  344. {
  345. public:
  346. OpacityProperty (PaintElementImage* const e)
  347. : SliderPropertyComponent ("opacity", 0.0, 1.0, 0.001),
  348. element (e)
  349. {
  350. element->getDocument()->addChangeListener (this);
  351. }
  352. ~OpacityProperty()
  353. {
  354. element->getDocument()->removeChangeListener (this);
  355. }
  356. void setValue (double newValue)
  357. {
  358. element->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  359. element->setOpacity (newValue, true);
  360. }
  361. double getValue() const
  362. {
  363. return element->getOpacity();
  364. }
  365. void changeListenerCallback (ChangeBroadcaster*)
  366. {
  367. refresh();
  368. }
  369. private:
  370. PaintElementImage* const element;
  371. };
  372. class StretchModeProperty : public ChoicePropertyComponent,
  373. private ChangeListener
  374. {
  375. public:
  376. StretchModeProperty (PaintElementImage* const e)
  377. : ChoicePropertyComponent ("stretch mode"),
  378. element (e)
  379. {
  380. choices.add ("Stretched to fit");
  381. choices.add ("Maintain aspect ratio");
  382. choices.add ("Maintain aspect ratio, only reduce in size");
  383. element->getDocument()->addChangeListener (this);
  384. }
  385. ~StretchModeProperty()
  386. {
  387. element->getDocument()->removeChangeListener (this);
  388. }
  389. void setIndex (int newIndex)
  390. {
  391. element->setStretchMode ((StretchMode) newIndex, true);
  392. }
  393. int getIndex() const
  394. {
  395. return (int) element->getStretchMode();
  396. }
  397. void changeListenerCallback (ChangeBroadcaster*)
  398. {
  399. refresh();
  400. }
  401. private:
  402. PaintElementImage* const element;
  403. };
  404. class ResetSizeProperty : public ButtonPropertyComponent
  405. {
  406. public:
  407. ResetSizeProperty (PaintElementImage* const e)
  408. : ButtonPropertyComponent ("reset", false),
  409. element (e)
  410. {
  411. }
  412. void buttonClicked()
  413. {
  414. element->resetToImageSize();
  415. }
  416. String getButtonText() const { return "reset to image size"; }
  417. private:
  418. PaintElementImage* const element;
  419. };
  420. };
  421. #endif // __JUCER_PAINTELEMENTIMAGE_JUCEHEADER__