refactor tree_move() into src/move.c, change config (!), change testcase

Due to lots of cases which were added and added to tree_move(), the function
was not really easy to understand. For this refactoring, I wrote tree_move()
from scratch, thinking about (hopefully) all cases. The testsuite still passes.

The move command also has different parameters now. Instead of the hard to
understand 'before v' stuff, we use 'move [left|right|up|down]'.
This commit is contained in:
Michael Stapelberg
2011-02-14 23:05:20 +01:00
parent 28dd226259
commit 26a416e016
14 changed files with 302 additions and 250 deletions

View File

@ -365,213 +365,6 @@ void tree_next(char way, orientation_t orientation) {
con_focus(con_descend_focused(next));
}
/*
* Moves the current container in the given way (next/previous) and given
* orientation (horizontal/vertical).
*
*/
void tree_move(char way, orientation_t orientation) {
/* 1: get the first parent with the same orientation */
Con *con = focused;
Con *parent = con->parent;
Con *old_parent = parent;
if (con->type == CT_WORKSPACE)
return;
DLOG("con = %p / %s\n", con, con->name);
bool level_changed = false;
while (con_orientation(parent) != orientation) {
DLOG("need to go one level further up\n");
/* If the current parent is an output, we are at a workspace
* and the orientation still does not match. In this case, we split the
* workspace to have the same look & feel as in older i3 releases. */
if (parent->type == CT_WORKSPACE) {
DLOG("Arrived at workspace (%p / %s)\n", parent, parent->name);
/* In case of moving a window out of a floating con, there might be
* not a single tiling container. Makes no sense to split then, so
* just use the workspace as target */
if (TAILQ_EMPTY(&(parent->nodes_head)))
break;
/* Check if there are any other cons at all. If not, there is no
* point in creating a new split con and changing workspace
* orientation. Instead, the operation is a no-op. */
Con *child;
bool other_container = false;
TAILQ_FOREACH(child, &(parent->nodes_head), nodes)
if (child != con)
other_container = true;
if (!other_container) {
DLOG("No other container found, we are not creating this split container.\n");
return;
}
/* 1: create a new split container */
Con *new = con_new(NULL);
new->parent = parent;
/* 2: copy layout and orientation from workspace */
new->layout = parent->layout;
new->orientation = parent->orientation;
Con *old_focused = TAILQ_FIRST(&(parent->focus_head));
if (old_focused == TAILQ_END(&(parent->focus_head)))
old_focused = NULL;
/* 3: move the existing cons of this workspace below the new con */
DLOG("Moving cons\n");
while (!TAILQ_EMPTY(&(parent->nodes_head))) {
child = TAILQ_FIRST(&(parent->nodes_head));
con_detach(child);
con_attach(child, new, true);
}
/* 4: switch workspace orientation */
parent->orientation = orientation;
/* 5: attach the new split container to the workspace */
DLOG("Attaching new split to ws\n");
con_attach(new, parent, false);
/* 6: fix the percentages */
con_fix_percent(parent);
if (old_focused)
con_focus(old_focused);
level_changed = true;
break;
}
parent = parent->parent;
level_changed = true;
}
/* If we have no tiling cons (when moving a window out of a floating con to
* an otherwise empty workspace for example), we just attach the window to
* the workspace. */
bool fix_percent = false;
if (TAILQ_EMPTY(&(parent->nodes_head))) {
con_detach(con);
con_fix_percent(con->parent);
con->parent = parent;
fix_percent = true;
TAILQ_INSERT_HEAD(&(parent->nodes_head), con, nodes);
TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
} else {
Con *current = NULL, *loop;
/* Get the first tiling container in focus stack */
TAILQ_FOREACH(loop, &(parent->focus_head), focused) {
if (loop->type == CT_FLOATING_CON)
continue;
current = loop;
break;
}
assert(current != TAILQ_END(&(parent->focus_head)));
/* 2: chose next (or previous) */
Con *next = current;
if (way == 'n') {
LOG("i would insert it after %p / %s\n", next, next->name);
/* Have a look at the next container: If there is no next container or
* if it is a leaf node, we move the con one left to it. However,
* for split containers, we descend into it. */
next = TAILQ_NEXT(next, nodes);
if (next == TAILQ_END(&(next->parent->nodes_head))) {
if (con == current)
return;
next = current;
} else {
if (level_changed && con_is_leaf(next)) {
next = current;
} else {
/* if this is a split container, we need to go down */
next = con_descend_focused(next);
}
}
con_detach(con);
if (con->parent != next->parent) {
con_fix_percent(con->parent);
con->parent = next->parent;
fix_percent = true;
}
CALL(con->parent, on_remove_child);
TAILQ_INSERT_AFTER(&(next->parent->nodes_head), next, con, nodes);
TAILQ_INSERT_HEAD(&(next->parent->focus_head), con, focused);
/* TODO: dont influence focus handling? */
} else {
LOG("i would insert it before %p / %s\n", current, current->name);
bool gone_down = false;
next = TAILQ_PREV(next, nodes_head, nodes);
if (next == TAILQ_END(&(next->parent->nodes_head))) {
if (con == current) {
DLOG("Cannot move, no other container in that direction\n");
return;
}
next = current;
} else {
if (level_changed && con_is_leaf(next)) {
next = current;
} else {
/* if this is a split container, we need to go down */
while (!TAILQ_EMPTY(&(next->focus_head))) {
gone_down = true;
next = TAILQ_FIRST(&(next->focus_head));
}
}
}
DLOG("detaching con = %p / %s, next = %p / %s\n",
con, con->name, next, next->name);
con_detach(con);
if (con->parent != next->parent) {
DLOG("different parents. new parent = %p / %s\n", next->parent, next->parent->name);
con_fix_percent(con->parent);
con->parent = next->parent;
fix_percent = true;
}
/* After going down in the tree, we insert the container *after*
* the currently focused one even though the command used "before".
* This is to keep the user experience clear, since the before/after
* only signifies the direction of the movement on top-level */
if (gone_down)
TAILQ_INSERT_AFTER(&(next->parent->nodes_head), next, con, nodes);
else TAILQ_INSERT_BEFORE(next, con, nodes);
TAILQ_INSERT_HEAD(&(next->parent->focus_head), con, focused);
/* TODO: dont influence focus handling? */
}
}
/* fix the percentages in the container we moved to */
if (fix_percent) {
int children = con_num_children(con->parent);
if (children == 1) {
con->percent = 1.0;
} else {
con->percent = 1.0 / (children - 1);
con_fix_percent(con->parent);
}
}
/* We need to call con_focus() to fix the focus stack "above" the container
* we just inserted the focused container into (otherwise, the parent
* container(s) would still point to the old container(s)). */
con_focus(con);
/* fix the percentages in the container we moved from */
if (level_changed)
con_fix_percent(old_parent);
CALL(old_parent, on_remove_child);
tree_flatten(croot);
}
/*
* tree_flatten() removes pairs of redundant split containers, e.g.:
* [workspace, horizontal]