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