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.

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