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_FOCUS_CHAIN_H
21#define KWIN_FOCUS_CHAIN_H
22// KWin
23#include <kwinglobals.h>
24// Qt
25#include <QObject>
26#include <QHash>
27
28namespace KWin
29{
30// forward declarations
31class Client;
32
33/**
34 * @brief Singleton class to handle the various focus chains.
35 *
36 * A focus chain is a list of Clients containing information on which Client should be activated.
37 *
38 * Internally this FocusChain holds multiple independent chains. There is one chain of most recently
39 * used Clients which is primarily used by TabBox to build up the list of Clients for navigation.
40 * The chains are organized as a normal QList of Clients with the most recently used Client being the
41 * last item of the list, that is a LIFO like structure.
42 *
43 * In addition there is one chain for each virtual desktop which is used to determine which Client
44 * should get activated when the user switches to another virtual desktop.
45 *
46 * Furthermore this class contains various helper methods for the two different kind of chains.
47 **/
48class FocusChain : public QObject
49{
50 Q_OBJECT
51public:
52 enum Change {
53 MakeFirst,
54 MakeLast,
55 Update,
56 MakeFirstMinimized = MakeFirst
57 };
58 virtual ~FocusChain();
59 /**
60 * @brief Updates the position of the @p client according to the requested @p change in the
61 * focus chain.
62 *
63 * This method affects both the most recently used focus chain and the per virtual desktop focus
64 * chain.
65 *
66 * In case the client does no longer want to get focus, it is removed from all chains. In case
67 * the client is on all virtual desktops it is ensured that it is present in each of the virtual
68 * desktops focus chain. In case it's on exactly one virtual desktop it is ensured that it is only
69 * in the focus chain for that virtual desktop.
70 *
71 * Depending on @p change the Client is inserted at different positions in the focus chain. In case
72 * of @c MakeFirst it is moved to the first position of the chain, in case of
73 * @c MakeLast it is moved to the last position of the chain. In all other cases it
74 * depends on whether the @p client is the currently active Client. If it is the active Client it
75 * becomes the first Client in the chain, otherwise it is inserted at the second position that is
76 * directly after the currently active Client.
77 *
78 * @param client The Client which should be moved inside the chains.
79 * @param change Where to move the Client
80 * @return void
81 **/
82 void update(Client *client, Change change);
83 /**
84 * @brief Moves @p client behind the @p reference Client in all focus chains.
85 *
86 * @param client The Client to move in the chains
87 * @param reference The Client behind which the @p client should be moved
88 * @return void
89 **/
90 void moveAfterClient(Client *client, Client *reference);
91 /**
92 * @brief Finds the best Client to become the new active Client in the focus chain for the given
93 * virtual @p desktop.
94 *
95 * In case that separate screen focus is used only Clients on the current screen are considered.
96 * If no Client for activation is found @c null is returned.
97 *
98 * @param desktop The virtual desktop to look for a Client for activation
99 * @return :Client* The Client which could be activated or @c null if there is none.
100 **/
101 Client *getForActivation(uint desktop) const;
102 /**
103 * @brief Finds the best Client to become the new active Client in the focus chain for the given
104 * virtual @p desktop on the given @p screen.
105 *
106 * This method makes only sense to use if separate screen focus is used. If separate screen focus
107 * is disabled the @p screen is ignored.
108 * If no Client for activation is found @c null is returned.
109 *
110 * @param desktop The virtual desktop to look for a Client for activation
111 * @param screen The screen to constrain the search on with separate screen focus
112 * @return :Client* The Client which could be activated or @c null if there is none.
113 **/
114 Client *getForActivation(uint desktop, int screen) const;
115
116 /**
117 * @brief Checks whether the most recently used focus chain contains the given @p client.
118 *
119 * Does not consider the per-desktop focus chains.
120 * @param client The Client to look for.
121 * @return bool @c true if the most recently used focus chain contains @p client, @c false otherwise.
122 **/
123 bool contains(Client *client) const;
124 /**
125 * @brief Checks whether the focus chain for the given @p desktop contains the given @p client.
126 *
127 * Does not consider the most recently used focus chain.
128 *
129 * @param client The Client to look for.
130 * @param desktop The virtual desktop whose focus chain should be used
131 * @return bool @c true if the focus chain for @p desktop contains @p client, @c false otherwise.
132 **/
133 bool contains(Client *client, uint desktop) const;
134 /**
135 * @brief Queries the most recently used focus chain for the next Client after the given
136 * @p reference Client.
137 *
138 * The navigation wraps around the borders of the chain. That is if the @p reference Client is
139 * the last item of the focus chain, the first Client will be returned.
140 *
141 * If the @p reference Client cannot be found in the focus chain, the first element of the focus
142 * chain is returned.
143 *
144 * @param reference The start point in the focus chain to search
145 * @return :Client* The relatively next Client in the most recently used chain.
146 **/
147 Client *nextMostRecentlyUsed(Client *reference) const;
148 /**
149 * @brief Queries the focus chain for @p desktop for the next Client in relation to the given
150 * @p reference Client.
151 *
152 * The method finds the first usable Client which is not the @p reference Client. If no Client
153 * can be found @c null is returned
154 *
155 * @param reference The reference Client which should not be returned
156 * @param desktop The virtual desktop whose focus chain should be used
157 * @return :Client* The next usable Client or @c null if none can be found.
158 **/
159 Client *nextForDesktop(Client *reference, uint desktop) const;
160 /**
161 * @brief Returns the first Client in the most recently used focus chain. First Client in this
162 * case means really the first Client in the chain and not the most recently used Client.
163 *
164 * @return :Client* The first Client in the most recently used chain.
165 **/
166 Client *firstMostRecentlyUsed() const;
167
168public slots:
169 /**
170 * @brief Resizes the per virtual desktop focus chains from @p previousSize to @p newSize.
171 * This means that for each virtual desktop between previous and new size a new focus chain is
172 * created and in case the number is reduced the focus chains are destroyed.
173 *
174 * @param previousSize The previous number of virtual desktops
175 * @param newSize The new number of virtual desktops
176 * @return void
177 **/
178 void resize(uint previousSize, uint newSize);
179 /**
180 * @brief Removes @p client from all focus chains.
181 *
182 * @param client The Client to remove from all focus chains.
183 * @return void
184 **/
185 void remove(KWin::Client *client);
186 void setSeparateScreenFocus(bool enabled);
187 void setActiveClient(KWin::Client *client);
188 void setCurrentDesktop(uint previous, uint newDesktop);
189 bool isUsableFocusCandidate(Client *c, Client *prev) const;
190
191private:
192 /**
193 * @brief Makes @p client the first Client in the given focus @p chain.
194 *
195 * This means the existing position of @p client is dropped and @p client is appended to the
196 * @p chain which makes it the first item.
197 *
198 * @param client The Client to become the first in @p chain
199 * @param chain The focus chain to operate on
200 * @return void
201 **/
202 void makeFirstInChain(Client *client, QList<Client*> &chain);
203 /**
204 * @brief Makes @p client the last Client in the given focus @p chain.
205 *
206 * This means the existing position of @p client is dropped and @p client is prepended to the
207 * @p chain which makes it the last item.
208 *
209 * @param client The Client to become the last in @p chain
210 * @param chain The focus chain to operate on
211 * @return void
212 **/
213 void makeLastInChain(Client *client, QList<Client*> &chain);
214 void moveAfterClientInChain(Client *client, Client *reference, QList<Client*> &chain);
215 void updateClientInChain(Client *client, Change change, QList<Client*> &chain);
216 void insertClientIntoChain(Client *client, QList<Client*> &chain);
217 typedef QHash<uint, QList<Client*> > DesktopChains;
218 QList<Client*> m_mostRecentlyUsed;
219 DesktopChains m_desktopFocusChains;
220 bool m_separateScreenFocus;
221 Client *m_activeClient;
222 uint m_currentDesktop;
223
224 KWIN_SINGLETON_VARIABLE(FocusChain, s_manager)
225};
226
227inline
228bool FocusChain::contains(Client *client) const
229{
230 return m_mostRecentlyUsed.contains(client);
231}
232
233inline
234void FocusChain::setSeparateScreenFocus(bool enabled)
235{
236 m_separateScreenFocus = enabled;
237}
238
239inline
240void FocusChain::setActiveClient(Client *client)
241{
242 m_activeClient = client;
243}
244
245inline
246void FocusChain::setCurrentDesktop(uint previous, uint newDesktop)
247{
248 Q_UNUSED(previous)
249 m_currentDesktop = newDesktop;
250}
251
252} // namespace
253
254#endif // KWIN_FOCUS_CHAIN_H
255