text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

class MouseInputSourceInternal   : private AsyncUpdater
{
public:
    MouseInputSourceInternal (int i, MouseInputSource::InputSourceType type)  : index (i), inputType (type)
    {
    }

    //==============================================================================
    bool isDragging() const noexcept
    {
        return buttonState.isAnyMouseButtonDown();
    }

    Component* getComponentUnderMouse() const noexcept
    {
        return componentUnderMouse.get();
    }

    ModifierKeys getCurrentModifiers() const noexcept
    {
        return ModifierKeys::getCurrentModifiers().withoutMouseButtons().withFlags (buttonState.getRawFlags());
    }

    ComponentPeer* getPeer() noexcept
    {
        if (! ComponentPeer::isValidPeer (lastPeer))
            lastPeer = nullptr;

        return lastPeer;
    }

    static Point<float> screenPosToLocalPos (Component& comp, Point<float> pos)
    {
        if (auto* peer = comp.getPeer())
        {
            pos = peer->globalToLocal (pos);
            auto& peerComp = peer->getComponent();
            return comp.getLocalPoint (&peerComp, ScalingHelpers::unscaledScreenPosToScaled (peerComp, pos));
        }

        return comp.getLocalPoint (nullptr, ScalingHelpers::unscaledScreenPosToScaled (comp, pos));
    }

    Component* findComponentAt (Point<float> screenPos)
    {
        if (auto* peer = getPeer())
        {
            auto relativePos = ScalingHelpers::unscaledScreenPosToScaled (peer->getComponent(),
                                                                          peer->globalToLocal (screenPos));
            auto& comp = peer->getComponent();
            auto pos = relativePos.roundToInt();

            // (the contains() call is needed to test for overlapping desktop windows)
            if (comp.contains (pos))
                return comp.getComponentAt (pos);
        }

        return nullptr;
    }

    Point<float> getScreenPosition() const noexcept
    {
        // This needs to return the live position if possible, but it mustn't update the lastScreenPos
        // value, because that can cause continuity problems.
        return ScalingHelpers::unscaledScreenPosToScaled (getRawScreenPosition());
    }

    Point<float> getRawScreenPosition() const noexcept
    {
        return unboundedMouseOffset + (inputType != MouseInputSource::InputSourceType::touch ? MouseInputSource::getCurrentRawMousePosition()
                                                                                : lastScreenPos);
    }

    void setScreenPosition (Point<float> p)
    {
        MouseInputSource::setRawMousePosition (ScalingHelpers::scaledScreenPosToUnscaled (p));
    }

    bool isPressureValid()    const noexcept     { return pressure >= 0.0f && pressure <= 1.0f; }
    bool isOrientationValid() const noexcept     { return orientation >= 0.0f && orientation <= 2.0f * float_Pi; }
    bool isRotationValid() const noexcept        { return rotation >= 0.0f && rotation <= 2.0f * float_Pi; }
    bool isTiltValid (bool isX) const noexcept   { return isX ? (tiltX >= -1.0f && tiltX <= 1.0f) : (tiltY >= -1.0f && tiltY <= 1.0f); }

    //==============================================================================
   #if JUCE_DUMP_MOUSE_EVENTS
    #define JUCE_MOUSE_EVENT_DBG(desc)   DBG ("Mouse " << desc << " #" << index \
                                                << ": " << screenPosToLocalPos (comp, screenPos).toString() \
                                                << " - Comp: " << String::toHexString ((pointer_sized_int) &comp));
   #else
    #define JUCE_MOUSE_EVENT_DBG(desc)
   #endif

    void sendMouseEnter (Component& comp, Point<float> screenPos, Time time)
    {
        JUCE_MOUSE_EVENT_DBG ("enter")
        comp.internalMouseEnter (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
    }

    void sendMouseExit (Component& comp, Point<float> screenPos, Time time)
    {
        JUCE_MOUSE_EVENT_DBG ("exit")
        comp.internalMouseExit (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
    }

    void sendMouseMove (Component& comp, Point<float> screenPos, Time time)
    {
        JUCE_MOUSE_EVENT_DBG ("move")
        comp.internalMouseMove (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
    }

    void sendMouseDown (Component& comp, Point<float> screenPos, Time time)
    {
        JUCE_MOUSE_EVENT_DBG ("down")
        comp.internalMouseDown (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, pressure, orientation, rotation, tiltX, tiltY);
    }

    void sendMouseDrag (Component& comp, Point<float> screenPos, Time time)
    {
        JUCE_MOUSE_EVENT_DBG ("drag")
        comp.internalMouseDrag (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, pressure, orientation, rotation, tiltX, tiltY);
    }

    void sendMouseUp (Component& comp, Point<float> screenPos, Time time, const ModifierKeys oldMods)
    {
        JUCE_MOUSE_EVENT_DBG ("up")
            comp.internalMouseUp (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, oldMods, pressure, orientation, rotation, tiltX, tiltY);
    }

    void sendMouseWheel (Component& comp, Point<float> screenPos, Time time, const MouseWheelDetails& wheel)
    {
        JUCE_MOUSE_EVENT_DBG ("wheel")
        comp.internalMouseWheel (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, wheel);
    }

    void sendMagnifyGesture (Component& comp, Point<float> screenPos, Time time, const float amount)
    {
        JUCE_MOUSE_EVENT_DBG ("magnify")
        comp.internalMagnifyGesture (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, amount);
    }

    //==============================================================================
    // (returns true if the button change caused a modal event loop)
    bool setButtons (Point<float> screenPos, Time time, const ModifierKeys newButtonState)
    {
        if (buttonState == newButtonState)
            return false;

        // (avoid sending a spurious mouse-drag when we receive a mouse-up)
        if (! (isDragging() && ! newButtonState.isAnyMouseButtonDown()))
            setScreenPos (screenPos, time, false);

        // (ignore secondary clicks when there's already a button down)
        if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
        {
            buttonState = newButtonState;
            return false;
        }

        auto lastCounter = mouseEventCounter;

        if (buttonState.isAnyMouseButtonDown())
        {
            if (auto* current = getComponentUnderMouse())
            {
                auto oldMods = getCurrentModifiers();
                buttonState = newButtonState; // must change this before calling sendMouseUp, in case it runs a modal loop

                sendMouseUp (*current, screenPos + unboundedMouseOffset, time, oldMods);

                if (lastCounter != mouseEventCounter)
                    return true; // if a modal loop happened, then newButtonState is no longer valid.
            }

            enableUnboundedMouseMovement (false, false);
        }

        buttonState = newButtonState;

        if (buttonState.isAnyMouseButtonDown())
        {
            Desktop::getInstance().incrementMouseClickCounter();

            if (auto* current = getComponentUnderMouse())
            {
                registerMouseDown (screenPos, time, *current, buttonState);
                sendMouseDown (*current, screenPos, time);
            }
        }

        return lastCounter != mouseEventCounter;
    }

    void setComponentUnderMouse (Component* const newComponent, Point<float> screenPos, Time time)
    {
        auto* current = getComponentUnderMouse();

        if (newComponent != current)
        {
            WeakReference<Component> safeNewComp (newComponent);
            auto originalButtonState = buttonState;

            if (current != nullptr)
            {
                WeakReference<Component> safeOldComp (current);
                setButtons (screenPos, time, ModifierKeys());

                if (safeOldComp != nullptr)
                {
                    componentUnderMouse = safeNewComp;
                    sendMouseExit (*safeOldComp, screenPos, time);
                }

                buttonState = originalButtonState;
            }

            current = componentUnderMouse = safeNewComp;

            if (current != nullptr)
                sendMouseEnter (*current, screenPos, time);

            revealCursor (false);
            setButtons (screenPos, time, originalButtonState);
        }
    }

    void setPeer (ComponentPeer& newPeer, Point<float> screenPos, Time time)
    {
        ModifierKeys::updateCurrentModifiers();

        if (&newPeer != lastPeer)
        {
            setComponentUnderMouse (nullptr, screenPos, time);
            lastPeer = &newPeer;
            setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
        }
    }

    void setScreenPos (Point<float> newScreenPos, Time time, const bool forceUpdate)
    {
        if (! isDragging())
            setComponentUnderMouse (findComponentAt (newScreenPos), newScreenPos, time);

        if (newScreenPos != lastScreenPos || forceUpdate)
        {
            cancelPendingUpdate();
            lastScreenPos = newScreenPos;

            if (auto* current = getComponentUnderMouse())
            {
                if (isDragging())
                {
                    registerMouseDrag (newScreenPos);
                    sendMouseDrag (*current, newScreenPos + unboundedMouseOffset, time);

                    if (isUnboundedMouseModeOn)
                        handleUnboundedDrag (*current);
                }
                else
                {
                    sendMouseMove (*current, newScreenPos, time);
                }
            }

            revealCursor (false);
        }
    }

    //==============================================================================
    void handleEvent (ComponentPeer& newPeer, Point<float> positionWithinPeer, Time time,
                      const ModifierKeys newMods, float newPressure, float newOrientation, PenDetails pen)
    {
        lastTime = time;

        const bool pressureChanged = (pressure != newPressure);
        pressure = newPressure;

        const bool orientationChanged = (orientation != newOrientation);
        orientation = newOrientation;

        const bool rotationChanged = (rotation != pen.rotation);
        rotation = pen.rotation;

        const bool tiltChanged = (tiltX != pen.tiltX || tiltY != pen.tiltY);
        tiltX = pen.tiltX;
        tiltY = pen.tiltY;

        const bool shouldUpdate = (pressureChanged || orientationChanged || rotationChanged || tiltChanged);

        ++mouseEventCounter;

        auto screenPos = newPeer.localToGlobal (positionWithinPeer);

        if (isDragging() && newMods.isAnyMouseButtonDown())
        {
            setScreenPos (screenPos, time, shouldUpdate);
        }
        else
        {
            setPeer (newPeer, screenPos, time);

            if (auto* peer = getPeer())
            {
                if (setButtons (screenPos, time, newMods))
                    return; // some modal events have been dispatched, so the current event is now out-of-date

                peer = getPeer();

                if (peer != nullptr)
                    setScreenPos (screenPos, time, shouldUpdate);
            }
        }
    }

    Component* getTargetForGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
                                    Time time, Point<float>& screenPos)
    {
        lastTime = time;
        ++mouseEventCounter;

        screenPos = peer.localToGlobal (positionWithinPeer);
        setPeer (peer, screenPos, time);
        setScreenPos (screenPos, time, false);
        triggerFakeMove();

        return getComponentUnderMouse();
    }

    void handleWheel (ComponentPeer& peer, Point<float> positionWithinPeer,
                      Time time, const MouseWheelDetails& wheel)
    {
        Desktop::getInstance().incrementMouseWheelCounter();
        Point<float> screenPos;

        // This will make sure that when the wheel spins in its inertial phase, any events
        // continue to be sent to the last component that the mouse was over when it was being
        // actively controlled by the user. This avoids confusion when scrolling through nested
        // scrollable components.
        if (lastNonInertialWheelTarget == nullptr || ! wheel.isInertial)
            lastNonInertialWheelTarget = getTargetForGesture (peer, positionWithinPeer, time, screenPos);
        else
            screenPos = peer.localToGlobal (positionWithinPeer);

        if (Component* target = lastNonInertialWheelTarget)
            sendMouseWheel (*target, screenPos, time, wheel);
    }

    void handleMagnifyGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
                               Time time, const float scaleFactor)
    {
        Point<float> screenPos;

        if (auto* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
            sendMagnifyGesture (*current, screenPos, time, scaleFactor);
    }

    //==============================================================================
    Time getLastMouseDownTime() const noexcept              { return mouseDowns[0].time; }
    Point<float> getLastMouseDownPosition() const noexcept  { return ScalingHelpers::unscaledScreenPosToScaled (mouseDowns[0].position); }

    int getNumberOfMultipleClicks() const noexcept
    {
        int numClicks = 1;

        if (! hasMouseMovedSignificantlySincePressed())
        {
            for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
            {
                if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
                    ++numClicks;
                else
                    break;
            }
        }

        return numClicks;
    }

    bool hasMouseMovedSignificantlySincePressed() const noexcept
    {
        return mouseMovedSignificantlySincePressed
                || lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
    }

    //==============================================================================
    void triggerFakeMove()
    {
        triggerAsyncUpdate();
    }

    void handleAsyncUpdate() override
    {
        setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
    }

    //==============================================================================
    void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
    {
        enable = enable && isDragging();
        isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;

        if (enable != isUnboundedMouseModeOn)
        {
            if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
            {
                // when released, return the mouse to within the component's bounds
                if (auto* current = getComponentUnderMouse())
                    setScreenPosition (current->getScreenBounds().toFloat()
                                          .getConstrainedPoint (ScalingHelpers::unscaledScreenPosToScaled (lastScreenPos)));
            }

            isUnboundedMouseModeOn = enable;
            unboundedMouseOffset = {};

            revealCursor (true);
        }
    }

    void handleUnboundedDrag (Component& current)
    {
        auto componentScreenBounds = ScalingHelpers::scaledScreenPosToUnscaled (current.getParentMonitorArea().reduced (2, 2).toFloat());

        if (! componentScreenBounds.contains (lastScreenPos))
        {
            auto componentCentre = current.getScreenBounds().toFloat().getCentre();
            unboundedMouseOffset += (lastScreenPos - ScalingHelpers::scaledScreenPosToUnscaled (componentCentre));
            setScreenPosition (componentCentre);
        }
        else if (isCursorVisibleUntilOffscreen
                  && (! unboundedMouseOffset.isOrigin())
                  && componentScreenBounds.contains (lastScreenPos + unboundedMouseOffset))
        {
            MouseInputSource::setRawMousePosition (lastScreenPos + unboundedMouseOffset);
            unboundedMouseOffset = {};
        }
    }

    //==============================================================================
    void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
    {
        if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
        {
            cursor = MouseCursor::NoCursor;
            forcedUpdate = true;
        }

        if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
        {
            currentCursorHandle = cursor.getHandle();
            cursor.showInWindow (getPeer());
        }
    }

    void hideCursor()
    {
        showMouseCursor (MouseCursor::NoCursor, true);
    }

    void revealCursor (bool forcedUpdate)
    {
        MouseCursor mc (MouseCursor::NormalCursor);

        if (auto* current = getComponentUnderMouse())
            mc = current->getLookAndFeel().getMouseCursorFor (*current);

        showMouseCursor (mc, forcedUpdate);
    }

    //==============================================================================
    const int index;
    const MouseInputSource::InputSourceType inputType;
    Point<float> lastScreenPos, unboundedMouseOffset; // NB: these are unscaled coords
    ModifierKeys buttonState;
    float pressure = 0;
    float orientation = 0;
    float rotation = 0;
    float tiltX = 0;
    float tiltY = 0;

    bool isUnboundedMouseModeOn = false, isCursorVisibleUntilOffscreen = false;

private:
    WeakReference<Component> componentUnderMouse, lastNonInertialWheelTarget;
    ComponentPeer* lastPeer = nullptr;

    void* currentCursorHandle = nullptr;
    int mouseEventCounter = 0;

    struct RecentMouseDown
    {
        RecentMouseDown() = default;

        Point<float> position;
        Time time;
        ModifierKeys buttons;
        uint32 peerID = 0;

        bool canBePartOfMultipleClickWith (const RecentMouseDown& other, int maxTimeBetweenMs) const noexcept
        {
            return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
                    && std::abs (position.x - other.position.x) < 8
                    && std::abs (position.y - other.position.y) < 8
                    && buttons == other.buttons
                    && peerID == other.peerID;
        }
    };

    RecentMouseDown mouseDowns[4];
    Time lastTime;
    bool mouseMovedSignificantlySincePressed = false;

    void registerMouseDown (Point<float> screenPos, Time time,
                            Component& component, const ModifierKeys modifiers) noexcept
    {
        for (int i = numElementsInArray (mouseDowns); --i > 0;)
            mouseDowns[i] = mouseDowns[i - 1];

        mouseDowns[0].position = screenPos;
        mouseDowns[0].time = time;
        mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();

        if (auto* peer = component.getPeer())
            mouseDowns[0].peerID = peer->getUniqueID();
        else
            mouseDowns[0].peerID = 0;

        mouseMovedSignificantlySincePressed = false;
        lastNonInertialWheelTarget = nullptr;
    }

    void registerMouseDrag (Point<float> screenPos) noexcept
    {
        mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
               || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
    }

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceInternal)
};

//==============================================================================
MouseInputSource::MouseInputSource (MouseInputSourceInternal* s) noexcept   : pimpl (s)  {}
MouseInputSource::MouseInputSource (const MouseInputSource& other) noexcept : pimpl (other.pimpl)  {}
MouseInputSource::~MouseInputSource() noexcept {}

MouseInputSource& MouseInputSource::operator= (const MouseInputSource& other) noexcept
{
    pimpl = other.pimpl;
    return *this;
}

MouseInputSource::InputSourceType MouseInputSource::getType() const noexcept    { return pimpl->inputType; }
bool MouseInputSource::isMouse() const noexcept                                 { return (getType() == MouseInputSource::InputSourceType::mouse); }
bool MouseInputSource::isTouch() const noexcept                                 { return (getType() == MouseInputSource::InputSourceType::touch); }
bool MouseInputSource::isPen() const noexcept                                   { return (getType() == MouseInputSource::InputSourceType::pen); }
bool MouseInputSource::canHover() const noexcept                                { return ! isTouch(); }
bool MouseInputSource::hasMouseWheel() const noexcept                           { return ! isTouch(); }
int MouseInputSource::getIndex() const noexcept                                 { return pimpl->index; }
bool MouseInputSource::isDragging() const noexcept                              { return pimpl->isDragging(); }
Point<float> MouseInputSource::getScreenPosition() const noexcept               { return pimpl->getScreenPosition(); }
Point<float> MouseInputSource::getRawScreenPosition() const noexcept            { return pimpl->getRawScreenPosition();  }
ModifierKeys MouseInputSource::getCurrentModifiers() const noexcept             { return pimpl->getCurrentModifiers(); }
float MouseInputSource::getCurrentPressure() const noexcept                     { return pimpl->pressure; }
bool MouseInputSource::isPressureValid() const noexcept                         { return pimpl->isPressureValid(); }
float MouseInputSource::getCurrentOrientation() const noexcept                  { return pimpl->orientation; }
bool MouseInputSource::isOrientationValid() const noexcept                      { return pimpl->isOrientationValid(); }
float MouseInputSource::getCurrentRotation() const noexcept                     { return pimpl->rotation; }
bool MouseInputSource::isRotationValid() const noexcept                         { return pimpl->isRotationValid(); }
float MouseInputSource::getCurrentTilt (bool tiltX) const noexcept              { return tiltX ? pimpl->tiltX : pimpl->tiltY; }
bool MouseInputSource::isTiltValid (bool isX) const noexcept                    { return pimpl->isTiltValid (isX); }
Component* MouseInputSource::getComponentUnderMouse() const                     { return pimpl->getComponentUnderMouse(); }
void MouseInputSource::triggerFakeMove() const                                  { pimpl->triggerFakeMove(); }
int MouseInputSource::getNumberOfMultipleClicks() const noexcept                { return pimpl->getNumberOfMultipleClicks(); }
Time MouseInputSource::getLastMouseDownTime() const noexcept                    { return pimpl->getLastMouseDownTime(); }
Point<float> MouseInputSource::getLastMouseDownPosition() const noexcept        { return pimpl->getLastMouseDownPosition(); }
bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept  { return pimpl->hasMouseMovedSignificantlySincePressed(); }
bool MouseInputSource::canDoUnboundedMovement() const noexcept                  { return ! isTouch(); }
void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) const
                                                                         { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
bool MouseInputSource::isUnboundedMouseMovementEnabled() const           { return pimpl->isUnboundedMouseModeOn; }
bool MouseInputSource::hasMouseCursor() const noexcept                   { return ! isTouch(); }
void MouseInputSource::showMouseCursor (const MouseCursor& cursor)       { pimpl->showMouseCursor (cursor, false); }
void MouseInputSource::hideCursor()                                      { pimpl->hideCursor(); }
void MouseInputSource::revealCursor()                                    { pimpl->revealCursor (false); }
void MouseInputSource::forceMouseCursorUpdate()                          { pimpl->revealCursor (true); }
void MouseInputSource::setScreenPosition (Point<float> p)                { pimpl->setScreenPosition (p); }

void MouseInputSource::handleEvent (ComponentPeer& peer, Point<float> pos, int64 time, ModifierKeys mods,
                                    float pressure, float orientation, const PenDetails& penDetails)
{
    pimpl->handleEvent (peer, pos, Time (time), mods.withOnlyMouseButtons(), pressure, orientation, penDetails);
}

void MouseInputSource::handleWheel (ComponentPeer& peer, Point<float> pos, int64 time, const MouseWheelDetails& wheel)
{
    pimpl->handleWheel (peer, pos, Time (time), wheel);
}

void MouseInputSource::handleMagnifyGesture (ComponentPeer& peer, Point<float> pos, int64 time, float scaleFactor)
{
    pimpl->handleMagnifyGesture (peer, pos, Time (time), scaleFactor);
}

const float MouseInputSource::invalidPressure = 0.0f;
const float MouseInputSource::invalidOrientation = 0.0f;
const float MouseInputSource::invalidRotation = 0.0f;

const float MouseInputSource::invalidTiltX = 0.0f;
const float MouseInputSource::invalidTiltY = 0.0f;

//==============================================================================
struct MouseInputSource::SourceList  : public Timer
{
    SourceList()
    {
       #if JUCE_ANDROID || JUCE_IOS
        auto mainMouseInputType = MouseInputSource::InputSourceType::touch;
       #else
        auto mainMouseInputType = MouseInputSource::InputSourceType::mouse;
       #endif

        addSource (0, mainMouseInputType);
    }

    bool addSource();
    bool canUseTouch();

    MouseInputSource* addSource (int index, MouseInputSource::InputSourceType type)
    {
        auto* s = new MouseInputSourceInternal (index, type);
        sources.add (s);
        sourceArray.add (MouseInputSource (s));

        return &sourceArray.getReference (sourceArray.size() - 1);
    }

    MouseInputSource* getMouseSource (int index) const noexcept
    {
        return isPositiveAndBelow (index, sourceArray.size()) ? &sourceArray.getReference (index)
                                                              : nullptr;
    }

    MouseInputSource* getOrCreateMouseInputSource (MouseInputSource::InputSourceType type, int touchIndex = 0)
    {
        if (type == MouseInputSource::InputSourceType::mouse || type == MouseInputSource::InputSourceType::pen)
        {
            for (auto& m : sourceArray)
                if (type == m.getType())
                    return &m;

            addSource (0, type);
        }
        else if (type == MouseInputSource::InputSourceType::touch)
        {
            jassert (touchIndex >= 0 && touchIndex < 100); // sanity-check on number of fingers

            for (auto& m : sourceArray)
                if (type == m.getType() && touchIndex == m.getIndex())
                    return &m;

            if (canUseTouch())
                return addSource (touchIndex, type);
        }

        return nullptr;
    }

    int getNumDraggingMouseSources() const noexcept
    {
        int num = 0;

        for (auto* s : sources)
            if (s->isDragging())
                ++num;

        return num;
    }

    MouseInputSource* getDraggingMouseSource (int index) const noexcept
    {
        int num = 0;

        for (auto& s : sourceArray)
        {
            if (s.isDragging())
            {
                if (index == num)
                    return &s;

                ++num;
            }
        }

        return nullptr;
    }

    void beginDragAutoRepeat (const int interval)
    {
        if (interval > 0)
        {
            if (getTimerInterval() != interval)
                startTimer (interval);
        }
        else
        {
            stopTimer();
        }
    }

    void timerCallback() override
    {
        bool anyDragging = false;

        for (auto* s : sources)
        {
            // NB: when doing auto-repeat, we need to force an update of the current position and button state,
            // because on some OSes the queue can get overloaded with messages so that mouse-events don't get through..
            if (s->isDragging() && ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown())
            {
                s->lastScreenPos = s->getRawScreenPosition();
                s->triggerFakeMove();
                anyDragging = true;
            }
        }

        if (! anyDragging)
            stopTimer();
    }

    OwnedArray<MouseInputSourceInternal> sources;
    Array<MouseInputSource> sourceArray;
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    A MouseListener can be registered with a component to receive callbacks
    about mouse events that happen to that component.

    @see Component::addMouseListener, Component::removeMouseListener
*/
class JUCE_API  MouseListener
{
public:
    /** Destructor. */
    virtual ~MouseListener()  {}

    /** Called when the mouse moves inside a component.

        If the mouse button isn't pressed and the mouse moves over a component,
        this will be called to let the component react to this.

        A component will always get a mouseEnter callback before a mouseMove.

        @param event details about the position and status of the mouse event, including
                     the source component in which it occurred
        @see mouseEnter, mouseExit, mouseDrag, contains
    */
    virtual void mouseMove (const MouseEvent& event);

    /** Called when the mouse first enters a component.

        If the mouse button isn't pressed and the mouse moves into a component,
        this will be called to let the component react to this.

        When the mouse button is pressed and held down while being moved in
        or out of a component, no mouseEnter or mouseExit callbacks are made - only
        mouseDrag messages are sent to the component that the mouse was originally
        clicked on, until the button is released.

        @param event details about the position and status of the mouse event, including
                     the source component in which it occurred
        @see mouseExit, mouseDrag, mouseMove, contains
    */
    virtual void mouseEnter (const MouseEvent& event);

    /** Called when the mouse moves out of a component.

        This will be called when the mouse moves off the edge of this
        component.

        If the mouse button was pressed, and it was then dragged off the
        edge of the component and released, then this callback will happen
        when the button is released, after the mouseUp callback.

        @param event  details about the position and status of the mouse event, including
                      the source component in which it occurred
        @see mouseEnter, mouseDrag, mouseMove, contains
    */
    virtual void mouseExit (const MouseEvent& event);

    /** Called when a mouse button is pressed.

        The MouseEvent object passed in contains lots of methods for finding out
        which button was pressed, as well as which modifier keys (e.g. shift, ctrl)
        were held down at the time.

        Once a button is held down, the mouseDrag method will be called when the
        mouse moves, until the button is released.

        @param event  details about the position and status of the mouse event, including
                      the source component in which it occurred
        @see mouseUp, mouseDrag, mouseDoubleClick, contains
    */
    virtual void mouseDown (const MouseEvent& event);

    /** Called when the mouse is moved while a button is held down.

        When a mouse button is pressed inside a component, that component
        receives mouseDrag callbacks each time the mouse moves, even if the
        mouse strays outside the component's bounds.

        @param event  details about the position and status of the mouse event, including
                      the source component in which it occurred
        @see mouseDown, mouseUp, mouseMove, contains, setDragRepeatInterval
    */
    virtual void mouseDrag (const MouseEvent& event);

    /** Called when a mouse button is released.

        A mouseUp callback is sent to the component in which a button was pressed
        even if the mouse is actually over a different component when the
        button is released.

        The MouseEvent object passed in contains lots of methods for finding out
        which buttons were down just before they were released.

        @param event  details about the position and status of the mouse event, including
                      the source component in which it occurred
        @see mouseDown, mouseDrag, mouseDoubleClick, contains
    */
    virtual void mouseUp (const MouseEvent& event);

    /** Called when a mouse button has been double-clicked on a component.

        The MouseEvent object passed in contains lots of methods for finding out
        which button was pressed, as well as which modifier keys (e.g. shift, ctrl)
        were held down at the time.

        @param event  details about the position and status of the mouse event, including
                      the source component in which it occurred
        @see mouseDown, mouseUp
    */
    virtual void mouseDoubleClick (const MouseEvent& event);

    /** Called when the mouse-wheel is moved.

        This callback is sent to the component that the mouse is over when the
        wheel is moved.

        If not overridden, a component will forward this message to its parent, so
        that parent components can collect mouse-wheel messages that happen to
        child components which aren't interested in them.

        @param event   details about the mouse event
        @param wheel   details about the wheel movement
    */
    virtual void mouseWheelMove (const MouseEvent& event,
                                 const MouseWheelDetails& wheel);

private:
   #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
    // This is just here to cause a compile error in old code that hasn't been
    // updated to use the new version of this method.
    virtual int mouseWheelMove (const MouseEvent&, float, float) { return 0; }
   #endif
};

} // namespace juce

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

/**
    Components derived from this class can have files dropped onto them by an external application.

    @see DragAndDropContainer
*/
class JUCE_API  FileDragAndDropTarget
{
public:
    /** Destructor. */
    virtual ~FileDragAndDropTarget()  {}

    /** Callback to check whether this target is interested in the set of files being offered.

        Note that this will be called repeatedly when the user is dragging the mouse around over your
        component, so don't do anything time-consuming in here, like opening the files to have a look
        inside them!

        @param files        the set of (absolute) pathnames of the files that the user is dragging
        @returns            true if this component wants to receive the other callbacks regarging this
                            type of object; if it returns false, no other callbacks will be made.
    */
    virtual bool isInterestedInFileDrag (const StringArray& files) = 0;

    /** Callback to indicate that some files are being dragged over this component.

        This gets called when the user moves the mouse into this component while dragging.

        Use this callback as a trigger to make your component repaint itself to give the
        user feedback about whether the files can be dropped here or not.

        @param files        the set of (absolute) pathnames of the files that the user is dragging
        @param x            the mouse x position, relative to this component
        @param y            the mouse y position, relative to this component
    */
    virtual void fileDragEnter (const StringArray& files, int x, int y);

    /** Callback to indicate that the user is dragging some files over this component.

        This gets called when the user moves the mouse over this component while dragging.
        Normally overriding itemDragEnter() and itemDragExit() are enough, but
        this lets you know what happens in-between.

        @param files        the set of (absolute) pathnames of the files that the user is dragging
        @param x            the mouse x position, relative to this component
        @param y            the mouse y position, relative to this component
    */
    virtual void fileDragMove (const StringArray& files, int x, int y);

    /** Callback to indicate that the mouse has moved away from this component.

        This gets called when the user moves the mouse out of this component while dragging
        the files.

        If you've used fileDragEnter() to repaint your component and give feedback, use this
        as a signal to repaint it in its normal state.

        @param files        the set of (absolute) pathnames of the files that the user is dragging
    */
    virtual void fileDragExit (const StringArray& files);

    /** Callback to indicate that the user has dropped the files onto this component.

        When the user drops the files, this get called, and you can use the files in whatever
        way is appropriate.

        Note that after this is called, the fileDragExit method may not be called, so you should
        clean up in here if there's anything you need to do when the drag finishes.

        @param files        the set of (absolute) pathnames of the files that the user is dragging
        @param x            the mouse x position, relative to this component
        @param y            the mouse y position, relative to this component
    */
    virtual void filesDropped (const StringArray& files, int x, int y) = 0;
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

MouseInactivityDetector::MouseInactivityDetector (Component& c)
    : targetComp (c), delayMs (1500), toleranceDistance (15), isActive (true)
{
    targetComp.addMouseListener (this, true);
}

MouseInactivityDetector::~MouseInactivityDetector()
{
    targetComp.removeMouseListener (this);
}

void MouseInactivityDetector::setDelay (int newDelay) noexcept                  { delayMs = newDelay; }
void MouseInactivityDetector::setMouseMoveTolerance (int newDistance) noexcept  { toleranceDistance = newDistance; }

void MouseInactivityDetector::addListener    (Listener* l)   { listenerList.add (l); }
void MouseInactivityDetector::removeListener (Listener* l)   { listenerList.remove (l); }

void MouseInactivityDetector::timerCallback()
{
    setActive (false);
}

void MouseInactivityDetector::wakeUp (const MouseEvent& e, bool alwaysWake)
{
    const Point<int> newPos (e.getEventRelativeTo (&targetComp).getPosition());

    if ((! isActive) && (alwaysWake || e.source.isTouch() || newPos.getDistanceFrom (lastMousePos) > toleranceDistance))
        setActive (true);

    if (lastMousePos != newPos)
    {
        lastMousePos = newPos;
        startTimer (delayMs);
    }
}

void MouseInactivityDetector::setActive (bool b)
{
    if (isActive != b)
    {
        isActive = b;

        listenerList.call (b ? &Listener::mouseBecameActive
                             : &Listener::mouseBecameInactive);
    }
}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Components derived from this class can have text dropped onto them by an external application.

    @see DragAndDropContainer
*/
class JUCE_API  TextDragAndDropTarget
{
public:
    /** Destructor. */
    virtual ~TextDragAndDropTarget()  {}

    /** Callback to check whether this target is interested in the set of text being offered.

        Note that this will be called repeatedly when the user is dragging the mouse around over your
        component, so don't do anything time-consuming in here!

        @param text         the text that the user is dragging
        @returns            true if this component wants to receive the other callbacks regarging this
                            type of object; if it returns false, no other callbacks will be made.
    */
    virtual bool isInterestedInTextDrag (const String& text) = 0;

    /** Callback to indicate that some text is being dragged over this component.

        This gets called when the user moves the mouse into this component while dragging.

        Use this callback as a trigger to make your component repaint itself to give the
        user feedback about whether the text can be dropped here or not.

        @param text         the text that the user is dragging
        @param x            the mouse x position, relative to this component
        @param y            the mouse y position, relative to this component
    */
    virtual void textDragEnter (const String& text, int x, int y);

    /** Callback to indicate that the user is dragging some text over this component.

        This gets called when the user moves the mouse over this component while dragging.
        Normally overriding itemDragEnter() and itemDragExit() are enough, but
        this lets you know what happens in-between.

        @param text         the text that the user is dragging
        @param x            the mouse x position, relative to this component
        @param y            the mouse y position, relative to this component
    */
    virtual void textDragMove (const String& text, int x, int y);

    /** Callback to indicate that the mouse has moved away from this component.

        This gets called when the user moves the mouse out of this component while dragging
        the text.

        If you've used textDragEnter() to repaint your component and give feedback, use this
        as a signal to repaint it in its normal state.

        @param text        the text that the user is dragging
    */
    virtual void textDragExit (const String& text);

    /** Callback to indicate that the user has dropped the text onto this component.

        When the user drops the text, this get called, and you can use the text in whatever
        way is appropriate.

        Note that after this is called, the textDragExit method may not be called, so you should
        clean up in here if there's anything you need to do when the drag finishes.

        @param text         the text that the user is dragging
        @param x            the mouse x position, relative to this component
        @param y            the mouse y position, relative to this component
    */
    virtual void textDropped (const String& text, int x, int y) = 0;
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

struct CustomMouseCursorInfo
{
    CustomMouseCursorInfo (const Image& im, int hsX, int hsY) noexcept
        : image (im), hotspot (hsX, hsY), scaleFactor (1.0f)
    {}

    CustomMouseCursorInfo (const Image& im, Point<int> hs, float scale) noexcept
        : image (im), hotspot (hs), scaleFactor (scale)
    {}

    void* create() const;

    Image image;
    const Point<int> hotspot;
    float scaleFactor;

private:
    JUCE_DECLARE_NON_COPYABLE (CustomMouseCursorInfo)
};

class MouseCursor::SharedCursorHandle
{
public:
    explicit SharedCursorHandle (const MouseCursor::StandardCursorType type)
        : handle (createStandardMouseCursor (type)),
          refCount (1),
          standardType (type),
          isStandard (true)
    {
    }

    SharedCursorHandle (const Image& image, Point<int> hotSpot, const float scaleFactor)
        : handle (CustomMouseCursorInfo (image, hotSpot, scaleFactor).create()),
          refCount (1),
          standardType (MouseCursor::NormalCursor),
          isStandard (false)
    {
    }

    ~SharedCursorHandle()
    {
        deleteMouseCursor (handle, isStandard);
    }

    static SharedCursorHandle* createStandard (const MouseCursor::StandardCursorType type)
    {
        jassert (isPositiveAndBelow (type, MouseCursor::NumStandardCursorTypes));

        const SpinLock::ScopedLockType sl (lock);

        SharedCursorHandle*& c = getSharedCursor (type);

        if (c == nullptr)
            c = new SharedCursorHandle (type);
        else
            c->retain();

        return c;
    }

    bool isStandardType (MouseCursor::StandardCursorType type) const noexcept
    {
        return type == standardType && isStandard;
    }

    SharedCursorHandle* retain() noexcept
    {
        ++refCount;
        return this;
    }

    void release()
    {
        if (--refCount == 0)
        {
            if (isStandard)
            {
                const SpinLock::ScopedLockType sl (lock);
                getSharedCursor (standardType) = nullptr;
            }

            delete this;
        }
    }

    void* getHandle() const noexcept        { return handle; }

private:
    void* const handle;
    Atomic <int> refCount;
    const MouseCursor::StandardCursorType standardType;
    const bool isStandard;
    static SpinLock lock;

    static SharedCursorHandle*& getSharedCursor (const MouseCursor::StandardCursorType type)
    {
        static SharedCursorHandle* cursors [MouseCursor::NumStandardCursorTypes] = {};
        return cursors [type];
    }

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedCursorHandle)
};

SpinLock MouseCursor::SharedCursorHandle::lock;

//==============================================================================
MouseCursor::MouseCursor() noexcept
    : cursorHandle (nullptr)
{
}

MouseCursor::MouseCursor (const StandardCursorType type)
    : cursorHandle (type != MouseCursor::NormalCursor ? SharedCursorHandle::createStandard (type) : nullptr)
{
}

MouseCursor::MouseCursor (const Image& image, const int hotSpotX, const int hotSpotY)
    : cursorHandle (new SharedCursorHandle (image, Point<int> (hotSpotX, hotSpotY), 1.0f))
{
}

MouseCursor::MouseCursor (const Image& image, const int hotSpotX, const int hotSpotY, float scaleFactor)
    : cursorHandle (new SharedCursorHandle (image, Point<int> (hotSpotX, hotSpotY), scaleFactor))
{
}

MouseCursor::MouseCursor (const MouseCursor& other)
    : cursorHandle (other.cursorHandle == nullptr ? nullptr : other.cursorHandle->retain())
{
}

MouseCursor::~MouseCursor()
{
    if (cursorHandle != nullptr)
        cursorHandle->release();
}

MouseCursor& MouseCursor::operator= (const MouseCursor& other)
{
    if (other.cursorHandle != nullptr)
        other.cursorHandle->retain();

    if (cursorHandle != nullptr)
        cursorHandle->release();

    cursorHandle = other.cursorHandle;
    return *this;
}

MouseCursor::MouseCursor (MouseCursor&& other) noexcept
    : cursorHandle (other.cursorHandle)
{
    other.cursorHandle = nullptr;
}

MouseCursor& MouseCursor::operator= (MouseCursor&& other) noexcept
{
    std::swap (cursorHandle, other.cursorHandle);
    return *this;
}

bool MouseCursor::operator== (const MouseCursor& other) const noexcept
{
    return getHandle() == other.getHandle();
}

bool MouseCursor::operator== (StandardCursorType type) const noexcept
{
    return cursorHandle != nullptr ? cursorHandle->isStandardType (type)
                                   : (type == NormalCursor);
}

bool MouseCursor::operator!= (const MouseCursor& other) const noexcept  { return ! operator== (other); }
bool MouseCursor::operator!= (StandardCursorType type)  const noexcept  { return ! operator== (type); }

void* MouseCursor::getHandle() const noexcept
{
    return cursorHandle != nullptr ? cursorHandle->getHandle() : nullptr;
}

void MouseCursor::showWaitCursor()
{
    Desktop::getInstance().getMainMouseSource().showMouseCursor (MouseCursor::WaitCursor);
}

void MouseCursor::hideWaitCursor()
{
    Desktop::getInstance().getMainMouseSource().revealCursor();
}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Components derived from this class can have things dropped onto them by a DragAndDropContainer.

    To create a component that can receive things drag-and-dropped by a DragAndDropContainer,
    derive your component from this class, and make sure that it is somewhere inside a
    DragAndDropContainer component.

    Note: If all that you need to do is to respond to files being drag-and-dropped from
    the operating system onto your component, you don't need any of these classes: instead
    see the FileDragAndDropTarget class.

    @see DragAndDropContainer, FileDragAndDropTarget
*/
class JUCE_API  DragAndDropTarget
{
public:
    /** Destructor. */
    virtual ~DragAndDropTarget()  {}

    //==============================================================================
    /** Contains details about the source of a drag-and-drop operation.
        The contents of this
    */
    class JUCE_API  SourceDetails
    {
    public:
        /** Creates a SourceDetails object from its various settings. */
        SourceDetails (const var& description,
                       Component* sourceComponent,
                       Point<int> localPosition) noexcept;

        /** A descriptor for the drag - this is set DragAndDropContainer::startDragging(). */
        var description;

        /** The component from the drag operation was started. */
        WeakReference<Component> sourceComponent;

        /** The local position of the mouse, relative to the target component.
            Note that for calls such as isInterestedInDragSource(), this may be a null position.
        */
        Point<int> localPosition;
    };

    //==============================================================================
    /** Callback to check whether this target is interested in the type of object being
        dragged.

        @param dragSourceDetails    contains information about the source of the drag operation.
        @returns                    true if this component wants to receive the other callbacks regarging this
                                    type of object; if it returns false, no other callbacks will be made.
    */
    virtual bool isInterestedInDragSource (const SourceDetails& dragSourceDetails) = 0;

    /** Callback to indicate that something is being dragged over this component.

        This gets called when the user moves the mouse into this component while dragging
        something.

        Use this callback as a trigger to make your component repaint itself to give the
        user feedback about whether the item can be dropped here or not.

        @param dragSourceDetails    contains information about the source of the drag operation.
        @see itemDragExit
    */
    virtual void itemDragEnter (const SourceDetails& dragSourceDetails);

    /** Callback to indicate that the user is dragging something over this component.

        This gets called when the user moves the mouse over this component while dragging
        something. Normally overriding itemDragEnter() and itemDragExit() are enough, but
        this lets you know what happens in-between.

        @param dragSourceDetails    contains information about the source of the drag operation.
    */
    virtual void itemDragMove (const SourceDetails& dragSourceDetails);

    /** Callback to indicate that something has been dragged off the edge of this component.

        This gets called when the user moves the mouse out of this component while dragging
        something.

        If you've used itemDragEnter() to repaint your component and give feedback, use this
        as a signal to repaint it in its normal state.

        @param dragSourceDetails    contains information about the source of the drag operation.
        @see itemDragEnter
    */
    virtual void itemDragExit (const SourceDetails& dragSourceDetails);

    /** Callback to indicate that the user has dropped something onto this component.

        When the user drops an item this get called, and you can use the description to
        work out whether your object wants to deal with it or not.

        Note that after this is called, the itemDragExit method may not be called, so you should
        clean up in here if there's anything you need to do when the drag finishes.

        @param dragSourceDetails    contains information about the source of the drag operation.
    */
    virtual void itemDropped (const SourceDetails& dragSourceDetails) = 0;

    /** Overriding this allows the target to tell the drag container whether to
        draw the drag image while the cursor is over it.

        By default it returns true, but if you return false, then the normal drag
        image will not be shown when the cursor is over this target.
    */
    virtual bool shouldDrawDragImageWhenOver();

    //==============================================================================
private:
   #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
    // The parameters for these methods have changed - please update your code!
    virtual void isInterestedInDragSource (const String&, Component*) {}
    virtual int itemDragEnter (const String&, Component*, int, int) { return 0; }
    virtual int itemDragMove (const String&, Component*, int, int) { return 0; }
    virtual int itemDragExit (const String&, Component*) { return 0; }
    virtual int itemDropped (const String&, Component*, int, int) { return 0; }
   #endif
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

ComponentDragger::ComponentDragger() {}
ComponentDragger::~ComponentDragger() {}

//==============================================================================
void ComponentDragger::startDraggingComponent (Component* const componentToDrag, const MouseEvent& e)
{
    jassert (componentToDrag != nullptr);
    jassert (e.mods.isAnyMouseButtonDown()); // The event has to be a drag event!

    if (componentToDrag != nullptr)
        mouseDownWithinTarget = e.getEventRelativeTo (componentToDrag).getMouseDownPosition();
}

void ComponentDragger::dragComponent (Component* const componentToDrag, const MouseEvent& e,
                                      ComponentBoundsConstrainer* const constrainer)
{
    jassert (componentToDrag != nullptr);
    jassert (e.mods.isAnyMouseButtonDown()); // The event has to be a drag event!

    if (componentToDrag != nullptr)
    {
        Rectangle<int> bounds (componentToDrag->getBounds());

        // If the component is a window, multiple mouse events can get queued while it's in the same position,
        // so their coordinates become wrong after the first one moves the window, so in that case, we'll use
        // the current mouse position instead of the one that the event contains...
        if (componentToDrag->isOnDesktop())
            bounds += componentToDrag->getLocalPoint (nullptr, e.source.getScreenPosition()).roundToInt() - mouseDownWithinTarget;
        else
            bounds += e.getEventRelativeTo (componentToDrag).getPosition() - mouseDownWithinTarget;

        if (constrainer != nullptr)
            constrainer->setBoundsForComponent (componentToDrag, bounds, false, false, false, false);
        else
            componentToDrag->setBounds (bounds);
    }
}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Represents a linear source of mouse events from a mouse device or individual finger
    in a multi-touch environment.

    Each MouseEvent object contains a reference to the MouseInputSource that generated
    it. In an environment with a single mouse for input, all events will come from the
    same source, but in a multi-touch system, there may be multiple MouseInputSource
    obects active, each representing a stream of events coming from a particular finger.

    Events coming from a single MouseInputSource are always sent in a fixed and predictable
    order: a mouseMove will never be called without a mouseEnter having been sent beforehand,
    the only events that can happen between a mouseDown and its corresponding mouseUp are
    mouseDrags, etc.
    When there are multiple touches arriving from multiple MouseInputSources, their
    event streams may arrive in an interleaved order, so you should use the getIndex()
    method to find out which finger each event came from.

    @see MouseEvent
*/
class JUCE_API  MouseInputSource  final
{
public:
    /** Possible mouse input sources. */
    enum InputSourceType
    {
        mouse,
        touch,
        pen
    };

    //==============================================================================
    MouseInputSource (const MouseInputSource&) noexcept;
    MouseInputSource& operator= (const MouseInputSource&) noexcept;
    ~MouseInputSource() noexcept;

    //==============================================================================
    bool operator== (const MouseInputSource& other) const noexcept     { return pimpl == other.pimpl; }
    bool operator!= (const MouseInputSource& other) const noexcept     { return pimpl != other.pimpl; }

    //==============================================================================
    /** Returns the type of input source that this object represents. */
    MouseInputSource::InputSourceType getType() const noexcept;

    /** Returns true if this object represents a normal desk-based mouse device. */
    bool isMouse() const noexcept;

    /** Returns true if this object represents a source of touch events. */
    bool isTouch() const noexcept;

    /** Returns true if this object represents a pen device. */
    bool isPen() const noexcept;

    /** Returns true if this source has an on-screen pointer that can hover over
        items without clicking them.
    */
    bool canHover() const noexcept;

    /** Returns true if this source may have a scroll wheel. */
    bool hasMouseWheel() const noexcept;

    /** Returns this source's index in the global list of possible sources.
        If the system only has a single mouse, there will only be a single MouseInputSource
        with an index of 0.

        If the system supports multi-touch input, then the index will represent a finger
        number, starting from 0. When the first touch event begins, it will have finger
        number 0, and then if a second touch happens while the first is still down, it
        will have index 1, etc.
    */
    int getIndex() const noexcept;

    /** Returns true if this device is currently being pressed. */
    bool isDragging() const noexcept;

    /** Returns the last-known screen position of this source. */
    Point<float> getScreenPosition() const noexcept;

    /** Returns the last-known screen position of this source without any scaling applied. */
    Point<float> getRawScreenPosition() const noexcept;

    /** Returns a set of modifiers that indicate which buttons are currently
        held down on this device.
    */
    ModifierKeys getCurrentModifiers() const noexcept;

    /** Returns the device's current touch or pen pressure.
        The range is 0 (soft) to 1 (hard).
        If the input device doesn't provide any pressure data, it may return a negative
        value here, or 0.0 or 1.0, depending on the platform.
    */
    float getCurrentPressure() const noexcept;

    /** Returns the device's current orientation in radians. 0 indicates a touch pointer
        aligned with the x-axis and pointing from left to right; increasing values indicate
        rotation in the clockwise direction. Only reported by a touch pointer.
    */
    float getCurrentOrientation() const noexcept;

    /** Returns the device's current rotation. Indicates the clockwise rotation, or twist, of the pointer
        in radians. The default is 0. Only reported by a pen pointer.
    */
    float getCurrentRotation() const noexcept;

    /** Returns the angle of tilt of the pointer in a range of -1.0 to 1.0 either in the x- or y-axis. The default is 0.
        If x-axis, a positive value indicates a tilt to the right and if y-axis, a positive value indicates a tilt toward the user.
        Only reported by a pen pointer.
    */
    float getCurrentTilt (bool tiltX) const noexcept;

    /** Returns true if the current pressure value is meaningful. */
    bool isPressureValid() const noexcept;

    /** Returns true if the current orientation value is meaningful. */
    bool isOrientationValid() const noexcept;

    /** Returns true if the current rotation value is meaningful. */
    bool isRotationValid() const noexcept;

    /** Returns true if the current tilt value (either x- or y-axis) is meaningful. */
    bool isTiltValid (bool tiltX) const noexcept;

    /** Returns the component that was last known to be under this pointer. */
    Component* getComponentUnderMouse() const;

    /** Tells the device to dispatch a mouse-move or mouse-drag event.
        This is asynchronous - the event will occur on the message thread.
    */
    void triggerFakeMove() const;

    /** Returns the number of clicks that should be counted as belonging to the
        current mouse event.
        So the mouse is currently down and it's the second click of a double-click, this
        will return 2.
    */
    int getNumberOfMultipleClicks() const noexcept;

    /** Returns the time at which the last mouse-down occurred. */
    Time getLastMouseDownTime() const noexcept;

    /** Returns the screen position at which the last mouse-down occurred. */
    Point<float> getLastMouseDownPosition() const noexcept;

    /** Returns true if this mouse is currently down, and if it has been dragged more
        than a couple of pixels from the place it was pressed.
    */
    bool hasMouseMovedSignificantlySincePressed() const noexcept;

    /** Returns true if this input source uses a visible mouse cursor. */
    bool hasMouseCursor() const noexcept;

    /** Changes the mouse cursor, (if there is one). */
    void showMouseCursor (const MouseCursor& cursor);

    /** Hides the mouse cursor (if there is one). */
    void hideCursor();

    /** Un-hides the mouse cursor if it was hidden by hideCursor(). */
    void revealCursor();

    /** Forces an update of the mouse cursor for whatever component it's currently over. */
    void forceMouseCursorUpdate();

    /** Returns true if this mouse can be moved indefinitely in any direction without running out of space. */
    bool canDoUnboundedMovement() const noexcept;

    /** Allows the mouse to move beyond the edges of the screen.

        Calling this method when the mouse button is currently pressed will remove the cursor
        from the screen and allow the mouse to (seem to) move beyond the edges of the screen.

        This means that the coordinates returned to mouseDrag() will be unbounded, and this
        can be used for things like custom slider controls or dragging objects around, where
        movement would be otherwise be limited by the mouse hitting the edges of the screen.

        The unbounded mode is automatically turned off when the mouse button is released, or
        it can be turned off explicitly by calling this method again.

        @param isEnabled                            whether to turn this mode on or off
        @param keepCursorVisibleUntilOffscreen      if set to false, the cursor will immediately be
                                                    hidden; if true, it will only be hidden when it
                                                    is moved beyond the edge of the screen
    */
    void enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen = false) const;

    /** Returns true if this source is currently in "unbounded" mode. */
    bool isUnboundedMouseMovementEnabled() const;

    /** Attempts to set this mouse pointer's screen position. */
    void setScreenPosition (Point<float> newPosition);

    /** A default value for pressure, which is used when a device doesn't support it, or for
        mouse-moves, mouse-ups, etc.
    */
    static const float invalidPressure;

    /** A default value for orientation, which is used when a device doesn't support it */
    static const float invalidOrientation;

    /** A default value for rotation, which is used when a device doesn't support it */
    static const float invalidRotation;

    /** Default values for tilt, which are used when a device doesn't support it */
    static const float invalidTiltX;
    static const float invalidTiltY;

private:
    //==============================================================================
    friend class ComponentPeer;
    friend class Desktop;
    friend class MouseInputSourceInternal;
    MouseInputSourceInternal* pimpl;

    struct SourceList;

    explicit MouseInputSource (MouseInputSourceInternal*) noexcept;
    void handleEvent (ComponentPeer&, Point<float>, int64 time, ModifierKeys, float, float, const PenDetails&);
    void handleWheel (ComponentPeer&, Point<float>, int64 time, const MouseWheelDetails&);
    void handleMagnifyGesture (ComponentPeer&, Point<float>, int64 time, float scaleFactor);

    static Point<float> getCurrentRawMousePosition();
    static void setRawMousePosition (Point<float>);

    JUCE_LEAK_DETECTOR (MouseInputSource)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

void ComponentListener::componentMovedOrResized (Component&, bool, bool)    {}
void ComponentListener::componentBroughtToFront (Component&)                {}
void ComponentListener::componentVisibilityChanged (Component&)             {}
void ComponentListener::componentChildrenChanged (Component&)               {}
void ComponentListener::componentParentHierarchyChanged (Component&)        {}
void ComponentListener::componentNameChanged (Component&)                   {}
void ComponentListener::componentBeingDeleted (Component&)                  {}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Classes can implement this interface and register themselves with the Desktop class
    to receive callbacks when the currently focused component changes.

    @see Desktop::addFocusChangeListener, Desktop::removeFocusChangeListener
*/
class JUCE_API  FocusChangeListener
{
public:
    /** Destructor. */
    virtual ~FocusChangeListener()  {}

    /** Callback to indicate that the currently focused component has changed. */
    virtual void globalFocusChanged (Component* focusedComponent) = 0;
};

//==============================================================================
/**
    Describes and controls aspects of the computer's desktop.

*/
class JUCE_API  Desktop  : private DeletedAtShutdown,
                           private Timer,
                           private AsyncUpdater
{
public:
    //==============================================================================
    /** There's only one desktop object, and this method will return it. */
    static Desktop& JUCE_CALLTYPE getInstance();

    //==============================================================================
    /** Returns the mouse position.

        The coordinates are relative to the top-left of the main monitor.

        Note that this is just a shortcut for calling getMainMouseSource().getScreenPosition(), and
        you should only resort to grabbing the global mouse position if there's really no
        way to get the coordinates via a mouse event callback instead.
    */
    static Point<int> getMousePosition();

    /** Makes the mouse pointer jump to a given location.
        The coordinates are relative to the top-left of the main monitor.
        Note that this is a pretty old method, kept around mainly for backwards-compatibility,
        and you should use the MouseInputSource class directly in new code.
    */
    static void setMousePosition (Point<int> newPosition);

    /** Returns the last position at which a mouse button was pressed.

        Note that this is just a shortcut for calling getMainMouseSource().getLastMouseDownPosition(),
        and in a multi-touch environment, it doesn't make much sense. ALWAYS prefer to
        get this information via other means, such as MouseEvent::getMouseDownScreenPosition()
        if possible, and only ever call this as a last resort.
    */
    static Point<int> getLastMouseDownPosition();

    /** Returns the number of times the mouse button has been clicked since the app started.
        Each mouse-down event increments this number by 1.
        @see getMouseWheelMoveCounter
    */
    int getMouseButtonClickCounter() const noexcept;

    /** Returns the number of times the mouse wheel has been moved since the app started.
        Each mouse-wheel event increments this number by 1.
        @see getMouseButtonClickCounter
    */
    int getMouseWheelMoveCounter() const noexcept;

    //==============================================================================
    /** This lets you prevent the screensaver from becoming active.

        Handy if you're running some sort of presentation app where having a screensaver
        appear would be annoying.

        Pass false to disable the screensaver, and true to re-enable it. (Note that this
        won't enable a screensaver unless the user has actually set one up).

        The disablement will only happen while the Juce application is the foreground
        process - if another task is running in front of it, then the screensaver will
        be unaffected.

        @see isScreenSaverEnabled
    */
    static void setScreenSaverEnabled (bool isEnabled);

    /** Returns true if the screensaver has not been turned off.

        This will return the last value passed into setScreenSaverEnabled(). Note that
        it won't tell you whether the user is actually using a screen saver, just
        whether this app is deliberately preventing one from running.

        @see setScreenSaverEnabled
    */
    static bool isScreenSaverEnabled();

    //==============================================================================
    /** Registers a MouseListener that will receive all mouse events that occur on
        any component.

        @see removeGlobalMouseListener
    */
    void addGlobalMouseListener (MouseListener* listener);

    /** Unregisters a MouseListener that was added with the addGlobalMouseListener()
        method.

        @see addGlobalMouseListener
    */
    void removeGlobalMouseListener (MouseListener* listener);

    //==============================================================================
    /** Registers a MouseListener that will receive a callback whenever the focused
        component changes.
    */
    void addFocusChangeListener (FocusChangeListener* listener);

    /** Unregisters a listener that was added with addFocusChangeListener(). */
    void removeFocusChangeListener (FocusChangeListener* listener);

    //==============================================================================
    /** Takes a component and makes it full-screen, removing the taskbar, dock, etc.

        The component must already be on the desktop for this method to work. It will
        be resized to completely fill the screen and any extraneous taskbars, menu bars,
        etc will be hidden.

        To exit kiosk mode, just call setKioskModeComponent (nullptr). When this is called,
        the component that's currently being used will be resized back to the size
        and position it was in before being put into this mode.

        If allowMenusAndBars is true, things like the menu and dock (on mac) are still
        allowed to pop up when the mouse moves onto them. If this is false, it'll try
        to hide as much on-screen paraphernalia as possible.
    */
    void setKioskModeComponent (Component* componentToUse,
                                bool allowMenusAndBars = true);

    /** Returns the component that is currently being used in kiosk-mode.

        This is the component that was last set by setKioskModeComponent(). If none
        has been set, this returns nullptr.
    */
    Component* getKioskModeComponent() const noexcept               { return kioskModeComponent; }

    //==============================================================================
    /** Returns the number of components that are currently active as top-level
        desktop windows.

        @see getComponent, Component::addToDesktop
    */
    int getNumComponents() const noexcept;

    /** Returns one of the top-level desktop window components.

        The index is from 0 to getNumComponents() - 1. This could return 0 if the
        index is out-of-range.

        @see getNumComponents, Component::addToDesktop
    */
    Component* getComponent (int index) const noexcept;

    /** Finds the component at a given screen location.

        This will drill down into top-level windows to find the child component at
        the given position.

        Returns nullptr if the coordinates are inside a non-Juce window.
    */
    Component* findComponentAt (Point<int> screenPosition) const;

    /** The Desktop object has a ComponentAnimator instance which can be used for performing
        your animations.

        Having a single shared ComponentAnimator object makes it more efficient when multiple
        components are being moved around simultaneously. It's also more convenient than having
        to manage your own instance of one.

        @see ComponentAnimator
    */
    ComponentAnimator& getAnimator() noexcept                       { return animator; }

    //==============================================================================
    /** Returns the current default look-and-feel for components which don't have one
        explicitly set.
        @see setDefaultLookAndFeel
    */
    LookAndFeel& getDefaultLookAndFeel() noexcept;

    /** Changes the default look-and-feel.
        @param newDefaultLookAndFeel    the new look-and-feel object to use - if this is
                                        set to nullptr, it will revert to using the system's
                                        default one. The object passed-in must be deleted by the
                                        caller when it's no longer needed.
        @see getDefaultLookAndFeel
    */
    void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel);

    //==============================================================================
    /** Provides access to the array of mouse sources, for iteration.
        In a traditional single-mouse system, there might be only one MouseInputSource. On a
        multi-touch system, there could be one input source per potential finger. The number
        of mouse sources returned here may increase dynamically as the program runs.
        To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
    */
    const Array<MouseInputSource>& getMouseSources() const noexcept;

    /** Returns the number of MouseInputSource objects the system has at its disposal.
        In a traditional single-mouse system, there might be only one MouseInputSource. On a
        multi-touch system, there could be one input source per potential finger. The number
        of mouse sources returned here may increase dynamically as the program runs.
        To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
        @see getMouseSource
    */
    int getNumMouseSources() const noexcept;

    /** Returns one of the system's MouseInputSource objects.
        The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return
        a null pointer.
        In a traditional single-mouse system, there might be only one object. On a multi-touch
        system, there could be one input source per potential finger.
    */
    MouseInputSource* getMouseSource (int index) const noexcept;

    /** Returns the main mouse input device that the system is using.
        @see getNumMouseSources()
    */
    MouseInputSource getMainMouseSource() const noexcept;

    /** Returns the number of mouse-sources that are currently being dragged.
        In a traditional single-mouse system, this will be 0 or 1, depending on whether a
        juce component has the button down on it. In a multi-touch system, this could
        be any number from 0 to the number of simultaneous touches that can be detected.
    */
    int getNumDraggingMouseSources() const noexcept;

    /** Returns one of the mouse sources that's currently being dragged.
        The index should be between 0 and getNumDraggingMouseSources() - 1. If the index is
        out of range, or if no mice or fingers are down, this will return a null pointer.
    */
    MouseInputSource* getDraggingMouseSource (int index) const noexcept;

    /** Ensures that a non-stop stream of mouse-drag events will be sent during the
        current mouse-drag operation.

        This allows you to make sure that mouseDrag() events are sent continuously, even
        when the mouse isn't moving. This can be useful for things like auto-scrolling
        components when the mouse is near an edge.

        Call this method during a mouseDown() or mouseDrag() callback, specifying the
        minimum interval between consecutive mouse drag callbacks. The callbacks
        will continue until the mouse is released, and then the interval will be reset,
        so you need to make sure it's called every time you begin a drag event.
        Passing an interval of 0 or less will cancel the auto-repeat.

        @see mouseDrag
    */
    void beginDragAutoRepeat (int millisecondsBetweenCallbacks);

    //==============================================================================
    /** In a tablet/mobile device which can be turned around, this is used to indicate the orientation. */
    enum DisplayOrientation
    {
        upright                 = 1,  /**< Indicates that the device is the normal way up. */
        upsideDown              = 2,  /**< Indicates that the device is upside-down. */
        rotatedClockwise        = 4,  /**< Indicates that the device is turned 90 degrees clockwise from its upright position. */
        rotatedAntiClockwise    = 8,  /**< Indicates that the device is turned 90 degrees anti-clockwise from its upright position. */

        allOrientations         = 1 + 2 + 4 + 8   /**< A combination of all the orientation values */
    };

    /** In a tablet device which can be turned around, this returns the current orientation. */
    DisplayOrientation getCurrentOrientation() const;

    /** Sets which orientations the display is allowed to auto-rotate to.

        For devices that support rotating desktops, this lets you specify which of the orientations your app can use.

        The parameter is a bitwise or-ed combination of the values in DisplayOrientation, and must contain at least one
        set bit.
    */
    void setOrientationsEnabled (int allowedOrientations);

    /** Returns the set of orientations the display is allowed to rotate to.
        @see setOrientationsEnabled
    */
    int getOrientationsEnabled() const noexcept;

    /** Returns whether the display is allowed to auto-rotate to the given orientation.
        Each orientation can be enabled using setOrientationEnabled(). By default, all orientations are allowed.
    */
    bool isOrientationEnabled (DisplayOrientation orientation) const noexcept;

    //==============================================================================
    class JUCE_API  Displays
    {
    public:
        /** Contains details about a display device. */
        struct Display
        {
            /** This is the bounds of the area of this display which isn't covered by
                OS-dependent objects like the taskbar, menu bar, etc. */
            Rectangle<int> userArea;

            /** This is the total physical area of this display, including any taskbars, etc */
            Rectangle<int> totalArea;

            /** This is the scale-factor of this display.
                If you create a component with size 1x1, this scale factor indicates the actual
                size of the component in terms of physical pixels.
                For higher-resolution displays, it may be a value greater than 1.0
            */
            double scale;

            /** The DPI of the display.
                This is the number of physical pixels per inch. To get the number of logical
                pixels per inch, divide this by the Display::scale value.
            */
            double dpi;

            /** This will be true if this is the user's main screen. */
            bool isMain;
        };

        /** Returns the display which acts as user's main screen. */
        const Display& getMainDisplay() const noexcept;

        /** Returns the display which contains a particular point.
            If the point lies outside all the displays, the nearest one will be returned.
        */
        const Display& getDisplayContaining (Point<int> position) const noexcept;

        /** Returns a RectangleList made up of all the displays. */
        RectangleList<int> getRectangleList (bool userAreasOnly) const;

        /** Returns the smallest bounding box which contains all the displays. */
        Rectangle<int> getTotalBounds (bool userAreasOnly) const;

        /** The list of displays. */
        Array<Display> displays;

       #ifndef DOXYGEN
        /** @internal */
        void refresh();
       #endif

    private:
        friend class Desktop;
        friend struct ContainerDeletePolicy<Displays>;
        Displays (Desktop&);
        ~Displays();

        void init (Desktop&);
        void findDisplays (float masterScale);
    };

    const Displays& getDisplays() const noexcept        { return *displays; }

    //==============================================================================
    /** Sets a global scale factor to be used for all desktop windows.
        Setting this will also scale the monitor sizes that are returned by getDisplays().
    */
    void setGlobalScaleFactor (float newScaleFactor) noexcept;

    /** Returns the current global scale factor, as set by setGlobalScaleFactor().
        @see setGlobalScaleFactor
    */
    float getGlobalScaleFactor() const noexcept         { return masterScaleFactor; }

    //==============================================================================
    /** True if the OS supports semitransparent windows */
    static bool canUseSemiTransparentWindows() noexcept;

   #if JUCE_MAC
    /** OSX-specific function to check for the "dark" title-bar and menu mode. */
    static bool isOSXDarkModeActive();
   #endif

private:
    //==============================================================================
    static Desktop* instance;

    friend class Component;
    friend class ComponentPeer;
    friend class MouseInputSourceInternal;
    friend class DeletedAtShutdown;
    friend class TopLevelWindowManager;

    ScopedPointer<MouseInputSource::SourceList> mouseSources;

    ListenerList<MouseListener> mouseListeners;
    ListenerList<FocusChangeListener> focusListeners;

    Array<Component*> desktopComponents;
    Array<ComponentPeer*> peers;

    ScopedPointer<Displays> displays;

    Point<float> lastFakeMouseMove;
    void sendMouseMove();

    int mouseClickCounter, mouseWheelCounter;
    void incrementMouseClickCounter() noexcept;
    void incrementMouseWheelCounter() noexcept;

    ScopedPointer<LookAndFeel> defaultLookAndFeel;
    WeakReference<LookAndFeel> currentLookAndFeel;

    Component* kioskModeComponent;
    Rectangle<int> kioskComponentOriginalBounds;
    bool kioskModeReentrant;

    int allowedOrientations;
    void allowedOrientationsChanged();

    float masterScaleFactor;

    ComponentAnimator animator;

    void timerCallback() override;
    void resetTimer();
    ListenerList<MouseListener>& getMouseListeners();

    void addDesktopComponent (Component*);
    void removeDesktopComponent (Component*);
    void componentBroughtToFront (Component*);

    void setKioskComponent (Component*, bool shouldBeEnabled, bool allowMenusAndBars);

    void triggerFocusCallback();
    void handleAsyncUpdate() override;

    static Point<float> getMousePositionFloat();

    static double getDefaultMasterScale();

    Desktop();
    ~Desktop();

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Gets informed about changes to a component's hierarchy or position.

    To monitor a component for changes, register a subclass of ComponentListener
    with the component using Component::addComponentListener().

    Be sure to deregister listeners before you delete them!

    @see Component::addComponentListener, Component::removeComponentListener
*/
class JUCE_API  ComponentListener
{
public:
    /** Destructor. */
    virtual ~ComponentListener()  {}

    /** Called when the component's position or size changes.

        @param component    the component that was moved or resized
        @param wasMoved     true if the component's top-left corner has just moved
        @param wasResized   true if the component's width or height has just changed
        @see Component::setBounds, Component::resized, Component::moved
    */
    virtual void componentMovedOrResized (Component& component,
                                          bool wasMoved,
                                          bool wasResized);

    /** Called when the component is brought to the top of the z-order.

        @param component    the component that was moved
        @see Component::toFront, Component::broughtToFront
    */
    virtual void componentBroughtToFront (Component& component);

    /** Called when the component is made visible or invisible.

        @param component    the component that changed
        @see Component::setVisible
    */
    virtual void componentVisibilityChanged (Component& component);

    /** Called when the component has children added or removed, or their z-order
        changes.

        @param component    the component whose children have changed
        @see Component::childrenChanged, Component::addChildComponent,
             Component::removeChildComponent
    */
    virtual void componentChildrenChanged (Component& component);

    /** Called to indicate that the component's parents have changed.

        When a component is added or removed from its parent, all of its children
        will produce this notification (recursively - so all children of its
        children will also be called as well).

        @param component    the component that this listener is registered with
        @see Component::parentHierarchyChanged
    */
    virtual void componentParentHierarchyChanged (Component& component);

    /** Called when the component's name is changed.

        @see Component::setName, Component::getName
    */
    virtual void componentNameChanged (Component& component);

    /** Called when the component is in the process of being deleted.

        This callback is made from inside the destructor, so be very, very cautious
        about what you do in here.

        In particular, bear in mind that it's the Component base class's destructor that calls
        this - so if the object that's being deleted is a subclass of Component, then the
        subclass layers of the object will already have been destructed when it gets to this
        point!
    */
    virtual void componentBeingDeleted (Component& component);
};

} // namespace juce