|  | /*
  ==============================================================================
   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-11 by Raw Material Software Ltd.
  ------------------------------------------------------------------------------
   JUCE can be redistributed and/or modified under the terms of the GNU General
   Public License (Version 2), as published by the Free Software Foundation.
   A copy of the license is included in the JUCE distribution, or can be found
   online 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.rawmaterialsoftware.com/juce for more information.
  ==============================================================================
*/
class PropertyPanel::SectionComponent  : public Component
{
public:
    SectionComponent (const String& sectionTitle,
                      const Array <PropertyComponent*>& newProperties,
                      const bool sectionIsOpen_)
        : Component (sectionTitle),
          titleHeight (sectionTitle.isNotEmpty() ? 22 : 0),
          sectionIsOpen (sectionIsOpen_)
    {
        propertyComps.addArray (newProperties);
        for (int i = propertyComps.size(); --i >= 0;)
        {
            addAndMakeVisible (propertyComps.getUnchecked(i));
            propertyComps.getUnchecked(i)->refresh();
        }
    }
    ~SectionComponent()
    {
        propertyComps.clear();
    }
    void paint (Graphics& g)
    {
        if (titleHeight > 0)
            getLookAndFeel().drawPropertyPanelSectionHeader (g, getName(), isOpen(), getWidth(), titleHeight);
    }
    void resized()
    {
        int y = titleHeight;
        for (int i = 0; i < propertyComps.size(); ++i)
        {
            PropertyComponent* const pec = propertyComps.getUnchecked (i);
            pec->setBounds (1, y, getWidth() - 2, pec->getPreferredHeight());
            y = pec->getBottom();
        }
    }
    int getPreferredHeight() const
    {
        int y = titleHeight;
        if (isOpen())
        {
            for (int i = propertyComps.size(); --i >= 0;)
                y += propertyComps.getUnchecked(i)->getPreferredHeight();
        }
        return y;
    }
    void setOpen (const bool open)
    {
        if (sectionIsOpen != open)
        {
            sectionIsOpen = open;
            for (int i = propertyComps.size(); --i >= 0;)
                propertyComps.getUnchecked(i)->setVisible (open);
            PropertyPanel* const pp = findParentComponentOfClass<PropertyPanel>();
            if (pp != nullptr)
                pp->resized();
        }
    }
    bool isOpen() const
    {
        return sectionIsOpen;
    }
    void refreshAll() const
    {
        for (int i = propertyComps.size(); --i >= 0;)
            propertyComps.getUnchecked (i)->refresh();
    }
    void mouseUp (const MouseEvent& e)
    {
        if (e.getMouseDownX() < titleHeight
             && e.x < titleHeight
             && e.y < titleHeight
             && e.getNumberOfClicks() != 2)
        {
            setOpen (! isOpen());
        }
    }
    void mouseDoubleClick (const MouseEvent& e)
    {
        if (e.y < titleHeight)
            setOpen (! isOpen());
    }
private:
    OwnedArray <PropertyComponent> propertyComps;
    int titleHeight;
    bool sectionIsOpen;
    JUCE_DECLARE_NON_COPYABLE (SectionComponent);
};
//==============================================================================
class PropertyPanel::PropertyHolderComponent  : public Component
{
public:
    PropertyHolderComponent() {}
    void paint (Graphics&) {}
    void updateLayout (int width)
    {
        int y = 0;
        for (int i = 0; i < sections.size(); ++i)
        {
            SectionComponent* const section = sections.getUnchecked(i);
            section->setBounds (0, y, width, section->getPreferredHeight());
            y = section->getBottom();
        }
        setSize (width, y);
        repaint();
    }
    void refreshAll() const
    {
        for (int i = 0; i < sections.size(); ++i)
            sections.getUnchecked(i)->refreshAll();
    }
    void clear()
    {
        sections.clear();
    }
    void addSection (SectionComponent* newSection)
    {
        sections.add (newSection);
        addAndMakeVisible (newSection, 0);
    }
    int getNumSections() const noexcept                     { return sections.size(); }
    SectionComponent* getSection (const int index) const    { return sections [index]; }
private:
    OwnedArray<SectionComponent> sections;
    JUCE_DECLARE_NON_COPYABLE (PropertyHolderComponent);
};
//==============================================================================
PropertyPanel::PropertyPanel()
{
    messageWhenEmpty = TRANS("(nothing selected)");
    addAndMakeVisible (&viewport);
    viewport.setViewedComponent (propertyHolderComponent = new PropertyHolderComponent());
    viewport.setFocusContainer (true);
}
PropertyPanel::~PropertyPanel()
{
    clear();
}
//==============================================================================
void PropertyPanel::paint (Graphics& g)
{
    if (propertyHolderComponent->getNumSections() == 0)
    {
        g.setColour (Colours::black.withAlpha (0.5f));
        g.setFont (14.0f);
        g.drawText (messageWhenEmpty, getLocalBounds().withHeight (30),
                    Justification::centred, true);
    }
}
void PropertyPanel::resized()
{
    viewport.setBounds (getLocalBounds());
    updatePropHolderLayout();
}
//==============================================================================
void PropertyPanel::clear()
{
    if (propertyHolderComponent->getNumSections() > 0)
    {
        propertyHolderComponent->clear();
        repaint();
    }
}
void PropertyPanel::addProperties (const Array <PropertyComponent*>& newProperties)
{
    if (propertyHolderComponent->getNumSections() == 0)
        repaint();
    propertyHolderComponent->addSection (new SectionComponent (String::empty, newProperties, true));
    updatePropHolderLayout();
}
void PropertyPanel::addSection (const String& sectionTitle,
                                const Array <PropertyComponent*>& newProperties,
                                const bool shouldBeOpen)
{
    jassert (sectionTitle.isNotEmpty());
    if (propertyHolderComponent->getNumSections() == 0)
        repaint();
    propertyHolderComponent->addSection (new SectionComponent (sectionTitle, newProperties, shouldBeOpen));
    updatePropHolderLayout();
}
void PropertyPanel::updatePropHolderLayout() const
{
    const int maxWidth = viewport.getMaximumVisibleWidth();
    propertyHolderComponent->updateLayout (maxWidth);
    const int newMaxWidth = viewport.getMaximumVisibleWidth();
    if (maxWidth != newMaxWidth)
    {
        // need to do this twice because of scrollbars changing the size, etc.
        propertyHolderComponent->updateLayout (newMaxWidth);
    }
}
void PropertyPanel::refreshAll() const
{
    propertyHolderComponent->refreshAll();
}
//==============================================================================
StringArray PropertyPanel::getSectionNames() const
{
    StringArray s;
    for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i)
    {
        SectionComponent* const section = propertyHolderComponent->getSection (i);
        if (section->getName().isNotEmpty())
            s.add (section->getName());
    }
    return s;
}
bool PropertyPanel::isSectionOpen (const int sectionIndex) const
{
    int index = 0;
    for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i)
    {
        SectionComponent* const section = propertyHolderComponent->getSection (i);
        if (section->getName().isNotEmpty())
        {
            if (index == sectionIndex)
                return section->isOpen();
            ++index;
        }
    }
    return false;
}
void PropertyPanel::setSectionOpen (const int sectionIndex, const bool shouldBeOpen)
{
    int index = 0;
    for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i)
    {
        SectionComponent* const section = propertyHolderComponent->getSection (i);
        if (section->getName().isNotEmpty())
        {
            if (index == sectionIndex)
            {
                section->setOpen (shouldBeOpen);
                break;
            }
            ++index;
        }
    }
}
void PropertyPanel::setSectionEnabled (const int sectionIndex, const bool shouldBeEnabled)
{
    int index = 0;
    for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i)
    {
        SectionComponent* const section = propertyHolderComponent->getSection (i);
        if (section->getName().isNotEmpty())
        {
            if (index == sectionIndex)
            {
                section->setEnabled (shouldBeEnabled);
                break;
            }
            ++index;
        }
    }
}
//==============================================================================
XmlElement* PropertyPanel::getOpennessState() const
{
    XmlElement* const xml = new XmlElement ("PROPERTYPANELSTATE");
    xml->setAttribute ("scrollPos", viewport.getViewPositionY());
    const StringArray sections (getSectionNames());
    for (int i = 0; i < sections.size(); ++i)
    {
        if (sections[i].isNotEmpty())
        {
            XmlElement* const e = xml->createNewChildElement ("SECTION");
            e->setAttribute ("name", sections[i]);
            e->setAttribute ("open", isSectionOpen (i) ? 1 : 0);
        }
    }
    return xml;
}
void PropertyPanel::restoreOpennessState (const XmlElement& xml)
{
    if (xml.hasTagName ("PROPERTYPANELSTATE"))
    {
        const StringArray sections (getSectionNames());
        forEachXmlChildElementWithTagName (xml, e, "SECTION")
        {
            setSectionOpen (sections.indexOf (e->getStringAttribute ("name")),
                            e->getBoolAttribute ("open"));
        }
        viewport.setViewPosition (viewport.getViewPositionX(),
                                  xml.getIntAttribute ("scrollPos", viewport.getViewPositionY()));
    }
}
//==============================================================================
void PropertyPanel::setMessageWhenEmpty (const String& newMessage)
{
    if (messageWhenEmpty != newMessage)
    {
        messageWhenEmpty = newMessage;
        repaint();
    }
}
const String& PropertyPanel::getMessageWhenEmpty() const
{
    return messageWhenEmpty;
}
 |