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.

391 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #pragma once
  20. //==============================================================================
  21. class ButtonHandler : public ComponentTypeHandler
  22. {
  23. public:
  24. ButtonHandler (const String& typeDescription_,
  25. const String& className_,
  26. const std::type_info& componentClass,
  27. const int defaultWidth_,
  28. const int defaultHeight_)
  29. : ComponentTypeHandler (typeDescription_, className_, componentClass,
  30. defaultWidth_, defaultHeight_)
  31. {}
  32. void getEditableProperties (Component* component, JucerDocument& document,
  33. Array<PropertyComponent*>& props, bool multipleSelected) override
  34. {
  35. ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
  36. if (multipleSelected)
  37. return;
  38. if (auto* b = dynamic_cast<Button*> (component))
  39. {
  40. props.add (new ButtonTextProperty (b, document));
  41. props.add (new ButtonCallbackProperty (b, document));
  42. props.add (new ButtonRadioGroupProperty (b, document));
  43. props.add (new ButtonConnectedEdgeProperty ("connected left", Button::ConnectedOnLeft, b, document));
  44. props.add (new ButtonConnectedEdgeProperty ("connected right", Button::ConnectedOnRight, b, document));
  45. props.add (new ButtonConnectedEdgeProperty ("connected top", Button::ConnectedOnTop, b, document));
  46. props.add (new ButtonConnectedEdgeProperty ("connected bottom", Button::ConnectedOnBottom, b, document));
  47. }
  48. }
  49. XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
  50. {
  51. Button* const b = dynamic_cast<Button*> (comp);
  52. XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
  53. e->setAttribute ("buttonText", b->getButtonText());
  54. e->setAttribute ("connectedEdges", b->getConnectedEdgeFlags());
  55. e->setAttribute ("needsCallback", needsButtonListener (b));
  56. e->setAttribute ("radioGroupId", b->getRadioGroupId());
  57. return e;
  58. }
  59. bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
  60. {
  61. Button* const b = dynamic_cast<Button*> (comp);
  62. if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
  63. return false;
  64. b->setButtonText (xml.getStringAttribute ("buttonText", b->getButtonText()));
  65. b->setConnectedEdges (xml.getIntAttribute ("connectedEdges", 0));
  66. setNeedsButtonListener (b, xml.getBoolAttribute ("needsCallback", true));
  67. b->setRadioGroupId (xml.getIntAttribute ("radioGroupId", 0));
  68. return true;
  69. }
  70. String getCreationParameters (GeneratedCode&, Component* component) override
  71. {
  72. return quotedString (component->getName(), false);
  73. }
  74. void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
  75. {
  76. ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
  77. Button* const b = dynamic_cast<Button*> (component);
  78. if (b->getButtonText() != b->getName())
  79. {
  80. code.constructorCode
  81. << memberVariableName << "->setButtonText ("
  82. << quotedString (b->getButtonText(), code.shouldUseTransMacro()) << ");\n";
  83. }
  84. if (b->getConnectedEdgeFlags() != 0)
  85. {
  86. StringArray flags;
  87. if (b->isConnectedOnLeft())
  88. flags.add ("Button::ConnectedOnLeft");
  89. if (b->isConnectedOnRight())
  90. flags.add ("Button::ConnectedOnRight");
  91. if (b->isConnectedOnTop())
  92. flags.add ("Button::ConnectedOnTop");
  93. if (b->isConnectedOnBottom())
  94. flags.add ("Button::ConnectedOnBottom");
  95. String s;
  96. s << memberVariableName << "->setConnectedEdges ("
  97. << flags.joinIntoString (" | ") << ");\n";
  98. code.constructorCode += s;
  99. }
  100. if (b->getRadioGroupId() != 0)
  101. code.constructorCode << memberVariableName << "->setRadioGroupId ("
  102. << b->getRadioGroupId() << ");\n";
  103. if (needsButtonListener (component))
  104. code.constructorCode << memberVariableName << "->addListener (this);\n";
  105. }
  106. void fillInGeneratedCode (Component* component, GeneratedCode& code) override
  107. {
  108. ComponentTypeHandler::fillInGeneratedCode (component, code);
  109. if (needsButtonListener (component))
  110. {
  111. String& callback = code.getCallbackCode ("public Button::Listener",
  112. "void",
  113. "buttonClicked (Button* buttonThatWasClicked)",
  114. true);
  115. if (callback.isNotEmpty())
  116. callback << "else ";
  117. const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
  118. const String userCodeComment ("UserButtonCode_" + memberVariableName);
  119. callback
  120. << "if (buttonThatWasClicked == " << memberVariableName
  121. << ")\n{\n //[" << userCodeComment << "] -- add your button handler code here..\n //[/" << userCodeComment << "]\n}\n";
  122. }
  123. }
  124. static bool needsButtonListener (Component* button)
  125. {
  126. return button->getProperties().getWithDefault ("generateListenerCallback", true);
  127. }
  128. static void setNeedsButtonListener (Component* button, const bool shouldDoCallback)
  129. {
  130. button->getProperties().set ("generateListenerCallback", shouldDoCallback);
  131. }
  132. private:
  133. //==============================================================================
  134. class ButtonTextProperty : public ComponentTextProperty <Button>
  135. {
  136. public:
  137. ButtonTextProperty (Button* button_, JucerDocument& doc)
  138. : ComponentTextProperty <Button> ("text", 100, false, button_, doc)
  139. {
  140. }
  141. void setText (const String& newText) override
  142. {
  143. document.perform (new ButtonTextChangeAction (component, *document.getComponentLayout(), newText),
  144. "Change button text");
  145. }
  146. String getText() const override
  147. {
  148. return component->getButtonText();
  149. }
  150. private:
  151. class ButtonTextChangeAction : public ComponentUndoableAction <Button>
  152. {
  153. public:
  154. ButtonTextChangeAction (Button* const comp, ComponentLayout& l, const String& newName_)
  155. : ComponentUndoableAction <Button> (comp, l),
  156. newName (newName_)
  157. {
  158. oldName = comp->getButtonText();
  159. }
  160. bool perform()
  161. {
  162. showCorrectTab();
  163. getComponent()->setButtonText (newName);
  164. changed();
  165. return true;
  166. }
  167. bool undo()
  168. {
  169. showCorrectTab();
  170. getComponent()->setButtonText (oldName);
  171. changed();
  172. return true;
  173. }
  174. String newName, oldName;
  175. };
  176. };
  177. class ButtonCallbackProperty : public ComponentBooleanProperty <Button>
  178. {
  179. public:
  180. ButtonCallbackProperty (Button* b, JucerDocument& doc)
  181. : ComponentBooleanProperty <Button> ("callback", "Generate ButtonListener", "Generate ButtonListener", b, doc)
  182. {
  183. }
  184. void setState (bool newState)
  185. {
  186. document.perform (new ButtonCallbackChangeAction (component, *document.getComponentLayout(), newState),
  187. "Change button callback");
  188. }
  189. bool getState() const { return needsButtonListener (component); }
  190. private:
  191. class ButtonCallbackChangeAction : public ComponentUndoableAction <Button>
  192. {
  193. public:
  194. ButtonCallbackChangeAction (Button* const comp, ComponentLayout& l, const bool newState_)
  195. : ComponentUndoableAction <Button> (comp, l),
  196. newState (newState_)
  197. {
  198. oldState = needsButtonListener (comp);
  199. }
  200. bool perform()
  201. {
  202. showCorrectTab();
  203. setNeedsButtonListener (getComponent(), newState);
  204. changed();
  205. return true;
  206. }
  207. bool undo()
  208. {
  209. showCorrectTab();
  210. setNeedsButtonListener (getComponent(), oldState);
  211. changed();
  212. return true;
  213. }
  214. bool newState, oldState;
  215. };
  216. };
  217. class ButtonRadioGroupProperty : public ComponentTextProperty <Button>
  218. {
  219. public:
  220. ButtonRadioGroupProperty (Button* const button_, JucerDocument& doc)
  221. : ComponentTextProperty <Button> ("radio group", 10, false, button_, doc)
  222. {
  223. }
  224. void setText (const String& newText) override
  225. {
  226. document.perform (new ButtonRadioGroupChangeAction (component, *document.getComponentLayout(), newText.getIntValue()),
  227. "Change radio group ID");
  228. }
  229. String getText() const override
  230. {
  231. return String (component->getRadioGroupId());
  232. }
  233. private:
  234. class ButtonRadioGroupChangeAction : public ComponentUndoableAction <Button>
  235. {
  236. public:
  237. ButtonRadioGroupChangeAction (Button* const comp, ComponentLayout& l, const int newId_)
  238. : ComponentUndoableAction <Button> (comp, l),
  239. newId (newId_)
  240. {
  241. oldId = comp->getRadioGroupId();
  242. }
  243. bool perform()
  244. {
  245. showCorrectTab();
  246. getComponent()->setRadioGroupId (newId);
  247. changed();
  248. return true;
  249. }
  250. bool undo()
  251. {
  252. showCorrectTab();
  253. getComponent()->setRadioGroupId (oldId);
  254. changed();
  255. return true;
  256. }
  257. int newId, oldId;
  258. };
  259. };
  260. class ButtonConnectedEdgeProperty : public ComponentBooleanProperty <Button>
  261. {
  262. public:
  263. ButtonConnectedEdgeProperty (const String& name, const int flag_,
  264. Button* b, JucerDocument& doc)
  265. : ComponentBooleanProperty <Button> (name, "Connected", "Connected", b, doc),
  266. flag (flag_)
  267. {
  268. }
  269. void setState (bool newState)
  270. {
  271. document.perform (new ButtonConnectedChangeAction (component, *document.getComponentLayout(), flag, newState),
  272. "Change button connected edges");
  273. }
  274. bool getState() const
  275. {
  276. return (component->getConnectedEdgeFlags() & flag) != 0;
  277. }
  278. private:
  279. const int flag;
  280. class ButtonConnectedChangeAction : public ComponentUndoableAction <Button>
  281. {
  282. public:
  283. ButtonConnectedChangeAction (Button* const comp, ComponentLayout& l, const int flag_, const bool newState_)
  284. : ComponentUndoableAction <Button> (comp, l),
  285. flag (flag_),
  286. newState (newState_)
  287. {
  288. oldState = ((comp->getConnectedEdgeFlags() & flag) != 0);
  289. }
  290. bool perform()
  291. {
  292. showCorrectTab();
  293. if (newState)
  294. getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
  295. else
  296. getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
  297. changed();
  298. return true;
  299. }
  300. bool undo()
  301. {
  302. showCorrectTab();
  303. if (oldState)
  304. getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
  305. else
  306. getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
  307. changed();
  308. return true;
  309. }
  310. const int flag;
  311. bool newState, oldState;
  312. };
  313. };
  314. };