diff options
Diffstat (limited to 'src/FocusControl.cc')
-rw-r--r-- | src/FocusControl.cc | 187 |
1 files changed, 127 insertions, 60 deletions
diff --git a/src/FocusControl.cc b/src/FocusControl.cc index d4cb410..6ccc9a1 100644 --- a/src/FocusControl.cc +++ b/src/FocusControl.cc | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include "FocusControl.hh" | 24 | #include "FocusControl.hh" |
25 | 25 | ||
26 | #include "ClientPattern.hh" | ||
26 | #include "Screen.hh" | 27 | #include "Screen.hh" |
27 | #include "Window.hh" | 28 | #include "Window.hh" |
28 | #include "WinClient.hh" | 29 | #include "WinClient.hh" |
@@ -48,6 +49,22 @@ WinClient *FocusControl::s_focused_window = 0; | |||
48 | FluxboxWindow *FocusControl::s_focused_fbwindow = 0; | 49 | FluxboxWindow *FocusControl::s_focused_fbwindow = 0; |
49 | bool FocusControl::s_reverting = false; | 50 | bool FocusControl::s_reverting = false; |
50 | 51 | ||
52 | namespace { | ||
53 | |||
54 | bool doSkipWindow(const Focusable &win, const ClientPattern *pat) { | ||
55 | const FluxboxWindow *fbwin = win.fbwindow(); | ||
56 | if (!fbwin || fbwin->isFocusHidden()) | ||
57 | return true; // skip if no fbwindow or if focushidden | ||
58 | if (pat && !pat->match(win)) | ||
59 | return true; // skip if it doesn't match the pattern | ||
60 | if (fbwin->workspaceNumber() != win.screen().currentWorkspaceID() && | ||
61 | !fbwin->isStuck()) | ||
62 | return true; // for now, we only cycle through the current workspace | ||
63 | return false; // else don't skip | ||
64 | } | ||
65 | |||
66 | }; // end anonymous namespace | ||
67 | |||
51 | FocusControl::FocusControl(BScreen &screen): | 68 | FocusControl::FocusControl(BScreen &screen): |
52 | m_screen(screen), | 69 | m_screen(screen), |
53 | m_focus_model(screen.resourceManager(), | 70 | m_focus_model(screen.resourceManager(), |
@@ -65,28 +82,12 @@ FocusControl::FocusControl(BScreen &screen): | |||
65 | m_was_iconic(false), | 82 | m_was_iconic(false), |
66 | m_cycling_last(0) { | 83 | m_cycling_last(0) { |
67 | 84 | ||
68 | m_cycling_window = m_focused_list.end(); | 85 | m_cycling_window = m_focused_win_list.end(); |
69 | 86 | ||
70 | } | 87 | } |
71 | 88 | ||
72 | // true if the windows should be skiped else false | 89 | void FocusControl::cycleFocus(Focusables &window_list, const ClientPattern *pat, |
73 | bool doSkipWindow(const WinClient &winclient, int opts) { | 90 | bool cycle_reverse) { |
74 | const FluxboxWindow *win = winclient.fbwindow(); | ||
75 | return (!win || | ||
76 | // skip if stuck | ||
77 | (opts & FocusControl::CYCLESKIPSTUCK) != 0 && win->isStuck() || | ||
78 | // skip if not active client (i.e. only visit each fbwin once) | ||
79 | (opts & FocusControl::CYCLEGROUPS) != 0 && win->winClient().window() != winclient.window() || | ||
80 | // skip if shaded | ||
81 | (opts & FocusControl::CYCLESKIPSHADED) != 0 && win->isShaded() || | ||
82 | // skip if iconic | ||
83 | (opts & FocusControl::CYCLESKIPICONIC) != 0 && win->isIconic() || | ||
84 | // skip if hidden | ||
85 | win->isFocusHidden() | ||
86 | ); | ||
87 | } | ||
88 | |||
89 | void FocusControl::cycleFocus(FocusedWindows &window_list, int opts, bool cycle_reverse) { | ||
90 | 91 | ||
91 | if (!m_cycling_list) { | 92 | if (!m_cycling_list) { |
92 | if (&m_screen == FbTk::EventManager::instance()->grabbingKeyboard()) | 93 | if (&m_screen == FbTk::EventManager::instance()->grabbingKeyboard()) |
@@ -97,13 +98,15 @@ void FocusControl::cycleFocus(FocusedWindows &window_list, int opts, bool cycle_ | |||
97 | } else if (m_cycling_list != &window_list) | 98 | } else if (m_cycling_list != &window_list) |
98 | m_cycling_list = &window_list; | 99 | m_cycling_list = &window_list; |
99 | 100 | ||
100 | FocusedWindows::iterator it_begin = window_list.begin(); | 101 | Focusables::iterator it_begin = window_list.begin(); |
101 | FocusedWindows::iterator it_end = window_list.end(); | 102 | Focusables::iterator it_end = window_list.end(); |
102 | 103 | ||
103 | // too many things can go wrong with remembering this | 104 | // too many things can go wrong with remembering this |
104 | m_cycling_window = find(it_begin, it_end, s_focused_window); | 105 | m_cycling_window = find(it_begin, it_end, s_focused_window); |
106 | if (m_cycling_window == it_end) | ||
107 | m_cycling_window = find(it_begin, it_end, s_focused_fbwindow); | ||
105 | 108 | ||
106 | FocusedWindows::iterator it = m_cycling_window; | 109 | Focusables::iterator it = m_cycling_window; |
107 | FluxboxWindow *fbwin = 0; | 110 | FluxboxWindow *fbwin = 0; |
108 | WinClient *last_client = 0; | 111 | WinClient *last_client = 0; |
109 | WinClient *was_iconic = 0; | 112 | WinClient *was_iconic = 0; |
@@ -123,11 +126,7 @@ void FocusControl::cycleFocus(FocusedWindows &window_list, int opts, bool cycle_ | |||
123 | continue; | 126 | continue; |
124 | 127 | ||
125 | fbwin = (*it)->fbwindow(); | 128 | fbwin = (*it)->fbwindow(); |
126 | // make sure the window is on the same workspace, | 129 | if (!fbwin) |
127 | // unless its sticky, which is ok | ||
128 | if (!fbwin || | ||
129 | ( fbwin->workspaceNumber() != m_screen.currentWorkspaceID() && | ||
130 | ! fbwin->isStuck())) | ||
131 | continue; | 130 | continue; |
132 | 131 | ||
133 | // keep track of the originally selected window in a group | 132 | // keep track of the originally selected window in a group |
@@ -135,7 +134,7 @@ void FocusControl::cycleFocus(FocusedWindows &window_list, int opts, bool cycle_ | |||
135 | was_iconic = (fbwin->isIconic() ? last_client : 0); | 134 | was_iconic = (fbwin->isIconic() ? last_client : 0); |
136 | 135 | ||
137 | // now we actually try to focus the window | 136 | // now we actually try to focus the window |
138 | if (!doSkipWindow(**it, opts) && (*it)->focus()) | 137 | if (!doSkipWindow(**it, pat) && (*it)->focus()) |
139 | break; | 138 | break; |
140 | } | 139 | } |
141 | 140 | ||
@@ -168,6 +167,33 @@ void FocusControl::cycleFocus(FocusedWindows &window_list, int opts, bool cycle_ | |||
168 | 167 | ||
169 | } | 168 | } |
170 | 169 | ||
170 | void FocusControl::goToWindowNumber(Focusables &winlist, int num, | ||
171 | const ClientPattern *pat) { | ||
172 | Focusable *last_matched = 0; | ||
173 | if (num > 0) { | ||
174 | Focusables::iterator it = winlist.begin(); | ||
175 | Focusables::iterator it_end = winlist.end(); | ||
176 | for (; it != it_end; ++it) { | ||
177 | if (!doSkipWindow(**it, pat) && (*it)->acceptsFocus()) { | ||
178 | --num; | ||
179 | last_matched = *it; | ||
180 | if (!num) break; | ||
181 | } | ||
182 | } | ||
183 | } else if (num < 0) { | ||
184 | Focusables::reverse_iterator it = winlist.rbegin(); | ||
185 | Focusables::reverse_iterator it_end = winlist.rend(); | ||
186 | for (; it != it_end; ++it) { | ||
187 | if (!doSkipWindow(**it, pat) && (*it)->acceptsFocus()) { | ||
188 | ++num; | ||
189 | last_matched = *it; | ||
190 | if (!num) break; | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | if (last_matched) last_matched->focus(); | ||
195 | } | ||
196 | |||
171 | void FocusControl::addFocusBack(WinClient &client) { | 197 | void FocusControl::addFocusBack(WinClient &client) { |
172 | m_focused_list.push_back(&client); | 198 | m_focused_list.push_back(&client); |
173 | m_creation_order_list.push_back(&client); | 199 | m_creation_order_list.push_back(&client); |
@@ -178,6 +204,16 @@ void FocusControl::addFocusFront(WinClient &client) { | |||
178 | m_creation_order_list.push_back(&client); | 204 | m_creation_order_list.push_back(&client); |
179 | } | 205 | } |
180 | 206 | ||
207 | void FocusControl::addFocusWinBack(Focusable &win) { | ||
208 | m_focused_win_list.push_back(&win); | ||
209 | m_creation_order_win_list.push_back(&win); | ||
210 | } | ||
211 | |||
212 | void FocusControl::addFocusWinFront(Focusable &win) { | ||
213 | m_focused_win_list.push_front(&win); | ||
214 | m_creation_order_win_list.push_back(&win); | ||
215 | } | ||
216 | |||
181 | // move all clients in given window to back of focused list | 217 | // move all clients in given window to back of focused list |
182 | void FocusControl::setFocusBack(FluxboxWindow *fbwin) { | 218 | void FocusControl::setFocusBack(FluxboxWindow *fbwin) { |
183 | // do nothing if there are no windows open | 219 | // do nothing if there are no windows open |
@@ -185,9 +221,19 @@ void FocusControl::setFocusBack(FluxboxWindow *fbwin) { | |||
185 | if (m_focused_list.empty() || s_reverting) | 221 | if (m_focused_list.empty() || s_reverting) |
186 | return; | 222 | return; |
187 | 223 | ||
188 | FocusedWindows::iterator it = m_focused_list.begin(); | 224 | // if the window isn't already in this list, we could accidentally add it |
225 | Focusables::iterator win_begin = m_focused_win_list.begin(), | ||
226 | win_end = m_focused_win_list.end(); | ||
227 | Focusables::iterator win_it = find(win_begin, win_end, fbwin); | ||
228 | if (win_it == win_end) | ||
229 | return; | ||
230 | |||
231 | m_focused_win_list.erase(win_it); | ||
232 | m_focused_win_list.push_back(fbwin); | ||
233 | |||
234 | Focusables::iterator it = m_focused_list.begin(); | ||
189 | // use back to avoid an infinite loop | 235 | // use back to avoid an infinite loop |
190 | FocusedWindows::iterator it_back = --m_focused_list.end(); | 236 | Focusables::iterator it_back = --m_focused_list.end(); |
191 | 237 | ||
192 | while (it != it_back) { | 238 | while (it != it_back) { |
193 | if ((*it)->fbwindow() == fbwin) { | 239 | if ((*it)->fbwindow() == fbwin) { |
@@ -201,6 +247,7 @@ void FocusControl::setFocusBack(FluxboxWindow *fbwin) { | |||
201 | m_focused_list.push_back(*it); | 247 | m_focused_list.push_back(*it); |
202 | m_focused_list.erase(it); | 248 | m_focused_list.erase(it); |
203 | } | 249 | } |
250 | |||
204 | } | 251 | } |
205 | 252 | ||
206 | void FocusControl::stopCyclingFocus() { | 253 | void FocusControl::stopCyclingFocus() { |
@@ -208,7 +255,7 @@ void FocusControl::stopCyclingFocus() { | |||
208 | if (m_cycling_list == 0) | 255 | if (m_cycling_list == 0) |
209 | return; | 256 | return; |
210 | 257 | ||
211 | FocusedWindows::iterator it_end = m_cycling_list->end(); | 258 | Focusables::iterator it_end = m_cycling_list->end(); |
212 | m_cycling_last = 0; | 259 | m_cycling_last = 0; |
213 | m_cycling_list = 0; | 260 | m_cycling_list = 0; |
214 | 261 | ||
@@ -217,10 +264,9 @@ void FocusControl::stopCyclingFocus() { | |||
217 | // in which case we'll do a proper revert focus | 264 | // in which case we'll do a proper revert focus |
218 | if (m_cycling_window != it_end && (*m_cycling_window)->fbwindow() && | 265 | if (m_cycling_window != it_end && (*m_cycling_window)->fbwindow() && |
219 | (*m_cycling_window)->fbwindow()->isVisible()) { | 266 | (*m_cycling_window)->fbwindow()->isVisible()) { |
220 | WinClient *client = *m_cycling_window; | 267 | (*m_cycling_window)->fbwindow()->raise(); |
221 | m_focused_list.remove(client); | 268 | if (s_focused_window) |
222 | m_focused_list.push_front(client); | 269 | setScreenFocusedWindow(*s_focused_window); |
223 | client->fbwindow()->raise(); | ||
224 | } else | 270 | } else |
225 | revertFocus(m_screen); | 271 | revertFocus(m_screen); |
226 | } | 272 | } |
@@ -230,13 +276,13 @@ void FocusControl::stopCyclingFocus() { | |||
230 | * If workspace is outside the ID range, then the absolute last focused window | 276 | * If workspace is outside the ID range, then the absolute last focused window |
231 | * is given. | 277 | * is given. |
232 | */ | 278 | */ |
233 | WinClient *FocusControl::lastFocusedWindow(int workspace) { | 279 | Focusable *FocusControl::lastFocusedWindow(int workspace) { |
234 | if (m_focused_list.empty()) return 0; | 280 | if (m_focused_list.empty() || m_screen.isShuttingdown()) return 0; |
235 | if (workspace < 0 || workspace >= (int) m_screen.numberOfWorkspaces()) | 281 | if (workspace < 0 || workspace >= (int) m_screen.numberOfWorkspaces()) |
236 | return m_focused_list.front(); | 282 | return m_focused_list.front(); |
237 | 283 | ||
238 | FocusedWindows::iterator it = m_focused_list.begin(); | 284 | Focusables::iterator it = m_focused_list.begin(); |
239 | FocusedWindows::iterator it_end = m_focused_list.end(); | 285 | Focusables::iterator it_end = m_focused_list.end(); |
240 | for (; it != it_end; ++it) { | 286 | for (; it != it_end; ++it) { |
241 | if ((*it)->fbwindow() && | 287 | if ((*it)->fbwindow() && |
242 | ((((int)(*it)->fbwindow()->workspaceNumber()) == workspace || | 288 | ((((int)(*it)->fbwindow()->workspaceNumber()) == workspace || |
@@ -253,14 +299,15 @@ WinClient *FocusControl::lastFocusedWindow(int workspace) { | |||
253 | * Stuck, iconic etc don't matter within a group | 299 | * Stuck, iconic etc don't matter within a group |
254 | */ | 300 | */ |
255 | WinClient *FocusControl::lastFocusedWindow(FluxboxWindow &group, WinClient *ignore_client) { | 301 | WinClient *FocusControl::lastFocusedWindow(FluxboxWindow &group, WinClient *ignore_client) { |
256 | if (m_focused_list.empty()) return 0; | 302 | if (m_focused_list.empty() || m_screen.isShuttingdown()) |
303 | return 0; | ||
257 | 304 | ||
258 | FocusedWindows::iterator it = m_focused_list.begin(); | 305 | Focusables::iterator it = m_focused_list.begin(); |
259 | FocusedWindows::iterator it_end = m_focused_list.end(); | 306 | Focusables::iterator it_end = m_focused_list.end(); |
260 | for (; it != it_end; ++it) { | 307 | for (; it != it_end; ++it) { |
261 | if (((*it)->fbwindow() == &group) && | 308 | if (((*it)->fbwindow() == &group) && |
262 | (*it) != ignore_client) | 309 | (*it) != ignore_client) |
263 | return *it; | 310 | return dynamic_cast<WinClient *>(*it); |
264 | } | 311 | } |
265 | return 0; | 312 | return 0; |
266 | } | 313 | } |
@@ -269,11 +316,28 @@ void FocusControl::setScreenFocusedWindow(WinClient &win_client) { | |||
269 | 316 | ||
270 | // raise newly focused window to the top of the focused list | 317 | // raise newly focused window to the top of the focused list |
271 | // don't change the order if we're cycling or shutting down | 318 | // don't change the order if we're cycling or shutting down |
272 | // don't change on startup, as it may add windows that aren't listed yet | 319 | if (!isCycling() && !m_screen.isShuttingdown() && !s_reverting) { |
273 | if (!isCycling() && !m_screen.isShuttingdown() && !s_reverting && | 320 | |
274 | !Fluxbox::instance()->isStartup()) { | 321 | // make sure client is in our list, or else we could end up adding it |
275 | m_focused_list.remove(&win_client); | 322 | Focusables::iterator it_begin = m_focused_list.begin(), |
323 | it_end = m_focused_list.end(); | ||
324 | Focusables::iterator it = find(it_begin, it_end, &win_client); | ||
325 | if (it == it_end) | ||
326 | return; | ||
327 | |||
328 | m_focused_list.erase(it); | ||
276 | m_focused_list.push_front(&win_client); | 329 | m_focused_list.push_front(&win_client); |
330 | |||
331 | // also check the fbwindow | ||
332 | it_begin = m_focused_win_list.begin(); | ||
333 | it_end = m_focused_win_list.end(); | ||
334 | it = find(it_begin, it_end, win_client.fbwindow()); | ||
335 | |||
336 | if (it != it_end) { | ||
337 | m_focused_win_list.erase(it); | ||
338 | m_focused_win_list.push_front(win_client.fbwindow()); | ||
339 | } | ||
340 | |||
277 | } | 341 | } |
278 | } | 342 | } |
279 | 343 | ||
@@ -380,7 +444,7 @@ void FocusControl::dirFocus(FluxboxWindow &win, FocusDir dir) { | |||
380 | } | 444 | } |
381 | 445 | ||
382 | if (foundwin) | 446 | if (foundwin) |
383 | foundwin->setInputFocus(); | 447 | foundwin->focus(); |
384 | 448 | ||
385 | } | 449 | } |
386 | 450 | ||
@@ -388,7 +452,7 @@ void FocusControl::removeClient(WinClient &client) { | |||
388 | if (client.screen().isShuttingdown()) | 452 | if (client.screen().isShuttingdown()) |
389 | return; | 453 | return; |
390 | 454 | ||
391 | WinClient *cyc = 0; | 455 | Focusable *cyc = 0; |
392 | if (m_cycling_list && m_cycling_window != m_cycling_list->end()) | 456 | if (m_cycling_list && m_cycling_window != m_cycling_list->end()) |
393 | cyc = *m_cycling_window; | 457 | cyc = *m_cycling_window; |
394 | 458 | ||
@@ -401,27 +465,30 @@ void FocusControl::removeClient(WinClient &client) { | |||
401 | } | 465 | } |
402 | } | 466 | } |
403 | 467 | ||
468 | void FocusControl::removeWindow(Focusable &win) { | ||
469 | m_focused_win_list.remove(&win); | ||
470 | m_creation_order_win_list.remove(&win); | ||
471 | } | ||
472 | |||
404 | void FocusControl::shutdown() { | 473 | void FocusControl::shutdown() { |
405 | // restore windows backwards so they get put back correctly on restart | 474 | // restore windows backwards so they get put back correctly on restart |
406 | FocusedWindows::reverse_iterator it = m_focused_list.rbegin(); | 475 | Focusables::reverse_iterator it = m_focused_list.rbegin(); |
407 | for (; it != m_focused_list.rend(); ++it) { | 476 | for (; it != m_focused_list.rend(); ++it) { |
408 | if (*it && (*it)->fbwindow()) | 477 | WinClient *client = dynamic_cast<WinClient *>(*it); |
409 | (*it)->fbwindow()->restore(*it, true); | 478 | if (client && client->fbwindow()) |
479 | client->fbwindow()->restore(client, true); | ||
410 | } | 480 | } |
411 | } | 481 | } |
412 | 482 | ||
413 | /** | 483 | /** |
414 | * This function is called whenever we aren't quite sure what | 484 | * This function is called whenever we aren't quite sure what |
415 | * focus is meant to be, it'll make things right ;-) | 485 | * focus is meant to be, it'll make things right ;-) |
416 | * last_focused is set to something if we want to make use of the | ||
417 | * previously focused window (it must NOT be set focused now, it | ||
418 | * is probably dying). | ||
419 | */ | 486 | */ |
420 | void FocusControl::revertFocus(BScreen &screen) { | 487 | void FocusControl::revertFocus(BScreen &screen) { |
421 | if (s_reverting) | 488 | if (s_reverting || screen.isShuttingdown()) |
422 | return; | 489 | return; |
423 | 490 | ||
424 | WinClient *next_focus = | 491 | Focusable *next_focus = |
425 | screen.focusControl().lastFocusedWindow(screen.currentWorkspaceID()); | 492 | screen.focusControl().lastFocusedWindow(screen.currentWorkspaceID()); |
426 | 493 | ||
427 | if (next_focus && next_focus->fbwindow() && | 494 | if (next_focus && next_focus->fbwindow() && |
@@ -430,6 +497,7 @@ void FocusControl::revertFocus(BScreen &screen) { | |||
430 | 497 | ||
431 | // if setting focus fails, or isn't possible, fallback correctly | 498 | // if setting focus fails, or isn't possible, fallback correctly |
432 | if (!(next_focus && next_focus->focus())) { | 499 | if (!(next_focus && next_focus->focus())) { |
500 | |||
433 | setFocusedWindow(0); // so we don't get dangling m_focused_window pointer | 501 | setFocusedWindow(0); // so we don't get dangling m_focused_window pointer |
434 | // if there's a menu open, focus it | 502 | // if there's a menu open, focus it |
435 | if (FbTk::Menu::shownMenu()) | 503 | if (FbTk::Menu::shownMenu()) |
@@ -458,9 +526,8 @@ void FocusControl::revertFocus(BScreen &screen) { | |||
458 | * If unfocus_frame is true, we won't focus anything in the same frame | 526 | * If unfocus_frame is true, we won't focus anything in the same frame |
459 | * as the client. | 527 | * as the client. |
460 | * | 528 | * |
461 | * So, we first prefer to choose a transient parent, then the last | 529 | * So, we first prefer to choose the last client in this window, and if no luck |
462 | * client in this window, and if no luck (or unfocus_frame), then | 530 | * (or unfocus_frame), then we just use the normal revertFocus on the screen. |
463 | * we just use the normal revertFocus on the screen. | ||
464 | * | 531 | * |
465 | * assumption: client has focus | 532 | * assumption: client has focus |
466 | */ | 533 | */ |