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.

447 lines
16KB

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