Add remaining files
[juce-lv2.git] / juce / source / src / gui / components / mouse / juce_MouseInputSource.cpp
blob976c63725a54bf9123c2b3b988aaa1734dc940fc
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
28 BEGIN_JUCE_NAMESPACE
30 #include "juce_MouseInputSource.h"
31 #include "juce_MouseEvent.h"
32 #include "../juce_Component.h"
33 #include "../../../events/juce_AsyncUpdater.h"
34 #include "../../../events/juce_MessageManager.h"
35 #include "../lookandfeel/juce_LookAndFeel.h"
36 #include "../windows/juce_ComponentPeer.h"
39 //==============================================================================
40 class MouseInputSourceInternal : public AsyncUpdater
42 public:
43 //==============================================================================
44 MouseInputSourceInternal (MouseInputSource& source_, const int index_, const bool isMouseDevice_)
45 : index (index_), isMouseDevice (isMouseDevice_), source (source_), lastPeer (nullptr),
46 isUnboundedMouseModeOn (false), isCursorVisibleUntilOffscreen (false), currentCursorHandle (nullptr),
47 mouseEventCounter (0)
51 //==============================================================================
52 bool isDragging() const noexcept
54 return buttonState.isAnyMouseButtonDown();
57 Component* getComponentUnderMouse() const
59 return static_cast <Component*> (componentUnderMouse);
62 const ModifierKeys getCurrentModifiers() const
64 return ModifierKeys::getCurrentModifiers().withoutMouseButtons().withFlags (buttonState.getRawFlags());
67 ComponentPeer* getPeer()
69 if (! ComponentPeer::isValidPeer (lastPeer))
70 lastPeer = nullptr;
72 return lastPeer;
75 Component* findComponentAt (const Point<int>& screenPos)
77 ComponentPeer* const peer = getPeer();
79 if (peer != nullptr)
81 Component* const comp = peer->getComponent();
82 const Point<int> relativePos (comp->getLocalPoint (nullptr, screenPos));
84 // (the contains() call is needed to test for overlapping desktop windows)
85 if (comp->contains (relativePos))
86 return comp->getComponentAt (relativePos);
89 return nullptr;
92 const Point<int> getScreenPosition() const
94 // This needs to return the live position if possible, but it mustn't update the lastScreenPos
95 // value, because that can cause continuity problems.
96 return unboundedMouseOffset + (isMouseDevice ? MouseInputSource::getCurrentMousePosition()
97 : lastScreenPos);
100 //==============================================================================
101 void sendMouseEnter (Component* const comp, const Point<int>& screenPos, const Time& time)
103 //DBG ("Mouse " + String (source.getIndex()) + " enter: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
104 comp->internalMouseEnter (source, comp->getLocalPoint (nullptr, screenPos), time);
107 void sendMouseExit (Component* const comp, const Point<int>& screenPos, const Time& time)
109 //DBG ("Mouse " + String (source.getIndex()) + " exit: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
110 comp->internalMouseExit (source, comp->getLocalPoint (nullptr, screenPos), time);
113 void sendMouseMove (Component* const comp, const Point<int>& screenPos, const Time& time)
115 //DBG ("Mouse " + String (source.getIndex()) + " move: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
116 comp->internalMouseMove (source, comp->getLocalPoint (nullptr, screenPos), time);
119 void sendMouseDown (Component* const comp, const Point<int>& screenPos, const Time& time)
121 //DBG ("Mouse " + String (source.getIndex()) + " down: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
122 comp->internalMouseDown (source, comp->getLocalPoint (nullptr, screenPos), time);
125 void sendMouseDrag (Component* const comp, const Point<int>& screenPos, const Time& time)
127 //DBG ("Mouse " + String (source.getIndex()) + " drag: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
128 comp->internalMouseDrag (source, comp->getLocalPoint (nullptr, screenPos), time);
131 void sendMouseUp (Component* const comp, const Point<int>& screenPos, const Time& time)
133 //DBG ("Mouse " + String (source.getIndex()) + " up: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
134 comp->internalMouseUp (source, comp->getLocalPoint (nullptr, screenPos), time, getCurrentModifiers());
137 void sendMouseWheel (Component* const comp, const Point<int>& screenPos, const Time& time, float x, float y)
139 //DBG ("Mouse " + String (source.getIndex()) + " wheel: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
140 comp->internalMouseWheel (source, comp->getLocalPoint (nullptr, screenPos), time, x, y);
143 //==============================================================================
144 // (returns true if the button change caused a modal event loop)
145 bool setButtons (const Point<int>& screenPos, const Time& time, const ModifierKeys& newButtonState)
147 if (buttonState == newButtonState)
148 return false;
150 setScreenPos (screenPos, time, false);
152 // (ignore secondary clicks when there's already a button down)
153 if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
155 buttonState = newButtonState;
156 return false;
159 const int lastCounter = mouseEventCounter;
161 if (buttonState.isAnyMouseButtonDown())
163 Component* const current = getComponentUnderMouse();
165 if (current != nullptr)
166 sendMouseUp (current, screenPos + unboundedMouseOffset, time);
168 enableUnboundedMouseMovement (false, false);
171 buttonState = newButtonState;
173 if (buttonState.isAnyMouseButtonDown())
175 Desktop::getInstance().incrementMouseClickCounter();
177 Component* const current = getComponentUnderMouse();
179 if (current != nullptr)
181 registerMouseDown (screenPos, time, current, buttonState);
182 sendMouseDown (current, screenPos, time);
186 return lastCounter != mouseEventCounter;
189 void setComponentUnderMouse (Component* const newComponent, const Point<int>& screenPos, const Time& time)
191 Component* current = getComponentUnderMouse();
193 if (newComponent != current)
195 WeakReference<Component> safeNewComp (newComponent);
196 const ModifierKeys originalButtonState (buttonState);
198 if (current != nullptr)
200 setButtons (screenPos, time, ModifierKeys());
201 sendMouseExit (current, screenPos, time);
202 buttonState = originalButtonState;
205 componentUnderMouse = safeNewComp;
206 current = getComponentUnderMouse();
208 if (current != nullptr)
209 sendMouseEnter (current, screenPos, time);
211 revealCursor (false);
212 setButtons (screenPos, time, originalButtonState);
216 void setPeer (ComponentPeer* const newPeer, const Point<int>& screenPos, const Time& time)
218 ModifierKeys::updateCurrentModifiers();
220 if (newPeer != lastPeer)
222 setComponentUnderMouse (nullptr, screenPos, time);
223 lastPeer = newPeer;
224 setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
228 void setScreenPos (const Point<int>& newScreenPos, const Time& time, const bool forceUpdate)
230 if (! isDragging())
231 setComponentUnderMouse (findComponentAt (newScreenPos), newScreenPos, time);
233 if (newScreenPos != lastScreenPos || forceUpdate)
235 cancelPendingUpdate();
237 lastScreenPos = newScreenPos;
238 Component* const current = getComponentUnderMouse();
240 if (current != nullptr)
242 if (isDragging())
244 registerMouseDrag (newScreenPos);
245 sendMouseDrag (current, newScreenPos + unboundedMouseOffset, time);
247 if (isUnboundedMouseModeOn)
248 handleUnboundedDrag (current);
250 else
252 sendMouseMove (current, newScreenPos, time);
256 revealCursor (false);
260 //==============================================================================
261 void handleEvent (ComponentPeer* const newPeer, const Point<int>& positionWithinPeer, const Time& time, const ModifierKeys& newMods)
263 jassert (newPeer != nullptr);
264 lastTime = time;
265 ++mouseEventCounter;
266 const Point<int> screenPos (newPeer->localToGlobal (positionWithinPeer));
268 if (isDragging() && newMods.isAnyMouseButtonDown())
270 setScreenPos (screenPos, time, false);
272 else
274 setPeer (newPeer, screenPos, time);
276 ComponentPeer* peer = getPeer();
277 if (peer != nullptr)
279 if (setButtons (screenPos, time, newMods))
280 return; // some modal events have been dispatched, so the current event is now out-of-date
282 peer = getPeer();
283 if (peer != nullptr)
284 setScreenPos (screenPos, time, false);
289 void handleWheel (ComponentPeer* const peer, const Point<int>& positionWithinPeer, const Time& time, float x, float y)
291 jassert (peer != nullptr);
292 lastTime = time;
293 ++mouseEventCounter;
294 const Point<int> screenPos (peer->localToGlobal (positionWithinPeer));
296 setPeer (peer, screenPos, time);
297 setScreenPos (screenPos, time, false);
298 triggerFakeMove();
300 if (! isDragging())
302 Component* current = getComponentUnderMouse();
303 if (current != nullptr)
304 sendMouseWheel (current, screenPos, time, x, y);
308 //==============================================================================
309 Time getLastMouseDownTime() const noexcept
311 return Time (mouseDowns[0].time);
314 Point<int> getLastMouseDownPosition() const noexcept
316 return mouseDowns[0].position;
319 int getNumberOfMultipleClicks() const noexcept
321 int numClicks = 0;
323 if (mouseDowns[0].time != Time())
325 if (! mouseMovedSignificantlySincePressed)
326 ++numClicks;
328 for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
330 if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1)))))
331 ++numClicks;
332 else
333 break;
337 return numClicks;
340 bool hasMouseMovedSignificantlySincePressed() const noexcept
342 return mouseMovedSignificantlySincePressed
343 || lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
346 //==============================================================================
347 void triggerFakeMove()
349 triggerAsyncUpdate();
352 void handleAsyncUpdate()
354 setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
357 //==============================================================================
358 void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
360 enable = enable && isDragging();
361 isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
363 if (enable != isUnboundedMouseModeOn)
365 if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
367 // when released, return the mouse to within the component's bounds
368 Component* current = getComponentUnderMouse();
369 if (current != nullptr)
370 Desktop::setMousePosition (current->getScreenBounds()
371 .getConstrainedPoint (lastScreenPos));
374 isUnboundedMouseModeOn = enable;
375 unboundedMouseOffset = Point<int>();
377 revealCursor (true);
381 void handleUnboundedDrag (Component* current)
383 const Rectangle<int> screenArea (current->getParentMonitorArea().expanded (-2, -2));
385 if (! screenArea.contains (lastScreenPos))
387 const Point<int> componentCentre (current->getScreenBounds().getCentre());
388 unboundedMouseOffset += (lastScreenPos - componentCentre);
389 Desktop::setMousePosition (componentCentre);
391 else if (isCursorVisibleUntilOffscreen
392 && (! unboundedMouseOffset.isOrigin())
393 && screenArea.contains (lastScreenPos + unboundedMouseOffset))
395 Desktop::setMousePosition (lastScreenPos + unboundedMouseOffset);
396 unboundedMouseOffset = Point<int>();
400 //==============================================================================
401 void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
403 if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
405 cursor = MouseCursor::NoCursor;
406 forcedUpdate = true;
409 if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
411 currentCursorHandle = cursor.getHandle();
412 cursor.showInWindow (getPeer());
416 void hideCursor()
418 showMouseCursor (MouseCursor::NoCursor, true);
421 void revealCursor (bool forcedUpdate)
423 MouseCursor mc (MouseCursor::NormalCursor);
425 Component* current = getComponentUnderMouse();
426 if (current != nullptr)
427 mc = current->getLookAndFeel().getMouseCursorFor (*current);
429 showMouseCursor (mc, forcedUpdate);
432 //==============================================================================
433 const int index;
434 const bool isMouseDevice;
435 Point<int> lastScreenPos;
436 ModifierKeys buttonState;
438 private:
439 MouseInputSource& source;
440 WeakReference<Component> componentUnderMouse;
441 ComponentPeer* lastPeer;
443 Point<int> unboundedMouseOffset;
444 bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
445 void* currentCursorHandle;
446 int mouseEventCounter;
448 struct RecentMouseDown
450 RecentMouseDown() : component (nullptr)
454 Point<int> position;
455 Time time;
456 Component* component;
457 ModifierKeys buttons;
459 bool canBePartOfMultipleClickWith (const RecentMouseDown& other, const int maxTimeBetweenMs) const
461 return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
462 && abs (position.getX() - other.position.getX()) < 8
463 && abs (position.getY() - other.position.getY()) < 8
464 && buttons == other.buttons;;
468 RecentMouseDown mouseDowns[4];
469 bool mouseMovedSignificantlySincePressed;
470 Time lastTime;
472 void registerMouseDown (const Point<int>& screenPos, const Time& time,
473 Component* const component, const ModifierKeys& modifiers) noexcept
475 for (int i = numElementsInArray (mouseDowns); --i > 0;)
476 mouseDowns[i] = mouseDowns[i - 1];
478 mouseDowns[0].position = screenPos;
479 mouseDowns[0].time = time;
480 mouseDowns[0].component = component;
481 mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
482 mouseMovedSignificantlySincePressed = false;
485 void registerMouseDrag (const Point<int>& screenPos) noexcept
487 mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
488 || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
491 JUCE_DECLARE_NON_COPYABLE (MouseInputSourceInternal);
494 //==============================================================================
495 MouseInputSource::MouseInputSource (const int index, const bool isMouseDevice)
497 pimpl = new MouseInputSourceInternal (*this, index, isMouseDevice);
500 MouseInputSource::~MouseInputSource()
504 bool MouseInputSource::isMouse() const { return pimpl->isMouseDevice; }
505 bool MouseInputSource::isTouch() const { return ! isMouse(); }
506 bool MouseInputSource::canHover() const { return isMouse(); }
507 bool MouseInputSource::hasMouseWheel() const { return isMouse(); }
508 int MouseInputSource::getIndex() const { return pimpl->index; }
509 bool MouseInputSource::isDragging() const { return pimpl->isDragging(); }
510 const Point<int> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
511 const ModifierKeys MouseInputSource::getCurrentModifiers() const { return pimpl->getCurrentModifiers(); }
512 Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
513 void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
514 int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
515 Time MouseInputSource::getLastMouseDownTime() const noexcept { return pimpl->getLastMouseDownTime(); }
516 Point<int> MouseInputSource::getLastMouseDownPosition() const noexcept { return pimpl->getLastMouseDownPosition(); }
517 bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept { return pimpl->hasMouseMovedSignificantlySincePressed(); }
518 bool MouseInputSource::canDoUnboundedMovement() const noexcept { return isMouse(); }
519 void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
520 bool MouseInputSource::hasMouseCursor() const noexcept { return isMouse(); }
521 void MouseInputSource::showMouseCursor (const MouseCursor& cursor) { pimpl->showMouseCursor (cursor, false); }
522 void MouseInputSource::hideCursor() { pimpl->hideCursor(); }
523 void MouseInputSource::revealCursor() { pimpl->revealCursor (false); }
524 void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
526 void MouseInputSource::handleEvent (ComponentPeer* peer, const Point<int>& positionWithinPeer, const int64 time, const ModifierKeys& mods)
528 pimpl->handleEvent (peer, positionWithinPeer, Time (time), mods.withOnlyMouseButtons());
531 void MouseInputSource::handleWheel (ComponentPeer* const peer, const Point<int>& positionWithinPeer, const int64 time, const float x, const float y)
533 pimpl->handleWheel (peer, positionWithinPeer, Time (time), x, y);
537 END_JUCE_NAMESPACE