Forgot to init restack sibling to NULL
[mikachu/openbox.git] / openbox / stacking.c
index 03a62dd..166d382 100644 (file)
 #include "window.h"
 #include "event.h"
 #include "debug.h"
+#include "dock.h"
 #include "obt/prop.h"
 
 GList  *stacking_list = NULL;
+GList  *stacking_list_tail = NULL;
 /*! When true, stacking changes will not be reflected on the screen.  This is
   to freeze the on-screen stacking order while a window is being temporarily
   raised during focus cycling */
@@ -69,6 +71,8 @@ static void do_restack(GList *wins, GList *before)
 
 #ifdef DEBUG
     GList *next;
+
+    g_assert(wins);
     /* pls only restack stuff in the same layer at a time */
     for (it = wins; it; it = next) {
         next = g_list_next(it);
@@ -216,11 +220,21 @@ static void restack_windows(ObClient *selected, gboolean raise)
     GList *it, *last, *below, *above, *next;
     GList *wins = NULL;
 
+    GList *group_helpers = NULL;
     GList *group_modals = NULL;
     GList *group_trans = NULL;
     GList *modals = NULL;
     GList *trans = NULL;
 
+    if (raise) {
+        ObClient *p;
+
+        /* if a window is modal for another single window, then raise it to the
+           top too, the same is done with the focus order */
+        while (selected->modal && (p = client_direct_parent(selected)))
+            selected = p;
+    }
+
     /* remove first so we can't run into ourself */
     it = g_list_find(stacking_list, selected);
     g_assert(it);
@@ -237,6 +251,8 @@ static void restack_windows(ObClient *selected, gboolean raise)
 
                 /* only move windows in the same stacking layer */
                 if (ch->layer == selected->layer &&
+                    /* looking for windows that are transients, and so would
+                       remain above the selected window */
                     client_search_transient(selected, ch))
                 {
                     if (client_is_direct_child(selected, ch)) {
@@ -245,6 +261,13 @@ static void restack_windows(ObClient *selected, gboolean raise)
                         else
                             trans = g_list_prepend(trans, ch);
                     }
+                    else if (client_helper(ch)) {
+                        if (selected->transient) {
+                            /* helpers do not stay above transient windows */
+                            continue;
+                        }
+                        group_helpers = g_list_prepend(group_helpers, ch);
+                    }
                     else {
                         if (ch->modal)
                             group_modals = g_list_prepend(group_modals, ch);
@@ -257,8 +280,13 @@ static void restack_windows(ObClient *selected, gboolean raise)
         }
     }
 
-    /* put transients of the selected window right above it */
+    /* put modals above other direct transients */
     wins = g_list_concat(modals, trans);
+
+    /* put helpers below direct transients */
+    wins = g_list_concat(wins, group_helpers);
+
+    /* put the selected window right below these children */
     wins = g_list_append(wins, selected);
 
     /* if selected window is transient for group then raise it above others */
@@ -377,6 +405,7 @@ void stacking_raise(ObWindow *window)
         do_raise(wins);
         g_list_free(wins);
     }
+    stacking_list_tail = g_list_last(stacking_list);
 }
 
 void stacking_lower(ObWindow *window)
@@ -392,6 +421,21 @@ void stacking_lower(ObWindow *window)
         do_lower(wins);
         g_list_free(wins);
     }
+    stacking_list_tail = g_list_last(stacking_list);
+}
+
+void stacking_above(ObWindow *window, ObWindow *above)
+{
+    GList *wins, *before;
+
+    if (window_layer(window) != window_layer(above))
+        return;
+
+    wins = g_list_append(NULL, window);
+    stacking_list = g_list_remove(stacking_list, window);
+    before = g_list_previous(g_list_find(stacking_list, above));
+    do_restack(wins, before);
+    g_list_free(wins);
 }
 
 void stacking_below(ObWindow *window, ObWindow *below)
@@ -406,15 +450,20 @@ void stacking_below(ObWindow *window, ObWindow *below)
     before = g_list_next(g_list_find(stacking_list, below));
     do_restack(wins, before);
     g_list_free(wins);
+    stacking_list_tail = g_list_last(stacking_list);
 }
 
 void stacking_add(ObWindow *win)
 {
     g_assert(screen_support_win != None); /* make sure I dont break this in the
                                              future */
+    /* don't add windows that are being unmanaged ! */
+    if (WINDOW_IS_CLIENT(win)) g_assert(WINDOW_AS_CLIENT(win)->managed);
 
     stacking_list = g_list_append(stacking_list, win);
+
     stacking_raise(win);
+    /* stacking_list_tail set by stacking_raise() */
 }
 
 static GList *find_highest_relative(ObClient *client)
@@ -472,6 +521,9 @@ void stacking_add_nonintrusive(ObWindow *win)
 
     client = WINDOW_AS_CLIENT(win);
 
+    /* don't add windows that are being unmanaged ! */
+    g_assert(client->managed);
+
     /* insert above its highest parent (or its highest child !) */
     it_below = find_highest_relative(client);
 
@@ -524,26 +576,27 @@ void stacking_add_nonintrusive(ObWindow *win)
     wins = g_list_append(NULL, win);
     do_restack(wins, it_below);
     g_list_free(wins);
+    stacking_list_tail = g_list_last(stacking_list);
 }
 
-/*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
-  tries against all other clients.
-*/
-static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
+ObWindow *stacking_occluded(ObClient *client, ObWindow *sibling_win)
 {
     GList *it;
-    gboolean occluded = FALSE;
-    gboolean found = FALSE;
+    ObWindow *occluded = NULL;
+    ObClient *sibling = NULL;
+
+    if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
+        sibling = WINDOW_AS_CLIENT(sibling_win);
 
     /* no need for any looping in this case */
     if (sibling && client->layer != sibling->layer)
-        return occluded;
+        return NULL;
 
-    for (it = stacking_list; it;
-         it = (found ? g_list_previous(it) :g_list_next(it)))
+    for (it = g_list_previous(g_list_find(stacking_list, client)); it;
+         it = g_list_previous(it))
         if (WINDOW_IS_CLIENT(it->data)) {
             ObClient *c = it->data;
-            if (found && !c->iconic &&
+            if (!c->iconic &&
                 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
                  c->desktop == client->desktop) &&
                 !client_search_transient(client, c))
@@ -552,41 +605,54 @@ static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
                 {
                     if (sibling != NULL) {
                         if (c == sibling) {
-                            occluded = TRUE;
+                            occluded = sibling_win;
                             break;
                         }
                     }
                     else if (c->layer == client->layer) {
-                        occluded = TRUE;
+                        occluded = CLIENT_AS_WINDOW(c);
                         break;
                     }
                     else if (c->layer > client->layer)
                         break; /* we past its layer */
                 }
             }
-            else if (c == client)
-                found = TRUE;
+        }
+        else if (WINDOW_IS_DOCK(it->data)) {
+            ObDock *dock = it->data;
+            if (RECT_INTERSECTS_RECT(dock->area, client->frame->area))
+            {
+                if (sibling_win != NULL) {
+                    if (DOCK_AS_WINDOW(dock) == sibling_win) {
+                        occluded = sibling_win;
+                        break;
+                    }
+                }
+                else
+                    occluded = DOCK_AS_WINDOW(dock);
+            }
         }
     return occluded;
 }
 
-/*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
-  against all other clients.
-*/
-static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
+ObWindow *stacking_occludes(ObClient *client, ObWindow *sibling_win)
 {
     GList *it;
-    gboolean occludes = FALSE;
-    gboolean found = FALSE;
+    ObWindow *occludes = NULL;
+    ObClient *sibling = NULL;
+
+    if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
+        sibling = WINDOW_AS_CLIENT(sibling_win);
 
     /* no need for any looping in this case */
     if (sibling && client->layer != sibling->layer)
-        return occludes;
+        return NULL;
 
-    for (it = stacking_list; it; it = g_list_next(it))
+    for (it = g_list_next(g_list_find(stacking_list, client));
+         it; it = g_list_next(it))
         if (WINDOW_IS_CLIENT(it->data)) {
             ObClient *c = it->data;
-            if (found && !c->iconic &&
+            if (!c->iconic &&
                 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
                  c->desktop == client->desktop) &&
                 !client_search_transient(c, client))
@@ -595,29 +661,46 @@ static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
                 {
                     if (sibling != NULL) {
                         if (c == sibling) {
-                            occludes = TRUE;
+                            occludes = sibling_win;
                             break;
                         }
                     }
                     else if (c->layer == client->layer) {
-                        occludes = TRUE;
+                        occludes = CLIENT_AS_WINDOW(c);
                         break;
                     }
                     else if (c->layer < client->layer)
                         break; /* we past its layer */
                 }
             }
-            else if (c == client)
-                found = TRUE;
+        }
+        else if (WINDOW_IS_DOCK(it->data)) {
+            ObDock *dock = it->data;
+            if (RECT_INTERSECTS_RECT(dock->area, client->frame->area))
+            {
+                if (sibling_win != NULL) {
+                    if (DOCK_AS_WINDOW(dock) == sibling_win) {
+                        occludes = sibling_win;
+                        break;
+                    }
+                }
+                else
+                    occludes = DOCK_AS_WINDOW(dock);
+            }
         }
     return occludes;
 }
 
-gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
+gboolean stacking_restack_request(ObClient *client, ObWindow *sibling_win,
                                   gint detail)
 {
     gboolean ret = FALSE;
 
+    ObClient *sibling = NULL;
+
+    if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
+        sibling = WINDOW_AS_CLIENT(sibling_win);
+
     if (sibling && ((client->desktop != sibling->desktop &&
                      client->desktop != DESKTOP_ALL &&
                      sibling->desktop != DESKTOP_ALL) ||
@@ -641,7 +724,7 @@ gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
                  client->title, sibling ? sibling->title : "(all)");
         /* if this client occludes sibling (or anything if NULL), then
            lower it to the bottom */
-        if (stacking_occludes(client, sibling)) {
+        if (stacking_occludes(client, sibling_win)) {
             stacking_lower(CLIENT_AS_WINDOW(client));
             ret = TRUE;
         }
@@ -655,7 +738,7 @@ gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
     case TopIf:
         ob_debug("Restack request TopIf for client %s sibling %s",
                  client->title, sibling ? sibling->title : "(all)");
-        if (stacking_occluded(client, sibling)) {
+        if (stacking_occluded(client, sibling_win)) {
             stacking_raise(CLIENT_AS_WINDOW(client));
             ret = TRUE;
         }
@@ -663,11 +746,11 @@ gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
     case Opposite:
         ob_debug("Restack request Opposite for client %s sibling %s",
                  client->title, sibling ? sibling->title : "(all)");
-        if (stacking_occluded(client, sibling)) {
+        if (stacking_occluded(client, sibling_win)) {
             stacking_raise(CLIENT_AS_WINDOW(client));
             ret = TRUE;
         }
-        else if (stacking_occludes(client, sibling)) {
+        else if (stacking_occludes(client, sibling_win)) {
             stacking_lower(CLIENT_AS_WINDOW(client));
             ret = TRUE;
         }