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