Audio plugin host https://kx.studio/carla
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.

416 lines
14KB

  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 getMousePositionFloat().roundToInt();
  118. }
  119. Point<float> Desktop::getMousePositionFloat()
  120. {
  121. return getInstance().getMainMouseSource().getScreenPosition();
  122. }
  123. void Desktop::setMousePosition (Point<int> newPosition)
  124. {
  125. getInstance().getMainMouseSource().setScreenPosition (newPosition.toFloat());
  126. }
  127. Point<int> Desktop::getLastMouseDownPosition()
  128. {
  129. return getInstance().getMainMouseSource().getLastMouseDownPosition().roundToInt();
  130. }
  131. int Desktop::getMouseButtonClickCounter() const noexcept { return mouseClickCounter; }
  132. int Desktop::getMouseWheelMoveCounter() const noexcept { return mouseWheelCounter; }
  133. void Desktop::incrementMouseClickCounter() noexcept { ++mouseClickCounter; }
  134. void Desktop::incrementMouseWheelCounter() noexcept { ++mouseWheelCounter; }
  135. const Array<MouseInputSource>& Desktop::getMouseSources() const noexcept { return mouseSources->sourceArray; }
  136. int Desktop::getNumMouseSources() const noexcept { return mouseSources->sources.size(); }
  137. int Desktop::getNumDraggingMouseSources() const noexcept { return mouseSources->getNumDraggingMouseSources(); }
  138. MouseInputSource* Desktop::getMouseSource (int index) const noexcept { return mouseSources->getMouseSource (index); }
  139. MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept { return mouseSources->getDraggingMouseSource (index); }
  140. MouseInputSource Desktop::getMainMouseSource() const noexcept { return MouseInputSource (mouseSources->sources.getUnchecked(0)); }
  141. void Desktop::beginDragAutoRepeat (int interval) { mouseSources->beginDragAutoRepeat (interval); }
  142. //==============================================================================
  143. void Desktop::addFocusChangeListener (FocusChangeListener* const listener) { focusListeners.add (listener); }
  144. void Desktop::removeFocusChangeListener (FocusChangeListener* const listener) { focusListeners.remove (listener); }
  145. void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); }
  146. void Desktop::handleAsyncUpdate()
  147. {
  148. // The component may be deleted during this operation, but we'll use a SafePointer rather than a
  149. // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
  150. WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
  151. focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus);
  152. }
  153. //==============================================================================
  154. void Desktop::resetTimer()
  155. {
  156. if (mouseListeners.size() == 0)
  157. stopTimer();
  158. else
  159. startTimer (100);
  160. lastFakeMouseMove = getMousePositionFloat();
  161. }
  162. ListenerList<MouseListener>& Desktop::getMouseListeners()
  163. {
  164. resetTimer();
  165. return mouseListeners;
  166. }
  167. void Desktop::addGlobalMouseListener (MouseListener* const listener)
  168. {
  169. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  170. mouseListeners.add (listener);
  171. resetTimer();
  172. }
  173. void Desktop::removeGlobalMouseListener (MouseListener* const listener)
  174. {
  175. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  176. mouseListeners.remove (listener);
  177. resetTimer();
  178. }
  179. void Desktop::timerCallback()
  180. {
  181. if (lastFakeMouseMove != getMousePositionFloat())
  182. sendMouseMove();
  183. }
  184. void Desktop::sendMouseMove()
  185. {
  186. if (! mouseListeners.isEmpty())
  187. {
  188. startTimer (20);
  189. lastFakeMouseMove = getMousePositionFloat();
  190. if (Component* const target = findComponentAt (lastFakeMouseMove.roundToInt()))
  191. {
  192. Component::BailOutChecker checker (target);
  193. const Point<float> pos (target->getLocalPoint (nullptr, lastFakeMouseMove));
  194. const Time now (Time::getCurrentTime());
  195. const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(),
  196. target, target, now, pos, now, 0, false);
  197. if (me.mods.isAnyMouseButtonDown())
  198. mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me);
  199. else
  200. mouseListeners.callChecked (checker, &MouseListener::mouseMove, me);
  201. }
  202. }
  203. }
  204. //==============================================================================
  205. Desktop::Displays::Displays (Desktop& desktop) { init (desktop); }
  206. Desktop::Displays::~Displays() {}
  207. const Desktop::Displays::Display& Desktop::Displays::getMainDisplay() const noexcept
  208. {
  209. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  210. jassert (displays.getReference(0).isMain);
  211. return displays.getReference(0);
  212. }
  213. const Desktop::Displays::Display& Desktop::Displays::getDisplayContaining (Point<int> position) const noexcept
  214. {
  215. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  216. const Display* best = &displays.getReference(0);
  217. double bestDistance = 1.0e10;
  218. for (int i = displays.size(); --i >= 0;)
  219. {
  220. const Display& d = displays.getReference(i);
  221. if (d.totalArea.contains (position))
  222. {
  223. best = &d;
  224. break;
  225. }
  226. const double distance = d.totalArea.getCentre().getDistanceFrom (position);
  227. if (distance < bestDistance)
  228. {
  229. bestDistance = distance;
  230. best = &d;
  231. }
  232. }
  233. return *best;
  234. }
  235. RectangleList<int> Desktop::Displays::getRectangleList (bool userAreasOnly) const
  236. {
  237. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  238. RectangleList<int> rl;
  239. for (int i = 0; i < displays.size(); ++i)
  240. {
  241. const Display& d = displays.getReference(i);
  242. rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
  243. }
  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. jassert (displays.size() > 0);
  267. }
  268. void Desktop::Displays::refresh()
  269. {
  270. Array<Display> oldDisplays;
  271. oldDisplays.swapWith (displays);
  272. init (Desktop::getInstance());
  273. jassert (displays.size() > 0);
  274. if (oldDisplays != displays)
  275. {
  276. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  277. if (ComponentPeer* const peer = ComponentPeer::getPeer (i))
  278. peer->handleScreenSizeChange();
  279. }
  280. }
  281. //==============================================================================
  282. void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars)
  283. {
  284. if (kioskModeReentrant)
  285. return;
  286. const ScopedValueSetter<bool> setter (kioskModeReentrant, true, false);
  287. if (kioskModeComponent != componentToUse)
  288. {
  289. // agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
  290. jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  291. if (Component* const oldKioskComp = kioskModeComponent)
  292. {
  293. kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one)
  294. setKioskComponent (oldKioskComp, false, allowMenusAndBars);
  295. oldKioskComp->setBounds (kioskComponentOriginalBounds);
  296. }
  297. kioskModeComponent = componentToUse;
  298. if (kioskModeComponent != nullptr)
  299. {
  300. // Only components that are already on the desktop can be put into kiosk mode!
  301. jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  302. kioskComponentOriginalBounds = kioskModeComponent->getBounds();
  303. setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
  304. }
  305. }
  306. }
  307. //==============================================================================
  308. void Desktop::setOrientationsEnabled (const int 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. }
  314. bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept
  315. {
  316. // Make sure you only pass one valid flag in here...
  317. jassert (orientation == upright || orientation == upsideDown
  318. || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
  319. return (allowedOrientations & orientation) != 0;
  320. }
  321. void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept
  322. {
  323. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  324. if (masterScaleFactor != newScaleFactor)
  325. {
  326. masterScaleFactor = newScaleFactor;
  327. displays->refresh();
  328. }
  329. }