/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2020 - Raw Material Software Limited You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #pragma once //============================================================================== class ComboBoxHandler : public ComponentTypeHandler { public: ComboBoxHandler() : ComponentTypeHandler ("Combo Box", "juce::ComboBox", typeid (ComboBox), 150, 24) {} Component* createNewComponent (JucerDocument*) override { return new ComboBox ("new combo box"); } XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override { ComboBox* const c = dynamic_cast (comp); jassert (c != nullptr); XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout); e->setAttribute ("editable", c->isTextEditable()); e->setAttribute ("layout", c->getJustificationType().getFlags()); e->setAttribute ("items", c->getProperties() ["items"].toString()); e->setAttribute ("textWhenNonSelected", c->getTextWhenNothingSelected()); e->setAttribute ("textWhenNoItems", c->getTextWhenNoChoicesAvailable()); return e; } bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override { if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout)) return false; ComboBox defaultBox; ComboBox* const c = dynamic_cast (comp); jassert (c != nullptr); c->setEditableText (xml.getBoolAttribute ("editable", defaultBox.isTextEditable())); c->setJustificationType (Justification (xml.getIntAttribute ("layout", defaultBox.getJustificationType().getFlags()))); c->getProperties().set ("items", xml.getStringAttribute ("items", String())); c->setTextWhenNothingSelected (xml.getStringAttribute ("textWhenNonSelected", defaultBox.getTextWhenNothingSelected())); c->setTextWhenNoChoicesAvailable (xml.getStringAttribute ("textWhenNoItems", defaultBox.getTextWhenNoChoicesAvailable())); updateItems (c); return true; } void getEditableProperties (Component* component, JucerDocument& document, Array& props, bool multipleSelected) override { ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected); if (multipleSelected) return; if (auto* c = dynamic_cast (component)) { props.add (new ComboItemsProperty (c, document)); props.add (new ComboEditableProperty (c, document)); props.add (new ComboJustificationProperty (c, document)); props.add (new ComboTextWhenNoneSelectedProperty (c, document)); props.add (new ComboTextWhenNoItemsProperty (c, document)); } } String getCreationParameters (GeneratedCode&, Component* component) override { return quotedString (component->getName(), false); } void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override { ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName); ComboBox* const c = dynamic_cast (component); jassert (c != nullptr); String s; s << memberVariableName << "->setEditableText (" << CodeHelpers::boolLiteral (c->isTextEditable()) << ");\n" << memberVariableName << "->setJustificationType (" << CodeHelpers::justificationToCode (c->getJustificationType()) << ");\n" << memberVariableName << "->setTextWhenNothingSelected (" << quotedString (c->getTextWhenNothingSelected(), code.shouldUseTransMacro()) << ");\n" << memberVariableName << "->setTextWhenNoChoicesAvailable (" << quotedString (c->getTextWhenNoChoicesAvailable(), code.shouldUseTransMacro()) << ");\n"; StringArray lines; lines.addLines (c->getProperties() ["items"].toString()); int itemId = 1; for (int i = 0; i < lines.size(); ++i) { if (lines[i].trim().isEmpty()) s << memberVariableName << "->addSeparator();\n"; else s << memberVariableName << "->addItem (" << quotedString (lines[i], code.shouldUseTransMacro()) << ", " << itemId++ << ");\n"; } if (needsCallback (component)) s << memberVariableName << "->addListener (this);\n"; s << '\n'; code.constructorCode += s; } void fillInGeneratedCode (Component* component, GeneratedCode& code) override { ComponentTypeHandler::fillInGeneratedCode (component, code); if (needsCallback (component)) { String& callback = code.getCallbackCode ("public juce::ComboBox::Listener", "void", "comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)", true); if (callback.trim().isNotEmpty()) callback << "else "; const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component)); const String userCodeComment ("UserComboBoxCode_" + memberVariableName); callback << "if (comboBoxThatHasChanged == " << memberVariableName << ".get())\n" << "{\n //[" << userCodeComment << "] -- add your combo box handling code here..\n //[/" << userCodeComment << "]\n}\n"; } } static void updateItems (ComboBox* c) { StringArray lines; lines.addLines (c->getProperties() ["items"].toString()); c->clear(); int itemId = 1; for (int i = 0; i < lines.size(); ++i) { if (lines[i].trim().isEmpty()) c->addSeparator(); else c->addItem (lines[i], itemId++); } } static bool needsCallback (Component*) { return true; // xxx should be configurable } private: class ComboEditableProperty : public ComponentBooleanProperty { public: ComboEditableProperty (ComboBox* comp, JucerDocument& doc) : ComponentBooleanProperty ("editable", "Text is editable", "Text is editable", comp, doc) { } void setState (bool newState) { document.perform (new ComboEditableChangeAction (component, *document.getComponentLayout(), newState), "Change combo box editability"); } bool getState() const { return component->isTextEditable(); } private: class ComboEditableChangeAction : public ComponentUndoableAction { public: ComboEditableChangeAction (ComboBox* const comp, ComponentLayout& l, const bool newState_) : ComponentUndoableAction (comp, l), newState (newState_) { oldState = comp->isTextEditable(); } bool perform() { showCorrectTab(); getComponent()->setEditableText (newState); changed(); return true; } bool undo() { showCorrectTab(); getComponent()->setEditableText (oldState); changed(); return true; } bool newState, oldState; }; }; //============================================================================== class ComboJustificationProperty : public JustificationProperty { public: ComboJustificationProperty (ComboBox* comp, JucerDocument& doc) : JustificationProperty ("text layout", false), component (comp), document (doc) { } void setJustification (Justification newJustification) { document.perform (new ComboJustifyChangeAction (component, *document.getComponentLayout(), newJustification), "Change combo box justification"); } Justification getJustification() const { return component->getJustificationType(); } private: ComboBox* const component; JucerDocument& document; class ComboJustifyChangeAction : public ComponentUndoableAction { public: ComboJustifyChangeAction (ComboBox* const comp, ComponentLayout& l, Justification newState_) : ComponentUndoableAction (comp, l), newState (newState_), oldState (comp->getJustificationType()) { } bool perform() { showCorrectTab(); getComponent()->setJustificationType (newState); changed(); return true; } bool undo() { showCorrectTab(); getComponent()->setJustificationType (oldState); changed(); return true; } Justification newState, oldState; }; }; //============================================================================== class ComboItemsProperty : public ComponentTextProperty { public: ComboItemsProperty (ComboBox* comp, JucerDocument& doc) : ComponentTextProperty ("items", 10000, true, comp, doc) {} void setText (const String& newText) override { document.perform (new ComboItemsChangeAction (component, *document.getComponentLayout(), newText), "Change combo box items"); } String getText() const override { return component->getProperties() ["items"]; } private: class ComboItemsChangeAction : public ComponentUndoableAction { public: ComboItemsChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_) : ComponentUndoableAction (comp, l), newState (newState_) { oldState = comp->getProperties() ["items"]; } bool perform() { showCorrectTab(); getComponent()->getProperties().set ("items", newState); ComboBoxHandler::updateItems (getComponent()); changed(); return true; } bool undo() { showCorrectTab(); getComponent()->getProperties().set ("items", oldState); ComboBoxHandler::updateItems (getComponent()); changed(); return true; } String newState, oldState; }; }; //============================================================================== class ComboTextWhenNoneSelectedProperty : public ComponentTextProperty { public: ComboTextWhenNoneSelectedProperty (ComboBox* comp, JucerDocument& doc) : ComponentTextProperty ("text when none selected", 200, false, comp, doc) {} void setText (const String& newText) override { document.perform (new ComboNonSelTextChangeAction (component, *document.getComponentLayout(), newText), "Change combo box text when nothing selected"); } String getText() const override { return component->getTextWhenNothingSelected(); } private: class ComboNonSelTextChangeAction : public ComponentUndoableAction { public: ComboNonSelTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_) : ComponentUndoableAction (comp, l), newState (newState_) { oldState = comp->getTextWhenNothingSelected(); } bool perform() { showCorrectTab(); getComponent()->setTextWhenNothingSelected (newState); changed(); return true; } bool undo() { showCorrectTab(); getComponent()->setTextWhenNothingSelected (oldState); changed(); return true; } String newState, oldState; }; }; //============================================================================== class ComboTextWhenNoItemsProperty : public ComponentTextProperty { public: ComboTextWhenNoItemsProperty (ComboBox* comp, JucerDocument& doc) : ComponentTextProperty ("text when no items", 200, false, comp, doc) {} void setText (const String& newText) override { document.perform (new ComboNoItemTextChangeAction (component, *document.getComponentLayout(), newText), "Change combo box 'no items' text"); } String getText() const override { return component->getTextWhenNoChoicesAvailable(); } private: class ComboNoItemTextChangeAction : public ComponentUndoableAction { public: ComboNoItemTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_) : ComponentUndoableAction (comp, l), newState (newState_) { oldState = comp->getTextWhenNoChoicesAvailable(); } bool perform() { showCorrectTab(); getComponent()->setTextWhenNoChoicesAvailable (newState); changed(); return true; } bool undo() { showCorrectTab(); getComponent()->setTextWhenNoChoicesAvailable (oldState); changed(); return true; } String newState, oldState; }; }; };