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.

428 lines
15KB

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