]> Dogcows Code - chaz/openbox/blob - openbox/focus_cycle.c
Redraw the focus cycle popup when the list of focusable windows changes, rather than...
[chaz/openbox] / openbox / focus_cycle.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 focus_cycle.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "focus_cycle.h"
21 #include "focus_cycle_indicator.h"
22 #include "focus_cycle_popup.h"
23 #include "client.h"
24 #include "frame.h"
25 #include "focus.h"
26 #include "screen.h"
27 #include "openbox.h"
28 #include "debug.h"
29
30 #include <X11/Xlib.h>
31 #include <glib.h>
32
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;
39
40 static ObClient *focus_find_directional(ObClient *c,
41 ObDirection dir,
42 gboolean dock_windows,
43 gboolean desktop_windows);
44
45 void focus_cycle_startup(gboolean reconfig)
46 {
47 if (reconfig) return;
48 }
49
50 void focus_cycle_shutdown(gboolean reconfig)
51 {
52 if (reconfig) return;
53 }
54
55 void focus_cycle_add(ObClient *ifclient)
56 {
57 if (!(focus_cycle_target && ifclient && !focus_cycle_directional))
58 return;
59
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,
65 FALSE))
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);
71 }
72
73 void focus_cycle_remove(ObClient *ifclient)
74 {
75 if (!(focus_cycle_target && ifclient))
76 return;
77
78 if (focus_cycle_directional) {
79 if (focus_cycle_target == ifclient) {
80 focus_directional_cycle(0, TRUE, TRUE, TRUE, TRUE,
81 TRUE, TRUE, TRUE);
82 }
83 }
84 else {
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,
90 FALSE)) {
91 if (focus_cycle_target == ifclient) {
92 focus_cycle_target =
93 focus_cycle_popup_revert(focus_cycle_target);
94 focus_cycle_update_indicator(focus_cycle_target);
95 }
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);
101 }
102 }
103 }
104
105 void focus_cycle_reorder()
106 {
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);
113 }
114
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)
120 {
121 static GList *order = NULL;
122 GList *it, *start, *list;
123 ObClient *ft = NULL;
124 ObClient *ret = NULL;
125
126 if (interactive) {
127 if (cancel) {
128 focus_cycle_target = NULL;
129 focus_cycle_directional = FALSE;
130 goto done_cycle;
131 } else if (done)
132 goto done_cycle;
133
134 if (!focus_order)
135 goto done_cycle;
136
137 if (linear) list = client_list;
138 else list = focus_order;
139 } else {
140 if (!focus_order)
141 goto done_cycle;
142 list = client_list;
143 }
144
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);
151 } else
152 start = it = g_list_find(list, focus_cycle_target);
153
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;
157
158 do {
159 if (forward) {
160 it = it->next;
161 if (it == NULL) it = g_list_first(list);
162 } else {
163 it = it->prev;
164 if (it == NULL) it = g_list_last(list);
165 }
166 ft = it->data;
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,
172 FALSE))
173 {
174 if (interactive) {
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);
179 }
180 if (dialog)
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;
191 done = TRUE;
192 break;
193 }
194 }
195 } while (it != start);
196
197 done_cycle:
198 if (done && !cancel) ret = focus_cycle_target;
199
200 focus_cycle_target = NULL;
201 focus_cycle_directional = FALSE;
202 g_list_free(order);
203 order = NULL;
204
205 if (interactive) {
206 focus_cycle_draw_indicator(NULL);
207 focus_cycle_popup_hide();
208 }
209
210 return ret;
211 }
212
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)
217 {
218 gint my_cx, my_cy, his_cx, his_cy;
219 gint offset = 0;
220 gint distance = 0;
221 gint score, best_score;
222 ObClient *best_client, *cur;
223 GList *it;
224
225 if (!client_list)
226 return NULL;
227
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;
231
232 best_score = -1;
233 best_client = c;
234
235 for (it = g_list_first(client_list); it; it = g_list_next(it)) {
236 cur = it->data;
237
238 /* the currently selected window isn't interesting */
239 if (cur == c)
240 continue;
241 if (!focus_valid_target(it->data, TRUE, FALSE, FALSE, dock_windows,
242 desktop_windows, FALSE))
243 continue;
244
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;
251
252 if (dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
253 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST)
254 {
255 gint tx;
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;
263 his_cx = tx;
264 }
265
266 switch (dir) {
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) ?
274 -his_cy : his_cy);
275 break;
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) ?
283 -his_cx : his_cx);
284 break;
285 }
286
287 /* the target must be in the requested direction */
288 if (distance <= 0)
289 continue;
290
291 /* Calculate score for this window. The smaller the better. */
292 score = distance + offset;
293
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)
298 score += 1000000;
299
300 if (best_score == -1 || score < best_score) {
301 best_client = cur;
302 best_score = score;
303 }
304 }
305
306 return best_client;
307 }
308
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)
314 {
315 static ObClient *first = NULL;
316 ObClient *ft = NULL;
317 ObClient *ret = NULL;
318
319 if (cancel) {
320 focus_cycle_target = NULL;
321 focus_cycle_directional = FALSE;
322 goto done_cycle;
323 } else if (done && interactive)
324 goto done_cycle;
325
326 if (!focus_order)
327 goto done_cycle;
328
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;
334 }
335
336 if (!first) first = focus_client;
337
338 if (focus_cycle_target)
339 ft = focus_find_directional(focus_cycle_target, dir, dock_windows,
340 desktop_windows);
341 else if (first)
342 ft = focus_find_directional(first, dir, dock_windows, desktop_windows);
343 else {
344 GList *it;
345
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))
352 ft = it->data;
353 }
354
355 if (ft && ft != focus_cycle_target) {/* prevents flicker */
356 focus_cycle_target = ft;
357 focus_cycle_directional = TRUE;
358 if (!interactive)
359 goto done_cycle;
360 focus_cycle_draw_indicator(showbar ? ft : NULL);
361 }
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;
370
371 done_cycle:
372 if (done && !cancel) ret = focus_cycle_target;
373
374 first = NULL;
375 focus_cycle_target = NULL;
376 focus_cycle_directional = FALSE;
377
378 focus_cycle_draw_indicator(NULL);
379 focus_cycle_popup_single_hide();
380
381 return ret;
382 }
This page took 0.054014 seconds and 5 git commands to generate.