]> git.sur5r.net Git - i3/i3/blob - src/move.c
a4fc77b3b3b3267dd3976bdbf691d2a5336d2a54
[i3/i3] / src / move.c
1 #line 2 "move.c"
2 /*
3  * vim:ts=4:sw=4:expandtab
4  *
5  * i3 - an improved dynamic tiling window manager
6  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
7  *
8  * move.c: Moving containers into some direction.
9  *
10  */
11 #include "all.h"
12
13 typedef enum { BEFORE, AFTER } position_t;
14
15 /*
16  * This function detaches 'con' from its parent and inserts it either before or
17  * after 'target'.
18  *
19  */
20 static void insert_con_into(Con *con, Con *target, position_t position) {
21     Con *parent = target->parent;
22     /* We need to preserve the old con->parent. While it might still be used to
23      * insert the entry before/after it, we call the on_remove_child callback
24      * afterwards which might then close the con if it is empty. */
25     Con *old_parent = con->parent;
26
27     con_detach(con);
28     con_fix_percent(con->parent);
29
30     /* When moving to a workspace, we respect the user’s configured
31      * workspace_layout */
32     if (parent->type == CT_WORKSPACE) {
33         Con *split = workspace_attach_to(parent);
34         if (split != parent) {
35             DLOG("Got a new split con, using that one instead\n");
36             con->parent = split;
37             con_attach(con, split, false);
38             DLOG("attached\n");
39             con->percent = 0.0;
40             con_fix_percent(split);
41             con = split;
42             DLOG("ok, continuing with con %p instead\n", con);
43             con_detach(con);
44         }
45     }
46
47     con->parent = parent;
48
49     if (position == BEFORE) {
50         TAILQ_INSERT_BEFORE(target, con, nodes);
51         TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
52     } else if (position == AFTER) {
53         TAILQ_INSERT_AFTER(&(parent->nodes_head), target, con, nodes);
54         TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
55     }
56
57     /* Pretend the con was just opened with regards to size percent values.
58      * Since the con is moved to a completely different con, the old value
59      * does not make sense anyways. */
60     con->percent = 0.0;
61     con_fix_percent(parent);
62
63     CALL(old_parent, on_remove_child);
64 }
65
66 /*
67  * This function detaches 'con' from its parent and inserts it at the given
68  * workspace.
69  *
70  */
71 static void attach_to_workspace(Con *con, Con *ws) {
72     con_detach(con);
73     con_fix_percent(con->parent);
74
75     CALL(con->parent, on_remove_child);
76
77     con->parent = ws;
78
79     TAILQ_INSERT_TAIL(&(ws->nodes_head), con, nodes);
80     TAILQ_INSERT_TAIL(&(ws->focus_head), con, focused);
81
82     /* Pretend the con was just opened with regards to size percent values.
83      * Since the con is moved to a completely different con, the old value
84      * does not make sense anyways. */
85     con->percent = 0.0;
86     con_fix_percent(ws);
87 }
88
89 /*
90  * Moves the current container in the given direction (D_LEFT, D_RIGHT,
91  * D_UP, D_DOWN).
92  *
93  */
94 void tree_move(int direction) {
95     DLOG("Moving in direction %d\n", direction);
96     /* 1: get the first parent with the same orientation */
97     Con *con = focused;
98
99     if (con->type == CT_WORKSPACE) {
100         DLOG("Not moving workspace\n");
101         return;
102     }
103
104     if (con->parent->type == CT_WORKSPACE && con_num_children(con->parent) == 1) {
105         DLOG("This is the only con on this workspace, not doing anything\n");
106         return;
107     }
108
109     orientation_t o = (direction == D_LEFT || direction == D_RIGHT ? HORIZ : VERT);
110
111     Con *same_orientation = con_parent_with_orientation(con, o);
112     /* The do {} while is used to 'restart' at this point with a different
113      * same_orientation, see the very last lines before the end of this block
114      * */
115     do {
116         /* There is no parent container with the same orientation */
117         if (!same_orientation) {
118             if (con_is_floating(con)) {
119                 /* this is a floating con, we just disable floating */
120                 floating_disable(con, true);
121                 return;
122             }
123             if (con_inside_floating(con)) {
124                 /* 'con' should be moved out of a floating container */
125                 DLOG("Inside floating, moving to workspace\n");
126                 attach_to_workspace(con, con_get_workspace(con));
127                 goto end;
128             }
129             DLOG("Force-changing orientation\n");
130             ws_force_orientation(con_get_workspace(con), o);
131             same_orientation = con_parent_with_orientation(con, o);
132         }
133
134         /* easy case: the move is within this container */
135         if (same_orientation == con->parent) {
136             DLOG("We are in the same container\n");
137             Con *swap;
138             if ((swap = (direction == D_LEFT || direction == D_UP ?
139                           TAILQ_PREV(con, nodes_head, nodes) :
140                           TAILQ_NEXT(con, nodes)))) {
141                 if (!con_is_leaf(swap)) {
142                     insert_con_into(con, con_descend_focused(swap), AFTER);
143                     goto end;
144                 }
145                 if (direction == D_LEFT || direction == D_UP)
146                     TAILQ_SWAP(swap, con, &(swap->parent->nodes_head), nodes);
147                 else TAILQ_SWAP(con, swap, &(swap->parent->nodes_head), nodes);
148
149                 TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
150                 TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
151
152                 DLOG("Swapped.\n");
153                 return;
154             }
155
156             /* If there was no con with which we could swap the current one, search
157              * again, but starting one level higher. If we are on the workspace
158              * level, don’t do that. The result would be a force change of
159              * workspace orientation, which is not necessary. */
160             if (con->parent == con_get_workspace(con))
161                 return;
162             same_orientation = con_parent_with_orientation(con->parent, o);
163         }
164     } while (same_orientation == NULL);
165
166     /* this time, we have to move to another container */
167     /* This is the container *above* 'con' (an ancestor of con) which is inside
168      * 'same_orientation' */
169     Con *above = con;
170     while (above->parent != same_orientation)
171         above = above->parent;
172
173     /* Enforce the fullscreen focus restrictions. */
174     if (!con_fullscreen_permits_focusing(above->parent)) {
175         LOG("Cannot move out of fullscreen container\n");
176         return;
177     }
178
179     DLOG("above = %p\n", above);
180     Con *next;
181     position_t position;
182     if (direction == D_UP || direction == D_LEFT) {
183         position = BEFORE;
184         next = TAILQ_PREV(above, nodes_head, nodes);
185     } else {
186         position = AFTER;
187         next = TAILQ_NEXT(above, nodes);
188     }
189
190     /* special case: there is a split container in the direction we are moving
191      * to, so descend and append */
192     if (next && !con_is_leaf(next))
193         insert_con_into(con, con_descend_focused(next), AFTER);
194     else
195         insert_con_into(con, above, position);
196
197 end:
198     /* We need to call con_focus() to fix the focus stack "above" the container
199      * we just inserted the focused container into (otherwise, the parent
200      * container(s) would still point to the old container(s)). */
201     con_focus(con);
202
203     /* force re-painting the indicators */
204     FREE(con->deco_render_params);
205
206     tree_flatten(croot);
207 }