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.

462 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. }
  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.removeValue (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()
  123. {
  124. return getInstance().mouseClickCounter;
  125. }
  126. void Desktop::incrementMouseClickCounter() noexcept
  127. {
  128. ++mouseClickCounter;
  129. }
  130. int Desktop::getNumDraggingMouseSources() const noexcept
  131. {
  132. int num = 0;
  133. for (int i = mouseSources.size(); --i >= 0;)
  134. if (mouseSources.getUnchecked(i)->isDragging())
  135. ++num;
  136. return num;
  137. }
  138. MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept
  139. {
  140. int num = 0;
  141. for (int i = mouseSources.size(); --i >= 0;)
  142. {
  143. MouseInputSource* const mi = mouseSources.getUnchecked(i);
  144. if (mi->isDragging())
  145. {
  146. if (index == num)
  147. return mi;
  148. ++num;
  149. }
  150. }
  151. return nullptr;
  152. }
  153. //==============================================================================
  154. class MouseDragAutoRepeater : public Timer
  155. {
  156. public:
  157. MouseDragAutoRepeater() {}
  158. void timerCallback()
  159. {
  160. Desktop& desktop = Desktop::getInstance();
  161. int numMiceDown = 0;
  162. for (int i = desktop.getNumMouseSources(); --i >= 0;)
  163. {
  164. MouseInputSource* const source = desktop.getMouseSource(i);
  165. if (source->isDragging())
  166. {
  167. source->triggerFakeMove();
  168. ++numMiceDown;
  169. }
  170. }
  171. if (numMiceDown == 0)
  172. desktop.beginDragAutoRepeat (0);
  173. }
  174. private:
  175. JUCE_DECLARE_NON_COPYABLE (MouseDragAutoRepeater);
  176. };
  177. void Desktop::beginDragAutoRepeat (const int interval)
  178. {
  179. if (interval > 0)
  180. {
  181. if (dragRepeater == nullptr)
  182. dragRepeater = new MouseDragAutoRepeater();
  183. if (dragRepeater->getTimerInterval() != interval)
  184. dragRepeater->startTimer (interval);
  185. }
  186. else
  187. {
  188. dragRepeater = nullptr;
  189. }
  190. }
  191. //==============================================================================
  192. void Desktop::addFocusChangeListener (FocusChangeListener* const listener)
  193. {
  194. focusListeners.add (listener);
  195. }
  196. void Desktop::removeFocusChangeListener (FocusChangeListener* const listener)
  197. {
  198. focusListeners.remove (listener);
  199. }
  200. void Desktop::triggerFocusCallback()
  201. {
  202. triggerAsyncUpdate();
  203. }
  204. void Desktop::handleAsyncUpdate()
  205. {
  206. // The component may be deleted during this operation, but we'll use a SafePointer rather than a
  207. // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
  208. WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
  209. focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus);
  210. }
  211. //==============================================================================
  212. void Desktop::resetTimer()
  213. {
  214. if (mouseListeners.size() == 0)
  215. stopTimer();
  216. else
  217. startTimer (100);
  218. lastFakeMouseMove = getMousePosition();
  219. }
  220. ListenerList <MouseListener>& Desktop::getMouseListeners()
  221. {
  222. resetTimer();
  223. return mouseListeners;
  224. }
  225. void Desktop::addGlobalMouseListener (MouseListener* const listener)
  226. {
  227. mouseListeners.add (listener);
  228. resetTimer();
  229. }
  230. void Desktop::removeGlobalMouseListener (MouseListener* const listener)
  231. {
  232. mouseListeners.remove (listener);
  233. resetTimer();
  234. }
  235. void Desktop::timerCallback()
  236. {
  237. if (lastFakeMouseMove != getMousePosition())
  238. sendMouseMove();
  239. }
  240. void Desktop::sendMouseMove()
  241. {
  242. if (! mouseListeners.isEmpty())
  243. {
  244. startTimer (20);
  245. lastFakeMouseMove = getMousePosition();
  246. Component* const target = findComponentAt (lastFakeMouseMove);
  247. if (target != nullptr)
  248. {
  249. Component::BailOutChecker checker (target);
  250. const Point<int> pos (target->getLocalPoint (nullptr, lastFakeMouseMove));
  251. const Time now (Time::getCurrentTime());
  252. const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(),
  253. target, target, now, pos, now, 0, false);
  254. if (me.mods.isAnyMouseButtonDown())
  255. mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me);
  256. else
  257. mouseListeners.callChecked (checker, &MouseListener::mouseMove, me);
  258. }
  259. }
  260. }
  261. //==============================================================================
  262. Desktop::Displays::Displays() { refresh(); }
  263. Desktop::Displays::~Displays() {}
  264. const Desktop::Displays::Display& Desktop::Displays::getMainDisplay() const noexcept
  265. {
  266. jassert (displays.getReference(0).isMain);
  267. return displays.getReference(0);
  268. }
  269. const Desktop::Displays::Display& Desktop::Displays::getDisplayContaining (const Point<int>& position) const noexcept
  270. {
  271. const Display* best = &displays.getReference(0);
  272. double bestDistance = 1.0e10;
  273. for (int i = displays.size(); --i >= 0;)
  274. {
  275. const Display& d = displays.getReference(i);
  276. if (d.totalArea.contains (position))
  277. {
  278. best = &d;
  279. break;
  280. }
  281. const double distance = d.totalArea.getCentre().getDistanceFrom (position);
  282. if (distance < bestDistance)
  283. {
  284. bestDistance = distance;
  285. best = &d;
  286. }
  287. }
  288. return *best;
  289. }
  290. RectangleList Desktop::Displays::getRectangleList (bool userAreasOnly) const
  291. {
  292. RectangleList rl;
  293. for (int i = 0; i < displays.size(); ++i)
  294. {
  295. const Display& d = displays.getReference(i);
  296. rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
  297. }
  298. return rl;
  299. }
  300. Rectangle<int> Desktop::Displays::getTotalBounds (bool userAreasOnly) const
  301. {
  302. return getRectangleList (userAreasOnly).getBounds();
  303. }
  304. bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept
  305. {
  306. return d1.userArea == d2.userArea
  307. && d1.totalArea == d2.totalArea
  308. && d1.scale == d2.scale
  309. && d1.isMain == d2.isMain;
  310. }
  311. bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept
  312. {
  313. return ! (d1 == d2);
  314. }
  315. void Desktop::Displays::refresh()
  316. {
  317. Array<Display> oldDisplays;
  318. oldDisplays.swapWithArray (displays);
  319. findDisplays();
  320. jassert (displays.size() > 0);
  321. if (oldDisplays != displays)
  322. {
  323. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  324. {
  325. ComponentPeer* const p = ComponentPeer::getPeer (i);
  326. if (p != nullptr)
  327. p->handleScreenSizeChange();
  328. }
  329. }
  330. }
  331. //==============================================================================
  332. void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars)
  333. {
  334. if (kioskModeComponent != componentToUse)
  335. {
  336. // agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
  337. jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  338. if (kioskModeComponent != nullptr)
  339. {
  340. setKioskComponent (kioskModeComponent, false, allowMenusAndBars);
  341. kioskModeComponent->setBounds (kioskComponentOriginalBounds);
  342. }
  343. kioskModeComponent = componentToUse;
  344. if (kioskModeComponent != nullptr)
  345. {
  346. // Only components that are already on the desktop can be put into kiosk mode!
  347. jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  348. kioskComponentOriginalBounds = kioskModeComponent->getBounds();
  349. setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
  350. }
  351. }
  352. }
  353. //==============================================================================
  354. void Desktop::setOrientationsEnabled (const int newOrientations)
  355. {
  356. // Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
  357. jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
  358. allowedOrientations = newOrientations;
  359. }
  360. bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept
  361. {
  362. // Make sure you only pass one valid flag in here...
  363. jassert (orientation == upright || orientation == upsideDown || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
  364. return (allowedOrientations & orientation) != 0;
  365. }