|  | /*
  ==============================================================================
   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 DropShadower::ShadowWindow  : public Component
{
public:
    ShadowWindow (Component& owner, const int type_, const Image shadowImageSections [12])
        : topLeft (shadowImageSections [type_ * 3]),
          bottomRight (shadowImageSections [type_ * 3 + 1]),
          filler (shadowImageSections [type_ * 3 + 2]),
          type (type_)
    {
        setInterceptsMouseClicks (false, false);
        if (owner.isOnDesktop())
        {
            setSize (1, 1); // to keep the OS happy by not having zero-size windows
            addToDesktop (ComponentPeer::windowIgnoresMouseClicks
                            | ComponentPeer::windowIsTemporary
                            | ComponentPeer::windowIgnoresKeyPresses);
        }
        else if (owner.getParentComponent() != nullptr)
        {
            owner.getParentComponent()->addChildComponent (this);
        }
    }
    void paint (Graphics& g)
    {
        g.setOpacity (1.0f);
        if (type < 2)
        {
            int imH = jmin (topLeft.getHeight(), getHeight() / 2);
            g.drawImage (topLeft,
                         0, 0, topLeft.getWidth(), imH,
                         0, 0, topLeft.getWidth(), imH);
            imH = jmin (bottomRight.getHeight(), getHeight() - getHeight() / 2);
            g.drawImage (bottomRight,
                         0, getHeight() - imH, bottomRight.getWidth(), imH,
                         0, bottomRight.getHeight() - imH, bottomRight.getWidth(), imH);
            g.setTiledImageFill (filler, 0, 0, 1.0f);
            g.fillRect (0, topLeft.getHeight(), getWidth(), getHeight() - (topLeft.getHeight() + bottomRight.getHeight()));
        }
        else
        {
            int imW = jmin (topLeft.getWidth(), getWidth() / 2);
            g.drawImage (topLeft,
                         0, 0, imW, topLeft.getHeight(),
                         0, 0, imW, topLeft.getHeight());
            imW = jmin (bottomRight.getWidth(), getWidth() - getWidth() / 2);
            g.drawImage (bottomRight,
                         getWidth() - imW, 0, imW, bottomRight.getHeight(),
                         bottomRight.getWidth() - imW, 0, imW, bottomRight.getHeight());
            g.setTiledImageFill (filler, 0, 0, 1.0f);
            g.fillRect (topLeft.getWidth(), 0, getWidth() - (topLeft.getWidth() + bottomRight.getWidth()), getHeight());
        }
    }
    void resized()
    {
        repaint();  // (needed for correct repainting)
    }
private:
    const Image topLeft, bottomRight, filler;
    const int type;   // 0 = left, 1 = right, 2 = top, 3 = bottom. left + right are full-height
    JUCE_DECLARE_NON_COPYABLE (ShadowWindow);
};
//==============================================================================
DropShadower::DropShadower (const DropShadow& shadow_)
   : owner (nullptr), shadow (shadow_), reentrant (false)
{
}
DropShadower::~DropShadower()
{
    if (owner != nullptr)
        owner->removeComponentListener (this);
    reentrant = true;
    shadowWindows.clear();
}
void DropShadower::setOwner (Component* componentToFollow)
{
    if (componentToFollow != owner)
    {
        if (owner != nullptr)
            owner->removeComponentListener (this);
        // (the component can't be null)
        jassert (componentToFollow != nullptr);
        owner = componentToFollow;
        jassert (owner != nullptr);
        jassert (owner->isOpaque()); // doesn't work properly for semi-transparent comps!
        owner->addComponentListener (this);
        updateShadows();
    }
}
void DropShadower::componentMovedOrResized (Component&, bool /*wasMoved*/, bool /*wasResized*/)
{
    updateShadows();
}
void DropShadower::componentBroughtToFront (Component&)
{
    bringShadowWindowsToFront();
}
void DropShadower::componentParentHierarchyChanged (Component&)
{
    shadowWindows.clear();
    updateShadows();
}
void DropShadower::componentVisibilityChanged (Component&)
{
    updateShadows();
}
void DropShadower::updateShadows()
{
    if (reentrant || owner == nullptr)
        return;
    ComponentPeer* const peer = owner->getPeer();
    const bool isOwnerVisible = owner->isVisible() && (peer == nullptr || ! peer->isMinimised());
    const bool createShadowWindows  = shadowWindows.size() == 0
                                         && owner->getWidth() > 0
                                         && owner->getHeight() > 0
                                         && isOwnerVisible
                                         && (Desktop::canUseSemiTransparentWindows()
                                              || owner->getParentComponent() != nullptr);
    {
        const ScopedValueSetter<bool> setter (reentrant, true, false);
        const int shadowEdge = jmax (shadow.offset.x, shadow.offset.y) + shadow.radius;
        if (createShadowWindows)
        {
            const int shadowEdge2 = shadowEdge * 2;
            const int imageSize = shadowEdge * 5;
            // keep a cached version of the image to save doing the gaussian too often
            int64 hash = shadow.radius ^ 0x2342dfa7;
            hash = hash * 101 + shadow.offset.x;
            hash = hash * 101 + shadow.offset.y;
            hash = hash * 65537 + shadow.colour.getARGB();
            Image bigIm (ImageCache::getFromHashCode (hash));
            if (bigIm.isNull())
            {
                bigIm = Image (Image::ARGB, imageSize, imageSize, true);
                Graphics g (bigIm);
                Path p;
                p.addRectangle ((float) (shadowEdge + shadow.offset.x),
                                (float) (shadowEdge + shadow.offset.y),
                                (float) (imageSize - shadowEdge2),
                                (float) (imageSize - shadowEdge2));
                shadow.drawForPath (g, p);
                ImageCache::addImageToCache (bigIm, hash);
            }
            jassert (imageSize == bigIm.getWidth() && imageSize == bigIm.getHeight());
            setShadowImage (bigIm, 0,  shadowEdge, shadowEdge2, 0, 0);
            setShadowImage (bigIm, 1,  shadowEdge, shadowEdge2, 0, imageSize - shadowEdge2);
            setShadowImage (bigIm, 2,  shadowEdge, shadowEdge,  0, shadowEdge2);
            setShadowImage (bigIm, 3,  shadowEdge, shadowEdge2, imageSize - shadowEdge, 0);
            setShadowImage (bigIm, 4,  shadowEdge, shadowEdge2, imageSize - shadowEdge, imageSize - shadowEdge2);
            setShadowImage (bigIm, 5,  shadowEdge, shadowEdge,  imageSize - shadowEdge, shadowEdge2);
            setShadowImage (bigIm, 6,  shadowEdge, shadowEdge,  shadowEdge, 0);
            setShadowImage (bigIm, 7,  shadowEdge, shadowEdge,  imageSize - shadowEdge2, 0);
            setShadowImage (bigIm, 8,  shadowEdge, shadowEdge,  shadowEdge2, 0);
            setShadowImage (bigIm, 9,  shadowEdge, shadowEdge,  shadowEdge, imageSize - shadowEdge);
            setShadowImage (bigIm, 10, shadowEdge, shadowEdge,  imageSize - shadowEdge2, imageSize - shadowEdge);
            setShadowImage (bigIm, 11, shadowEdge, shadowEdge,  shadowEdge2, imageSize - shadowEdge);
            for (int i = 0; i < 4; ++i)
                shadowWindows.add (new ShadowWindow (*owner, i, shadowImageSections));
        }
        if (shadowWindows.size() >= 4)
        {
            const int x = owner->getX();
            const int y = owner->getY() - shadowEdge;
            const int w = owner->getWidth();
            const int h = owner->getHeight() + shadowEdge + shadowEdge;
            for (int i = shadowWindows.size(); --i >= 0;)
            {
                // there seem to be rare situations where the dropshadower may be deleted by
                // callbacks during this loop, so use a weak ref to watch out for this..
                WeakReference<Component> sw (shadowWindows[i]);
                if (sw != nullptr)
                    sw->setAlwaysOnTop (owner->isAlwaysOnTop());
                if (sw != nullptr)
                    sw->setVisible (isOwnerVisible);
                if (sw != nullptr)
                {
                    switch (i)
                    {
                        case 0: sw->setBounds (x - shadowEdge, y, shadowEdge, h); break;
                        case 1: sw->setBounds (x + w, y, shadowEdge, h); break;
                        case 2: sw->setBounds (x, y, w, shadowEdge); break;
                        case 3: sw->setBounds (x, owner->getBottom(), w, shadowEdge); break;
                        default: break;
                    }
                }
                if (sw == nullptr)
                    return;
            }
        }
    }
    if (createShadowWindows)
        bringShadowWindowsToFront();
}
void DropShadower::setShadowImage (const Image& src, const int num, const int w, const int h,
                                   const int sx, const int sy)
{
    shadowImageSections[num] = Image (Image::ARGB, w, h, true);
    Graphics g (shadowImageSections[num]);
    g.drawImage (src, 0, 0, w, h, sx, sy, w, h);
}
void DropShadower::bringShadowWindowsToFront()
{
    if (! reentrant)
    {
        updateShadows();
        const ScopedValueSetter<bool> setter (reentrant, true, false);
        for (int i = shadowWindows.size(); --i >= 0;)
            shadowWindows.getUnchecked(i)->toBehind (owner);
    }
}
 |