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.

440 lines
15KB

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