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.

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