1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org> |
6 | |
7 | This program is free software; you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation; either version 2 of the License, or |
10 | (at your option) any later version. |
11 | |
12 | This program is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along 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 | |
31 | class KActionCollection; |
32 | class KLocalizedString; |
33 | class KShortcut; |
34 | class NETRootInfo; |
35 | |
36 | namespace 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 | **/ |
46 | class VirtualDesktopGrid |
47 | { |
48 | public: |
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; |
64 | private: |
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 | **/ |
87 | class 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) |
103 | public: |
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 | |
188 | public 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 | |
234 | Q_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 | |
270 | private 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 | |
302 | private: |
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 | **/ |
371 | class DesktopAbove |
372 | { |
373 | public: |
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 | **/ |
389 | class DesktopBelow |
390 | { |
391 | public: |
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 | **/ |
407 | class DesktopLeft |
408 | { |
409 | public: |
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 | **/ |
425 | class DesktopRight |
426 | { |
427 | public: |
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 | **/ |
443 | class DesktopNext |
444 | { |
445 | public: |
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 | **/ |
461 | class DesktopPrevious |
462 | { |
463 | public: |
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 | **/ |
482 | template <typename Direction> |
483 | uint getDesktop(int desktop = 0, bool wrap = true); |
484 | |
485 | template <typename Direction> |
486 | uint getDesktop(int d, bool wrap) |
487 | { |
488 | Direction direction; |
489 | return direction(d, wrap); |
490 | } |
491 | |
492 | inline |
493 | int VirtualDesktopGrid::width() const |
494 | { |
495 | return m_size.width(); |
496 | } |
497 | |
498 | inline |
499 | int VirtualDesktopGrid::height() const |
500 | { |
501 | return m_size.height(); |
502 | } |
503 | |
504 | inline |
505 | const QSize &VirtualDesktopGrid::size() const |
506 | { |
507 | return m_size; |
508 | } |
509 | |
510 | inline |
511 | uint 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 | |
520 | inline |
521 | uint VirtualDesktopManager::maximum() |
522 | { |
523 | return 20; |
524 | } |
525 | |
526 | inline |
527 | uint VirtualDesktopManager::current() const |
528 | { |
529 | return m_current; |
530 | } |
531 | |
532 | inline |
533 | uint VirtualDesktopManager::count() const |
534 | { |
535 | return m_count; |
536 | } |
537 | |
538 | inline |
539 | bool VirtualDesktopManager::isNavigationWrappingAround() const |
540 | { |
541 | return m_navigationWrapsAround; |
542 | } |
543 | |
544 | inline |
545 | void VirtualDesktopManager::setRootInfo(NETRootInfo *info) |
546 | { |
547 | m_rootInfo = info; |
548 | } |
549 | |
550 | inline |
551 | void VirtualDesktopManager::setConfig(KSharedConfig::Ptr config) |
552 | { |
553 | m_config = config; |
554 | } |
555 | |
556 | inline |
557 | const VirtualDesktopGrid &VirtualDesktopManager::grid() const |
558 | { |
559 | return m_grid; |
560 | } |
561 | |
562 | template <typename Direction> |
563 | void VirtualDesktopManager::moveTo(bool wrap) |
564 | { |
565 | Direction functor; |
566 | setCurrent(functor(0, wrap)); |
567 | } |
568 | |
569 | } // namespace KWin |
570 | #endif |
571 | |