1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program. If not, see <http://www.gnu.org/licenses/>.
19*********************************************************************/
20#ifndef KWIN_VIRTUAL_DESKTOPS_H
21#define KWIN_VIRTUAL_DESKTOPS_H
22// KWin
23#include <kwinglobals.h>
24// Qt includes
25#include <QObject>
26#include <QPoint>
27#include <QSize>
28// KDE includes
29#include <KDE/KConfig>
30
31class KActionCollection;
32class KLocalizedString;
33class KShortcut;
34class NETRootInfo;
35
36namespace KWin {
37
38/**
39 * @brief Two dimensional grid containing the ID of the virtual desktop at a specific position
40 * in the grid.
41 *
42 * The VirtualDesktopGrid represents a visual layout of the Virtual Desktops as they are in e.g.
43 * a Pager. This grid is used for getting a desktop next to a given desktop in any direction by
44 * making use of the layout information. This allows navigation like move to desktop on left.
45 **/
46class VirtualDesktopGrid
47{
48public:
49 VirtualDesktopGrid();
50 ~VirtualDesktopGrid();
51 void update(const QSize &size, Qt::Orientation orientation);
52 /**
53 * @returns The coords of desktop @a id in grid units.
54 */
55 QPoint gridCoords(uint id) const;
56 /**
57 * @returns The ID of the desktop at the point @a coords or 0 if no desktop exists at that
58 * point. @a coords is to be in grid units.
59 */
60 uint at(QPoint coords) const;
61 int width() const;
62 int height() const;
63 const QSize &size() const;
64private:
65 QSize m_size;
66 uint *m_grid;
67};
68
69/**
70 * @brief Manages the number of available virtual desktops, the layout of those and which virtual
71 * desktop is the current one.
72 *
73 * This manager is responsible for Virtual Desktop handling inside KWin. It has a property for the
74 * count of available virtual desktops and a property for the currently active virtual desktop. All
75 * changes to the number of virtual desktops and the current virtual desktop need to go through this
76 * manager.
77 *
78 * On all changes a signal is emitted and interested parties should connect to the signal. The manager
79 * itself does not interact with other parts of the system. E.g. it does not hide/show windows of
80 * desktop changes. This is outside the scope of this manager.
81 *
82 * Internally the manager organizes the virtual desktops in a grid allowing to navigate over the
83 * virtual desktops. For this a set of convenient methods are available which allow to get the id
84 * of an adjacent desktop or to switch to an adjacent desktop. Interested parties should make use of
85 * these methods and not replicate the logic to switch to the next desktop.
86 **/
87class VirtualDesktopManager : public QObject
88{
89 Q_OBJECT
90 /**
91 * The number of virtual desktops currently available.
92 * The ids of the virtual desktops are in the range [1, VirtualDesktopManager::maximum()].
93 **/
94 Q_PROPERTY(uint count READ count WRITE setCount NOTIFY countChanged)
95 /**
96 * The id of the virtual desktop which is currently in use.
97 **/
98 Q_PROPERTY(uint current READ current WRITE setCurrent NOTIFY currentChanged)
99 /**
100 * Whether navigation in the desktop layout wraps around at the borders.
101 **/
102 Q_PROPERTY(bool navigationWrappingAround READ isNavigationWrappingAround WRITE setNavigationWrappingAround NOTIFY navigationWrappingAroundChanged)
103public:
104 virtual ~VirtualDesktopManager();
105 /**
106 * @internal
107 **/
108 void setRootInfo(NETRootInfo *info);
109 /**
110 * @internal
111 **/
112 void setConfig(KSharedConfig::Ptr config);
113 /**
114 * @returns Total number of desktops currently in existence.
115 * @see setCount
116 * @see countChanged
117 */
118 uint count() const;
119 /**
120 * @returns The ID of the current desktop.
121 * @see setCurrent
122 * @see currentChanged
123 */
124 uint current() const;
125 /**
126 * Moves to the desktop through the algorithm described by Direction.
127 * @param wrap If @c true wraps around to the other side of the layout
128 * @see setCurrent
129 **/
130 template <typename Direction>
131 void moveTo(bool wrap = false);
132
133 /**
134 * @returns The name of the @p desktop
135 **/
136 QString name(uint desktop) const;
137
138 /**
139 * @returns @c true if navigation at borders of layout wraps around, @c false otherwise
140 * @see setNavigationWrappingAround
141 * @see navigationWrappingAroundChanged
142 **/
143 bool isNavigationWrappingAround() const;
144
145 /**
146 * @returns The layout aware virtual desktop grid used by this manager.
147 **/
148 const VirtualDesktopGrid &grid() const;
149
150 /**
151 * @returns The ID of the desktop above desktop @a id. Wraps around to the bottom of
152 * the layout if @a wrap is set. If @a id is not set use the current one.
153 */
154 uint above(uint id = 0, bool wrap = true) const;
155 /**
156 * @returns The ID of the desktop to the right of desktop @a id. Wraps around to the
157 * left of the layout if @a wrap is set. If @a id is not set use the current one.
158 */
159 uint toRight(uint id = 0, bool wrap = true) const;
160 /**
161 * @returns The ID of the desktop below desktop @a id. Wraps around to the top of the
162 * layout if @a wrap is set. If @a id is not set use the current one.
163 */
164 uint below(uint id = 0, bool wrap = true) const;
165 /**
166 * @returns The ID of the desktop to the left of desktop @a id. Wraps around to the
167 * right of the layout if @a wrap is set. If @a id is not set use the current one.
168 */
169 uint toLeft(uint id = 0, bool wrap = true) const;
170 /**
171 * @returns The ID of the desktop after the desktop @a id. Wraps around to the first
172 * desktop if @a wrap is set. If @a id is not set use the current desktop.
173 **/
174 uint next(uint id = 0, bool wrap = true) const;
175 /**
176 * @returns The ID of the desktop in front of the desktop @a id. Wraps around to the
177 * last desktop if @a wrap is set. If @a id is not set use the current desktop.
178 **/
179 uint previous(uint id = 0, bool wrap = true) const;
180
181 void initShortcuts(KActionCollection *keys);
182
183 /**
184 * @returns The maximum number of desktops that KWin supports.
185 */
186 static uint maximum();
187
188public slots:
189 /**
190 * Set the number of available desktops to @a count. This function overrides any previous
191 * grid layout.
192 * There needs to be at least one virtual desktop and the new value is capped at the maximum
193 * number of desktops. A caller of this function cannot expect that the change has been applied.
194 * It is the callers responsibility to either check the @link numberOfDesktops or connect to the
195 * @link countChanged signal.
196 *
197 * In case the @link current desktop is on a desktop higher than the new count, the current desktop
198 * is changed to be the new desktop with highest id. In that situation the signal @link desktopsRemoved
199 * is emitted.
200 * @param count The new number of desktops to use
201 * @see count
202 * @see maximum
203 * @see countChanged
204 * @see desktopsRemoved
205 */
206 void setCount(uint count);
207 /**
208 * Set the current desktop to @a current.
209 * @returns True on success, false otherwise.
210 * @see current
211 * @see currentChanged
212 * @see moveTo
213 */
214 bool setCurrent(uint current);
215 /**
216 * Called from within setCount() to ensure the desktop layout is still valid.
217 */
218 void updateLayout();
219 /**
220 * @param enable wrapping around borders for navigation in desktop layout
221 * @see isNavigationWrappingAround
222 * @see navigationWrappingAroundChanged
223 **/
224 void setNavigationWrappingAround(bool enabled);
225 /**
226 * Loads number of desktops and names from configuration file
227 **/
228 void load();
229 /**
230 * Saves number of desktops and names to configuration file
231 **/
232 void save();
233
234Q_SIGNALS:
235 /**
236 * Signal emitted whenever the number of virtual desktops changes.
237 * @param previousCount The number of desktops prior to the change
238 * @param newCount The new current number of desktops
239 **/
240 void countChanged(uint previousCount, uint newCount);
241 /**
242 * Signal emitted whenever the number of virtual desktops changes in a way
243 * that existing desktops are removed.
244 *
245 * The signal is emitted after the @c count property has been updated but prior
246 * to the @link countChanged signal being emitted.
247 * @param previousCount The number of desktops prior to the change.
248 * @see countChanged
249 * @see setCount
250 * @see count
251 **/
252 void desktopsRemoved(uint previousCount);
253 /**
254 * Signal emitted whenever the current desktop changes.
255 * @param previousDesktop The virtual desktop changed from
256 * @param newDesktop The virtual desktop changed to
257 **/
258 void currentChanged(uint previousDesktop, uint newDesktop);
259 /**
260 * Signal emitted whenever the desktop layout changes.
261 * @param columns The new number of columns in the layout
262 * @param rows The new number of rows in the layout
263 **/
264 void layoutChanged(int columns, int rows);
265 /**
266 * Signal emitted whenever the navigationWrappingAround property changes.
267 **/
268 void navigationWrappingAroundChanged();
269
270private slots:
271 /**
272 * Common slot for all "Switch to Desktop n" shortcuts.
273 * This method uses the sender() method to access some data.
274 * DO NOT CALL DIRECTLY! ONLY TO BE USED FROM AN ACTION!
275 **/
276 void slotSwitchTo();
277 /**
278 * Slot for switch to next desktop action.
279 **/
280 void slotNext();
281 /**
282 * Slot for switch to previous desktop action.
283 **/
284 void slotPrevious();
285 /**
286 * Slot for switch to right desktop action.
287 **/
288 void slotRight();
289 /**
290 * Slot for switch to left desktop action.
291 **/
292 void slotLeft();
293 /**
294 * Slot for switch to desktop above action.
295 **/
296 void slotUp();
297 /**
298 * Slot for switch to desktop below action.
299 **/
300 void slotDown();
301
302private:
303 /**
304 * This method is called when the number of desktops is updated in a way that desktops
305 * are removed. At the time when this method is invoked the count property is already
306 * updated but the corresponding signal has not been emitted yet.
307 *
308 * Ensures that in case the current desktop is on one of the removed
309 * desktops the last desktop after the change becomes the new desktop.
310 * Emits the signal @link desktopsRemoved.
311 *
312 * @param previousCount The number of desktops prior to the change.
313 * @see setCount
314 * @see desktopsRemoved
315 **/
316 void handleDesktopsRemoved(uint previousCount);
317 /**
318 * Generate a desktop layout from EWMH _NET_DESKTOP_LAYOUT property parameters.
319 */
320 void setNETDesktopLayout(Qt::Orientation orientation, uint width, uint height, int startingCorner);
321 /**
322 * Updates the net root info for new number of desktops
323 **/
324 void updateRootInfo();
325 /**
326 * @returns A default name for the given @p desktop
327 **/
328 QString defaultName(int desktop) const;
329 /**
330 * Creates all the global keyboard shortcuts for "Switch To Desktop n" actions.
331 **/
332 void initSwitchToShortcuts(KActionCollection *keys);
333 /**
334 * Creates an action and connects it to the @p slot in this Manager. This method is
335 * meant to be used for the case that an additional information needs to be stored in
336 * the action and the label.
337 * @param keys The ActionCollection used to create the Action
338 * @param name The name of the action to be created
339 * @param label The localized name for the action to be created
340 * @param value An additional value added to the label and to the created action
341 * @param key The global shortcut for the action
342 * @param slot The slot to invoke when the action is triggered
343 **/
344 void addAction(KActionCollection *keys, const QString &name, const KLocalizedString &label, uint value, const KShortcut &key, const char *slot);
345 /**
346 * Creates an action and connects it to the @p slot in this Manager.
347 * Overloaded method for the case that no additional value needs to be passed to the action and
348 * no global shortcut is defined by default.
349 * @param keys The ActionCollection used to create the Action
350 * @param name The name of the action to be created
351 * @param label The localized name for the action to be created
352 * @param slot The slot to invoke when the action is triggered
353 **/
354 void addAction(KActionCollection *keys, const QString &name, const QString &label, const char *slot);
355
356 uint m_current;
357 uint m_count;
358 bool m_navigationWrapsAround;
359 VirtualDesktopGrid m_grid;
360 // TODO: QPointer
361 NETRootInfo *m_rootInfo;
362 KSharedConfig::Ptr m_config;
363
364 KWIN_SINGLETON_VARIABLE(VirtualDesktopManager, s_manager)
365};
366
367/**
368 * Function object to select the desktop above in the layout.
369 * Note: does not switch to the desktop!
370 **/
371class DesktopAbove
372{
373public:
374 DesktopAbove() {}
375 /**
376 * @param desktop The desktop from which the desktop above should be selected. If @c 0 the current desktop is used
377 * @param wrap Whether to wrap around if already topmost desktop
378 * @returns Id of the desktop above @p desktop
379 **/
380 uint operator() (uint desktop, bool wrap) {
381 return VirtualDesktopManager::self()->above(desktop, wrap);
382 }
383};
384
385/**
386 * Function object to select the desktop below in the layout.
387 * Note: does not switch to the desktop!
388 **/
389class DesktopBelow
390{
391public:
392 DesktopBelow() {}
393 /**
394 * @param desktop The desktop from which the desktop below should be selected. If @c 0 the current desktop is used
395 * @param wrap Whether to wrap around if already lowest desktop
396 * @returns Id of the desktop below @p desktop
397 **/
398 uint operator() (uint desktop, bool wrap) {
399 return VirtualDesktopManager::self()->below(desktop, wrap);
400 }
401};
402
403/**
404 * Function object to select the desktop to the left in the layout.
405 * Note: does not switch to the desktop!
406 **/
407class DesktopLeft
408{
409public:
410 DesktopLeft() {}
411 /**
412 * @param desktop The desktop from which the desktop on the left should be selected. If @c 0 the current desktop is used
413 * @param wrap Whether to wrap around if already leftmost desktop
414 * @returns Id of the desktop left of @p desktop
415 **/
416 uint operator() (uint desktop, bool wrap) {
417 return VirtualDesktopManager::self()->toLeft(desktop, wrap);
418 }
419};
420
421/**
422 * Function object to select the desktop to the right in the layout.
423 * Note: does not switch to the desktop!
424 **/
425class DesktopRight
426{
427public:
428 DesktopRight() {}
429 /**
430 * @param desktop The desktop from which the desktop on the right should be selected. If @c 0 the current desktop is used
431 * @param wrap Whether to wrap around if already rightmost desktop
432 * @returns Id of the desktop right of @p desktop
433 **/
434 uint operator() (uint desktop, bool wrap) {
435 return VirtualDesktopManager::self()->toRight(desktop, wrap);
436 }
437};
438
439/**
440 * Function object to select the next desktop in the layout.
441 * Note: does not switch to the desktop!
442 **/
443class DesktopNext
444{
445public:
446 DesktopNext() {}
447 /**
448 * @param desktop The desktop from which the next desktop should be selected. If @c 0 the current desktop is used
449 * @param wrap Whether to wrap around if already last desktop
450 * @returns Id of the next desktop
451 **/
452 uint operator() (uint desktop, bool wrap) {
453 return VirtualDesktopManager::self()->next(desktop, wrap);
454 }
455};
456
457/**
458 * Function object to select the previous desktop in the layout.
459 * Note: does not switch to the desktop!
460 **/
461class DesktopPrevious
462{
463public:
464 DesktopPrevious() {}
465 /**
466 * @param desktop The desktop from which the previous desktop should be selected. If @c 0 the current desktop is used
467 * @param wrap Whether to wrap around if already first desktop
468 * @returns Id of the previous desktop
469 **/
470 uint operator() (uint desktop, bool wrap) {
471 return VirtualDesktopManager::self()->previous(desktop, wrap);
472 }
473};
474
475/**
476 * Helper function to get the ID of a virtual desktop in the direction from
477 * the given @p desktop. If @c 0 the current desktop is used as a starting point.
478 * @param desktop The desktop from which the desktop in given Direction should be selected.
479 * @param wrap Whether desktop navigation wraps around at the borders of the layout
480 * @returns The next desktop in specified direction
481 **/
482template <typename Direction>
483uint getDesktop(int desktop = 0, bool wrap = true);
484
485template <typename Direction>
486uint getDesktop(int d, bool wrap)
487{
488 Direction direction;
489 return direction(d, wrap);
490}
491
492inline
493int VirtualDesktopGrid::width() const
494{
495 return m_size.width();
496}
497
498inline
499int VirtualDesktopGrid::height() const
500{
501 return m_size.height();
502}
503
504inline
505const QSize &VirtualDesktopGrid::size() const
506{
507 return m_size;
508}
509
510inline
511uint VirtualDesktopGrid::at(QPoint coords) const
512{
513 const int index = coords.y() * m_size.width() + coords.x();
514 if (index > m_size.width() * m_size.height() || coords.x() >= width() || coords.y() >= height()) {
515 return 0;
516 }
517 return m_grid[index];
518}
519
520inline
521uint VirtualDesktopManager::maximum()
522{
523 return 20;
524}
525
526inline
527uint VirtualDesktopManager::current() const
528{
529 return m_current;
530}
531
532inline
533uint VirtualDesktopManager::count() const
534{
535 return m_count;
536}
537
538inline
539bool VirtualDesktopManager::isNavigationWrappingAround() const
540{
541 return m_navigationWrapsAround;
542}
543
544inline
545void VirtualDesktopManager::setRootInfo(NETRootInfo *info)
546{
547 m_rootInfo = info;
548}
549
550inline
551void VirtualDesktopManager::setConfig(KSharedConfig::Ptr config)
552{
553 m_config = config;
554}
555
556inline
557const VirtualDesktopGrid &VirtualDesktopManager::grid() const
558{
559 return m_grid;
560}
561
562template <typename Direction>
563void VirtualDesktopManager::moveTo(bool wrap)
564{
565 Direction functor;
566 setCurrent(functor(0, wrap));
567}
568
569} // namespace KWin
570#endif
571