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