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_FOCUS_CHAIN_H |
21 | #define KWIN_FOCUS_CHAIN_H |
22 | // KWin |
23 | #include <kwinglobals.h> |
24 | // Qt |
25 | #include <QObject> |
26 | #include <QHash> |
27 | |
28 | namespace KWin |
29 | { |
30 | // forward declarations |
31 | class 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 | **/ |
48 | class FocusChain : public QObject |
49 | { |
50 | Q_OBJECT |
51 | public: |
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 | |
168 | public 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 | |
191 | private: |
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 | |
227 | inline |
228 | bool FocusChain::contains(Client *client) const |
229 | { |
230 | return m_mostRecentlyUsed.contains(client); |
231 | } |
232 | |
233 | inline |
234 | void FocusChain::setSeparateScreenFocus(bool enabled) |
235 | { |
236 | m_separateScreenFocus = enabled; |
237 | } |
238 | |
239 | inline |
240 | void FocusChain::setActiveClient(Client *client) |
241 | { |
242 | m_activeClient = client; |
243 | } |
244 | |
245 | inline |
246 | void 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 | |