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.

juce_Desktop.cpp 14KB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. }