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