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.

430 lines
15KB

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