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.

411 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. Desktop::Desktop()
  18. : mouseSources (new MouseInputSource::SourceList()),
  19. mouseClickCounter (0), mouseWheelCounter (0),
  20. kioskModeComponent (nullptr),
  21. kioskModeReentrant (false),
  22. allowedOrientations (allOrientations),
  23. masterScaleFactor ((float) getDefaultMasterScale())
  24. {
  25. displays = new Displays (*this);
  26. }
  27. Desktop::~Desktop()
  28. {
  29. setScreenSaverEnabled (true);
  30. jassert (instance == this);
  31. instance = nullptr;
  32. // doh! If you don't delete all your windows before exiting, you're going to
  33. // be leaking memory!
  34. jassert (desktopComponents.size() == 0);
  35. }
  36. Desktop& JUCE_CALLTYPE Desktop::getInstance()
  37. {
  38. if (instance == nullptr)
  39. instance = new Desktop();
  40. return *instance;
  41. }
  42. Desktop* Desktop::instance = nullptr;
  43. //==============================================================================
  44. int Desktop::getNumComponents() const noexcept
  45. {
  46. return desktopComponents.size();
  47. }
  48. Component* Desktop::getComponent (const int index) const noexcept
  49. {
  50. return desktopComponents [index];
  51. }
  52. Component* Desktop::findComponentAt (Point<int> screenPosition) const
  53. {
  54. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  55. for (int i = desktopComponents.size(); --i >= 0;)
  56. {
  57. Component* const c = desktopComponents.getUnchecked(i);
  58. if (c->isVisible())
  59. {
  60. const Point<int> relative (c->getLocalPoint (nullptr, screenPosition));
  61. if (c->contains (relative))
  62. return c->getComponentAt (relative);
  63. }
  64. }
  65. return nullptr;
  66. }
  67. //==============================================================================
  68. LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
  69. {
  70. if (currentLookAndFeel == nullptr)
  71. {
  72. if (defaultLookAndFeel == nullptr)
  73. defaultLookAndFeel = new LookAndFeel_V2();
  74. currentLookAndFeel = defaultLookAndFeel;
  75. }
  76. return *currentLookAndFeel;
  77. }
  78. void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
  79. {
  80. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  81. currentLookAndFeel = newDefaultLookAndFeel;
  82. for (int i = getNumComponents(); --i >= 0;)
  83. if (Component* const c = getComponent (i))
  84. c->sendLookAndFeelChange();
  85. }
  86. //==============================================================================
  87. void Desktop::addDesktopComponent (Component* const c)
  88. {
  89. jassert (c != nullptr);
  90. jassert (! desktopComponents.contains (c));
  91. desktopComponents.addIfNotAlreadyThere (c);
  92. }
  93. void Desktop::removeDesktopComponent (Component* const c)
  94. {
  95. desktopComponents.removeFirstMatchingValue (c);
  96. }
  97. void Desktop::componentBroughtToFront (Component* const c)
  98. {
  99. const int index = desktopComponents.indexOf (c);
  100. jassert (index >= 0);
  101. if (index >= 0)
  102. {
  103. int newIndex = -1;
  104. if (! c->isAlwaysOnTop())
  105. {
  106. newIndex = desktopComponents.size();
  107. while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
  108. --newIndex;
  109. --newIndex;
  110. }
  111. desktopComponents.move (index, newIndex);
  112. }
  113. }
  114. //==============================================================================
  115. Point<int> Desktop::getMousePosition()
  116. {
  117. return getInstance().getMainMouseSource().getScreenPosition();
  118. }
  119. void Desktop::setMousePosition (Point<int> newPosition)
  120. {
  121. getInstance().getMainMouseSource().setScreenPosition (newPosition);
  122. }
  123. Point<int> Desktop::getLastMouseDownPosition()
  124. {
  125. return getInstance().getMainMouseSource().getLastMouseDownPosition();
  126. }
  127. int Desktop::getMouseButtonClickCounter() const noexcept { return mouseClickCounter; }
  128. int Desktop::getMouseWheelMoveCounter() const noexcept { return mouseWheelCounter; }
  129. void Desktop::incrementMouseClickCounter() noexcept { ++mouseClickCounter; }
  130. void Desktop::incrementMouseWheelCounter() noexcept { ++mouseWheelCounter; }
  131. const Array<MouseInputSource>& Desktop::getMouseSources() const noexcept { return mouseSources->sourceArray; }
  132. int Desktop::getNumMouseSources() const noexcept { return mouseSources->sources.size(); }
  133. int Desktop::getNumDraggingMouseSources() const noexcept { return mouseSources->getNumDraggingMouseSources(); }
  134. MouseInputSource* Desktop::getMouseSource (int index) const noexcept { return mouseSources->getMouseSource (index); }
  135. MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept { return mouseSources->getDraggingMouseSource (index); }
  136. MouseInputSource Desktop::getMainMouseSource() const noexcept { return MouseInputSource (mouseSources->sources.getUnchecked(0)); }
  137. void Desktop::beginDragAutoRepeat (int interval) { mouseSources->beginDragAutoRepeat (interval); }
  138. //==============================================================================
  139. void Desktop::addFocusChangeListener (FocusChangeListener* const listener) { focusListeners.add (listener); }
  140. void Desktop::removeFocusChangeListener (FocusChangeListener* const listener) { focusListeners.remove (listener); }
  141. void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); }
  142. void Desktop::handleAsyncUpdate()
  143. {
  144. // The component may be deleted during this operation, but we'll use a SafePointer rather than a
  145. // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
  146. WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
  147. focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus);
  148. }
  149. //==============================================================================
  150. void Desktop::resetTimer()
  151. {
  152. if (mouseListeners.size() == 0)
  153. stopTimer();
  154. else
  155. startTimer (100);
  156. lastFakeMouseMove = getMousePosition();
  157. }
  158. ListenerList<MouseListener>& Desktop::getMouseListeners()
  159. {
  160. resetTimer();
  161. return mouseListeners;
  162. }
  163. void Desktop::addGlobalMouseListener (MouseListener* const listener)
  164. {
  165. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  166. mouseListeners.add (listener);
  167. resetTimer();
  168. }
  169. void Desktop::removeGlobalMouseListener (MouseListener* const listener)
  170. {
  171. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  172. mouseListeners.remove (listener);
  173. resetTimer();
  174. }
  175. void Desktop::timerCallback()
  176. {
  177. if (lastFakeMouseMove != getMousePosition())
  178. sendMouseMove();
  179. }
  180. void Desktop::sendMouseMove()
  181. {
  182. if (! mouseListeners.isEmpty())
  183. {
  184. startTimer (20);
  185. lastFakeMouseMove = getMousePosition();
  186. if (Component* const target = findComponentAt (lastFakeMouseMove))
  187. {
  188. Component::BailOutChecker checker (target);
  189. const Point<int> pos (target->getLocalPoint (nullptr, lastFakeMouseMove));
  190. const Time now (Time::getCurrentTime());
  191. const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(),
  192. target, target, now, pos, now, 0, false);
  193. if (me.mods.isAnyMouseButtonDown())
  194. mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me);
  195. else
  196. mouseListeners.callChecked (checker, &MouseListener::mouseMove, me);
  197. }
  198. }
  199. }
  200. //==============================================================================
  201. Desktop::Displays::Displays (Desktop& desktop) { init (desktop); }
  202. Desktop::Displays::~Displays() {}
  203. const Desktop::Displays::Display& Desktop::Displays::getMainDisplay() const noexcept
  204. {
  205. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  206. jassert (displays.getReference(0).isMain);
  207. return displays.getReference(0);
  208. }
  209. const Desktop::Displays::Display& Desktop::Displays::getDisplayContaining (Point<int> position) const noexcept
  210. {
  211. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  212. const Display* best = &displays.getReference(0);
  213. double bestDistance = 1.0e10;
  214. for (int i = displays.size(); --i >= 0;)
  215. {
  216. const Display& d = displays.getReference(i);
  217. if (d.totalArea.contains (position))
  218. {
  219. best = &d;
  220. break;
  221. }
  222. const double distance = d.totalArea.getCentre().getDistanceFrom (position);
  223. if (distance < bestDistance)
  224. {
  225. bestDistance = distance;
  226. best = &d;
  227. }
  228. }
  229. return *best;
  230. }
  231. RectangleList<int> Desktop::Displays::getRectangleList (bool userAreasOnly) const
  232. {
  233. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  234. RectangleList<int> rl;
  235. for (int i = 0; i < displays.size(); ++i)
  236. {
  237. const Display& d = displays.getReference(i);
  238. rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
  239. }
  240. return rl;
  241. }
  242. Rectangle<int> Desktop::Displays::getTotalBounds (bool userAreasOnly) const
  243. {
  244. return getRectangleList (userAreasOnly).getBounds();
  245. }
  246. bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept;
  247. bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept
  248. {
  249. return d1.userArea == d2.userArea
  250. && d1.totalArea == d2.totalArea
  251. && d1.scale == d2.scale
  252. && d1.isMain == d2.isMain;
  253. }
  254. bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept;
  255. bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept
  256. {
  257. return ! (d1 == d2);
  258. }
  259. void Desktop::Displays::init (Desktop& desktop)
  260. {
  261. findDisplays (desktop.getGlobalScaleFactor());
  262. jassert (displays.size() > 0);
  263. }
  264. void Desktop::Displays::refresh()
  265. {
  266. Array<Display> oldDisplays;
  267. oldDisplays.swapWith (displays);
  268. init (Desktop::getInstance());
  269. jassert (displays.size() > 0);
  270. if (oldDisplays != displays)
  271. {
  272. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  273. if (ComponentPeer* const peer = ComponentPeer::getPeer (i))
  274. peer->handleScreenSizeChange();
  275. }
  276. }
  277. //==============================================================================
  278. void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars)
  279. {
  280. if (kioskModeReentrant)
  281. return;
  282. const ScopedValueSetter<bool> setter (kioskModeReentrant, true, false);
  283. if (kioskModeComponent != componentToUse)
  284. {
  285. // agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
  286. jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  287. if (Component* const oldKioskComp = kioskModeComponent)
  288. {
  289. kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one)
  290. setKioskComponent (oldKioskComp, false, allowMenusAndBars);
  291. oldKioskComp->setBounds (kioskComponentOriginalBounds);
  292. }
  293. kioskModeComponent = componentToUse;
  294. if (kioskModeComponent != nullptr)
  295. {
  296. // Only components that are already on the desktop can be put into kiosk mode!
  297. jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  298. kioskComponentOriginalBounds = kioskModeComponent->getBounds();
  299. setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
  300. }
  301. }
  302. }
  303. //==============================================================================
  304. void Desktop::setOrientationsEnabled (const int newOrientations)
  305. {
  306. // Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
  307. jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
  308. allowedOrientations = newOrientations;
  309. }
  310. bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept
  311. {
  312. // Make sure you only pass one valid flag in here...
  313. jassert (orientation == upright || orientation == upsideDown
  314. || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
  315. return (allowedOrientations & orientation) != 0;
  316. }
  317. void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept
  318. {
  319. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  320. if (masterScaleFactor != newScaleFactor)
  321. {
  322. masterScaleFactor = newScaleFactor;
  323. displays->refresh();
  324. }
  325. }