i3
src/move.c
Go to the documentation of this file.
00001 /*
00002  * vim:ts=4:sw=4:expandtab
00003  *
00004  * i3 - an improved dynamic tiling window manager
00005  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
00006  *
00007  * move.c: Moving containers into some direction.
00008  *
00009  */
00010 #include "all.h"
00011 
00012 #include "cmdparse.tab.h"
00013 
00014 typedef enum { BEFORE, AFTER } position_t;
00015 
00016 /*
00017  * This function detaches 'con' from its parent and inserts it either before or
00018  * after 'target'.
00019  *
00020  */
00021 static void insert_con_into(Con *con, Con *target, position_t position) {
00022     Con *parent = target->parent;
00023     /* We need to preserve the old con->parent. While it might still be used to
00024      * insert the entry before/after it, we call the on_remove_child callback
00025      * afterwards which might then close the con if it is empty. */
00026     Con *old_parent = con->parent;
00027 
00028     con_detach(con);
00029     con_fix_percent(con->parent);
00030 
00031     /* When moving to a workspace, we respect the user’s configured
00032      * workspace_layout */
00033     if (parent->type == CT_WORKSPACE) {
00034         Con *split = workspace_attach_to(parent);
00035         if (split != parent) {
00036             DLOG("Got a new split con, using that one instead\n");
00037             con->parent = split;
00038             con_attach(con, split, false);
00039             DLOG("attached\n");
00040             con->percent = 0.0;
00041             con_fix_percent(split);
00042             con = split;
00043             DLOG("ok, continuing with con %p instead\n", con);
00044             con_detach(con);
00045         }
00046     }
00047 
00048     con->parent = parent;
00049 
00050     if (position == BEFORE) {
00051         TAILQ_INSERT_BEFORE(target, con, nodes);
00052         TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
00053     } else if (position == AFTER) {
00054         TAILQ_INSERT_AFTER(&(parent->nodes_head), target, con, nodes);
00055         TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
00056     }
00057 
00058     /* Pretend the con was just opened with regards to size percent values.
00059      * Since the con is moved to a completely different con, the old value
00060      * does not make sense anyways. */
00061     con->percent = 0.0;
00062     con_fix_percent(parent);
00063 
00064     CALL(old_parent, on_remove_child);
00065 }
00066 
00067 /*
00068  * This function detaches 'con' from its parent and inserts it at the given
00069  * workspace.
00070  *
00071  */
00072 static void attach_to_workspace(Con *con, Con *ws) {
00073     con_detach(con);
00074     con_fix_percent(con->parent);
00075 
00076     CALL(con->parent, on_remove_child);
00077 
00078     con->parent = ws;
00079 
00080     TAILQ_INSERT_TAIL(&(ws->nodes_head), con, nodes);
00081     TAILQ_INSERT_TAIL(&(ws->focus_head), con, focused);
00082 
00083     /* Pretend the con was just opened with regards to size percent values.
00084      * Since the con is moved to a completely different con, the old value
00085      * does not make sense anyways. */
00086     con->percent = 0.0;
00087     con_fix_percent(ws);
00088 }
00089 
00090 /*
00091  * Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT,
00092  * TOK_UP, TOK_DOWN from cmdparse.l)
00093  *
00094  */
00095 void tree_move(int direction) {
00096     DLOG("Moving in direction %d\n", direction);
00097     /* 1: get the first parent with the same orientation */
00098     Con *con = focused;
00099 
00100     if (con->type == CT_WORKSPACE) {
00101         DLOG("Not moving workspace\n");
00102         return;
00103     }
00104 
00105     if (con->parent->type == CT_WORKSPACE && con_num_children(con->parent) == 1) {
00106         DLOG("This is the only con on this workspace, not doing anything\n");
00107         return;
00108     }
00109 
00110     orientation_t o = (direction == TOK_LEFT || direction == TOK_RIGHT ? HORIZ : VERT);
00111 
00112     Con *same_orientation = con_parent_with_orientation(con, o);
00113     /* The do {} while is used to 'restart' at this point with a different
00114      * same_orientation, see the very last lines before the end of this block
00115      * */
00116     do {
00117         /* There is no parent container with the same orientation */
00118         if (!same_orientation) {
00119             if (con_is_floating(con)) {
00120                 /* this is a floating con, we just disable floating */
00121                 floating_disable(con, true);
00122                 return;
00123             }
00124             if (con_inside_floating(con)) {
00125                 /* 'con' should be moved out of a floating container */
00126                 DLOG("Inside floating, moving to workspace\n");
00127                 attach_to_workspace(con, con_get_workspace(con));
00128                 goto end;
00129             }
00130             DLOG("Force-changing orientation\n");
00131             ws_force_orientation(con_get_workspace(con), o);
00132             same_orientation = con_parent_with_orientation(con, o);
00133         }
00134 
00135         /* easy case: the move is within this container */
00136         if (same_orientation == con->parent) {
00137             DLOG("We are in the same container\n");
00138             Con *swap;
00139             if ((swap = (direction == TOK_LEFT || direction == TOK_UP ?
00140                           TAILQ_PREV(con, nodes_head, nodes) :
00141                           TAILQ_NEXT(con, nodes)))) {
00142                 if (!con_is_leaf(swap)) {
00143                     insert_con_into(con, con_descend_focused(swap), AFTER);
00144                     goto end;
00145                 }
00146                 if (direction == TOK_LEFT || direction == TOK_UP)
00147                     TAILQ_SWAP(swap, con, &(swap->parent->nodes_head), nodes);
00148                 else TAILQ_SWAP(con, swap, &(swap->parent->nodes_head), nodes);
00149 
00150                 TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
00151                 TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
00152 
00153                 DLOG("Swapped.\n");
00154                 return;
00155             }
00156 
00157             /* If there was no con with which we could swap the current one, search
00158              * again, but starting one level higher. If we are on the workspace
00159              * level, don’t do that. The result would be a force change of
00160              * workspace orientation, which is not necessary. */
00161             if (con->parent == con_get_workspace(con))
00162                 return;
00163             same_orientation = con_parent_with_orientation(con->parent, o);
00164         }
00165     } while (same_orientation == NULL);
00166 
00167     /* this time, we have to move to another container */
00168     /* This is the container *above* 'con' (an ancestor of con) which is inside
00169      * 'same_orientation' */
00170     Con *above = con;
00171     while (above->parent != same_orientation)
00172         above = above->parent;
00173 
00174     DLOG("above = %p\n", above);
00175     Con *next;
00176     position_t position;
00177     if (direction == TOK_UP || direction == TOK_LEFT) {
00178         position = BEFORE;
00179         next = TAILQ_PREV(above, nodes_head, nodes);
00180     } else {
00181         position = AFTER;
00182         next = TAILQ_NEXT(above, nodes);
00183     }
00184 
00185     /* special case: there is a split container in the direction we are moving
00186      * to, so descend and append */
00187     if (next && !con_is_leaf(next))
00188         insert_con_into(con, con_descend_focused(next), AFTER);
00189     else
00190         insert_con_into(con, above, position);
00191 
00192 end:
00193     /* We need to call con_focus() to fix the focus stack "above" the container
00194      * we just inserted the focused container into (otherwise, the parent
00195      * container(s) would still point to the old container(s)). */
00196     con_focus(con);
00197 
00198     tree_flatten(croot);
00199 }