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.

425 lines
14KB

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