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.

531 lines
20KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 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. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_MouseInputSource.h"
  21. #include "juce_MouseEvent.h"
  22. #include "../juce_Component.h"
  23. #include "../../../events/juce_AsyncUpdater.h"
  24. #include "../lookandfeel/juce_LookAndFeel.h"
  25. #include "../windows/juce_ComponentPeer.h"
  26. //==============================================================================
  27. class MouseInputSourceInternal : public AsyncUpdater
  28. {
  29. public:
  30. //==============================================================================
  31. MouseInputSourceInternal (MouseInputSource& source_, const int index_, const bool isMouseDevice_)
  32. : index (index_), isMouseDevice (isMouseDevice_), source (source_), lastPeer (0), lastTime (0),
  33. isUnboundedMouseModeOn (false), isCursorVisibleUntilOffscreen (false), currentCursorHandle (0),
  34. mouseEventCounter (0)
  35. {
  36. zerostruct (mouseDowns);
  37. }
  38. ~MouseInputSourceInternal()
  39. {
  40. }
  41. //==============================================================================
  42. bool isDragging() const throw()
  43. {
  44. return buttonState.isAnyMouseButtonDown();
  45. }
  46. Component* getComponentUnderMouse() const
  47. {
  48. return static_cast <Component*> (componentUnderMouse);
  49. }
  50. const ModifierKeys getCurrentModifiers() const
  51. {
  52. return ModifierKeys::getCurrentModifiers().withoutMouseButtons().withFlags (buttonState.getRawFlags());
  53. }
  54. ComponentPeer* getPeer()
  55. {
  56. if (! ComponentPeer::isValidPeer (lastPeer))
  57. lastPeer = 0;
  58. return lastPeer;
  59. }
  60. Component* findComponentAt (const Point<int>& screenPos)
  61. {
  62. ComponentPeer* const peer = getPeer();
  63. if (peer != 0)
  64. {
  65. Component* const comp = peer->getComponent();
  66. const Point<int> relativePos (comp->globalPositionToRelative (screenPos));
  67. // (the contains() call is needed to test for overlapping desktop windows)
  68. if (comp->contains (relativePos.getX(), relativePos.getY()))
  69. return comp->getComponentAt (relativePos);
  70. }
  71. return 0;
  72. }
  73. const Point<int> getScreenPosition() const throw()
  74. {
  75. return lastScreenPos + unboundedMouseOffset;
  76. }
  77. //==============================================================================
  78. void sendMouseEnter (Component* const comp, const Point<int>& screenPos, const int64 time)
  79. {
  80. //DBG ("Mouse " + String (source.getIndex()) + " enter: " + comp->globalPositionToRelative (screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  81. comp->internalMouseEnter (source, comp->globalPositionToRelative (screenPos), time);
  82. }
  83. void sendMouseExit (Component* const comp, const Point<int>& screenPos, const int64 time)
  84. {
  85. //DBG ("Mouse " + String (source.getIndex()) + " exit: " + comp->globalPositionToRelative (screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  86. comp->internalMouseExit (source, comp->globalPositionToRelative (screenPos), time);
  87. }
  88. void sendMouseMove (Component* const comp, const Point<int>& screenPos, const int64 time)
  89. {
  90. //DBG ("Mouse " + String (source.getIndex()) + " move: " + comp->globalPositionToRelative (screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  91. comp->internalMouseMove (source, comp->globalPositionToRelative (screenPos), time);
  92. }
  93. void sendMouseDown (Component* const comp, const Point<int>& screenPos, const int64 time)
  94. {
  95. //DBG ("Mouse " + String (source.getIndex()) + " down: " + comp->globalPositionToRelative (screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  96. comp->internalMouseDown (source, comp->globalPositionToRelative (screenPos), time);
  97. }
  98. void sendMouseDrag (Component* const comp, const Point<int>& screenPos, const int64 time)
  99. {
  100. //DBG ("Mouse " + String (source.getIndex()) + " drag: " + comp->globalPositionToRelative (screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  101. comp->internalMouseDrag (source, comp->globalPositionToRelative (screenPos), time);
  102. }
  103. void sendMouseUp (Component* const comp, const Point<int>& screenPos, const int64 time)
  104. {
  105. //DBG ("Mouse " + String (source.getIndex()) + " up: " + comp->globalPositionToRelative (screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  106. comp->internalMouseUp (source, comp->globalPositionToRelative (screenPos), time, getCurrentModifiers());
  107. }
  108. void sendMouseWheel (Component* const comp, const Point<int>& screenPos, const int64 time, float x, float y)
  109. {
  110. //DBG ("Mouse " + String (source.getIndex()) + " wheel: " + comp->globalPositionToRelative (screenPos).toString() + " - Comp: " + String::toHexString ((int) comp));
  111. comp->internalMouseWheel (source, comp->globalPositionToRelative (screenPos), time, x, y);
  112. }
  113. //==============================================================================
  114. // (returns true if the button change caused a modal event loop)
  115. bool setButtons (const Point<int>& screenPos, const int64 time, const ModifierKeys& newButtonState)
  116. {
  117. if (buttonState == newButtonState)
  118. return false;
  119. setScreenPos (screenPos, time, false);
  120. // (ignore secondary clicks when there's already a button down)
  121. if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
  122. {
  123. buttonState = newButtonState;
  124. return false;
  125. }
  126. const int lastCounter = mouseEventCounter;
  127. if (buttonState.isAnyMouseButtonDown())
  128. {
  129. Component* const current = getComponentUnderMouse();
  130. if (current != 0)
  131. sendMouseUp (current, screenPos + unboundedMouseOffset, time);
  132. enableUnboundedMouseMovement (false, false);
  133. }
  134. buttonState = newButtonState;
  135. if (buttonState.isAnyMouseButtonDown())
  136. {
  137. Desktop::getInstance().incrementMouseClickCounter();
  138. Component* const current = getComponentUnderMouse();
  139. if (current != 0)
  140. {
  141. registerMouseDown (screenPos, time, current);
  142. sendMouseDown (current, screenPos, time);
  143. }
  144. }
  145. return lastCounter != mouseEventCounter;
  146. }
  147. void setComponentUnderMouse (Component* const newComponent, const Point<int>& screenPos, const int64 time)
  148. {
  149. Component* current = getComponentUnderMouse();
  150. if (newComponent != current)
  151. {
  152. Component::SafePointer<Component> safeNewComp (newComponent);
  153. const ModifierKeys originalButtonState (buttonState);
  154. if (current != 0)
  155. {
  156. setButtons (screenPos, time, ModifierKeys());
  157. sendMouseExit (current, screenPos, time);
  158. buttonState = originalButtonState;
  159. }
  160. componentUnderMouse = safeNewComp;
  161. current = getComponentUnderMouse();
  162. if (current != 0)
  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 int64 time)
  169. {
  170. ModifierKeys::updateCurrentModifiers();
  171. if (newPeer != lastPeer)
  172. {
  173. setComponentUnderMouse (0, screenPos, time);
  174. lastPeer = newPeer;
  175. setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
  176. }
  177. }
  178. void setScreenPos (const Point<int>& newScreenPos, const int64 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 != 0)
  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 int64 time, const ModifierKeys& newMods)
  206. {
  207. jassert (newPeer != 0);
  208. lastTime = time;
  209. ++mouseEventCounter;
  210. const Point<int> screenPos (newPeer->relativePositionToGlobal (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 != 0)
  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 != 0)
  225. setScreenPos (screenPos, time, false);
  226. }
  227. }
  228. }
  229. void handleWheel (ComponentPeer* const peer, const Point<int>& positionWithinPeer, int64 time, float x, float y)
  230. {
  231. jassert (peer != 0);
  232. lastTime = time;
  233. ++mouseEventCounter;
  234. const Point<int> screenPos (peer->relativePositionToGlobal (positionWithinPeer));
  235. setPeer (peer, screenPos, time);
  236. setScreenPos (screenPos, time, false);
  237. triggerFakeMove();
  238. if (! isDragging())
  239. {
  240. Component* current = getComponentUnderMouse();
  241. if (current != 0)
  242. sendMouseWheel (current, screenPos, time, x, y);
  243. }
  244. }
  245. //==============================================================================
  246. const Time getLastMouseDownTime() const throw()
  247. {
  248. return Time (mouseDowns[0].time);
  249. }
  250. const Point<int> getLastMouseDownPosition() const throw()
  251. {
  252. return mouseDowns[0].position;
  253. }
  254. int getNumberOfMultipleClicks() const throw()
  255. {
  256. int numClicks = 0;
  257. if (mouseDowns[0].time != 0)
  258. {
  259. if (! mouseMovedSignificantlySincePressed)
  260. ++numClicks;
  261. for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
  262. {
  263. if (mouseDowns[0].time - mouseDowns[i].time < (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1)))
  264. && abs (mouseDowns[0].position.getX() - mouseDowns[i].position.getX()) < 8
  265. && abs (mouseDowns[0].position.getY() - mouseDowns[i].position.getY()) < 8)
  266. {
  267. ++numClicks;
  268. }
  269. else
  270. {
  271. break;
  272. }
  273. }
  274. }
  275. return numClicks;
  276. }
  277. bool hasMouseMovedSignificantlySincePressed() const throw()
  278. {
  279. return mouseMovedSignificantlySincePressed
  280. || lastTime > mouseDowns[0].time + 300;
  281. }
  282. //==============================================================================
  283. void triggerFakeMove()
  284. {
  285. triggerAsyncUpdate();
  286. }
  287. void handleAsyncUpdate()
  288. {
  289. setScreenPos (Desktop::getMousePosition(), jmax (lastTime, Time::currentTimeMillis()), true);
  290. }
  291. //==============================================================================
  292. void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
  293. {
  294. enable = enable && isDragging();
  295. isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
  296. if (enable != isUnboundedMouseModeOn)
  297. {
  298. if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
  299. {
  300. // when released, return the mouse to within the component's bounds
  301. Component* current = getComponentUnderMouse();
  302. if (current != 0)
  303. Desktop::setMousePosition (current->getScreenBounds()
  304. .getConstrainedPoint (current->getMouseXYRelative()));
  305. }
  306. isUnboundedMouseModeOn = enable;
  307. unboundedMouseOffset = Point<int>();
  308. revealCursor (true);
  309. }
  310. }
  311. void handleUnboundedDrag (Component* current)
  312. {
  313. const Rectangle<int> screenArea (current->getParentMonitorArea().expanded (-2, -2));
  314. if (! screenArea.contains (lastScreenPos))
  315. {
  316. const Point<int> componentCentre (current->getScreenBounds().getCentre());
  317. unboundedMouseOffset += (lastScreenPos - componentCentre);
  318. Desktop::setMousePosition (componentCentre);
  319. }
  320. else if (isCursorVisibleUntilOffscreen
  321. && (! unboundedMouseOffset.isOrigin())
  322. && screenArea.contains (lastScreenPos + unboundedMouseOffset))
  323. {
  324. Desktop::setMousePosition (lastScreenPos + unboundedMouseOffset);
  325. unboundedMouseOffset = Point<int>();
  326. }
  327. }
  328. //==============================================================================
  329. void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
  330. {
  331. if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
  332. {
  333. cursor = MouseCursor::NoCursor;
  334. forcedUpdate = true;
  335. }
  336. if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
  337. {
  338. currentCursorHandle = cursor.getHandle();
  339. cursor.showInWindow (getPeer());
  340. }
  341. }
  342. void hideCursor()
  343. {
  344. showMouseCursor (MouseCursor::NoCursor, true);
  345. }
  346. void revealCursor (bool forcedUpdate)
  347. {
  348. MouseCursor mc (MouseCursor::NormalCursor);
  349. Component* current = getComponentUnderMouse();
  350. if (current != 0)
  351. mc = current->getLookAndFeel().getMouseCursorFor (*current);
  352. showMouseCursor (mc, forcedUpdate);
  353. }
  354. //==============================================================================
  355. int index;
  356. bool isMouseDevice;
  357. Point<int> lastScreenPos;
  358. ModifierKeys buttonState;
  359. private:
  360. MouseInputSource& source;
  361. Component::SafePointer<Component> componentUnderMouse;
  362. ComponentPeer* lastPeer;
  363. Point<int> unboundedMouseOffset;
  364. bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
  365. void* currentCursorHandle;
  366. int mouseEventCounter;
  367. struct RecentMouseDown
  368. {
  369. Point<int> position;
  370. int64 time;
  371. Component* component;
  372. };
  373. RecentMouseDown mouseDowns[4];
  374. bool mouseMovedSignificantlySincePressed;
  375. int64 lastTime;
  376. void registerMouseDown (const Point<int>& screenPos, const int64 time, Component* const component) throw()
  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. mouseMovedSignificantlySincePressed = false;
  384. }
  385. void registerMouseDrag (const Point<int>& screenPos) throw()
  386. {
  387. mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
  388. || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
  389. }
  390. MouseInputSourceInternal (const MouseInputSourceInternal&);
  391. MouseInputSourceInternal& operator= (const 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. const Point<int> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
  408. const 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 throw() { return pimpl->getNumberOfMultipleClicks(); }
  412. const Time MouseInputSource::getLastMouseDownTime() const throw() { return pimpl->getLastMouseDownTime(); }
  413. const Point<int> MouseInputSource::getLastMouseDownPosition() const throw() { return pimpl->getLastMouseDownPosition(); }
  414. bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const throw() { return pimpl->hasMouseMovedSignificantlySincePressed(); }
  415. bool MouseInputSource::canDoUnboundedMovement() const throw() { return isMouse(); }
  416. void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
  417. bool MouseInputSource::hasMouseCursor() const throw() { 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, const int64 time, const ModifierKeys& mods)
  423. {
  424. pimpl->handleEvent (peer, positionWithinPeer, time, mods.withOnlyMouseButtons());
  425. }
  426. void MouseInputSource::handleWheel (ComponentPeer* const peer, const Point<int>& positionWithinPeer, const int64 time, const float x, const float y)
  427. {
  428. pimpl->handleWheel (peer, positionWithinPeer, time, x, y);
  429. }
  430. END_JUCE_NAMESPACE