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