/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2013 - Raw Material Software Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class PropertyGroupComponent : public Component { public: PropertyGroupComponent() {} void setProperties (const PropertyListBuilder& newProps) { properties.clear(); properties.addArray (newProps.components); for (int i = properties.size(); --i >= 0;) addAndMakeVisible (properties.getUnchecked(i)); } int updateSize (int x, int y, int width) { int height = 38; for (int i = 0; i < properties.size(); ++i) { PropertyComponent* pp = properties.getUnchecked(i); pp->setBounds (10, height, width - 20, pp->getPreferredHeight()); height += pp->getHeight(); } height += 16; setBounds (x, y, width, height); return height; } void paint (Graphics& g) override { const Colour bkg (findColour (mainBackgroundColourId)); g.setColour (Colours::white.withAlpha (0.35f)); g.fillRect (0, 30, getWidth(), getHeight() - 38); g.setFont (Font (15.0f, Font::bold)); g.setColour (bkg.contrasting (0.7f)); g.drawFittedText (getName(), 12, 0, getWidth() - 16, 25, Justification::bottomLeft, 1); } OwnedArray properties; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent) }; //============================================================================== class ConfigTreeItemBase : public JucerTreeViewBase, public ValueTree::Listener { public: ConfigTreeItemBase() {} void showSettingsPage (Component* content) { content->setComponentID (getUniqueName()); ScopedPointer comp (content); if (ProjectContentComponent* pcc = getProjectContentComponent()) pcc->setEditorComponent (new PropertyPanelViewport (comp.release()), nullptr); } void closeSettingsPage() { if (ProjectContentComponent* pcc = getProjectContentComponent()) { if (PropertyPanelViewport* ppv = dynamic_cast (pcc->getEditorComponent())) if (ppv->viewport.getViewedComponent()->getComponentID() == getUniqueName()) pcc->hideEditor(); } } void deleteAllSelectedItems() override { TreeView* const tree = getOwnerView(); jassert (tree->getNumSelectedItems() <= 1); // multi-select should be disabled if (ConfigTreeItemBase* s = dynamic_cast (tree->getSelectedItem (0))) s->deleteItem(); } void itemOpennessChanged (bool isNowOpen) override { if (isNowOpen) refreshSubItems(); } void valueTreePropertyChanged (ValueTree&, const Identifier&) override {} void valueTreeChildAdded (ValueTree&, ValueTree&) override {} void valueTreeChildRemoved (ValueTree&, ValueTree&) override {} void valueTreeChildOrderChanged (ValueTree&) override {} void valueTreeParentChanged (ValueTree&) override {} virtual bool isProjectSettings() const { return false; } virtual bool isModulesList() const { return false; } static void updateSize (Component& comp, PropertyGroupComponent& group) { const int width = jmax (550, comp.getParentWidth() - 20); int y = 0; y += group.updateSize (12, y, width - 12); comp.setSize (width, y); } private: //============================================================================== class PropertyPanelViewport : public Component { public: PropertyPanelViewport (Component* content) { addAndMakeVisible (viewport); addAndMakeVisible (rolloverHelp); viewport.setViewedComponent (content, true); } void paint (Graphics& g) override { IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g); } void resized() override { Rectangle r (getLocalBounds()); rolloverHelp.setBounds (r.removeFromBottom (70).reduced (10, 0)); viewport.setBounds (r); } Viewport viewport; RolloverHelpComp rolloverHelp; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyPanelViewport) }; }; //============================================================================== class RootItem : public ConfigTreeItemBase { public: RootItem (Project& p) : project (p), exportersTree (p.getExporters()) { exportersTree.addListener (this); } bool isProjectSettings() const override { return true; } String getRenamingName() const override { return getDisplayName(); } String getDisplayName() const override { return project.getTitle(); } void setName (const String&) override {} bool isMissing() override { return false; } Icon getIcon() const override { return project.getMainGroup().getIcon().withContrastingColourTo (getBackgroundColour()); } void showDocument() override { showSettingsPage (new SettingsComp (project)); } bool canBeSelected() const override { return true; } bool mightContainSubItems() override { return project.getNumExporters() > 0; } String getUniqueName() const override { return "config_root"; } void addSubItems() override { addSubItem (new EnabledModulesItem (project)); IntrojucerApp::getApp().addExtraConfigItems (project, *this); int i = 0; for (Project::ExporterIterator exporter (project); exporter.next(); ++i) addSubItem (new ExporterItem (project, exporter.exporter.release(), i)); } void showPopupMenu() override { PopupMenu menu; const StringArray exporters (ProjectExporter::getExporterNames()); for (int i = 0; i < exporters.size(); ++i) menu.addItem (i + 1, "Create a new " + exporters[i] + " target"); launchPopupMenu (menu); } void handlePopupMenuResult (int resultCode) override { if (resultCode > 0) { String exporterName (ProjectExporter::getExporterNames() [resultCode - 1]); if (exporterName.isNotEmpty()) project.addNewExporter (exporterName); } } bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override { return dragSourceDetails.description.toString().startsWith (getUniqueName()); } void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override { int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue(); exportersTree.moveChild (oldIndex, jmax (0, insertIndex - 1), project.getUndoManagerFor (exportersTree)); } //============================================================================== void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); } void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); } void valueTreeChildOrderChanged (ValueTree& parentTree) override { refreshIfNeeded (parentTree); } void refreshIfNeeded (ValueTree& changedTree) { if (changedTree == exportersTree) refreshSubItems(); } private: Project& project; ValueTree exportersTree; //============================================================================== class SettingsComp : public Component, private ChangeListener { public: SettingsComp (Project& p) : project (p) { addAndMakeVisible (group); updatePropertyList(); project.addChangeListener (this); } ~SettingsComp() { project.removeChangeListener (this); } void parentSizeChanged() override { updateSize (*this, group); } void updatePropertyList() { PropertyListBuilder props; project.createPropertyEditors (props); group.setProperties (props); group.setName ("Project Settings"); lastProjectType = project.getProjectTypeValue().getValue(); parentSizeChanged(); } void changeListenerCallback (ChangeBroadcaster*) override { if (lastProjectType != project.getProjectTypeValue().getValue()) updatePropertyList(); } private: Project& project; var lastProjectType; PropertyGroupComponent group; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp) }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RootItem) };