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.

438 lines
15KB

  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 ComboBoxHandler : public ComponentTypeHandler
  22. {
  23. public:
  24. ComboBoxHandler()
  25. : ComponentTypeHandler ("Combo Box", "ComboBox", typeid (ComboBox), 150, 24)
  26. {}
  27. Component* createNewComponent (JucerDocument*) override
  28. {
  29. return new ComboBox ("new combo box");
  30. }
  31. XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
  32. {
  33. ComboBox* const c = dynamic_cast<ComboBox*> (comp);
  34. jassert (c != nullptr);
  35. XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
  36. e->setAttribute ("editable", c->isTextEditable());
  37. e->setAttribute ("layout", c->getJustificationType().getFlags());
  38. e->setAttribute ("items", c->getProperties() ["items"].toString());
  39. e->setAttribute ("textWhenNonSelected", c->getTextWhenNothingSelected());
  40. e->setAttribute ("textWhenNoItems", c->getTextWhenNoChoicesAvailable());
  41. return e;
  42. }
  43. bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
  44. {
  45. if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
  46. return false;
  47. ComboBox defaultBox;
  48. ComboBox* const c = dynamic_cast<ComboBox*> (comp);
  49. jassert (c != nullptr);
  50. c->setEditableText (xml.getBoolAttribute ("editable", defaultBox.isTextEditable()));
  51. c->setJustificationType (Justification (xml.getIntAttribute ("layout", defaultBox.getJustificationType().getFlags())));
  52. c->getProperties().set ("items", xml.getStringAttribute ("items", String()));
  53. c->setTextWhenNothingSelected (xml.getStringAttribute ("textWhenNonSelected", defaultBox.getTextWhenNothingSelected()));
  54. c->setTextWhenNoChoicesAvailable (xml.getStringAttribute ("textWhenNoItems", defaultBox.getTextWhenNoChoicesAvailable()));
  55. updateItems (c);
  56. return true;
  57. }
  58. void getEditableProperties (Component* component, JucerDocument& document,
  59. Array<PropertyComponent*>& props, bool multipleSelected) override
  60. {
  61. ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
  62. if (multipleSelected)
  63. return;
  64. if (auto* c = dynamic_cast<ComboBox*> (component))
  65. {
  66. props.add (new ComboItemsProperty (c, document));
  67. props.add (new ComboEditableProperty (c, document));
  68. props.add (new ComboJustificationProperty (c, document));
  69. props.add (new ComboTextWhenNoneSelectedProperty (c, document));
  70. props.add (new ComboTextWhenNoItemsProperty (c, document));
  71. }
  72. }
  73. String getCreationParameters (GeneratedCode&, Component* component) override
  74. {
  75. return quotedString (component->getName(), false);
  76. }
  77. void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
  78. {
  79. ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
  80. ComboBox* const c = dynamic_cast<ComboBox*> (component);
  81. jassert (c != nullptr);
  82. String s;
  83. s << memberVariableName << "->setEditableText (" << CodeHelpers::boolLiteral (c->isTextEditable()) << ");\n"
  84. << memberVariableName << "->setJustificationType (" << CodeHelpers::justificationToCode (c->getJustificationType()) << ");\n"
  85. << memberVariableName << "->setTextWhenNothingSelected (" << quotedString (c->getTextWhenNothingSelected(), code.shouldUseTransMacro()) << ");\n"
  86. << memberVariableName << "->setTextWhenNoChoicesAvailable (" << quotedString (c->getTextWhenNoChoicesAvailable(), code.shouldUseTransMacro()) << ");\n";
  87. StringArray lines;
  88. lines.addLines (c->getProperties() ["items"].toString());
  89. int itemId = 1;
  90. for (int i = 0; i < lines.size(); ++i)
  91. {
  92. if (lines[i].trim().isEmpty())
  93. s << memberVariableName << "->addSeparator();\n";
  94. else
  95. s << memberVariableName << "->addItem ("
  96. << quotedString (lines[i], code.shouldUseTransMacro()) << ", " << itemId++ << ");\n";
  97. }
  98. if (needsCallback (component))
  99. s << memberVariableName << "->addListener (this);\n";
  100. s << '\n';
  101. code.constructorCode += s;
  102. }
  103. void fillInGeneratedCode (Component* component, GeneratedCode& code) override
  104. {
  105. ComponentTypeHandler::fillInGeneratedCode (component, code);
  106. if (needsCallback (component))
  107. {
  108. String& callback = code.getCallbackCode ("public ComboBox::Listener",
  109. "void",
  110. "comboBoxChanged (ComboBox* comboBoxThatHasChanged)",
  111. true);
  112. if (callback.trim().isNotEmpty())
  113. callback << "else ";
  114. const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
  115. const String userCodeComment ("UserComboBoxCode_" + memberVariableName);
  116. callback
  117. << "if (comboBoxThatHasChanged == " << memberVariableName
  118. << ")\n{\n //[" << userCodeComment << "] -- add your combo box handling code here..\n //[/" << userCodeComment << "]\n}\n";
  119. }
  120. }
  121. static void updateItems (ComboBox* c)
  122. {
  123. StringArray lines;
  124. lines.addLines (c->getProperties() ["items"].toString());
  125. c->clear();
  126. int itemId = 1;
  127. for (int i = 0; i < lines.size(); ++i)
  128. {
  129. if (lines[i].trim().isEmpty())
  130. c->addSeparator();
  131. else
  132. c->addItem (lines[i], itemId++);
  133. }
  134. }
  135. static bool needsCallback (Component*)
  136. {
  137. return true; // xxx should be configurable
  138. }
  139. private:
  140. class ComboEditableProperty : public ComponentBooleanProperty <ComboBox>
  141. {
  142. public:
  143. ComboEditableProperty (ComboBox* comp, JucerDocument& doc)
  144. : ComponentBooleanProperty <ComboBox> ("editable", "Text is editable", "Text is editable", comp, doc)
  145. {
  146. }
  147. void setState (bool newState)
  148. {
  149. document.perform (new ComboEditableChangeAction (component, *document.getComponentLayout(), newState),
  150. "Change combo box editability");
  151. }
  152. bool getState() const
  153. {
  154. return component->isTextEditable();
  155. }
  156. private:
  157. class ComboEditableChangeAction : public ComponentUndoableAction <ComboBox>
  158. {
  159. public:
  160. ComboEditableChangeAction (ComboBox* const comp, ComponentLayout& l, const bool newState_)
  161. : ComponentUndoableAction <ComboBox> (comp, l),
  162. newState (newState_)
  163. {
  164. oldState = comp->isTextEditable();
  165. }
  166. bool perform()
  167. {
  168. showCorrectTab();
  169. getComponent()->setEditableText (newState);
  170. changed();
  171. return true;
  172. }
  173. bool undo()
  174. {
  175. showCorrectTab();
  176. getComponent()->setEditableText (oldState);
  177. changed();
  178. return true;
  179. }
  180. bool newState, oldState;
  181. };
  182. };
  183. //==============================================================================
  184. class ComboJustificationProperty : public JustificationProperty
  185. {
  186. public:
  187. ComboJustificationProperty (ComboBox* comp, JucerDocument& doc)
  188. : JustificationProperty ("text layout", false),
  189. component (comp),
  190. document (doc)
  191. {
  192. }
  193. void setJustification (Justification newJustification)
  194. {
  195. document.perform (new ComboJustifyChangeAction (component, *document.getComponentLayout(), newJustification),
  196. "Change combo box justification");
  197. }
  198. Justification getJustification() const { return component->getJustificationType(); }
  199. private:
  200. ComboBox* const component;
  201. JucerDocument& document;
  202. class ComboJustifyChangeAction : public ComponentUndoableAction <ComboBox>
  203. {
  204. public:
  205. ComboJustifyChangeAction (ComboBox* const comp, ComponentLayout& l, Justification newState_)
  206. : ComponentUndoableAction <ComboBox> (comp, l),
  207. newState (newState_),
  208. oldState (comp->getJustificationType())
  209. {
  210. }
  211. bool perform()
  212. {
  213. showCorrectTab();
  214. getComponent()->setJustificationType (newState);
  215. changed();
  216. return true;
  217. }
  218. bool undo()
  219. {
  220. showCorrectTab();
  221. getComponent()->setJustificationType (oldState);
  222. changed();
  223. return true;
  224. }
  225. Justification newState, oldState;
  226. };
  227. };
  228. //==============================================================================
  229. class ComboItemsProperty : public ComponentTextProperty <ComboBox>
  230. {
  231. public:
  232. ComboItemsProperty (ComboBox* comp, JucerDocument& doc)
  233. : ComponentTextProperty <ComboBox> ("items", 10000, true, comp, doc)
  234. {}
  235. void setText (const String& newText) override
  236. {
  237. document.perform (new ComboItemsChangeAction (component, *document.getComponentLayout(), newText),
  238. "Change combo box items");
  239. }
  240. String getText() const override
  241. {
  242. return component->getProperties() ["items"];
  243. }
  244. private:
  245. class ComboItemsChangeAction : public ComponentUndoableAction <ComboBox>
  246. {
  247. public:
  248. ComboItemsChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
  249. : ComponentUndoableAction <ComboBox> (comp, l),
  250. newState (newState_)
  251. {
  252. oldState = comp->getProperties() ["items"];
  253. }
  254. bool perform()
  255. {
  256. showCorrectTab();
  257. getComponent()->getProperties().set ("items", newState);
  258. ComboBoxHandler::updateItems (getComponent());
  259. changed();
  260. return true;
  261. }
  262. bool undo()
  263. {
  264. showCorrectTab();
  265. getComponent()->getProperties().set ("items", oldState);
  266. ComboBoxHandler::updateItems (getComponent());
  267. changed();
  268. return true;
  269. }
  270. String newState, oldState;
  271. };
  272. };
  273. //==============================================================================
  274. class ComboTextWhenNoneSelectedProperty : public ComponentTextProperty <ComboBox>
  275. {
  276. public:
  277. ComboTextWhenNoneSelectedProperty (ComboBox* comp, JucerDocument& doc)
  278. : ComponentTextProperty <ComboBox> ("text when none selected", 200, false, comp, doc)
  279. {}
  280. void setText (const String& newText) override
  281. {
  282. document.perform (new ComboNonSelTextChangeAction (component, *document.getComponentLayout(), newText),
  283. "Change combo box text when nothing selected");
  284. }
  285. String getText() const override
  286. {
  287. return component->getTextWhenNothingSelected();
  288. }
  289. private:
  290. class ComboNonSelTextChangeAction : public ComponentUndoableAction <ComboBox>
  291. {
  292. public:
  293. ComboNonSelTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
  294. : ComponentUndoableAction <ComboBox> (comp, l),
  295. newState (newState_)
  296. {
  297. oldState = comp->getTextWhenNothingSelected();
  298. }
  299. bool perform()
  300. {
  301. showCorrectTab();
  302. getComponent()->setTextWhenNothingSelected (newState);
  303. changed();
  304. return true;
  305. }
  306. bool undo()
  307. {
  308. showCorrectTab();
  309. getComponent()->setTextWhenNothingSelected (oldState);
  310. changed();
  311. return true;
  312. }
  313. String newState, oldState;
  314. };
  315. };
  316. //==============================================================================
  317. class ComboTextWhenNoItemsProperty : public ComponentTextProperty <ComboBox>
  318. {
  319. public:
  320. ComboTextWhenNoItemsProperty (ComboBox* comp, JucerDocument& doc)
  321. : ComponentTextProperty <ComboBox> ("text when no items", 200, false, comp, doc)
  322. {}
  323. void setText (const String& newText) override
  324. {
  325. document.perform (new ComboNoItemTextChangeAction (component, *document.getComponentLayout(), newText),
  326. "Change combo box 'no items' text");
  327. }
  328. String getText() const override
  329. {
  330. return component->getTextWhenNoChoicesAvailable();
  331. }
  332. private:
  333. class ComboNoItemTextChangeAction : public ComponentUndoableAction <ComboBox>
  334. {
  335. public:
  336. ComboNoItemTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
  337. : ComponentUndoableAction <ComboBox> (comp, l),
  338. newState (newState_)
  339. {
  340. oldState = comp->getTextWhenNoChoicesAvailable();
  341. }
  342. bool perform()
  343. {
  344. showCorrectTab();
  345. getComponent()->setTextWhenNoChoicesAvailable (newState);
  346. changed();
  347. return true;
  348. }
  349. bool undo()
  350. {
  351. showCorrectTab();
  352. getComponent()->setTextWhenNoChoicesAvailable (oldState);
  353. changed();
  354. return true;
  355. }
  356. String newState, oldState;
  357. };
  358. };
  359. };