clear the fuzzy strings and mark translations without the desktop strings as 3.4.2
[dana/openbox-history.git] / openbox / stacking.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    stacking.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 "openbox.h"
21 #include "prop.h"
22 #include "screen.h"
23 #include "focus.h"
24 #include "client.h"
25 #include "group.h"
26 #include "frame.h"
27 #include "window.h"
28 #include "debug.h"
29
30 GList  *stacking_list = NULL;
31
32 void stacking_set_list()
33 {
34     Window *windows = NULL;
35     GList *it;
36     guint i = 0;
37
38     /* on shutdown, don't update the properties, so that we can read it back
39        in on startup and re-stack the windows as they were before we shut down
40     */
41     if (ob_state() == OB_STATE_EXITING) return;
42
43     /* create an array of the window ids (from bottom to top,
44        reverse order!) */
45     if (stacking_list) {
46         windows = g_new(Window, g_list_length(stacking_list));
47         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
48             if (WINDOW_IS_CLIENT(it->data))
49                 windows[i++] = WINDOW_AS_CLIENT(it->data)->window;
50         }
51     }
52
53     PROP_SETA32(RootWindow(ob_display, ob_screen),
54                 net_client_list_stacking, window, (gulong*)windows, i);
55
56     g_free(windows);
57 }
58
59 static void do_restack(GList *wins, GList *before)
60 {
61     GList *it;
62     Window *win;
63     gint i;
64
65 #ifdef DEBUG
66     GList *next;
67     /* pls only restack stuff in the same layer at a time */
68     for (it = wins; it; it = next) {
69         next = g_list_next(it);
70         if (!next) break;
71         g_assert (window_layer(it->data) == window_layer(next->data));
72     }
73     if (before)
74         g_assert(window_layer(it->data) >= window_layer(before->data));
75 #endif
76
77     win = g_new(Window, g_list_length(wins) + 1);
78
79     if (before == stacking_list)
80         win[0] = screen_support_win;
81     else if (!before)
82         win[0] = window_top(g_list_last(stacking_list)->data);
83     else
84         win[0] = window_top(g_list_previous(before)->data);
85
86     for (i = 1, it = wins; it; ++i, it = g_list_next(it)) {
87         win[i] = window_top(it->data);
88         g_assert(win[i] != None); /* better not call stacking shit before
89                                      setting your top level window value */
90         stacking_list = g_list_insert_before(stacking_list, before, it->data);
91     }
92
93 #ifdef DEBUG
94     /* some debug checking of the stacking list's order */
95     for (it = stacking_list; ; it = next) {
96         next = g_list_next(it);
97         if (!next) break;
98         g_assert(window_layer(it->data) >= window_layer(next->data));
99     }
100 #endif
101
102     XRestackWindows(ob_display, win, i);
103     g_free(win);
104
105     stacking_set_list();
106 }
107
108 static void do_raise(GList *wins)
109 {
110     GList *it;
111     GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
112     gint i;
113
114     for (it = wins; it; it = g_list_next(it)) {
115         ObStackingLayer l;
116
117         l = window_layer(it->data);
118         layer[l] = g_list_append(layer[l], it->data);
119     }
120
121     it = stacking_list;
122     for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
123         if (layer[i]) {
124             for (; it; it = g_list_next(it)) {
125                 /* look for the top of the layer */
126                 if (window_layer(it->data) <= (ObStackingLayer) i)
127                     break;
128             }
129             do_restack(layer[i], it);
130             g_list_free(layer[i]);
131         }
132     }
133 }
134
135 static void do_lower(GList *wins)
136 {
137     GList *it;
138     GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
139     gint i;
140
141     for (it = wins; it; it = g_list_next(it)) {
142         ObStackingLayer l;
143
144         l = window_layer(it->data);
145         layer[l] = g_list_append(layer[l], it->data);
146     }
147
148     it = stacking_list;
149     for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
150         if (layer[i]) {
151             for (; it; it = g_list_next(it)) {
152                 /* look for the top of the next layer down */
153                 if (window_layer(it->data) < (ObStackingLayer) i)
154                     break;
155             }
156             do_restack(layer[i], it);
157             g_list_free(layer[i]);
158         }
159     }
160 }
161
162 static void restack_windows(ObClient *selected, gboolean raise)
163 {
164     GList *it, *last, *below, *above, *next;
165     GList *wins = NULL;
166
167     GList *group_modals = NULL;
168     GList *group_trans = NULL;
169     GList *modals = NULL;
170     GList *trans = NULL;
171
172     /* remove first so we can't run into ourself */
173     it = g_list_find(stacking_list, selected);
174     g_assert(it);
175     stacking_list = g_list_delete_link(stacking_list, it);
176
177     /* go from the bottom of the stacking list up. don't move any other windows
178        when lowering, we call this for each window independently */
179     if (raise) {
180         for (it = g_list_last(stacking_list); it; it = next) {
181             next = g_list_previous(it);
182
183             if (WINDOW_IS_CLIENT(it->data)) {
184                 ObClient *ch = it->data;
185
186                 /* only move windows in the same stacking layer */
187                 if (ch->layer == selected->layer &&
188                     client_search_transient(selected, ch))
189                 {
190                     if (client_is_direct_child(selected, ch)) {
191                         if (ch->modal)
192                             modals = g_list_prepend(modals, ch);
193                         else
194                             trans = g_list_prepend(trans, ch);
195                     }
196                     else {
197                         if (ch->modal)
198                             group_modals = g_list_prepend(group_modals, ch);
199                         else
200                             group_trans = g_list_prepend(group_trans, ch);
201                     }
202                     stacking_list = g_list_delete_link(stacking_list, it);
203                 }
204             }
205         }
206     }
207
208     /* put transients of the selected window right above it */
209     wins = g_list_concat(modals, trans);
210     wins = g_list_append(wins, selected);
211
212     /* if selected window is transient for group then raise it above others */
213     if (selected->transient_for_group) {
214         /* if it's modal, raise it above those also */
215         if (selected->modal) {
216             wins = g_list_concat(wins, group_modals);
217             group_modals = NULL;
218         }
219         wins = g_list_concat(wins, group_trans);
220         group_trans = NULL;
221     }
222
223     /* find where to put the selected window, start from bottom of list,
224        this is the window below everything we are re-adding to the list */
225     last = NULL;
226     for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
227     {
228         if (window_layer(it->data) < selected->layer) {
229             last = it;
230             continue;
231         }
232         /* if lowering, stop at the beginning of the layer */
233         if (!raise)
234             break;
235         /* if raising, stop at the end of the layer */
236         if (window_layer(it->data) > selected->layer)
237             break;
238
239         last = it;
240     }
241
242     /* save this position in the stacking list */
243     below = last;
244
245     /* find where to put the group transients, start from the top of list */
246     for (it = stacking_list; it; it = g_list_next(it)) {
247         /* skip past higher layers */
248         if (window_layer(it->data) > selected->layer)
249             continue;
250         /* if we reach the end of the layer (how?) then don't go further */
251         if (window_layer(it->data) < selected->layer)
252             break;
253         /* stop when we reach the first window in the group */
254         if (WINDOW_IS_CLIENT(it->data)) {
255             ObClient *c = it->data;
256             if (c->group == selected->group)
257                 break;
258         }
259         /* if we don't hit any other group members, stop here because this
260            is where we are putting the selected window (and its children) */
261         if (it == below)
262             break;
263     }
264
265     /* save this position, this is the top of the group of windows between the
266        group transient ones we're restacking and the others up above that we're
267        restacking
268
269        we actually want to save 1 position _above_ that, for for loops to work
270        nicely, so move back one position in the list while saving it
271     */
272     above = it ? g_list_previous(it) : g_list_last(stacking_list);
273
274     /* put the windows inside the gap to the other windows we're stacking
275        into the restacking list, go from the bottom up so that we can use
276        g_list_prepend */
277     if (below) it = g_list_previous(below);
278     else       it = g_list_last(stacking_list);
279     for (; it != above; it = next) {
280         next = g_list_previous(it);
281         wins = g_list_prepend(wins, it->data);
282         stacking_list = g_list_delete_link(stacking_list, it);
283     }
284
285     /* group transients go above the rest of the stuff acquired to now */
286     wins = g_list_concat(group_trans, wins);
287     /* group modals go on the very top */
288     wins = g_list_concat(group_modals, wins);
289
290     do_restack(wins, below);
291     g_list_free(wins);
292
293     /* lower our parents after us, so they go below us */
294     if (!raise && selected->parents) {
295         GSList *parents_copy, *sit;
296         GSList *reorder = NULL;
297
298         parents_copy = g_slist_copy(selected->parents);
299
300         /* go thru stacking list backwards so we can use g_slist_prepend */
301         for (it = g_list_last(stacking_list); it && parents_copy;
302              it = g_list_previous(it))
303             if ((sit = g_slist_find(parents_copy, it->data))) {
304                 reorder = g_slist_prepend(reorder, sit->data);
305                 parents_copy = g_slist_delete_link(parents_copy, sit);
306             }
307         g_assert(parents_copy == NULL);
308
309         /* call restack for each of these to lower them */
310         for (sit = reorder; sit; sit = g_slist_next(sit))
311             restack_windows(sit->data, raise);
312     }
313 }
314
315 void stacking_raise(ObWindow *window)
316 {
317     if (WINDOW_IS_CLIENT(window)) {
318         ObClient *selected;
319         selected = WINDOW_AS_CLIENT(window);
320         restack_windows(selected, TRUE);
321     } else {
322         GList *wins;
323         wins = g_list_append(NULL, window);
324         stacking_list = g_list_remove(stacking_list, window);
325         do_raise(wins);
326         g_list_free(wins);
327     }
328 }
329
330 void stacking_lower(ObWindow *window)
331 {
332     if (WINDOW_IS_CLIENT(window)) {
333         ObClient *selected;
334         selected = WINDOW_AS_CLIENT(window);
335         restack_windows(selected, FALSE);
336     } else {
337         GList *wins;
338         wins = g_list_append(NULL, window);
339         stacking_list = g_list_remove(stacking_list, window);
340         do_lower(wins);
341         g_list_free(wins);
342     }
343 }
344
345 void stacking_below(ObWindow *window, ObWindow *below)
346 {
347     GList *wins, *before;
348
349     if (window_layer(window) != window_layer(below))
350         return;
351
352     wins = g_list_append(NULL, window);
353     stacking_list = g_list_remove(stacking_list, window);
354     before = g_list_next(g_list_find(stacking_list, below));
355     do_restack(wins, before);
356     g_list_free(wins);
357 }
358
359 void stacking_add(ObWindow *win)
360 {
361     g_assert(screen_support_win != None); /* make sure I dont break this in the
362                                              future */
363
364     stacking_list = g_list_append(stacking_list, win);
365     stacking_raise(win);
366 }
367
368 static GList *find_highest_relative(ObClient *client)
369 {    
370     GList *ret = NULL;
371
372     if (client->parents) {
373         GList *it;
374         GSList *top;
375
376         /* get all top level relatives of this client */
377         top = client_search_all_top_parents_layer(client);
378
379         /* go from the top of the stacking order down */
380         for (it = stacking_list; !ret && it; it = g_list_next(it)) {
381             if (WINDOW_IS_CLIENT(it->data)) {
382                 ObClient *c = it->data;
383                 /* only look at windows in the same layer and that are
384                    visible */
385                 if (c->layer == client->layer &&
386                     !c->iconic && 
387                     (c->desktop == client->desktop ||
388                      c->desktop == DESKTOP_ALL ||
389                      client->desktop == DESKTOP_ALL))
390                 {
391                     GSList *sit;
392
393                     /* go through each top level parent and see it this window
394                        is related to them */
395                     for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
396                         ObClient *topc = sit->data;
397
398                         /* are they related ? */
399                         if (topc == c || client_search_transient(topc, c))
400                             ret = it;
401                     }
402                 }
403             }
404         }
405     }
406     return ret;
407 }
408
409 void stacking_add_nonintrusive(ObWindow *win)
410 {
411     ObClient *client;
412     GList *it_below = NULL; /* this client will be below us */
413     GList *it_above;
414
415     if (!WINDOW_IS_CLIENT(win)) {
416         stacking_add(win); /* no special rules for others */
417         return;
418     }
419
420     client = WINDOW_AS_CLIENT(win);
421
422     /* insert above its highest parent (or its highest child !) */
423     it_below = find_highest_relative(client);
424
425     if (!it_below) {
426         /* nothing to put it directly above, so try find the focused client
427            to put it underneath it */
428         if (focus_client && client != focus_client &&
429             focus_client->layer == client->layer)
430         {
431             it_below = g_list_find(stacking_list, focus_client);
432             /* this can give NULL, but it means the focused window is on the
433                bottom of the stacking order, so go to the bottom in that case,
434                below it */
435             it_below = g_list_next(it_below);
436         }
437         else {
438             /* There is no window to put this directly above, so put it at the
439                top, so you know it is there.
440
441                It used to do this only if the window was focused and lower
442                it otherwise.
443
444                We also put it at the top not the bottom to fix a bug with
445                fullscreen windows. When focusLast is off and followsMouse is
446                on, when you switch desktops, the fullscreen window loses
447                focus and goes into its lower layer. If this puts it at the
448                bottom then when you come back to the desktop, the window is
449                at the bottom and won't get focus back.
450             */
451             it_below = stacking_list;
452         }
453     }
454
455     /* make sure it's not in the wrong layer though ! */
456     for (; it_below; it_below = g_list_next(it_below)) {
457         /* stop when the window is not in a higher layer than the window
458            it is going above (it_below) */
459         if (client->layer >= window_layer(it_below->data))
460             break;
461     }
462     for (; it_below != stacking_list; it_below = it_above) {
463         /* stop when the window is not in a lower layer than the
464            window it is going under (it_above) */
465         it_above = it_below ?
466             g_list_previous(it_below) : g_list_last(stacking_list);
467         if (client->layer <= window_layer(it_above->data))
468             break;
469     }
470
471     GList *wins = g_list_append(NULL, win);
472     do_restack(wins, it_below);
473     g_list_free(wins);
474 }
475
476 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
477   tries against all other clients.
478 */
479 static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
480 {
481     GList *it;
482     gboolean occluded = FALSE;
483     gboolean found = FALSE;
484
485     /* no need for any looping in this case */
486     if (sibling && client->layer != sibling->layer)
487         return occluded;
488
489     for (it = stacking_list; it;
490          it = (found ? g_list_previous(it) :g_list_next(it)))
491         if (WINDOW_IS_CLIENT(it->data)) {
492             ObClient *c = it->data;
493             if (found && !c->iconic &&
494                 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
495                  c->desktop == client->desktop) &&
496                 !client_search_transient(client, c))
497             {
498                 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
499                 {
500                     if (sibling != NULL) {
501                         if (c == sibling) {
502                             occluded = TRUE;
503                             break;
504                         }
505                     }
506                     else if (c->layer == client->layer) {
507                         occluded = TRUE;
508                         break;
509                     }
510                     else if (c->layer > client->layer)
511                         break; /* we past its layer */
512                 }
513             }
514             else if (c == client)
515                 found = TRUE;
516         }
517     return occluded;
518 }
519
520 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
521   against all other clients.
522 */
523 static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
524 {
525     GList *it;
526     gboolean occludes = FALSE;
527     gboolean found = FALSE;
528
529     /* no need for any looping in this case */
530     if (sibling && client->layer != sibling->layer)
531         return occludes;
532
533     for (it = stacking_list; it; it = g_list_next(it))
534         if (WINDOW_IS_CLIENT(it->data)) {
535             ObClient *c = it->data;
536             if (found && !c->iconic &&
537                 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
538                  c->desktop == client->desktop) &&
539                 !client_search_transient(c, client))
540             {
541                 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
542                 {
543                     if (sibling != NULL) {
544                         if (c == sibling) {
545                             occludes = TRUE;
546                             break;
547                         }
548                     }
549                     else if (c->layer == client->layer) {
550                         occludes = TRUE;
551                         break;
552                     }
553                     else if (c->layer < client->layer)
554                         break; /* we past its layer */
555                 }
556             }
557             else if (c == client)
558                 found = TRUE;
559         }
560     return occludes;
561 }
562
563 gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
564                                   gint detail)
565 {
566     gboolean ret = FALSE;
567
568     if (sibling && ((client->desktop != sibling->desktop &&
569                      client->desktop != DESKTOP_ALL &&
570                      sibling->desktop != DESKTOP_ALL) ||
571                     sibling->iconic))
572     {
573         ob_debug("Setting restack sibling to NULL, they are not on the same "
574                  "desktop or it is iconified\n");
575         sibling = NULL;
576     }
577
578     switch (detail) {
579     case Below:
580         ob_debug("Restack request Below for client %s sibling %s\n",
581                  client->title, sibling ? sibling->title : "(all)");
582         /* just lower it */
583         stacking_lower(CLIENT_AS_WINDOW(client));
584         ret = TRUE;
585         break;
586     case BottomIf:
587         ob_debug("Restack request BottomIf for client %s sibling "
588                  "%s\n",
589                  client->title, sibling ? sibling->title : "(all)");
590         /* if this client occludes sibling (or anything if NULL), then
591            lower it to the bottom */
592         if (stacking_occludes(client, sibling)) {
593             stacking_lower(CLIENT_AS_WINDOW(client));
594             ret = TRUE;
595         }
596         break;
597     case Above:
598         ob_debug("Restack request Above for client %s sibling %s\n",
599                  client->title, sibling ? sibling->title : "(all)");
600         stacking_raise(CLIENT_AS_WINDOW(client));
601         ret = TRUE;
602         break;
603     case TopIf:
604         ob_debug("Restack request TopIf for client %s sibling %s\n",
605                  client->title, sibling ? sibling->title : "(all)");
606         if (stacking_occluded(client, sibling)) {
607             stacking_raise(CLIENT_AS_WINDOW(client));
608             ret = TRUE;
609         }
610         break;
611     case Opposite:
612         ob_debug("Restack request Opposite for client %s sibling "
613                  "%s\n",
614                  client->title, sibling ? sibling->title : "(all)");
615         if (stacking_occluded(client, sibling)) {
616             stacking_raise(CLIENT_AS_WINDOW(client));
617             ret = TRUE;
618         }
619         else if (stacking_occludes(client, sibling)) {
620             stacking_lower(CLIENT_AS_WINDOW(client));
621             ret = TRUE;
622         }
623         break;
624     }
625     return ret;
626 }