]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/focus.c
oh, 2 things in this commit..
[mikachu/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)
252 {
253     GList *it;
254     ObClient *old;
255     ObClient *target = NULL;
256
257     old = focus_client;
258
259     if (!allow_refocus && old && old->transient_for) {
260         gboolean trans = FALSE;
261
262         if (!config_focus_follow || config_focus_last)
263             trans = TRUE;
264         else if ((target = client_under_pointer()) &&
265                  client_search_transient
266                  (client_search_top_parent(target), old))
267             trans = TRUE;
268
269         /* try for transient relations */
270         if (trans) {
271             if (old->transient_for == OB_TRAN_GROUP) {
272                 for (it = focus_order[screen_desktop]; it;
273                      it = g_list_next(it))
274                 {
275                     GSList *sit;
276
277                     for (sit = old->group->members; sit;
278                          sit = g_slist_next(sit))
279                     {
280                         if (sit->data == it->data)
281                             if ((target =
282                                  focus_fallback_transient(sit->data, old)))
283                             {
284                                 ob_debug("found in transient #1\n");
285                                 return target;
286                             }
287                     }
288                 }
289             } else {
290                 if ((target =
291                      focus_fallback_transient(old->transient_for, old)))
292                 {
293                     ob_debug("found in transient #2\n");
294                     return target;
295                 }
296             }
297         }
298     }
299
300     ob_debug("trying pointer stuff\n");
301     if (config_focus_follow && !config_focus_last)
302     {
303         if ((target = client_under_pointer()))
304             if (allow_refocus || target != old)
305                 if (client_normal(target) && client_can_focus(target) &&
306                     client_validate(target)) {
307                     ob_debug("found in pointer stuff\n");
308                     return target;
309                 }
310     }
311
312 #if 0
313         /* try for group relations */
314         if (old->group) {
315             GSList *sit;
316
317             for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
318                 for (sit = old->group->members; sit; sit = g_slist_next(sit))
319                     if (sit->data == it->data)
320                         if (sit->data != old && client_normal(sit->data))
321                             if (client_can_focus(sit->data))
322                                 return sit->data;
323         }
324 #endif
325
326     ob_debug("trying  the focus order\n");
327     for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
328         if (allow_refocus || it->data != old)
329             if (client_normal(it->data) && client_can_focus(it->data) &&
330                 client_validate(it->data))
331             {
332                 ob_debug("found in focus order\n");
333                 return it->data;
334             }
335
336     /* XXX fallback to the "desktop window" if one exists ?
337        could store it while going through all the windows in the loop right
338        above this..
339     */
340
341     return NULL;
342 }
343
344 void focus_fallback(gboolean allow_refocus)
345 {
346     ObClient *new;
347
348     /* unfocus any focused clients.. they can be focused by Pointer events
349        and such, and then when I try focus them, I won't get a FocusIn event
350        at all for them.
351     */
352     focus_set_client(NULL);
353
354     if ((new = focus_fallback_target(allow_refocus)))
355         client_focus(new);
356 }
357
358 static void popup_cycle(ObClient *c, gboolean show)
359 {
360     if (!show) {
361         icon_popup_hide(focus_cycle_popup);
362     } else {
363         Rect *a;
364         ObClient *p = c;
365         gchar *title = NULL;
366
367         a = screen_physical_area_monitor(0);
368         icon_popup_position(focus_cycle_popup, CenterGravity,
369                             a->x + a->width / 2, a->y + a->height / 2);
370 /*        icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
371         icon_popup_show(focus_cycle_popup, c->title,
372                         client_icon(c, a->height/16, a->height/16));
373 */
374         /* XXX the size and the font extents need to be related on some level
375          */
376         icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
377
378         /* use the transient's parent's title/icon */
379         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
380             p = p->transient_for;
381
382         if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
383             title = g_strdup(p->iconic ? p->icon_title : p->title);
384             /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
385                                 " - ",
386                                 (p->iconic ? p->icon_title : p->title),
387                                 NULL);
388             */
389         icon_popup_show(focus_cycle_popup,
390                         (title ? title :
391                          (c->iconic ? c->icon_title : c->title)),
392                         client_icon(p, 48, 48));
393         g_free(title);
394     }
395 }
396
397 void focus_cycle_draw_indicator()
398 {
399     if (!focus_cycle_target) {
400         XUnmapWindow(ob_display, focus_indicator.top.win);
401         XUnmapWindow(ob_display, focus_indicator.left.win);
402         XUnmapWindow(ob_display, focus_indicator.right.win);
403         XUnmapWindow(ob_display, focus_indicator.bottom.win);
404     } else {
405         /*
406           if (focus_cycle_target)
407               frame_adjust_focus(focus_cycle_target->frame, FALSE);
408           frame_adjust_focus(focus_cycle_target->frame, TRUE);
409         */
410         gint x, y, w, h;
411         gint wt, wl, wr, wb;
412
413         wt = wl = wr = wb = MAX(3,
414                                 MAX(1, MAX(ob_rr_theme->paddingx,
415                                            ob_rr_theme->paddingy)) * 2 +
416                                 ob_rr_theme->fbwidth * 2);
417
418         x = focus_cycle_target->frame->area.x;
419         y = focus_cycle_target->frame->area.y;
420         w = focus_cycle_target->frame->area.width;
421         h = wt;
422
423         XMoveResizeWindow(ob_display, focus_indicator.top.win,
424                           x, y, w, h);
425         a_focus_indicator->texture[0].data.lineart.x1 = 0;
426         a_focus_indicator->texture[0].data.lineart.y1 = h-1;
427         a_focus_indicator->texture[0].data.lineart.x2 = 0;
428         a_focus_indicator->texture[0].data.lineart.y2 = 0;
429         a_focus_indicator->texture[1].data.lineart.x1 = 0;
430         a_focus_indicator->texture[1].data.lineart.y1 = 0;
431         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
432         a_focus_indicator->texture[1].data.lineart.y2 = 0;
433         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
434         a_focus_indicator->texture[2].data.lineart.y1 = 0;
435         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
436         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
437         a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
438         a_focus_indicator->texture[3].data.lineart.y1 = h-1;
439         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
440         a_focus_indicator->texture[3].data.lineart.y2 = h-1;
441         RrPaint(a_focus_indicator, focus_indicator.top.win,
442                 w, h);
443
444         x = focus_cycle_target->frame->area.x;
445         y = focus_cycle_target->frame->area.y;
446         w = wl;
447         h = focus_cycle_target->frame->area.height;
448
449         XMoveResizeWindow(ob_display, focus_indicator.left.win,
450                           x, y, w, h);
451         a_focus_indicator->texture[0].data.lineart.x1 = w-1;
452         a_focus_indicator->texture[0].data.lineart.y1 = 0;
453         a_focus_indicator->texture[0].data.lineart.x2 = 0;
454         a_focus_indicator->texture[0].data.lineart.y2 = 0;
455         a_focus_indicator->texture[1].data.lineart.x1 = 0;
456         a_focus_indicator->texture[1].data.lineart.y1 = 0;
457         a_focus_indicator->texture[1].data.lineart.x2 = 0;
458         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
459         a_focus_indicator->texture[2].data.lineart.x1 = 0;
460         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
461         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
462         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
463         a_focus_indicator->texture[3].data.lineart.x1 = w-1;
464         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
465         a_focus_indicator->texture[3].data.lineart.x2 = w-1;
466         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
467         RrPaint(a_focus_indicator, focus_indicator.left.win,
468                 w, h);
469
470         x = focus_cycle_target->frame->area.x +
471             focus_cycle_target->frame->area.width - wr;
472         y = focus_cycle_target->frame->area.y;
473         w = wr;
474         h = focus_cycle_target->frame->area.height ;
475
476         XMoveResizeWindow(ob_display, focus_indicator.right.win,
477                           x, y, w, h);
478         a_focus_indicator->texture[0].data.lineart.x1 = 0;
479         a_focus_indicator->texture[0].data.lineart.y1 = 0;
480         a_focus_indicator->texture[0].data.lineart.x2 = w-1;
481         a_focus_indicator->texture[0].data.lineart.y2 = 0;
482         a_focus_indicator->texture[1].data.lineart.x1 = w-1;
483         a_focus_indicator->texture[1].data.lineart.y1 = 0;
484         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
485         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
486         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
487         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
488         a_focus_indicator->texture[2].data.lineart.x2 = 0;
489         a_focus_indicator->texture[2].data.lineart.y2 = h-1;
490         a_focus_indicator->texture[3].data.lineart.x1 = 0;
491         a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
492         a_focus_indicator->texture[3].data.lineart.x2 = 0;
493         a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
494         RrPaint(a_focus_indicator, focus_indicator.right.win,
495                 w, h);
496
497         x = focus_cycle_target->frame->area.x;
498         y = focus_cycle_target->frame->area.y +
499             focus_cycle_target->frame->area.height - wb;
500         w = focus_cycle_target->frame->area.width;
501         h = wb;
502
503         XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
504                           x, y, w, h);
505         a_focus_indicator->texture[0].data.lineart.x1 = 0;
506         a_focus_indicator->texture[0].data.lineart.y1 = 0;
507         a_focus_indicator->texture[0].data.lineart.x2 = 0;
508         a_focus_indicator->texture[0].data.lineart.y2 = h-1;
509         a_focus_indicator->texture[1].data.lineart.x1 = 0;
510         a_focus_indicator->texture[1].data.lineart.y1 = h-1;
511         a_focus_indicator->texture[1].data.lineart.x2 = w-1;
512         a_focus_indicator->texture[1].data.lineart.y2 = h-1;
513         a_focus_indicator->texture[2].data.lineart.x1 = w-1;
514         a_focus_indicator->texture[2].data.lineart.y1 = h-1;
515         a_focus_indicator->texture[2].data.lineart.x2 = w-1;
516         a_focus_indicator->texture[2].data.lineart.y2 = 0;
517         a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
518         a_focus_indicator->texture[3].data.lineart.y1 = 0;
519         a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
520         a_focus_indicator->texture[3].data.lineart.y2 = 0;
521         RrPaint(a_focus_indicator, focus_indicator.bottom.win,
522                 w, h);
523
524         XMapWindow(ob_display, focus_indicator.top.win);
525         XMapWindow(ob_display, focus_indicator.left.win);
526         XMapWindow(ob_display, focus_indicator.right.win);
527         XMapWindow(ob_display, focus_indicator.bottom.win);
528     }
529 }
530
531 static gboolean valid_focus_target(ObClient *ft)
532 {
533     /* we don't use client_can_focus here, because that doesn't let you
534        focus an iconic window, but we want to be able to, so we just check
535        if the focus flags on the window allow it, and its on the current
536        desktop */
537     if ((ft->type == OB_CLIENT_TYPE_NORMAL ||
538          ft->type == OB_CLIENT_TYPE_DIALOG ||
539          (!client_has_group_siblings(ft) &&
540           (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
541            ft->type == OB_CLIENT_TYPE_MENU ||
542            ft->type == OB_CLIENT_TYPE_UTILITY))) &&
543         ((ft->can_focus || ft->focus_notify) &&
544          !ft->skip_pager &&
545          (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) &&
546         ft == client_focus_target(ft))
547         return TRUE;
548 /*
549     {
550         GSList *it;
551
552         for (it = ft->transients; it; it = g_slist_next(it)) {
553             ObClient *c = it->data;
554
555             if (c->frame->visible)
556                 return FALSE;
557         }
558         return TRUE;
559     }
560 */
561
562     return FALSE;
563 }
564
565 void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
566                  gboolean dialog, gboolean done, gboolean cancel, Time time)
567 {
568     static ObClient *first = NULL;
569     static ObClient *t = NULL;
570     static GList *order = NULL;
571     GList *it, *start, *list;
572     ObClient *ft = NULL;
573
574     if (interactive) {
575         if (cancel) {
576             focus_cycle_target = NULL;
577             goto done_cycle;
578         } else if (done)
579             goto done_cycle;
580
581         if (!focus_order[screen_desktop])
582             goto done_cycle;
583
584         if (!first) first = focus_client;
585
586         if (linear) list = client_list;
587         else        list = focus_order[screen_desktop];
588     } else {
589         if (!focus_order[screen_desktop])
590             goto done_cycle;
591         list = client_list;
592     }
593     if (!focus_cycle_target) focus_cycle_target = focus_client;
594
595     start = it = g_list_find(list, focus_cycle_target);
596     if (!start) /* switched desktops or something? */
597         start = it = forward ? g_list_last(list) : g_list_first(list);
598     if (!start) goto done_cycle;
599
600     do {
601         if (forward) {
602             it = it->next;
603             if (it == NULL) it = g_list_first(list);
604         } else {
605             it = it->prev;
606             if (it == NULL) it = g_list_last(list);
607         }
608         ft = it->data;
609         if (valid_focus_target(ft)) {
610             if (interactive) {
611                 if (ft != focus_cycle_target) { /* prevents flicker */
612                     focus_cycle_target = ft;
613                     focus_cycle_draw_indicator();
614                 }
615                 popup_cycle(ft, dialog);
616                 return;
617             } else if (ft != focus_cycle_target) {
618                 focus_cycle_target = ft;
619                 done = TRUE;
620                 break;
621             }
622         }
623     } while (it != start);
624
625 done_cycle:
626     if (done && focus_cycle_target)
627         client_activate(focus_cycle_target, FALSE, TRUE, time);
628
629     t = NULL;
630     first = NULL;
631     focus_cycle_target = NULL;
632     g_list_free(order);
633     order = NULL;
634
635     if (interactive) {
636         focus_cycle_draw_indicator();
637         popup_cycle(ft, FALSE);
638     }
639
640     return;
641 }
642
643 void focus_directional_cycle(ObDirection dir, gboolean interactive,
644                              gboolean dialog, gboolean done, gboolean cancel,
645                              Time time)
646 {
647     static ObClient *first = NULL;
648     ObClient *ft = NULL;
649
650     if (!interactive)
651         return;
652
653     if (cancel) {
654         focus_cycle_target = NULL;
655         goto done_cycle;
656     } else if (done)
657         goto done_cycle;
658
659     if (!focus_order[screen_desktop])
660         goto done_cycle;
661
662     if (!first) first = focus_client;
663     if (!focus_cycle_target) focus_cycle_target = focus_client;
664
665     if (focus_cycle_target)
666         ft = client_find_directional(focus_cycle_target, dir);
667     else {
668         GList *it;
669
670         for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
671             if (valid_focus_target(it->data))
672                 ft = it->data;
673     }
674         
675     if (ft) {
676         if (ft != focus_cycle_target) {/* prevents flicker */
677             focus_cycle_target = ft;
678             focus_cycle_draw_indicator();
679         }
680     }
681     if (focus_cycle_target) {
682         popup_cycle(focus_cycle_target, dialog);
683         if (dialog)
684             return;
685     }
686
687
688 done_cycle:
689     if (done && focus_cycle_target)
690         client_activate(focus_cycle_target, FALSE, TRUE, time);
691
692     first = NULL;
693     focus_cycle_target = NULL;
694
695     focus_cycle_draw_indicator();
696     popup_cycle(ft, FALSE);
697
698     return;
699 }
700
701 void focus_order_add_new(ObClient *c)
702 {
703     guint d, i;
704
705     if (c->iconic)
706         focus_order_to_top(c);
707     else {
708         d = c->desktop;
709         if (d == DESKTOP_ALL) {
710             for (i = 0; i < screen_num_desktops; ++i) {
711                 g_assert(!g_list_find(focus_order[i], c));
712                 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
713                     focus_order[i] = g_list_insert(focus_order[i], c, 0);
714                 else
715                     focus_order[i] = g_list_insert(focus_order[i], c, 1);
716             }
717         } else {
718             g_assert(!g_list_find(focus_order[d], c));
719             if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
720                 focus_order[d] = g_list_insert(focus_order[d], c, 0);
721             else
722                 focus_order[d] = g_list_insert(focus_order[d], c, 1);
723         }
724     }
725 }
726
727 void focus_order_remove(ObClient *c)
728 {
729     guint d, i;
730
731     d = c->desktop;
732     if (d == DESKTOP_ALL) {
733         for (i = 0; i < screen_num_desktops; ++i)
734             focus_order[i] = g_list_remove(focus_order[i], c);
735     } else
736         focus_order[d] = g_list_remove(focus_order[d], c);
737 }
738
739 static void to_top(ObClient *c, guint d)
740 {
741     focus_order[d] = g_list_remove(focus_order[d], c);
742     if (!c->iconic) {
743         focus_order[d] = g_list_prepend(focus_order[d], c);
744     } else {
745         GList *it;
746
747         /* insert before first iconic window */
748         for (it = focus_order[d];
749              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
750         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
751     }
752 }
753
754 void focus_order_to_top(ObClient *c)
755 {
756     guint d, i;
757
758     d = c->desktop;
759     if (d == DESKTOP_ALL) {
760         for (i = 0; i < screen_num_desktops; ++i)
761             to_top(c, i);
762     } else
763         to_top(c, d);
764 }
765
766 static void to_bottom(ObClient *c, guint d)
767 {
768     focus_order[d] = g_list_remove(focus_order[d], c);
769     if (c->iconic) {
770         focus_order[d] = g_list_append(focus_order[d], c);
771     } else {
772         GList *it;
773
774         /* insert before first iconic window */
775         for (it = focus_order[d];
776              it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
777         focus_order[d] = g_list_insert_before(focus_order[d], it, c);
778     }
779 }
780
781 void focus_order_to_bottom(ObClient *c)
782 {
783     guint d, i;
784
785     d = c->desktop;
786     if (d == DESKTOP_ALL) {
787         for (i = 0; i < screen_num_desktops; ++i)
788             to_bottom(c, i);
789     } else
790         to_bottom(c, d);
791 }