i3
|
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 }