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.

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