The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 lines
21KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. class MouseInputSourceInternal : private AsyncUpdater
  19. {
  20. public:
  21. //==============================================================================
  22. MouseInputSourceInternal (MouseInputSource& source_, const int index_, const bool isMouseDevice_)
  23. : index (index_), isMouseDevice (isMouseDevice_), source (source_), lastPeer (nullptr),
  24. isUnboundedMouseModeOn (false), isCursorVisibleUntilOffscreen (false), currentCursorHandle (nullptr),
  25. mouseEventCounter (0), mouseMovedSignificantlySincePressed (false)
  26. {
  27. }
  28. //==============================================================================
  29. bool isDragging() const noexcept
  30. {
  31. return buttonState.isAnyMouseButtonDown();
  32. }
  33. Component* getComponentUnderMouse() const
  34. {
  35. return static_cast <Component*> (componentUnderMouse);
  36. }
  37. ModifierKeys getCurrentModifiers() const
  38. {
  39. return ModifierKeys::getCurrentModifiers().withoutMouseButtons().withFlags (buttonState.getRawFlags());
  40. }
  41. ComponentPeer* getPeer()
  42. {
  43. if (! ComponentPeer::isValidPeer (lastPeer))
  44. lastPeer = nullptr;
  45. return lastPeer;
  46. }
  47. Component* findComponentAt (const Point<int>& screenPos)
  48. {
  49. ComponentPeer* const peer = getPeer();
  50. if (peer != nullptr)
  51. {
  52. Component& comp = peer->getComponent();
  53. const Point<int> relativePos (comp.getLocalPoint (nullptr, screenPos));
  54. // (the contains() call is needed to test for overlapping desktop windows)
  55. if (comp.contains (relativePos))
  56. return comp.getComponentAt (relativePos);
  57. }
  58. return nullptr;
  59. }
  60. Point<int> getScreenPosition() const
  61. {
  62. // This needs to return the live position if possible, but it mustn't update the lastScreenPos
  63. // value, because that can cause continuity problems.
  64. return unboundedMouseOffset + (isMouseDevice ? MouseInputSource::getCurrentMousePosition()
  65. : lastScreenPos);
  66. }
  67. //==============================================================================
  68. void sendMouseEnter (Component* const comp, const Point<int>& screenPos, const Time& time)
  69. {
  70. //DBG ("Mouse " + String (source.getIndex()) + " enter: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  71. comp->internalMouseEnter (source, comp->getLocalPoint (nullptr, screenPos), time);
  72. }
  73. void sendMouseExit (Component* const comp, const Point<int>& screenPos, const Time& time)
  74. {
  75. //DBG ("Mouse " + String (source.getIndex()) + " exit: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  76. comp->internalMouseExit (source, comp->getLocalPoint (nullptr, screenPos), time);
  77. }
  78. void sendMouseMove (Component* const comp, const Point<int>& screenPos, const Time& time)
  79. {
  80. //DBG ("Mouse " + String (source.getIndex()) + " move: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  81. comp->internalMouseMove (source, comp->getLocalPoint (nullptr, screenPos), time);
  82. }
  83. void sendMouseDown (Component* const comp, const Point<int>& screenPos, const Time& time)
  84. {
  85. //DBG ("Mouse " + String (source.getIndex()) + " down: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  86. comp->internalMouseDown (source, comp->getLocalPoint (nullptr, screenPos), time);
  87. }
  88. void sendMouseDrag (Component* const comp, const Point<int>& screenPos, const Time& time)
  89. {
  90. //DBG ("Mouse " + String (source.getIndex()) + " drag: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  91. comp->internalMouseDrag (source, comp->getLocalPoint (nullptr, screenPos), time);
  92. }
  93. void sendMouseUp (Component* const comp, const Point<int>& screenPos, const Time& time, const ModifierKeys& oldMods)
  94. {
  95. //DBG ("Mouse " + String (source.getIndex()) + " up: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  96. comp->internalMouseUp (source, comp->getLocalPoint (nullptr, screenPos), time, oldMods);
  97. }
  98. void sendMouseWheel (Component* const comp, const Point<int>& screenPos, const Time& time, const MouseWheelDetails& wheel)
  99. {
  100. //DBG ("Mouse " + String (source.getIndex()) + " wheel: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  101. comp->internalMouseWheel (source, comp->getLocalPoint (nullptr, screenPos), time, wheel);
  102. }
  103. //==============================================================================
  104. // (returns true if the button change caused a modal event loop)
  105. bool setButtons (const Point<int>& screenPos, const Time& time, const ModifierKeys& newButtonState)
  106. {
  107. if (buttonState == newButtonState)
  108. return false;
  109. // (avoid sending a spurious mouse-drag when we receive a mouse-up)
  110. if (! (isDragging() && ! newButtonState.isAnyMouseButtonDown()))
  111. setScreenPos (screenPos, time, false);
  112. // (ignore secondary clicks when there's already a button down)
  113. if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
  114. {
  115. buttonState = newButtonState;
  116. return false;
  117. }
  118. const int lastCounter = mouseEventCounter;
  119. if (buttonState.isAnyMouseButtonDown())
  120. {
  121. Component* const current = getComponentUnderMouse();
  122. if (current != nullptr)
  123. {
  124. const ModifierKeys oldMods (getCurrentModifiers());
  125. buttonState = newButtonState; // must change this before calling sendMouseUp, in case it runs a modal loop
  126. sendMouseUp (current, screenPos + unboundedMouseOffset, time, oldMods);
  127. }
  128. enableUnboundedMouseMovement (false, false);
  129. }
  130. buttonState = newButtonState;
  131. if (buttonState.isAnyMouseButtonDown())
  132. {
  133. Desktop::getInstance().incrementMouseClickCounter();
  134. Component* const current = getComponentUnderMouse();
  135. if (current != nullptr)
  136. {
  137. registerMouseDown (screenPos, time, current, buttonState);
  138. sendMouseDown (current, screenPos, time);
  139. }
  140. }
  141. return lastCounter != mouseEventCounter;
  142. }
  143. void setComponentUnderMouse (Component* const newComponent, const Point<int>& screenPos, const Time& time)
  144. {
  145. Component* current = getComponentUnderMouse();
  146. if (newComponent != current)
  147. {
  148. WeakReference<Component> safeNewComp (newComponent);
  149. const ModifierKeys originalButtonState (buttonState);
  150. if (current != nullptr)
  151. {
  152. WeakReference<Component> safeOldComp (current);
  153. setButtons (screenPos, time, ModifierKeys());
  154. if (safeOldComp != nullptr)
  155. {
  156. componentUnderMouse = safeNewComp;
  157. sendMouseExit (safeOldComp, screenPos, time);
  158. }
  159. buttonState = originalButtonState;
  160. }
  161. current = componentUnderMouse = safeNewComp;
  162. if (current != nullptr)
  163. sendMouseEnter (current, screenPos, time);
  164. revealCursor (false);
  165. setButtons (screenPos, time, originalButtonState);
  166. }
  167. }
  168. void setPeer (ComponentPeer* const newPeer, const Point<int>& screenPos, const Time& time)
  169. {
  170. ModifierKeys::updateCurrentModifiers();
  171. if (newPeer != lastPeer)
  172. {
  173. setComponentUnderMouse (nullptr, screenPos, time);
  174. lastPeer = newPeer;
  175. setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
  176. }
  177. }
  178. void setScreenPos (const Point<int>& newScreenPos, const Time& time, const bool forceUpdate)
  179. {
  180. if (! isDragging())
  181. setComponentUnderMouse (findComponentAt (newScreenPos), newScreenPos, time);
  182. if (newScreenPos != lastScreenPos || forceUpdate)
  183. {
  184. cancelPendingUpdate();
  185. lastScreenPos = newScreenPos;
  186. Component* const current = getComponentUnderMouse();
  187. if (current != nullptr)
  188. {
  189. if (isDragging())
  190. {
  191. registerMouseDrag (newScreenPos);
  192. sendMouseDrag (current, newScreenPos + unboundedMouseOffset, time);
  193. if (isUnboundedMouseModeOn)
  194. handleUnboundedDrag (current);
  195. }
  196. else
  197. {
  198. sendMouseMove (current, newScreenPos, time);
  199. }
  200. }
  201. revealCursor (false);
  202. }
  203. }
  204. //==============================================================================
  205. void handleEvent (ComponentPeer* const newPeer, const Point<int>& positionWithinPeer, const Time& time, const ModifierKeys& newMods)
  206. {
  207. jassert (newPeer != nullptr);
  208. lastTime = time;
  209. ++mouseEventCounter;
  210. const Point<int> screenPos (newPeer->localToGlobal (positionWithinPeer));
  211. if (isDragging() && newMods.isAnyMouseButtonDown())
  212. {
  213. setScreenPos (screenPos, time, false);
  214. }
  215. else
  216. {
  217. setPeer (newPeer, screenPos, time);
  218. ComponentPeer* peer = getPeer();
  219. if (peer != nullptr)
  220. {
  221. if (setButtons (screenPos, time, newMods))
  222. return; // some modal events have been dispatched, so the current event is now out-of-date
  223. peer = getPeer();
  224. if (peer != nullptr)
  225. setScreenPos (screenPos, time, false);
  226. }
  227. }
  228. }
  229. void handleWheel (ComponentPeer* const peer, const Point<int>& positionWithinPeer,
  230. const Time& time, const MouseWheelDetails& wheel)
  231. {
  232. jassert (peer != nullptr);
  233. lastTime = time;
  234. ++mouseEventCounter;
  235. Desktop::getInstance().incrementMouseWheelCounter();
  236. const Point<int> screenPos (peer->localToGlobal (positionWithinPeer));
  237. setPeer (peer, screenPos, time);
  238. setScreenPos (screenPos, time, false);
  239. triggerFakeMove();
  240. if (! isDragging())
  241. {
  242. Component* current = getComponentUnderMouse();
  243. if (current != nullptr)
  244. sendMouseWheel (current, screenPos, time, wheel);
  245. }
  246. }
  247. //==============================================================================
  248. const Time& getLastMouseDownTime() const noexcept { return mouseDowns[0].time; }
  249. const Point<int>& getLastMouseDownPosition() const noexcept { return mouseDowns[0].position; }
  250. int getNumberOfMultipleClicks() const noexcept
  251. {
  252. int numClicks = 0;
  253. if (mouseDowns[0].time != Time())
  254. {
  255. if (! mouseMovedSignificantlySincePressed)
  256. ++numClicks;
  257. for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
  258. {
  259. if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
  260. ++numClicks;
  261. else
  262. break;
  263. }
  264. }
  265. return numClicks;
  266. }
  267. bool hasMouseMovedSignificantlySincePressed() const noexcept
  268. {
  269. return mouseMovedSignificantlySincePressed
  270. || lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
  271. }
  272. //==============================================================================
  273. void triggerFakeMove()
  274. {
  275. triggerAsyncUpdate();
  276. }
  277. void handleAsyncUpdate()
  278. {
  279. setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
  280. }
  281. //==============================================================================
  282. void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
  283. {
  284. enable = enable && isDragging();
  285. isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
  286. if (enable != isUnboundedMouseModeOn)
  287. {
  288. if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
  289. {
  290. // when released, return the mouse to within the component's bounds
  291. Component* current = getComponentUnderMouse();
  292. if (current != nullptr)
  293. Desktop::setMousePosition (current->getScreenBounds()
  294. .getConstrainedPoint (lastScreenPos));
  295. }
  296. isUnboundedMouseModeOn = enable;
  297. unboundedMouseOffset = Point<int>();
  298. revealCursor (true);
  299. }
  300. }
  301. void handleUnboundedDrag (Component* current)
  302. {
  303. const Rectangle<int> screenArea (current->getParentMonitorArea().expanded (-2, -2));
  304. if (! screenArea.contains (lastScreenPos))
  305. {
  306. const Point<int> componentCentre (current->getScreenBounds().getCentre());
  307. unboundedMouseOffset += (lastScreenPos - componentCentre);
  308. Desktop::setMousePosition (componentCentre);
  309. }
  310. else if (isCursorVisibleUntilOffscreen
  311. && (! unboundedMouseOffset.isOrigin())
  312. && screenArea.contains (lastScreenPos + unboundedMouseOffset))
  313. {
  314. Desktop::setMousePosition (lastScreenPos + unboundedMouseOffset);
  315. unboundedMouseOffset = Point<int>();
  316. }
  317. }
  318. //==============================================================================
  319. void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
  320. {
  321. if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
  322. {
  323. cursor = MouseCursor::NoCursor;
  324. forcedUpdate = true;
  325. }
  326. if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
  327. {
  328. currentCursorHandle = cursor.getHandle();
  329. cursor.showInWindow (getPeer());
  330. }
  331. }
  332. void hideCursor()
  333. {
  334. showMouseCursor (MouseCursor::NoCursor, true);
  335. }
  336. void revealCursor (bool forcedUpdate)
  337. {
  338. MouseCursor mc (MouseCursor::NormalCursor);
  339. Component* current = getComponentUnderMouse();
  340. if (current != nullptr)
  341. mc = current->getLookAndFeel().getMouseCursorFor (*current);
  342. showMouseCursor (mc, forcedUpdate);
  343. }
  344. //==============================================================================
  345. const int index;
  346. const bool isMouseDevice;
  347. Point<int> lastScreenPos;
  348. ModifierKeys buttonState;
  349. private:
  350. MouseInputSource& source;
  351. WeakReference<Component> componentUnderMouse;
  352. ComponentPeer* lastPeer;
  353. Point<int> unboundedMouseOffset;
  354. bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
  355. void* currentCursorHandle;
  356. int mouseEventCounter;
  357. struct RecentMouseDown
  358. {
  359. RecentMouseDown() noexcept : component (nullptr) {}
  360. Point<int> position;
  361. Time time;
  362. Component* component;
  363. ModifierKeys buttons;
  364. bool canBePartOfMultipleClickWith (const RecentMouseDown& other, const int maxTimeBetweenMs) const
  365. {
  366. return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
  367. && abs (position.x - other.position.x) < 8
  368. && abs (position.y - other.position.y) < 8
  369. && buttons == other.buttons;;
  370. }
  371. };
  372. RecentMouseDown mouseDowns[4];
  373. Time lastTime;
  374. bool mouseMovedSignificantlySincePressed;
  375. void registerMouseDown (const Point<int>& screenPos, const Time& time,
  376. Component* const component, const ModifierKeys& modifiers) noexcept
  377. {
  378. for (int i = numElementsInArray (mouseDowns); --i > 0;)
  379. mouseDowns[i] = mouseDowns[i - 1];
  380. mouseDowns[0].position = screenPos;
  381. mouseDowns[0].time = time;
  382. mouseDowns[0].component = component;
  383. mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
  384. mouseMovedSignificantlySincePressed = false;
  385. }
  386. void registerMouseDrag (const Point<int>& screenPos) noexcept
  387. {
  388. mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
  389. || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
  390. }
  391. JUCE_DECLARE_NON_COPYABLE (MouseInputSourceInternal);
  392. };
  393. //==============================================================================
  394. MouseInputSource::MouseInputSource (const int index, const bool isMouseDevice)
  395. {
  396. pimpl = new MouseInputSourceInternal (*this, index, isMouseDevice);
  397. }
  398. MouseInputSource::~MouseInputSource()
  399. {
  400. }
  401. bool MouseInputSource::isMouse() const { return pimpl->isMouseDevice; }
  402. bool MouseInputSource::isTouch() const { return ! isMouse(); }
  403. bool MouseInputSource::canHover() const { return isMouse(); }
  404. bool MouseInputSource::hasMouseWheel() const { return isMouse(); }
  405. int MouseInputSource::getIndex() const { return pimpl->index; }
  406. bool MouseInputSource::isDragging() const { return pimpl->isDragging(); }
  407. Point<int> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
  408. ModifierKeys MouseInputSource::getCurrentModifiers() const { return pimpl->getCurrentModifiers(); }
  409. Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
  410. void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
  411. int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
  412. Time MouseInputSource::getLastMouseDownTime() const noexcept { return pimpl->getLastMouseDownTime(); }
  413. Point<int> MouseInputSource::getLastMouseDownPosition() const noexcept { return pimpl->getLastMouseDownPosition(); }
  414. bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept { return pimpl->hasMouseMovedSignificantlySincePressed(); }
  415. bool MouseInputSource::canDoUnboundedMovement() const noexcept { return isMouse(); }
  416. void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
  417. bool MouseInputSource::hasMouseCursor() const noexcept { return isMouse(); }
  418. void MouseInputSource::showMouseCursor (const MouseCursor& cursor) { pimpl->showMouseCursor (cursor, false); }
  419. void MouseInputSource::hideCursor() { pimpl->hideCursor(); }
  420. void MouseInputSource::revealCursor() { pimpl->revealCursor (false); }
  421. void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
  422. void MouseInputSource::handleEvent (ComponentPeer* peer, const Point<int>& positionWithinPeer,
  423. const int64 time, const ModifierKeys& mods)
  424. {
  425. pimpl->handleEvent (peer, positionWithinPeer, Time (time), mods.withOnlyMouseButtons());
  426. }
  427. void MouseInputSource::handleWheel (ComponentPeer* const peer, const Point<int>& positionWithinPeer,
  428. const int64 time, const MouseWheelDetails& wheel)
  429. {
  430. pimpl->handleWheel (peer, positionWithinPeer, Time (time), wheel);
  431. }