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.

449 lines
13KB

  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. Desktop::Desktop()
  19. : mouseClickCounter (0),
  20. kioskModeComponent (nullptr),
  21. allowedOrientations (allOrientations)
  22. {
  23. createMouseInputSources();
  24. refreshMonitorSizes();
  25. }
  26. Desktop::~Desktop()
  27. {
  28. setScreenSaverEnabled (true);
  29. jassert (instance == this);
  30. instance = nullptr;
  31. // doh! If you don't delete all your windows before exiting, you're going to
  32. // be leaking memory!
  33. jassert (desktopComponents.size() == 0);
  34. }
  35. Desktop& JUCE_CALLTYPE Desktop::getInstance()
  36. {
  37. if (instance == nullptr)
  38. instance = new Desktop();
  39. return *instance;
  40. }
  41. Desktop* Desktop::instance = nullptr;
  42. //==============================================================================
  43. void Desktop::refreshMonitorSizes()
  44. {
  45. Array <Rectangle<int> > oldClipped, oldUnclipped;
  46. oldClipped.swapWithArray (monitorCoordsClipped);
  47. oldUnclipped.swapWithArray (monitorCoordsUnclipped);
  48. getCurrentMonitorPositions (monitorCoordsClipped, true);
  49. getCurrentMonitorPositions (monitorCoordsUnclipped, false);
  50. jassert (monitorCoordsClipped.size() == monitorCoordsUnclipped.size());
  51. if (oldClipped != monitorCoordsClipped
  52. || oldUnclipped != monitorCoordsUnclipped)
  53. {
  54. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  55. {
  56. ComponentPeer* const p = ComponentPeer::getPeer (i);
  57. if (p != nullptr)
  58. p->handleScreenSizeChange();
  59. }
  60. }
  61. }
  62. int Desktop::getNumDisplayMonitors() const noexcept
  63. {
  64. return monitorCoordsClipped.size();
  65. }
  66. Rectangle<int> Desktop::getDisplayMonitorCoordinates (const int index, const bool clippedToWorkArea) const noexcept
  67. {
  68. return clippedToWorkArea ? monitorCoordsClipped [index]
  69. : monitorCoordsUnclipped [index];
  70. }
  71. RectangleList Desktop::getAllMonitorDisplayAreas (const bool clippedToWorkArea) const
  72. {
  73. RectangleList rl;
  74. for (int i = 0; i < getNumDisplayMonitors(); ++i)
  75. rl.addWithoutMerging (getDisplayMonitorCoordinates (i, clippedToWorkArea));
  76. return rl;
  77. }
  78. Rectangle<int> Desktop::getMainMonitorArea (const bool clippedToWorkArea) const noexcept
  79. {
  80. return getDisplayMonitorCoordinates (0, clippedToWorkArea);
  81. }
  82. Rectangle<int> Desktop::getMonitorAreaContaining (const Point<int>& position, const bool clippedToWorkArea) const
  83. {
  84. Rectangle<int> best (getMainMonitorArea (clippedToWorkArea));
  85. double bestDistance = 1.0e10;
  86. for (int i = getNumDisplayMonitors(); --i >= 0;)
  87. {
  88. const Rectangle<int> rect (getDisplayMonitorCoordinates (i, clippedToWorkArea));
  89. if (rect.contains (position))
  90. return rect;
  91. const double distance = rect.getCentre().getDistanceFrom (position);
  92. if (distance < bestDistance)
  93. {
  94. bestDistance = distance;
  95. best = rect;
  96. }
  97. }
  98. return best;
  99. }
  100. //==============================================================================
  101. int Desktop::getNumComponents() const noexcept
  102. {
  103. return desktopComponents.size();
  104. }
  105. Component* Desktop::getComponent (const int index) const noexcept
  106. {
  107. return desktopComponents [index];
  108. }
  109. Component* Desktop::findComponentAt (const Point<int>& screenPosition) const
  110. {
  111. for (int i = desktopComponents.size(); --i >= 0;)
  112. {
  113. Component* const c = desktopComponents.getUnchecked(i);
  114. if (c->isVisible())
  115. {
  116. const Point<int> relative (c->getLocalPoint (nullptr, screenPosition));
  117. if (c->contains (relative))
  118. return c->getComponentAt (relative);
  119. }
  120. }
  121. return nullptr;
  122. }
  123. //==============================================================================
  124. LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
  125. {
  126. if (currentLookAndFeel == nullptr)
  127. {
  128. if (defaultLookAndFeel == nullptr)
  129. defaultLookAndFeel = new LookAndFeel();
  130. currentLookAndFeel = defaultLookAndFeel;
  131. }
  132. return *currentLookAndFeel;
  133. }
  134. void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
  135. {
  136. currentLookAndFeel = newDefaultLookAndFeel;
  137. for (int i = getNumComponents(); --i >= 0;)
  138. {
  139. Component* const c = getComponent (i);
  140. if (c != nullptr)
  141. c->sendLookAndFeelChange();
  142. }
  143. }
  144. //==============================================================================
  145. void Desktop::addDesktopComponent (Component* const c)
  146. {
  147. jassert (c != nullptr);
  148. jassert (! desktopComponents.contains (c));
  149. desktopComponents.addIfNotAlreadyThere (c);
  150. }
  151. void Desktop::removeDesktopComponent (Component* const c)
  152. {
  153. desktopComponents.removeValue (c);
  154. }
  155. void Desktop::componentBroughtToFront (Component* const c)
  156. {
  157. const int index = desktopComponents.indexOf (c);
  158. jassert (index >= 0);
  159. if (index >= 0)
  160. {
  161. int newIndex = -1;
  162. if (! c->isAlwaysOnTop())
  163. {
  164. newIndex = desktopComponents.size();
  165. while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
  166. --newIndex;
  167. --newIndex;
  168. }
  169. desktopComponents.move (index, newIndex);
  170. }
  171. }
  172. //==============================================================================
  173. Point<int> Desktop::getMousePosition()
  174. {
  175. return getInstance().getMainMouseSource().getScreenPosition();
  176. }
  177. Point<int> Desktop::getLastMouseDownPosition()
  178. {
  179. return getInstance().getMainMouseSource().getLastMouseDownPosition();
  180. }
  181. int Desktop::getMouseButtonClickCounter()
  182. {
  183. return getInstance().mouseClickCounter;
  184. }
  185. void Desktop::incrementMouseClickCounter() noexcept
  186. {
  187. ++mouseClickCounter;
  188. }
  189. int Desktop::getNumDraggingMouseSources() const noexcept
  190. {
  191. int num = 0;
  192. for (int i = mouseSources.size(); --i >= 0;)
  193. if (mouseSources.getUnchecked(i)->isDragging())
  194. ++num;
  195. return num;
  196. }
  197. MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept
  198. {
  199. int num = 0;
  200. for (int i = mouseSources.size(); --i >= 0;)
  201. {
  202. MouseInputSource* const mi = mouseSources.getUnchecked(i);
  203. if (mi->isDragging())
  204. {
  205. if (index == num)
  206. return mi;
  207. ++num;
  208. }
  209. }
  210. return nullptr;
  211. }
  212. //==============================================================================
  213. class MouseDragAutoRepeater : public Timer
  214. {
  215. public:
  216. MouseDragAutoRepeater() {}
  217. void timerCallback()
  218. {
  219. Desktop& desktop = Desktop::getInstance();
  220. int numMiceDown = 0;
  221. for (int i = desktop.getNumMouseSources(); --i >= 0;)
  222. {
  223. MouseInputSource* const source = desktop.getMouseSource(i);
  224. if (source->isDragging())
  225. {
  226. source->triggerFakeMove();
  227. ++numMiceDown;
  228. }
  229. }
  230. if (numMiceDown == 0)
  231. desktop.beginDragAutoRepeat (0);
  232. }
  233. private:
  234. JUCE_DECLARE_NON_COPYABLE (MouseDragAutoRepeater);
  235. };
  236. void Desktop::beginDragAutoRepeat (const int interval)
  237. {
  238. if (interval > 0)
  239. {
  240. if (dragRepeater == nullptr)
  241. dragRepeater = new MouseDragAutoRepeater();
  242. if (dragRepeater->getTimerInterval() != interval)
  243. dragRepeater->startTimer (interval);
  244. }
  245. else
  246. {
  247. dragRepeater = nullptr;
  248. }
  249. }
  250. //==============================================================================
  251. void Desktop::addFocusChangeListener (FocusChangeListener* const listener)
  252. {
  253. focusListeners.add (listener);
  254. }
  255. void Desktop::removeFocusChangeListener (FocusChangeListener* const listener)
  256. {
  257. focusListeners.remove (listener);
  258. }
  259. void Desktop::triggerFocusCallback()
  260. {
  261. triggerAsyncUpdate();
  262. }
  263. void Desktop::handleAsyncUpdate()
  264. {
  265. // The component may be deleted during this operation, but we'll use a SafePointer rather than a
  266. // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
  267. WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
  268. focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus);
  269. }
  270. //==============================================================================
  271. void Desktop::resetTimer()
  272. {
  273. if (mouseListeners.size() == 0)
  274. stopTimer();
  275. else
  276. startTimer (100);
  277. lastFakeMouseMove = getMousePosition();
  278. }
  279. ListenerList <MouseListener>& Desktop::getMouseListeners()
  280. {
  281. resetTimer();
  282. return mouseListeners;
  283. }
  284. void Desktop::addGlobalMouseListener (MouseListener* const listener)
  285. {
  286. mouseListeners.add (listener);
  287. resetTimer();
  288. }
  289. void Desktop::removeGlobalMouseListener (MouseListener* const listener)
  290. {
  291. mouseListeners.remove (listener);
  292. resetTimer();
  293. }
  294. void Desktop::timerCallback()
  295. {
  296. if (lastFakeMouseMove != getMousePosition())
  297. sendMouseMove();
  298. }
  299. void Desktop::sendMouseMove()
  300. {
  301. if (! mouseListeners.isEmpty())
  302. {
  303. startTimer (20);
  304. lastFakeMouseMove = getMousePosition();
  305. Component* const target = findComponentAt (lastFakeMouseMove);
  306. if (target != nullptr)
  307. {
  308. Component::BailOutChecker checker (target);
  309. const Point<int> pos (target->getLocalPoint (nullptr, lastFakeMouseMove));
  310. const Time now (Time::getCurrentTime());
  311. const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(),
  312. target, target, now, pos, now, 0, false);
  313. if (me.mods.isAnyMouseButtonDown())
  314. mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me);
  315. else
  316. mouseListeners.callChecked (checker, &MouseListener::mouseMove, me);
  317. }
  318. }
  319. }
  320. //==============================================================================
  321. void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars)
  322. {
  323. if (kioskModeComponent != componentToUse)
  324. {
  325. // agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
  326. jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  327. if (kioskModeComponent != nullptr)
  328. {
  329. setKioskComponent (kioskModeComponent, false, allowMenusAndBars);
  330. kioskModeComponent->setBounds (kioskComponentOriginalBounds);
  331. }
  332. kioskModeComponent = componentToUse;
  333. if (kioskModeComponent != nullptr)
  334. {
  335. // Only components that are already on the desktop can be put into kiosk mode!
  336. jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  337. kioskComponentOriginalBounds = kioskModeComponent->getBounds();
  338. setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
  339. }
  340. }
  341. }
  342. //==============================================================================
  343. void Desktop::setOrientationsEnabled (const int newOrientations)
  344. {
  345. // Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
  346. jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
  347. allowedOrientations = newOrientations;
  348. }
  349. bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept
  350. {
  351. // Make sure you only pass one valid flag in here...
  352. jassert (orientation == upright || orientation == upsideDown || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
  353. return (allowedOrientations & orientation) != 0;
  354. }