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.

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