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.

437 lines
15KB

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