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 - "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. << "Drawable* " << imageVariable << ";\n";
  119. code.constructorCode
  120. << imageVariable << " = Drawable::createFromImageData ("
  121. << resourceName << ", " << resourceName << "Size);\n";
  122. code.destructorCode
  123. << "deleteAndZero (" << imageVariable << ");\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. const Drawable* const image = getDrawable();
  244. if (image != nullptr && getParentComponent() != nullptr)
  245. {
  246. const Rectangle<int> parentArea (((PaintRoutineEditor*) getParentComponent())->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. class SetStretchModeAction : public PaintElementUndoableAction <PaintElementImage>
  255. {
  256. public:
  257. SetStretchModeAction (PaintElementImage* const element, const StretchMode newValue_)
  258. : PaintElementUndoableAction <PaintElementImage> (element),
  259. newValue (newValue_)
  260. {
  261. oldValue = element->getStretchMode();
  262. }
  263. bool perform()
  264. {
  265. showCorrectTab();
  266. getElement()->setStretchMode (newValue, false);
  267. return true;
  268. }
  269. bool undo()
  270. {
  271. showCorrectTab();
  272. getElement()->setStretchMode (oldValue, false);
  273. return true;
  274. }
  275. private:
  276. StretchMode newValue, oldValue;
  277. };
  278. StretchMode getStretchMode() const noexcept { return mode; }
  279. void setStretchMode (const StretchMode newMode, const bool undoable)
  280. {
  281. if (mode != newMode)
  282. {
  283. if (undoable)
  284. {
  285. perform (new SetStretchModeAction (this, newMode),
  286. "Change image mode");
  287. }
  288. else
  289. {
  290. mode = newMode;
  291. changed();
  292. }
  293. }
  294. }
  295. //==============================================================================
  296. XmlElement* createXml() const
  297. {
  298. XmlElement* e = new XmlElement (getTagName());
  299. position.applyToXml (*e);
  300. e->setAttribute ("resource", resourceName);
  301. e->setAttribute ("opacity", opacity);
  302. e->setAttribute ("mode", (int) mode);
  303. return e;
  304. }
  305. bool loadFromXml (const XmlElement& xml)
  306. {
  307. if (xml.hasTagName (getTagName()))
  308. {
  309. position.restoreFromXml (xml, position);
  310. resourceName = xml.getStringAttribute ("resource", String::empty);
  311. opacity = xml.getDoubleAttribute ("opacity", 1.0);
  312. mode = (StretchMode) xml.getIntAttribute ("mode", (int) stretched);
  313. repaint();
  314. return true;
  315. }
  316. jassertfalse;
  317. return false;
  318. }
  319. private:
  320. String resourceName;
  321. double opacity;
  322. StretchMode mode;
  323. //==============================================================================
  324. class ImageElementResourceProperty : public ImageResourceProperty <PaintElementImage>
  325. {
  326. public:
  327. ImageElementResourceProperty (PaintElementImage* const e)
  328. : ImageResourceProperty <PaintElementImage> (e, "image source")
  329. {
  330. }
  331. void setResource (const String& newName)
  332. {
  333. if (element != nullptr)
  334. element->setResource (newName, true);
  335. }
  336. String getResource() const
  337. {
  338. if (element != nullptr)
  339. return element->getResource();
  340. return String::empty;
  341. }
  342. };
  343. //==============================================================================
  344. class OpacityProperty : public SliderPropertyComponent,
  345. private ElementListenerBase <PaintElementImage>
  346. {
  347. public:
  348. OpacityProperty (PaintElementImage* const e)
  349. : SliderPropertyComponent ("opacity", 0.0, 1.0, 0.001),
  350. ElementListenerBase <PaintElementImage> (e)
  351. {
  352. }
  353. void setValue (double newValue)
  354. {
  355. owner->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  356. owner->setOpacity (newValue, true);
  357. }
  358. double getValue() const
  359. {
  360. return owner->getOpacity();
  361. }
  362. };
  363. class StretchModeProperty : public ChoicePropertyComponent,
  364. private ElementListenerBase <PaintElementImage>
  365. {
  366. public:
  367. StretchModeProperty (PaintElementImage* const e)
  368. : ChoicePropertyComponent ("stretch mode"),
  369. ElementListenerBase <PaintElementImage> (e)
  370. {
  371. choices.add ("Stretched to fit");
  372. choices.add ("Maintain aspect ratio");
  373. choices.add ("Maintain aspect ratio, only reduce in size");
  374. }
  375. void setIndex (int newIndex)
  376. {
  377. owner->setStretchMode ((StretchMode) newIndex, true);
  378. }
  379. int getIndex() const
  380. {
  381. return (int) owner->getStretchMode();
  382. }
  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_JUCEHEADER__