]> icculus.org git repositories - dana/openbox.git/blob - openbox/focus.c
save the focus_client when falling back, so we don't actually refocus it when we...
[dana/openbox.git] / openbox / focus.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    focus.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 "debug.h"
21 #include "event.h"
22 #include "openbox.h"
23 #include "grab.h"
24 #include "framerender.h"
25 #include "client.h"
26 #include "config.h"
27 #include "frame.h"
28 #include "screen.h"
29 #include "group.h"
30 #include "prop.h"
31 #include "focus.h"
32 #include "stacking.h"
33 #include "popup.h"
34 #include "render/render.h"
35
36 #include <X11/Xlib.h>
37 #include <glib.h>
38 #include <assert.h>
39
40 ObClient *focus_client, *focus_hilite;
41 GList **focus_order; /* these lists are created when screen_startup
42                         sets the number of desktops */
43 ObClient *focus_cycle_target;
44
45 struct {
46     InternalWindow top;
47     InternalWindow left;
48     InternalWindow right;
49     InternalWindow bottom;
50 } focus_indicator;
51
52 RrAppearance *a_focus_indicator;
53 RrColor *color_white;
54
55 static ObIconPopup *focus_cycle_popup;
56
57 static void focus_cycle_destructor(ObClient *client, gpointer data)
58 {
59     /* end cycling if the target disappears. CurrentTime is fine, time won't
60        be used
61     */
62     if (focus_cycle_target == client)
63         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime);
64 }
65
66 static Window createWindow(Window parent, gulong mask,
67                            XSetWindowAttributes *attrib)
68 {
69     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
70                          RrDepth(ob_rr_inst), InputOutput,
71                          RrVisual(ob_rr_inst), mask, attrib);
72                        
73 }
74
75 void focus_startup(gboolean reconfig)
76 {
77     focus_cycle_popup = icon_popup_new(TRUE);
78
79     if (!reconfig) {
80         XSetWindowAttributes attr;
81
82         client_add_destructor(focus_cycle_destructor, NULL);
83
84         /* start with nothing focused */
85         focus_set_client(NULL);
86
87         focus_indicator.top.obwin.type = Window_Internal;
88         focus_indicator.left.obwin.type = Window_Internal;
89         focus_indicator.right.obwin.type = Window_Internal;
90         focus_indicator.bottom.obwin.type = Window_Internal;
91
92         attr.override_redirect = True;
93         attr.background_pixel = BlackPixel(ob_display, ob_screen);
94         focus_indicator.top.win =
95             createWindow(RootWindow(ob_display, ob_screen),
96                          CWOverrideRedirect | CWBackPixel, &attr);
97         focus_indicator.left.win =
98             createWindow(RootWindow(ob_display, ob_screen),
99                          CWOverrideRedirect | CWBackPixel, &attr);
100         focus_indicator.right.win =
101             createWindow(RootWindow(ob_display, ob_screen),
102                          CWOverrideRedirect | CWBackPixel, &attr);
103         focus_indicator.bottom.win =
104             createWindow(RootWindow(ob_display, ob_screen),
105                          CWOverrideRedirect | CWBackPixel, &attr);
106
107         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
108         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
109         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
110         stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
111
112         color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
113
114         a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
115         a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
116         a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
117         a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
118                                                         0, 0, 0);
119         a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
120         a_focus_indicator->texture[0].data.lineart.color = color_white;
121         a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
122         a_focus_indicator->texture[1].data.lineart.color = color_white;
123         a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
124         a_focus_indicator->texture[2].data.lineart.color = color_white;
125         a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
126         a_focus_indicator->texture[3].data.lineart.color = color_white;
127     }
128 }
129
130 void focus_shutdown(gboolean reconfig)
131 {
132     guint i;
133
134     icon_popup_free(focus_cycle_popup);
135
136     if (!reconfig) {
137         client_remove_destructor(focus_cycle_destructor);
138
139         for (i = 0; i < screen_num_desktops; ++i)
140             g_list_free(focus_order[i]);
141         g_free(focus_order);
142
143         /* reset focus to root */
144         XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
145
146         RrColorFree(color_white);
147
148         RrAppearanceFree(a_focus_indicator);
149
150         XDestroyWindow(ob_display, focus_indicator.top.win);
151         XDestroyWindow(ob_display, focus_indicator.left.win);
152         XDestroyWindow(ob_display, focus_indicator.right.win);
153         XDestroyWindow(ob_display, focus_indicator.bottom.win);
154     }
155 }
156
157 static void push_to_top(ObClient *client)
158 {
159     guint desktop;
160
161     desktop = client->desktop;
162     if (desktop == DESKTOP_ALL) desktop = screen_desktop;
163     focus_order[desktop] = g_list_remove(focus_order[desktop], client);
164     focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
165 }
166
167 void focus_set_client(ObClient *client)
168 {
169     Window active;
170     ObClient *old;
171
172 #ifdef DEBUG_FOCUS
173     ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
174 #endif
175
176     /* uninstall the old colormap, and install the new one */
177     screen_install_colormap(focus_client, FALSE);
178     screen_install_colormap(client, TRUE);
179
180     if (client == NULL) {
181 #ifdef DEBUG_FOCUS
182         ob_debug("actively focusing NONWINDOW\n");
183 #endif
184         /* when nothing will be focused, send focus to the backup target */
185         XSetInputFocus(ob_display, screen_support_win, RevertToNone,
186                        event_curtime);
187         XSync(ob_display, FALSE);
188     }
189
190     /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
191        be used.
192     */
193     if (focus_cycle_target)
194         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime);
195
196     old = focus_client;
197     focus_client = client;
198
199     /* move to the top of the list */
200     if (client != NULL)
201         push_to_top(client);
202
203     /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
204     if (ob_state() != OB_STATE_EXITING) {
205         active = client ? client->window : None;
206         PROP_SET32(RootWindow(ob_display, ob_screen),
207                    net_active_window, window, active);
208
209         /* remove hiliting from the window when it gets focused */
210         if (client != NULL)
211             client_hilite(client, FALSE);
212     }
213 }
214
215 /* finds the first transient that isn't 'skip' and ensure's that client_normal
216  is true for it */
217 static ObClient *find_transient_recursive(ObClient *c, ObClient *top,
218                                           ObClient *skip)
219 {
220     GSList *it;
221     ObClient *ret;
222
223     for (it = c->transients; it; it = g_slist_next(it)) {
224         if (it->data == top) return NULL;
225         ret = find_transient_recursive(it->data, top, skip);
226         if (ret && ret != skip && client_normal(ret) &&
227             client_can_focus(ret) && client_validate(ret))
228             return ret;
229         if (it->data != skip && client_normal(it->data) &&
230             client_can_focus(it->data) && client_validate(it->data))
231             return it->data;
232     }
233     return NULL;
234 }
235
236 static ObClient* focus_fallback_transient(ObClient *top, ObClient *old)
237 {
238     ObClient *target = find_transient_recursive(top, top, old);
239     if (!target) {
240         /* make sure client_normal is true always */
241         if (!client_normal(top))
242             return NULL;
243         target = top; /* no transient, keep the top */
244     }
245     if (client_can_focus(target))
246         return target;
247     else
248         return NULL;
249 }
250
251 ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
252 {
253     GList *it;
254     ObClient *target = NULL;
255
256     if (!allow_refocus && old && old->transient_for) {
257         gboolean trans = FALSE;
258
259         if (!config_focus_follow || config_focus_last)
260             trans = TRUE;
261         else if ((target = client_under_pointer()) &&
262                  client_search_transient
263                  (client_search_top_parent(target), old))
264             trans = TRUE;
265
266         /* try for transient relations */
267         if (trans) {
268             if (old->transient_for == OB_TRAN_GROUP) {
269                 for (it = focus_order[screen_desktop]; it;
270                      it = g_list_next(it))
271                 {
272                     GSList *sit;
273
274                     for (sit = old->group->members; sit;
275                          sit = g_slist_next(sit))
276                     {
277                         if (sit->data == it->data)
278                             if ((target =
279                                  focus_fallback_transient(sit->data, old)))
280                             {
281                                 ob_debug("found in transient #1\n");
282                                 return target;
283                             }
284                     }
285                 }
286             } else {
287                 if ((target =
288                      focus_fallback_transient(old->transient_for, old)))
289                 {
290                     ob_debug("found in transient #2\n");
291                     return target;
292                 }
293             }
294         }
295     }
296
297     ob_debug("trying pointer stuff\n");
298     if (config_focus_follow && !config_focus_last)
299     {
300         if ((target = client_under_pointer()))
301             if (allow_refocus || target != old)
302                 if (client_normal(target) && client_can_focus(target) &&
303                     client_validate(target)) {
304                     ob_debug("found in pointer stuff\n");
305                     return target;
306                 }
307     }
308
309 #if 0
310         /* try for group relations */
311         if (old->group) {
312             GSList *sit;
313
314             for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
315                 for (sit = old->group->members; sit; sit = g_slist_next(sit))
316                     if (sit->data == it->data)
317                         if (sit->data != old && client_normal(sit->data))
318                             if (client_can_focus(sit->data))
319                                 return sit->data;
320         }
321 #endif
322
323     ob_debug("trying  the focus order\n");
324     for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
325         if (allow_refocus || it->data != old)
326             if (client_normal(it->data) && client_can_focus(it->data) &&
327                 client_validate(it->data))
328             {
329                 ob_debug("found in focus order\n");
330                 return it->data;
331             }
332
333     /* XXX fallback to the "desktop window" if one exists ?
334        could store it while going through all the windows in the loop right
335        above this..
336     */
337
338     return NULL;
339 }
340
341 void focus_fallback(gboolean allow_refocus)
342 {
343     ObClient *new;
344     ObClient *old;
345
346     /* save this before moving focus away to nothing */
347     old = focus_client;
348
349     /* unfocus any focused clients.. they can be focused by Pointer events
350        and such, and then when I try focus them, I won't get a FocusIn event
351        at all for them.
352     */
353     focus_set_client(NULL);
354
355     if ((new = focus_fallback_target(allow_refocus, old)))
356         client_focus(new);
357 }
358
359 static void popup_cycle(ObClient *c, gboolean show)
360 {
361     if (!show) {
362         icon_popup_hide(focus_cycle_popup);
363     } else {
364         Rect *a;
365         ObClient *p = c;
366         gchar *title = NULL;
367
368         a = screen_physical_area_monitor(0);
369         icon_popup_position(focus_cycle_popup, CenterGravity,
370                             a->x + a->width / 2, a->y + a->height / 2);
371 /*        icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
372         icon_popup_show(focus_cycle_popup, c->title,
373                         client_icon(c, a->height/16, a->height/16));
374 */
375         /* XXX the size and the font extents need to be related on some level
376          */
377         icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
378
379         /* use the transient's parent's title/icon */
380         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
381             p = p->transient_for;
382
383         if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
384             title = g_strdup(p->iconic ? p->icon_title : p->title);
385             /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
386                                 " - ",
387                                 (p->iconic ? p->icon_title : p->title),
388                                 NULL);
389             */
390         icon_popup_show(focus_cycle_popup,
391                         (title ? title :
392                          (c->iconic ? c->icon_title : c->title)),
393                         client_icon(p, 48, 48));
394         g_free(title);
395     }
396 }
397
398 void focus_cycle_draw_indicator()
399 {
400     if (!focus_cycle_target) {
401         XUnmapWindow(ob_display, focus_indicator.top.win);
402         XUnmapWindow(ob_display, focus_indicator.left.win);
403         XUnmapWindow(ob_display, focus_indicator.right.win);
404         XUnmapWindow(ob_display, focus_indicator.bottom.win);
405     } else {
406         /*
407           if (focus_cycle_target)
408               frame_adjust_focus(focus_cycle_target->frame, FALSE);
409           frame_adjust_focus(focus_cycle_target->frame, TRUE);
410         */
411         gint x, y, w, h;
412         gint wt, wl, wr, wb;
413
414         wt = wl = wr = wb = MAX(3,
415                                 MAX(1, MAX(ob_rr_theme->paddingx,
416                                            ob_rr_theme->paddingy)) * 2 +
417                                 ob_rr_theme->fbwidth * 2);
418
419         x = focus_cycle_target->frame->area.x;
420         y = focus_cycle_target->frame->area.y;
421         w = focus_cycle_target->frame->area.width;
422         h = wt;
423
424         XMoveResizeWindow(ob_display, focus_indicator.top.win,
425                           x, y, w, h);
426         a_focus_indicator->texture[0].data.lineart.x1 = 0;
427         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
428         a_focus_indicator->texture[0].data.lineart.x2 = 0;
429         a_focus_indicator->texture[0].data.lineart.y2 = 0;
430         a_focus_indicator->texture[1].data.lineart.x1 = 0;
431         a_focus_indicator->texture[1].data.lineart.y1 = 0;
432         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
433         a_focus_indicator->texture[1].data.lineart.y2 = 0;
434         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
435         a_focus_indicator->texture[2].data.lineart.y1 = 0;
436         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
437         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
438         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
439         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
440         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
441         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
442         RrPaint(a_focus_indicator, focus_indicator.top.win,
443                 w, h);
444
445         x = focus_cycle_target->frame->area.x;
446         y = focus_cycle_target->frame->area.y;
447         w = wl;
448         h = focus_cycle_target->frame->area.height;
449
450         XMoveResizeWindow(ob_display, focus_indicator.left.win,
451                           x, y, w, h);
452         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
453         a_focus_indicator->texture[0].data.lineart.y1 = 0;
454         a_focus_indicator->texture[0].data.lineart.x2 = 0;
455         a_focus_indicator->texture[0].data.lineart.y2 = 0;
456         a_focus_indicator->texture[1].data.lineart.x1 = 0;
457         a_focus_indicator->texture[1].data.lineart.y1 = 0;
458         a_focus_indicator->texture[1].data.lineart.x2 = 0;
459         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
460         a_focus_indicator->texture[2].data.lineart.x1 = 0;
461         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
462         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
463         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
464         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
465         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
466         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
467         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
468         RrPaint(a_focus_indicator, focus_indicator.left.win,
469                 w, h);
470
471         x = focus_cycle_target->frame->area.x +
472             focus_cycle_target->frame->area.width - wr;
473         y = focus_cycle_target->frame->area.y;
474         w = wr;
475         h = focus_cycle_target->frame->area.height ;
476
477         XMoveResizeWindow(ob_display, focus_indicator.right.win,
478                           x, y, w, h);
479         a_focus_indicator->texture[0].data.lineart.x1 = 0;
480         a_focus_indicator->texture[0].data.lineart.y1 = 0;
481         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
482         a_focus_indicator->texture[0].data.lineart.y2 = 0;
483         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
484         a_focus_indicator->texture[1].data.lineart.y1 = 0;
485         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
486         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
487         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
488         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
489         a_focus_indicator->texture[2].data.lineart.x2 = 0;
490         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
491         a_focus_indicator->texture[3].data.lineart.x1 = 0;
492         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
493         a_focus_indicator->texture[3].data.lineart.x2 = 0;
494         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
495         RrPaint(a_focus_indicator, focus_indicator.right.win,
496                 w, h);
497
498         x = focus_cycle_target->frame->area.x;
499         y = focus_cycle_target->frame->area.y +
500             focus_cycle_target->frame->area.height - wb;
501         w = focus_cycle_target->frame->area.width;
502         h = wb;
503
504         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
505                           x, y, w, h);
506         a_focus_indicator->texture[0].data.lineart.x1 = 0;
507         a_focus_indicator->texture[0].data.lineart.y1 = 0;
508         a_focus_indicator->texture[0].data.lineart.x2 = 0;
509         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
510         a_focus_indicator->texture[1].data.lineart.x1 = 0;
511         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
512         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
513         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
514         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
515         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
516         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
517         a_focus_indicator->texture[2].data.lineart.y2 = 0;
518         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
519         a_focus_indicator->texture[3].data.lineart.y1 = 0;
520         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
521         a_focus_indicator->texture[3].data.lineart.y2 = 0;
522         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
523                 w, h);
524
525         XMapWindow(ob_display, focus_indicator.top.win);
526         XMapWindow(ob_display, focus_indicator.left.win);
527         XMapWindow(ob_display, focus_indicator.right.win);
528         XMapWindow(ob_display, focus_indicator.bottom.win);
529     }
530 }
531
532 static gboolean valid_focus_target(ObClient *ft)
533 {
534     /* we don't use client_can_focus here, because that doesn't let you
535        focus an iconic window, but we want to be able to, so we just check
536        if the focus flags on the window allow it, and its on the current
537        desktop */
538     if ((ft->type == OB_CLIENT_TYPE_NORMAL ||
539          ft->type == OB_CLIENT_TYPE_DIALOG ||
540          (!client_has_group_siblings(ft) &&
541           (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
542            ft->type == OB_CLIENT_TYPE_MENU ||
543            ft->type == OB_CLIENT_TYPE_UTILITY))) &&
544         ((ft->can_focus || ft->focus_notify) &&
545          !ft->skip_pager &&
546          (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) &&
547         ft == client_focus_target(ft))
548         return TRUE;
549 /*
550     {
551         GSList *it;
552
553         for (it = ft->transients; it; it = g_slist_next(it)) {
554             ObClient *c = it->data;
555
556             if (c->frame->visible)
557                 return FALSE;
558         }
559         return TRUE;
560     }
561 */
562
563     return FALSE;
564 }
565
566 void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
567                  gboolean dialog, gboolean done, gboolean cancel, Time time)
568 {
569     static ObClient *first = NULL;
570     static ObClient *t = NULL;
571     static GList *order = NULL;
572     GList *it, *start, *list;
573     ObClient *ft = NULL;
574
575     if (interactive) {
576         if (cancel) {
577             focus_cycle_target = NULL;
578             goto done_cycle;
579         } else if (done)
580             goto done_cycle;
581
582         if (!focus_order[screen_desktop])
583             goto done_cycle;
584
585         if (!first) first = focus_client;
586
587         if (linear) list = client_list;
588         else        list = focus_order[screen_desktop];
589     } else {
590         if (!focus_order[screen_desktop])
591             goto done_cycle;
592         list = client_list;
593     }
594     if (!focus_cycle_target) focus_cycle_target = focus_client;
595
596     start = it = g_list_find(list, focus_cycle_target);
597     if (!start) /* switched desktops or something? */
598         start = it = forward ? g_list_last(list) : g_list_first(list);
599     if (!start) goto done_cycle;
600
601     do {
602         if (forward) {
603             it = it->next;
604             if (it == NULL) it = g_list_first(list);
605         } else {
606             it = it->prev;
607             if (it == NULL) it = g_list_last(list);
608         }
609         ft = it->data;
610         if (valid_focus_target(ft)) {
611             if (interactive) {
612                 if (ft != focus_cycle_target) { /* prevents flicker */
613                     focus_cycle_target = ft;
614                     focus_cycle_draw_indicator();
615                 }
616                 popup_cycle(ft, dialog);
617                 return;
618             } else if (ft != focus_cycle_target) {
619                 focus_cycle_target = ft;
620                 done = TRUE;
621                 break;
622             }
623         }
624     } while (it != start);
625
626 done_cycle:
627     if (done && focus_cycle_target)
628         client_activate(focus_cycle_target, FALSE, TRUE, time);
629
630     t = NULL;
631     first = NULL;
632     focus_cycle_target = NULL;
633     g_list_free(order);
634     order = NULL;
635
636     if (interactive) {
637         focus_cycle_draw_indicator();
638         popup_cycle(ft, FALSE);
639     }
640
641     return;
642 }
643
644 void focus_directional_cycle(ObDirection dir, gboolean interactive,
645                              gboolean dialog, gboolean done, gboolean cancel,
646                              Time time)
647 {
648     static ObClient *first = NULL;
649     ObClient *ft = NULL;
650
651     if (!interactive)
652         return;
653
654     if (cancel) {
655         focus_cycle_target = NULL;
656         goto done_cycle;
657     } else if (done)
658         goto done_cycle;
659
660     if (!focus_order[screen_desktop])
661         goto done_cycle;
662
663     if (!first) first = focus_client;
664     if (!focus_cycle_target) focus_cycle_target = focus_client;
665
666     if (focus_cycle_target)
667         ft = client_find_directional(focus_cycle_target, dir);
668     else {
669         GList *it;
670
671         for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
672             if (valid_focus_target(it->data))
673                 ft = it->data;
674     }
675         
676     if (ft) {
677         if (ft != focus_cycle_target) {/* prevents flicker */
678             focus_cycle_target = ft;
679             focus_cycle_draw_indicator();
680         }
681     }
682     if (focus_cycle_target) {
683         popup_cycle(focus_cycle_target, dialog);
684         if (dialog)
685             return;
686     }
687
688
689 done_cycle:
690     if (done && focus_cycle_target)
691         client_activate(focus_cycle_target, FALSE, TRUE, time);
692
693     first = NULL;
694     focus_cycle_target = NULL;
695
696     focus_cycle_draw_indicator();
697     popup_cycle(ft, FALSE);
698
699     return;
700 }
701
702 void focus_order_add_new(ObClient *c)
703 {
704     guint d, i;
705
706     if (c->iconic)
707         focus_order_to_top(c);
708     else {
709         d = c->desktop;
710         if (d == DESKTOP_ALL) {
711             for (i = 0; i < screen_num_desktops; ++i) {
712                 g_assert(!g_list_find(focus_order[i], c));
713                 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
714                     focus_order[i] = g_list_insert(focus_order[i], c, 0);
715                 else
716                     focus_order[i] = g_list_insert(focus_order[i], c, 1);
717             }
718         } else {
719             g_assert(!g_list_find(focus_order[d], c));
720             if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
721                 focus_order[d] = g_list_insert(focus_order[d], c, 0);
722             else
723                 focus_order[d] = g_list_insert(focus_order[d], c, 1);
724         }
725     }
726 }
727
728 void focus_order_remove(ObClient *c)
729 {
730     guint d, i;
731
732     d = c->desktop;
733     if (d == DESKTOP_ALL) {
734         for (i = 0; i < screen_num_desktops; ++i)
735             focus_order[i] = g_list_remove(focus_order[i], c);
736     } else
737         focus_order[d] = g_list_remove(focus_order[d], c);
738 }
739
740 static void to_top(ObClient *c, guint d)
741 {
742     focus_order[d] = g_list_remove(focus_order[d], c);
743     if (!c->iconic) {
744         focus_order[d] = g_list_prepend(focus_order[d], c);
745     } else {
746         GList *it;
747
748         /* insert before first iconic window */
749         for (it = focus_order[d];
750              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
751         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
752     }
753 }
754
755 void focus_order_to_top(ObClient *c)
756 {
757     guint d, i;
758
759     d = c->desktop;
760     if (d == DESKTOP_ALL) {
761         for (i = 0; i < screen_num_desktops; ++i)
762             to_top(c, i);
763     } else
764         to_top(c, d);
765 }
766
767 static void to_bottom(ObClient *c, guint d)
768 {
769     focus_order[d] = g_list_remove(focus_order[d], c);
770     if (c->iconic) {
771         focus_order[d] = g_list_append(focus_order[d], c);
772     } else {
773         GList *it;
774
775         /* insert before first iconic window */
776         for (it = focus_order[d];
777              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
778         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
779     }
780 }
781
782 void focus_order_to_bottom(ObClient *c)
783 {
784     guint d, i;
785
786     d = c->desktop;
787     if (d == DESKTOP_ALL) {
788         for (i = 0; i < screen_num_desktops; ++i)
789             to_bottom(c, i);
790     } else
791         to_bottom(c, d);
792 }