1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 focus_cycle.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
20 #include "focus_cycle.h"
21 #include "focus_cycle_indicator.h"
22 #include "focus_cycle_popup.h"
33 ObClient *focus_cycle_target = NULL;
34 static gboolean focus_cycle_directional = FALSE;
35 static gboolean focus_cycle_iconic_windows;
36 static gboolean focus_cycle_all_desktops;
37 static gboolean focus_cycle_dock_windows;
38 static gboolean focus_cycle_desktop_windows;
40 static ObClient *focus_find_directional(ObClient *c,
42 gboolean dock_windows,
43 gboolean desktop_windows);
45 void focus_cycle_startup(gboolean reconfig)
50 void focus_cycle_shutdown(gboolean reconfig)
55 void focus_cycle_add(ObClient *ifclient)
57 if (!(focus_cycle_target && ifclient && !focus_cycle_directional))
60 if (focus_valid_target(ifclient, TRUE,
61 focus_cycle_iconic_windows,
62 focus_cycle_all_desktops,
63 focus_cycle_dock_windows,
64 focus_cycle_desktop_windows,
66 focus_cycle_popup_refresh(focus_cycle_target,
67 focus_cycle_iconic_windows,
68 focus_cycle_all_desktops,
69 focus_cycle_dock_windows,
70 focus_cycle_desktop_windows);
73 void focus_cycle_remove(ObClient *ifclient)
75 if (!(focus_cycle_target && ifclient))
78 if (focus_cycle_directional) {
79 if (focus_cycle_target == ifclient) {
80 focus_directional_cycle(0, TRUE, TRUE, TRUE, TRUE,
85 if (!focus_valid_target(ifclient, TRUE,
86 focus_cycle_iconic_windows,
87 focus_cycle_all_desktops,
88 focus_cycle_dock_windows,
89 focus_cycle_desktop_windows,
91 if (focus_cycle_target == ifclient) {
93 focus_cycle_popup_revert(focus_cycle_target);
94 focus_cycle_update_indicator(focus_cycle_target);
96 focus_cycle_popup_refresh(focus_cycle_target,
97 focus_cycle_iconic_windows,
98 focus_cycle_all_desktops,
99 focus_cycle_dock_windows,
100 focus_cycle_desktop_windows);
105 void focus_cycle_reorder()
107 if (focus_cycle_target && !focus_cycle_directional)
108 focus_cycle_popup_refresh(focus_cycle_target,
109 focus_cycle_iconic_windows,
110 focus_cycle_all_desktops,
111 focus_cycle_dock_windows,
112 focus_cycle_desktop_windows);
115 ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
116 gboolean dock_windows, gboolean desktop_windows,
117 gboolean linear, gboolean interactive,
118 gboolean showbar, gboolean dialog,
119 gboolean done, gboolean cancel)
121 static GList *order = NULL;
122 GList *it, *start, *list;
124 ObClient *ret = NULL;
128 focus_cycle_target = NULL;
129 focus_cycle_directional = FALSE;
137 if (linear) list = client_list;
138 else list = focus_order;
145 if (focus_cycle_target == NULL) {
146 focus_cycle_iconic_windows = TRUE;
147 focus_cycle_all_desktops = all_desktops;
148 focus_cycle_dock_windows = dock_windows;
149 focus_cycle_desktop_windows = desktop_windows;
150 start = it = g_list_find(list, focus_client);
152 start = it = g_list_find(list, focus_cycle_target);
154 if (!start) /* switched desktops or something? */
155 start = it = forward ? g_list_last(list) : g_list_first(list);
156 if (!start) goto done_cycle;
161 if (it == NULL) it = g_list_first(list);
164 if (it == NULL) it = g_list_last(list);
167 if (focus_valid_target(ft, TRUE,
168 focus_cycle_iconic_windows,
169 focus_cycle_all_desktops,
170 focus_cycle_dock_windows,
171 focus_cycle_desktop_windows,
175 if (ft != focus_cycle_target) { /* prevents flicker */
176 focus_cycle_target = ft;
177 focus_cycle_directional = FALSE;
178 focus_cycle_draw_indicator(showbar ? ft : NULL);
181 /* same arguments as focus_target_valid */
182 focus_cycle_popup_show(ft,
183 focus_cycle_iconic_windows,
184 focus_cycle_all_desktops,
185 focus_cycle_dock_windows,
186 focus_cycle_desktop_windows);
187 return focus_cycle_target;
188 } else if (ft != focus_cycle_target) {
189 focus_cycle_target = ft;
190 focus_cycle_directional = FALSE;
195 } while (it != start);
198 if (done && !cancel) ret = focus_cycle_target;
200 focus_cycle_target = NULL;
201 focus_cycle_directional = FALSE;
206 focus_cycle_draw_indicator(NULL);
207 focus_cycle_popup_hide();
213 /* this be mostly ripped from fvwm */
214 static ObClient *focus_find_directional(ObClient *c, ObDirection dir,
215 gboolean dock_windows,
216 gboolean desktop_windows)
218 gint my_cx, my_cy, his_cx, his_cy;
221 gint score, best_score;
222 ObClient *best_client, *cur;
228 /* first, find the centre coords of the currently focused window */
229 my_cx = c->frame->area.x + c->frame->area.width / 2;
230 my_cy = c->frame->area.y + c->frame->area.height / 2;
235 for (it = g_list_first(client_list); it; it = g_list_next(it)) {
238 /* the currently selected window isn't interesting */
241 if (!focus_valid_target(it->data, TRUE, FALSE, FALSE, dock_windows,
242 desktop_windows, FALSE))
245 /* find the centre coords of this window, from the
246 * currently focused window's point of view */
247 his_cx = (cur->frame->area.x - my_cx)
248 + cur->frame->area.width / 2;
249 his_cy = (cur->frame->area.y - my_cy)
250 + cur->frame->area.height / 2;
252 if (dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
253 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST)
256 /* Rotate the diagonals 45 degrees counterclockwise.
257 * To do this, multiply the matrix /+h +h\ with the
258 * vector (x y). \-h +h/
259 * h = sqrt(0.5). We can set h := 1 since absolute
260 * distance doesn't matter here. */
261 tx = his_cx + his_cy;
262 his_cy = -his_cx + his_cy;
267 case OB_DIRECTION_NORTH:
268 case OB_DIRECTION_SOUTH:
269 case OB_DIRECTION_NORTHEAST:
270 case OB_DIRECTION_SOUTHWEST:
271 offset = (his_cx < 0) ? -his_cx : his_cx;
272 distance = ((dir == OB_DIRECTION_NORTH ||
273 dir == OB_DIRECTION_NORTHEAST) ?
276 case OB_DIRECTION_EAST:
277 case OB_DIRECTION_WEST:
278 case OB_DIRECTION_SOUTHEAST:
279 case OB_DIRECTION_NORTHWEST:
280 offset = (his_cy < 0) ? -his_cy : his_cy;
281 distance = ((dir == OB_DIRECTION_WEST ||
282 dir == OB_DIRECTION_NORTHWEST) ?
287 /* the target must be in the requested direction */
291 /* Calculate score for this window. The smaller the better. */
292 score = distance + offset;
294 /* windows more than 45 degrees off the direction are
295 * heavily penalized and will only be chosen if nothing
296 * else within a million pixels */
297 if (offset > distance)
300 if (best_score == -1 || score < best_score) {
309 ObClient* focus_directional_cycle(ObDirection dir, gboolean dock_windows,
310 gboolean desktop_windows,
311 gboolean interactive,
312 gboolean showbar, gboolean dialog,
313 gboolean done, gboolean cancel)
315 static ObClient *first = NULL;
317 ObClient *ret = NULL;
320 focus_cycle_target = NULL;
321 focus_cycle_directional = FALSE;
323 } else if (done && interactive)
329 if (focus_cycle_target == NULL) {
330 focus_cycle_iconic_windows = FALSE;
331 focus_cycle_all_desktops = FALSE;
332 focus_cycle_dock_windows = dock_windows;
333 focus_cycle_desktop_windows = desktop_windows;
336 if (!first) first = focus_client;
338 if (focus_cycle_target)
339 ft = focus_find_directional(focus_cycle_target, dir, dock_windows,
342 ft = focus_find_directional(first, dir, dock_windows, desktop_windows);
346 for (it = focus_order; it; it = g_list_next(it))
347 if (focus_valid_target(it->data, TRUE,
348 focus_cycle_iconic_windows,
349 focus_cycle_all_desktops,
350 focus_cycle_dock_windows,
351 focus_cycle_desktop_windows, FALSE))
355 if (ft && ft != focus_cycle_target) {/* prevents flicker */
356 focus_cycle_target = ft;
357 focus_cycle_directional = TRUE;
360 focus_cycle_draw_indicator(showbar ? ft : NULL);
362 if (focus_cycle_target && dialog)
363 /* same arguments as focus_target_valid */
364 focus_cycle_popup_single_show(focus_cycle_target,
365 focus_cycle_iconic_windows,
366 focus_cycle_all_desktops,
367 focus_cycle_dock_windows,
368 focus_cycle_desktop_windows);
369 return focus_cycle_target;
372 if (done && !cancel) ret = focus_cycle_target;
375 focus_cycle_target = NULL;
376 focus_cycle_directional = FALSE;
378 focus_cycle_draw_indicator(NULL);
379 focus_cycle_popup_single_hide();