/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI 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 ComponentListComp : public TreePanelBase, private ActivityList::Listener { public: ComponentListComp (CompileEngineChildProcess& c) : TreePanelBase (&c.project, "compClassTreeState"), owner (c) { setName ("Components"); tree.setRootItemVisible (false); tree.setMultiSelectEnabled (false); tree.setDefaultOpenness (true); setRoot (new NamespaceItem (&owner.getComponentList().globalNamespace)); classListChanged (owner.getComponentList()); owner.activityList.addListener (this); } ~ComponentListComp() { saveOpenness(); owner.activityList.removeListener (this); } void classListChanged (const ClassDatabase::ClassList& newClasses) override { static_cast (rootItem.get())->setNamespace (&newClasses.globalNamespace); } void openPreview (const ClassDatabase::Class& comp) { owner.openPreview (comp); } void showClassDeclaration (const ClassDatabase::Class& comp) { owner.handleHighlightCode (comp.getClassDeclarationRange()); } private: CompileEngineChildProcess& owner; struct ClassItem; struct NamespaceItem : public JucerTreeViewBase { NamespaceItem (const ClassDatabase::Namespace* n) { setNamespace (n); } void setNamespace (const ClassDatabase::Namespace* newNamespace) { namespaceToShow = newNamespace; uniqueID = namespaceToShow != nullptr ? "ns_" + namespaceToShow->fullName : "null"; refreshSubItems(); } String getRenamingName() const override { return getDisplayName(); } String getDisplayName() const override { return (namespaceToShow != nullptr ? namespaceToShow->name : String::empty) + "::"; } void setName (const String&) override {} bool isMissing() override { return false; } Icon getIcon() const override { return Icon (getIcons().graph, getContrastingColour (Colours::darkred, 0.5f)); } bool canBeSelected() const override { return true; } bool mightContainSubItems() override { return namespaceToShow != nullptr && ! namespaceToShow->isEmpty(); } String getUniqueName() const override { return uniqueID; } void addSubItems() override { if (namespaceToShow != nullptr) { Array newComps; Array newNamespaces; for (const auto& c : namespaceToShow->components) newComps.addSorted (*this, new ClassItem (c, *namespaceToShow)); for(const auto& n : namespaceToShow->namespaces) { if (n.getTotalClassesAndNamespaces() < 10) createFlatItemList (n, newComps); else newNamespaces.add (new NamespaceItem (&n)); } for (auto c : newComps) addSubItem (c); for (auto n : newNamespaces) addSubItem (n); } } void createFlatItemList (const ClassDatabase::Namespace& ns, Array& newComps) { for (const auto& c : ns.components) newComps.addSorted (*this, new ClassItem (c, *namespaceToShow)); for (const auto& n : ns.namespaces) createFlatItemList (n, newComps); } static int compareElements (ClassItem* c1, ClassItem* c2) { return c1->comp.getName().compareIgnoreCase (c2->comp.getName()); } private: const ClassDatabase::Namespace* namespaceToShow = nullptr; String uniqueID; // must be stored rather than calculated, in case the namespace obj is dangling }; struct ClassItem : public JucerTreeViewBase { ClassItem (const ClassDatabase::Class& c, const ClassDatabase::Namespace& parentNS) : comp (c), displayName (comp.getName().substring (parentNS.fullName.length())) { } String getRenamingName() const override { return getDisplayName(); } String getDisplayName() const override { return displayName; } void setName (const String&) override {} bool isMissing() override { return false; } Icon getIcon() const override { return Icon (getIcons().box, getTextColour()); } bool canBeSelected() const override { return true; } bool mightContainSubItems() override { return false; } String getUniqueName() const override { return comp.getName(); } bool canBeLaunched() const { return comp.getInstantiationFlags().canBeInstantiated(); } void showClassDeclaration() const { if (ComponentListComp* clc = getOwnerView()->findParentComponentOfClass()) clc->showClassDeclaration (comp); } void launchEditor() const { if (ComponentListComp* clc = getOwnerView()->findParentComponentOfClass()) clc->openPreview (comp); } void itemClicked (const MouseEvent&) override { if (! canBeLaunched()) if (ProjectContentComponent* const pcc = getOwnerView()->findParentComponentOfClass()) pcc->showBubbleMessage (pcc->getLocalArea (getOwnerView(), getItemPosition (true)), "Cannot create a live view:\n" + comp.getInstantiationFlags().getReasonForUnavailability()); } void itemDoubleClicked (const MouseEvent&) override { if (canBeLaunched()) launchEditor(); else showClassDeclaration(); } void paintContent (Graphics& g, const Rectangle& area) override { g.setFont (getFont()); g.setColour (getTextColour()); g.drawFittedText (getDisplayName(), area.withWidth (area.getWidth() - 40), // to account for buttons Justification::centredLeft, 1, 0.8f); } Colour getTextColour() const { return getContrastingColour (comp.getInstantiationFlags().canBeInstantiated() ? 0.8f : 0.3f); } Component* createItemComponent() override { Component* c = JucerTreeViewBase::createItemComponent(); jassert (dynamic_cast (c) != nullptr); if (canBeLaunched()) static_cast (c)->addRightHandButton (new ClassItemButton (*this, false)); static_cast (c)->addRightHandButton (new ClassItemButton (*this, true)); return c; } struct ClassItemButton : public Button { ClassItemButton (const ClassItem& c, bool isShowCodeButton) : Button (String()), classItem (c), isShowCode (isShowCodeButton) { setMouseCursor (MouseCursor::PointingHandCursor); } void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override { const Path& path = isShowCode ? getIcons().code : getIcons().play; Colour col (classItem.getBackgroundColour().contrasting (isShowCode ? Colours::white : Colours::lightgreen, 0.6f)); Icon (path, col.withAlpha (isButtonDown ? 1.0f : (isMouseOverButton ? 0.8f : 0.5f))) .draw (g, getLocalBounds().reduced (getHeight() / 5).toFloat(), false); } void clicked() override { if (isShowCode) classItem.showClassDeclaration(); else classItem.launchEditor(); } const ClassItem& classItem; bool isShowCode; }; const ClassDatabase::Class comp; String displayName; }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentListComp) };