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.

414 lines
13KB

  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. #include "../../jucer_Headers.h"
  18. #include "jucer_ButtonDocument.h"
  19. #include "../jucer_UtilityFunctions.h"
  20. //==============================================================================
  21. static const int normalOff = 0;
  22. static const int overOff = 1;
  23. static const int downOff = 2;
  24. static const int normalOn = 3;
  25. static const int overOn = 4;
  26. static const int downOn = 5;
  27. static const int background = 6;
  28. //==============================================================================
  29. ButtonDocument::ButtonDocument (SourceCodeDocument* c)
  30. : JucerDocument (c)
  31. {
  32. paintStatesEnabled [normalOff] = true;
  33. paintStatesEnabled [overOff] = true;
  34. paintStatesEnabled [downOff] = true;
  35. paintStatesEnabled [normalOn] = false;
  36. paintStatesEnabled [overOn] = false;
  37. paintStatesEnabled [downOn] = false;
  38. paintStatesEnabled [background] = false;
  39. parentClasses = "public Button";
  40. for (int i = 7; --i >= 0;)
  41. {
  42. paintRoutines[i] = new PaintRoutine();
  43. paintRoutines[i]->setDocument (this);
  44. paintRoutines[i]->setBackgroundColour (Colours::transparentBlack);
  45. }
  46. }
  47. ButtonDocument::~ButtonDocument()
  48. {
  49. }
  50. static const char* const stateNames[] =
  51. {
  52. "normal", "over", "down",
  53. "normal on", "over on", "down on",
  54. "common background"
  55. };
  56. int stateNameToIndex (const String& name)
  57. {
  58. for (int i = 7; --i >= 0;)
  59. if (name.equalsIgnoreCase (stateNames[i]))
  60. return i;
  61. jassertfalse;
  62. return normalOff;
  63. }
  64. int ButtonDocument::getNumPaintRoutines() const
  65. {
  66. int n = 0;
  67. for (int i = 7; --i >= 0;)
  68. if (paintStatesEnabled [i])
  69. ++n;
  70. return n;
  71. }
  72. StringArray ButtonDocument::getPaintRoutineNames() const
  73. {
  74. StringArray s;
  75. for (int i = 0; i < 7; ++i)
  76. if (paintStatesEnabled [i])
  77. s.add (stateNames [i]);
  78. return s;
  79. }
  80. PaintRoutine* ButtonDocument::getPaintRoutine (const int index) const
  81. {
  82. int n = 0;
  83. for (int i = 0; i < 7; ++i)
  84. {
  85. if (paintStatesEnabled [i])
  86. {
  87. if (index == n)
  88. return paintRoutines [i];
  89. else
  90. ++n;
  91. }
  92. }
  93. jassertfalse;
  94. return 0;
  95. }
  96. void ButtonDocument::setStatePaintRoutineEnabled (const int index, bool b)
  97. {
  98. jassert (index > 0 && index < 7);
  99. if (paintStatesEnabled [index] != b)
  100. {
  101. paintStatesEnabled [index] = b;
  102. changed();
  103. }
  104. }
  105. bool ButtonDocument::isStatePaintRoutineEnabled (const int index) const
  106. {
  107. return paintStatesEnabled [index];
  108. }
  109. int ButtonDocument::chooseBestEnabledPaintRoutine (int paintRoutineWanted) const
  110. {
  111. switch (paintRoutineWanted)
  112. {
  113. case normalOff: return normalOff;
  114. case overOff: return paintStatesEnabled [overOff] ? overOff : normalOff;
  115. case downOff: return paintStatesEnabled [downOff] ? downOff : chooseBestEnabledPaintRoutine (overOff);
  116. case normalOn: return paintStatesEnabled [normalOn] ? normalOn : normalOff;
  117. case overOn: return paintStatesEnabled [overOn] ? overOn : (paintStatesEnabled [normalOn] ? normalOn : chooseBestEnabledPaintRoutine (overOff));
  118. case downOn: return paintStatesEnabled [downOn] ? downOn : ((paintStatesEnabled [overOn] || paintStatesEnabled [normalOn])
  119. ? chooseBestEnabledPaintRoutine (overOn)
  120. : chooseBestEnabledPaintRoutine (downOff));
  121. default: jassertfalse; break;
  122. }
  123. return normalOff;
  124. }
  125. //==============================================================================
  126. String ButtonDocument::getTypeName() const
  127. {
  128. return "Button";
  129. }
  130. JucerDocument* ButtonDocument::createCopy()
  131. {
  132. ButtonDocument* newOne = new ButtonDocument (cpp);
  133. newOne->resources = resources;
  134. ScopedPointer<XmlElement> xml (createXml());
  135. newOne->loadFromXml (*xml);
  136. return newOne;
  137. }
  138. XmlElement* ButtonDocument::createXml() const
  139. {
  140. XmlElement* const doc = JucerDocument::createXml();
  141. for (int i = 0; i < 7; ++i)
  142. {
  143. XmlElement* e = paintRoutines [i]->createXml();
  144. e->setAttribute ("buttonState", stateNames [i]);
  145. e->setAttribute ("enabled", paintStatesEnabled [i]);
  146. doc->addChildElement (e);
  147. }
  148. return doc;
  149. }
  150. bool ButtonDocument::loadFromXml (const XmlElement& xml)
  151. {
  152. if (JucerDocument::loadFromXml (xml))
  153. {
  154. for (int i = 7; --i >= 0;)
  155. paintStatesEnabled [i] = false;
  156. forEachXmlChildElementWithTagName (xml, e, PaintRoutine::xmlTagName)
  157. {
  158. const int stateIndex = stateNameToIndex (e->getStringAttribute ("buttonState"));
  159. paintRoutines [stateIndex]->loadFromXml (*e);
  160. paintStatesEnabled [stateIndex] = e->getBoolAttribute ("enabled", stateIndex < normalOn);
  161. }
  162. changed();
  163. getUndoManager().clearUndoHistory();
  164. return true;
  165. }
  166. return false;
  167. }
  168. void ButtonDocument::getOptionalMethods (StringArray& baseClasses,
  169. StringArray& returnValues,
  170. StringArray& methods,
  171. StringArray& initialContents) const
  172. {
  173. JucerDocument::getOptionalMethods (baseClasses, returnValues, methods, initialContents);
  174. addMethod ("Button", "void", "clicked()", "", baseClasses, returnValues, methods, initialContents);
  175. addMethod ("Button", "void", "buttonStateChanged()", "", baseClasses, returnValues, methods, initialContents);
  176. }
  177. //==============================================================================
  178. class ButtonStatePaintEnabledProperty : public BooleanPropertyComponent,
  179. private ChangeListener
  180. {
  181. public:
  182. ButtonStatePaintEnabledProperty (const String& name, ButtonDocument& doc, const int stateMethod_)
  183. : BooleanPropertyComponent (name, "enabled", "disabled"),
  184. document (doc),
  185. stateMethod (stateMethod_)
  186. {
  187. document.addChangeListener (this);
  188. }
  189. ~ButtonStatePaintEnabledProperty()
  190. {
  191. document.removeChangeListener (this);
  192. }
  193. void setState (bool newState)
  194. {
  195. document.setStatePaintRoutineEnabled (stateMethod, newState);
  196. }
  197. bool getState() const
  198. {
  199. return document.isStatePaintRoutineEnabled (stateMethod);
  200. }
  201. private:
  202. void changeListenerCallback (ChangeBroadcaster*)
  203. {
  204. refresh();
  205. }
  206. ButtonDocument& document;
  207. const int stateMethod;
  208. };
  209. void ButtonDocument::addExtraClassProperties (PropertyPanel& panel)
  210. {
  211. Array <PropertyComponent*> props;
  212. for (int i = 1; i < 7; ++i)
  213. props.add (new ButtonStatePaintEnabledProperty (stateNames[i], *this, i));
  214. panel.addSection ("Button paint routines", props);
  215. }
  216. //==============================================================================
  217. class ButtonTestComponent : public Button
  218. {
  219. public:
  220. ButtonTestComponent (ButtonDocument* const doc, const bool fillBackground)
  221. : Button (String()),
  222. document (doc),
  223. alwaysFillBackground (fillBackground)
  224. {
  225. setClickingTogglesState (true);
  226. }
  227. void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
  228. {
  229. if (document->paintStatesEnabled [background])
  230. {
  231. document->paintRoutines [background]->fillWithBackground (g, alwaysFillBackground);
  232. document->paintRoutines [background]->drawElements (g, getLocalBounds());
  233. }
  234. const int stateIndex
  235. = getToggleState()
  236. ? (isButtonDown ? document->chooseBestEnabledPaintRoutine (downOn)
  237. : (isMouseOverButton ? document->chooseBestEnabledPaintRoutine (overOn)
  238. : document->chooseBestEnabledPaintRoutine (normalOn)))
  239. : (isButtonDown ? document->chooseBestEnabledPaintRoutine (downOff)
  240. : (isMouseOverButton ? document->chooseBestEnabledPaintRoutine (overOff)
  241. : normalOff));
  242. document->paintRoutines [stateIndex]->fillWithBackground (g, ! document->paintStatesEnabled [background]);
  243. document->paintRoutines [stateIndex]->drawElements (g, getLocalBounds());
  244. }
  245. private:
  246. ButtonDocument* const document;
  247. const bool alwaysFillBackground;
  248. };
  249. Component* ButtonDocument::createTestComponent (const bool alwaysFillBackground)
  250. {
  251. return new ButtonTestComponent (this, alwaysFillBackground);
  252. }
  253. //==============================================================================
  254. void ButtonDocument::fillInGeneratedCode (GeneratedCode& code) const
  255. {
  256. JucerDocument::fillInGeneratedCode (code);
  257. code.parentClassInitialiser = "Button (" + quotedString (code.componentName, false) + ")";
  258. code.removeCallback ("void", "paint (Graphics& g)");
  259. }
  260. void ButtonDocument::fillInPaintCode (GeneratedCode& code) const
  261. {
  262. jassert (paintStatesEnabled [normalOff]);
  263. String paintCode [7];
  264. for (int i = 0; i < 7; ++i)
  265. if (paintStatesEnabled [i])
  266. paintRoutines[i]->fillInGeneratedCode (code, paintCode [i]);
  267. String& s = code.getCallbackCode ("public Button",
  268. "void",
  269. "paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown)",
  270. false);
  271. int numPaintRoutines = getNumPaintRoutines();
  272. if (paintStatesEnabled [background])
  273. {
  274. s << paintCode [background] << "\n";
  275. --numPaintRoutines;
  276. }
  277. if (numPaintRoutines == 1)
  278. {
  279. s << paintCode [normalOff];
  280. }
  281. else if (numPaintRoutines == downOff && (paintStatesEnabled [overOff] || paintStatesEnabled [downOff] || paintStatesEnabled [normalOn]))
  282. {
  283. if (paintStatesEnabled [normalOn])
  284. {
  285. s << "if (getToggleState())\n{\n "
  286. << CodeHelpers::indent (paintCode [normalOn], 4, false).trimEnd();
  287. }
  288. else if (paintStatesEnabled [overOff])
  289. {
  290. s << "if (isButtonDown || isMouseOverButton)\n{\n "
  291. << CodeHelpers::indent (paintCode [overOff], 4, false).trimEnd();
  292. }
  293. else
  294. {
  295. s << "if (isButtonDown)\n{\n "
  296. << CodeHelpers::indent (paintCode [downOff], 4, false).trimEnd();
  297. }
  298. s << "\n}\nelse\n{\n "
  299. << CodeHelpers::indent (paintCode [normalOff], 4, false).trimEnd()
  300. << "\n}\n";
  301. }
  302. else if (numPaintRoutines == normalOn && paintStatesEnabled [overOff] && paintStatesEnabled [downOff])
  303. {
  304. s << "if (isButtonDown)\n{\n "
  305. << CodeHelpers::indent (paintCode [downOff], 4, false).trimEnd()
  306. << "\n}\nelse if (isMouseOverButton)\n{\n "
  307. << CodeHelpers::indent (paintCode [overOff], 4, false).trimEnd()
  308. << "\n}\nelse\n{\n "
  309. << CodeHelpers::indent (paintCode [normalOff], 4, false).trimEnd()
  310. << "\n}\n";
  311. }
  312. else
  313. {
  314. if (paintStatesEnabled [normalOn] || paintStatesEnabled [overOn] || paintStatesEnabled [downOn])
  315. {
  316. s << "switch (getToggleState() ? (isButtonDown ? "
  317. << chooseBestEnabledPaintRoutine (downOn) << " : (isMouseOverButton ? "
  318. << chooseBestEnabledPaintRoutine (overOn) << " : "
  319. << chooseBestEnabledPaintRoutine (normalOn) << "))\n : (isButtonDown ? "
  320. << chooseBestEnabledPaintRoutine (downOff) << " : (isMouseOverButton ? "
  321. << chooseBestEnabledPaintRoutine (overOff) << " : 0)))\n{\n";
  322. }
  323. else
  324. {
  325. s << "switch (isButtonDown ? " << chooseBestEnabledPaintRoutine (downOff)
  326. << " : (isMouseOverButton ? " << chooseBestEnabledPaintRoutine (overOff)
  327. << " : 0))\n{\n";
  328. }
  329. for (int i = 0; i < 6; ++i)
  330. {
  331. if (paintStatesEnabled [i])
  332. {
  333. s << "case " << i << ":\n {\n "
  334. << CodeHelpers::indent (paintCode [i], 8, false).trimEnd()
  335. << "\n break;\n }\n\n";
  336. }
  337. }
  338. s << "default:\n break;\n}\n";
  339. }
  340. }