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.

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