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.

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