2012-08-12 12:18:43 +02:00
|
|
|
|
#undef I3__FILE__
|
|
|
|
|
#define I3__FILE__ "tree.c"
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/*
|
|
|
|
|
* vim:ts=4:sw=4:expandtab
|
2011-10-23 00:40:02 +02:00
|
|
|
|
*
|
|
|
|
|
* i3 - an improved dynamic tiling window manager
|
|
|
|
|
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
|
|
|
|
*
|
|
|
|
|
* tree.c: Everything that primarily modifies the layout tree data structure.
|
|
|
|
|
*
|
2010-03-27 15:25:51 +01:00
|
|
|
|
*/
|
|
|
|
|
#include "all.h"
|
|
|
|
|
|
|
|
|
|
struct Con *croot;
|
|
|
|
|
struct Con *focused;
|
|
|
|
|
|
|
|
|
|
struct all_cons_head all_cons = TAILQ_HEAD_INITIALIZER(all_cons);
|
|
|
|
|
|
|
|
|
|
/*
|
2011-12-22 00:15:32 +01:00
|
|
|
|
* Create the pseudo-output __i3. Output-independent workspaces such as
|
|
|
|
|
* __i3_scratch will live there.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-03-31 10:53:04 +02:00
|
|
|
|
static Con *_create___i3(void) {
|
2011-12-22 00:15:32 +01:00
|
|
|
|
Con *__i3 = con_new(croot, NULL);
|
|
|
|
|
FREE(__i3->name);
|
|
|
|
|
__i3->name = sstrdup("__i3");
|
|
|
|
|
__i3->type = CT_OUTPUT;
|
|
|
|
|
__i3->layout = L_OUTPUT;
|
|
|
|
|
con_fix_percent(croot);
|
|
|
|
|
x_set_name(__i3, "[i3 con] pseudo-output __i3");
|
|
|
|
|
/* For retaining the correct position/size of a scratchpad window, the
|
|
|
|
|
* dimensions of the real outputs should be multiples of the __i3
|
2012-08-08 16:22:03 +02:00
|
|
|
|
* pseudo-output. Ensuring that is the job of scratchpad_fix_resolution()
|
|
|
|
|
* which gets called after this function and after detecting all the
|
|
|
|
|
* outputs (or whenever an output changes). */
|
2011-12-22 00:15:32 +01:00
|
|
|
|
__i3->rect.width = 1280;
|
|
|
|
|
__i3->rect.height = 1024;
|
|
|
|
|
|
|
|
|
|
/* Add a content container. */
|
|
|
|
|
DLOG("adding main content container\n");
|
|
|
|
|
Con *content = con_new(NULL, NULL);
|
|
|
|
|
content->type = CT_CON;
|
|
|
|
|
FREE(content->name);
|
|
|
|
|
content->name = sstrdup("content");
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
content->layout = L_SPLITH;
|
2011-12-22 00:15:32 +01:00
|
|
|
|
|
|
|
|
|
x_set_name(content, "[i3 con] content __i3");
|
|
|
|
|
con_attach(content, __i3, false);
|
|
|
|
|
|
|
|
|
|
/* Attach the __i3_scratch workspace. */
|
|
|
|
|
Con *ws = con_new(NULL, NULL);
|
|
|
|
|
ws->type = CT_WORKSPACE;
|
|
|
|
|
ws->num = -1;
|
|
|
|
|
ws->name = sstrdup("__i3_scratch");
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
ws->layout = L_SPLITH;
|
2011-12-22 00:15:32 +01:00
|
|
|
|
con_attach(ws, content, false);
|
|
|
|
|
x_set_name(ws, "[i3 con] workspace __i3_scratch");
|
|
|
|
|
ws->fullscreen_mode = CF_OUTPUT;
|
|
|
|
|
|
|
|
|
|
return __i3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Loads tree from 'path' (used for in-place restarts).
|
2010-03-27 15:25:51 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2011-06-10 18:27:20 +02:00
|
|
|
|
bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
|
2010-12-01 03:14:08 +01:00
|
|
|
|
char *globbed = resolve_tilde(path);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
|
|
|
|
if (!path_exists(globbed)) {
|
|
|
|
|
LOG("%s does not exist, not restoring tree\n", globbed);
|
|
|
|
|
free(globbed);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: refactor the following */
|
2011-06-02 17:21:38 +02:00
|
|
|
|
croot = con_new(NULL, NULL);
|
2011-06-10 18:27:20 +02:00
|
|
|
|
croot->rect = (Rect){
|
|
|
|
|
geometry->x,
|
|
|
|
|
geometry->y,
|
|
|
|
|
geometry->width,
|
|
|
|
|
geometry->height
|
|
|
|
|
};
|
2010-03-27 15:25:51 +01:00
|
|
|
|
focused = croot;
|
|
|
|
|
|
|
|
|
|
tree_append_json(globbed);
|
2010-11-27 01:27:38 +01:00
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
printf("appended tree, using new root\n");
|
|
|
|
|
croot = TAILQ_FIRST(&(croot->nodes_head));
|
|
|
|
|
printf("new root = %p\n", croot);
|
|
|
|
|
Con *out = TAILQ_FIRST(&(croot->nodes_head));
|
|
|
|
|
printf("out = %p\n", out);
|
|
|
|
|
Con *ws = TAILQ_FIRST(&(out->nodes_head));
|
|
|
|
|
printf("ws = %p\n", ws);
|
|
|
|
|
|
2011-12-22 00:15:32 +01:00
|
|
|
|
/* For in-place restarting into v4.2, we need to make sure the new
|
|
|
|
|
* pseudo-output __i3 is present. */
|
|
|
|
|
if (strcmp(out->name, "__i3") != 0) {
|
|
|
|
|
DLOG("Adding pseudo-output __i3 during inplace restart\n");
|
|
|
|
|
Con *__i3 = _create___i3();
|
|
|
|
|
/* Ensure that it is the first output, other places in the code make
|
|
|
|
|
* that assumption. */
|
|
|
|
|
TAILQ_REMOVE(&(croot->nodes_head), __i3, nodes);
|
|
|
|
|
TAILQ_INSERT_HEAD(&(croot->nodes_head), __i3, nodes);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2011-01-05 00:16:10 +01:00
|
|
|
|
* Initializes the tree by creating the root node. The CT_OUTPUT Cons below the
|
|
|
|
|
* root node are created in randr.c for each Output.
|
2010-03-27 15:25:51 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2011-06-10 18:27:20 +02:00
|
|
|
|
void tree_init(xcb_get_geometry_reply_t *geometry) {
|
2011-06-02 17:21:38 +02:00
|
|
|
|
croot = con_new(NULL, NULL);
|
2011-01-04 22:38:33 +01:00
|
|
|
|
FREE(croot->name);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
croot->name = "root";
|
|
|
|
|
croot->type = CT_ROOT;
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
croot->layout = L_SPLITH;
|
2011-06-10 18:27:20 +02:00
|
|
|
|
croot->rect = (Rect){
|
|
|
|
|
geometry->x,
|
|
|
|
|
geometry->y,
|
|
|
|
|
geometry->width,
|
|
|
|
|
geometry->height
|
|
|
|
|
};
|
2011-12-22 00:15:32 +01:00
|
|
|
|
|
|
|
|
|
_create___i3();
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Opens an empty container in the current container
|
|
|
|
|
*
|
|
|
|
|
*/
|
2011-06-02 17:21:38 +02:00
|
|
|
|
Con *tree_open_con(Con *con, i3Window *window) {
|
2010-03-27 15:25:51 +01:00
|
|
|
|
if (con == NULL) {
|
|
|
|
|
/* every focusable Con has a parent (outputs have parent root) */
|
|
|
|
|
con = focused->parent;
|
|
|
|
|
/* If the parent is an output, we are on a workspace. In this case,
|
|
|
|
|
* the new container needs to be opened as a leaf of the workspace. */
|
2011-02-20 23:43:03 +01:00
|
|
|
|
if (con->parent->type == CT_OUTPUT && con->type != CT_DOCKAREA) {
|
2010-03-27 15:25:51 +01:00
|
|
|
|
con = focused;
|
2011-02-20 23:43:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-21 16:49:59 +01:00
|
|
|
|
/* If the currently focused container is a floating container, we
|
2011-05-13 21:29:01 +02:00
|
|
|
|
* attach the new container to the currently focused spot in its
|
|
|
|
|
* workspace. */
|
|
|
|
|
if (con->type == CT_FLOATING_CON) {
|
|
|
|
|
con = con_descend_tiling_focused(con->parent);
|
|
|
|
|
if (con->type != CT_WORKSPACE)
|
|
|
|
|
con = con->parent;
|
|
|
|
|
}
|
2011-02-20 23:43:03 +01:00
|
|
|
|
DLOG("con = %p\n", con);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(con != NULL);
|
|
|
|
|
|
2011-01-26 00:49:22 +01:00
|
|
|
|
/* 3. create the container and attach it to its parent */
|
2011-06-02 17:21:38 +02:00
|
|
|
|
Con *new = con_new(con, window);
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
new->layout = L_SPLITH;
|
2011-01-26 00:49:22 +01:00
|
|
|
|
|
|
|
|
|
/* 4: re-calculate child->percent for each child */
|
2011-01-26 01:12:43 +01:00
|
|
|
|
con_fix_percent(con);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
|
|
|
|
return new;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-30 23:01:58 +01:00
|
|
|
|
static bool _is_con_mapped(Con *con) {
|
|
|
|
|
Con *child;
|
|
|
|
|
|
|
|
|
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
|
|
|
|
|
if (_is_con_mapped(child))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return con->mapped;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/*
|
2011-05-11 20:22:47 +02:00
|
|
|
|
* Closes the given container including all children.
|
|
|
|
|
* Returns true if the container was killed or false if just WM_DELETE was sent
|
|
|
|
|
* and the window is expected to kill itself.
|
2010-03-27 15:25:51 +01:00
|
|
|
|
*
|
2011-09-22 00:28:01 +02:00
|
|
|
|
* The dont_kill_parent flag is specified when the function calls itself
|
|
|
|
|
* recursively while deleting a containers children.
|
|
|
|
|
*
|
|
|
|
|
* The force_set_focus flag is specified in the case of killing a floating
|
|
|
|
|
* window: tree_close() will be invoked for the CT_FLOATINGCON (the parent
|
|
|
|
|
* container) and focus should be set there.
|
|
|
|
|
*
|
2010-03-27 15:25:51 +01:00
|
|
|
|
*/
|
2011-09-22 00:28:01 +02:00
|
|
|
|
bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus) {
|
2010-12-28 02:27:11 +01:00
|
|
|
|
bool was_mapped = con->mapped;
|
2010-07-17 00:54:03 +02:00
|
|
|
|
Con *parent = con->parent;
|
|
|
|
|
|
2010-12-30 23:01:58 +01:00
|
|
|
|
if (!was_mapped) {
|
|
|
|
|
/* Even if the container itself is not mapped, its children may be
|
|
|
|
|
* mapped (for example split containers don't have a mapped window on
|
|
|
|
|
* their own but usually contain mapped children). */
|
|
|
|
|
was_mapped = _is_con_mapped(con);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/* Get the container which is next focused */
|
2010-07-17 13:27:34 +02:00
|
|
|
|
Con *next = con_next_focused(con);
|
2011-01-04 22:40:05 +01:00
|
|
|
|
DLOG("next = %p, focused = %p\n", next, focused);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2010-06-02 17:04:26 +02:00
|
|
|
|
DLOG("closing %p, kill_window = %d\n", con, kill_window);
|
2011-05-11 20:22:47 +02:00
|
|
|
|
Con *child, *nextchild;
|
|
|
|
|
bool abort_kill = false;
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/* We cannot use TAILQ_FOREACH because the children get deleted
|
|
|
|
|
* in their parent’s nodes_head */
|
2011-05-11 20:22:47 +02:00
|
|
|
|
for (child = TAILQ_FIRST(&(con->nodes_head)); child; ) {
|
|
|
|
|
nextchild = TAILQ_NEXT(child, nodes);
|
2010-11-14 20:14:09 +01:00
|
|
|
|
DLOG("killing child=%p\n", child);
|
2011-09-22 00:28:01 +02:00
|
|
|
|
if (!tree_close(child, kill_window, true, false))
|
2011-05-11 20:22:47 +02:00
|
|
|
|
abort_kill = true;
|
|
|
|
|
child = nextchild;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (abort_kill) {
|
|
|
|
|
DLOG("One of the children could not be killed immediately (WM_DELETE sent), aborting.\n");
|
|
|
|
|
return false;
|
2010-05-15 00:16:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (con->window != NULL) {
|
2011-05-13 20:41:03 +02:00
|
|
|
|
if (kill_window != DONT_KILL_WINDOW) {
|
|
|
|
|
x_window_kill(con->window->id, kill_window);
|
2011-05-11 20:22:47 +02:00
|
|
|
|
return false;
|
2011-03-09 18:08:01 +01:00
|
|
|
|
} else {
|
2011-07-10 23:44:13 +02:00
|
|
|
|
xcb_void_cookie_t cookie;
|
2010-05-15 00:16:59 +02:00
|
|
|
|
/* un-parent the window */
|
2011-07-10 23:44:13 +02:00
|
|
|
|
cookie = xcb_reparent_window(conn, con->window->id, root, 0, 0);
|
|
|
|
|
|
|
|
|
|
/* Ignore X11 errors for the ReparentWindow request.
|
|
|
|
|
* X11 Errors are returned when the window was already destroyed */
|
|
|
|
|
add_ignore_event(cookie.sequence, 0);
|
|
|
|
|
|
2011-05-13 17:03:52 +02:00
|
|
|
|
/* We are no longer handling this window, thus set WM_STATE to
|
|
|
|
|
* WM_STATE_WITHDRAWN (see ICCCM 4.1.3.1) */
|
|
|
|
|
long data[] = { XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE };
|
2011-07-10 23:44:13 +02:00
|
|
|
|
cookie = xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
|
|
|
|
|
con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data);
|
|
|
|
|
|
|
|
|
|
/* Ignore X11 errors for the ReparentWindow request.
|
|
|
|
|
* X11 Errors are returned when the window was already destroyed */
|
|
|
|
|
add_ignore_event(cookie.sequence, 0);
|
2010-05-15 00:16:59 +02:00
|
|
|
|
}
|
2011-01-04 22:39:45 +01:00
|
|
|
|
FREE(con->window->class_class);
|
|
|
|
|
FREE(con->window->class_instance);
|
2012-08-07 21:23:06 +02:00
|
|
|
|
i3string_free(con->window->name);
|
2010-05-15 00:16:59 +02:00
|
|
|
|
free(con->window);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* kill the X11 part of this container */
|
|
|
|
|
x_con_kill(con);
|
|
|
|
|
|
|
|
|
|
con_detach(con);
|
2012-09-22 13:48:22 +02:00
|
|
|
|
|
|
|
|
|
/* disable urgency timer, if needed */
|
|
|
|
|
if (con->urgency_timer != NULL) {
|
|
|
|
|
DLOG("Removing urgency timer of con %p\n", con);
|
|
|
|
|
workspace_update_urgent_flag(con_get_workspace(con));
|
|
|
|
|
ev_timer_stop(main_loop, con->urgency_timer);
|
|
|
|
|
FREE(con->urgency_timer);
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-30 02:39:14 +01:00
|
|
|
|
if (con->type != CT_FLOATING_CON) {
|
|
|
|
|
/* If the container is *not* floating, we might need to re-distribute
|
|
|
|
|
* percentage values for the resized containers. */
|
2011-01-26 01:12:43 +01:00
|
|
|
|
con_fix_percent(parent);
|
2010-12-30 02:39:14 +01:00
|
|
|
|
}
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2010-06-28 22:37:35 +02:00
|
|
|
|
if (con_is_floating(con)) {
|
2011-01-04 22:40:05 +01:00
|
|
|
|
Con *ws = con_get_workspace(con);
|
2010-06-28 22:37:35 +02:00
|
|
|
|
DLOG("Container was floating, killing floating container\n");
|
2011-09-22 00:28:01 +02:00
|
|
|
|
tree_close(parent, DONT_KILL_WINDOW, false, (con == focused));
|
2011-01-04 22:40:05 +01:00
|
|
|
|
DLOG("parent container killed\n");
|
|
|
|
|
if (con == focused) {
|
|
|
|
|
DLOG("This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws);
|
2011-01-27 16:08:25 +01:00
|
|
|
|
/* go down the focus stack as far as possible */
|
|
|
|
|
next = con_descend_focused(ws);
|
2011-01-08 00:10:49 +01:00
|
|
|
|
|
2011-01-04 22:40:05 +01:00
|
|
|
|
dont_kill_parent = true;
|
|
|
|
|
DLOG("Alright, focusing %p\n", next);
|
|
|
|
|
} else {
|
|
|
|
|
next = NULL;
|
|
|
|
|
}
|
2010-06-28 22:37:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
free(con->name);
|
2011-03-20 14:07:16 +01:00
|
|
|
|
FREE(con->deco_render_params);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
TAILQ_REMOVE(&all_cons, con, all_cons);
|
|
|
|
|
free(con);
|
|
|
|
|
|
2010-06-30 22:37:57 +02:00
|
|
|
|
/* in the case of floating windows, we already focused another container
|
|
|
|
|
* when closing the parent, so we can exit now. */
|
2011-01-04 22:40:05 +01:00
|
|
|
|
if (!next) {
|
|
|
|
|
DLOG("No next container, i will just exit now\n");
|
2011-05-11 20:22:47 +02:00
|
|
|
|
return true;
|
2011-01-04 22:40:05 +01:00
|
|
|
|
}
|
2010-06-30 22:37:57 +02:00
|
|
|
|
|
2011-01-02 18:08:45 +01:00
|
|
|
|
if (was_mapped || con == focused) {
|
2011-05-13 20:41:03 +02:00
|
|
|
|
if ((kill_window != DONT_KILL_WINDOW) || !dont_kill_parent || con == focused) {
|
2011-01-28 00:09:48 +01:00
|
|
|
|
DLOG("focusing %p / %s\n", next, next->name);
|
2011-03-03 14:03:06 +01:00
|
|
|
|
if (next->type == CT_DOCKAREA) {
|
|
|
|
|
/* Instead of focusing the dockarea, we need to restore focus to the workspace */
|
|
|
|
|
con_focus(con_descend_focused(output_get_content(next->parent)));
|
|
|
|
|
} else {
|
2011-09-22 00:28:01 +02:00
|
|
|
|
if (!force_set_focus && con != focused)
|
2011-09-18 18:43:02 +02:00
|
|
|
|
DLOG("not changing focus, the container was not focused before\n");
|
|
|
|
|
else con_focus(next);
|
2011-03-03 14:03:06 +01:00
|
|
|
|
}
|
2011-01-28 00:09:48 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2012-08-03 23:58:02 +02:00
|
|
|
|
DLOG("not focusing because we're not killing anybody\n");
|
2011-01-28 00:09:48 +01:00
|
|
|
|
}
|
2010-12-28 02:27:11 +01:00
|
|
|
|
} else {
|
|
|
|
|
DLOG("not focusing, was not mapped\n");
|
|
|
|
|
}
|
2010-07-17 00:54:47 +02:00
|
|
|
|
|
|
|
|
|
/* check if the parent container is empty now and close it */
|
2011-02-14 18:08:36 +01:00
|
|
|
|
if (!dont_kill_parent)
|
|
|
|
|
CALL(parent, on_remove_child);
|
2011-05-11 20:22:47 +02:00
|
|
|
|
|
|
|
|
|
return true;
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:35:05 +02:00
|
|
|
|
/*
|
|
|
|
|
* Closes the current container using tree_close().
|
|
|
|
|
*
|
|
|
|
|
*/
|
2011-05-13 20:41:03 +02:00
|
|
|
|
void tree_close_con(kill_window_t kill_window) {
|
2010-03-27 15:25:51 +01:00
|
|
|
|
assert(focused != NULL);
|
2010-05-31 00:11:11 +02:00
|
|
|
|
if (focused->type == CT_WORKSPACE) {
|
2010-03-27 15:25:51 +01:00
|
|
|
|
LOG("Cannot close workspace\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-30 23:01:58 +01:00
|
|
|
|
/* There *should* be no possibility to focus outputs / root container */
|
|
|
|
|
assert(focused->type != CT_OUTPUT);
|
|
|
|
|
assert(focused->type != CT_ROOT);
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/* Kill con */
|
2011-09-22 00:28:01 +02:00
|
|
|
|
tree_close(focused, kill_window, false, false);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Splits (horizontally or vertically) the given container by creating a new
|
|
|
|
|
* container which contains the old one and the future ones.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void tree_split(Con *con, orientation_t orientation) {
|
2010-05-10 09:33:10 +02:00
|
|
|
|
/* for a workspace, we just need to change orientation */
|
2010-05-31 00:11:11 +02:00
|
|
|
|
if (con->type == CT_WORKSPACE) {
|
2010-06-02 23:32:05 +02:00
|
|
|
|
DLOG("Workspace, simply changing orientation to %d\n", orientation);
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
|
2010-05-10 09:33:10 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2010-06-01 22:45:18 +02:00
|
|
|
|
|
|
|
|
|
Con *parent = con->parent;
|
2012-01-22 18:24:55 +01:00
|
|
|
|
|
|
|
|
|
/* Force re-rendering to make the indicator border visible. */
|
|
|
|
|
FREE(con->deco_render_params);
|
|
|
|
|
FREE(parent->deco_render_params);
|
|
|
|
|
|
2010-06-01 22:45:18 +02:00
|
|
|
|
/* if we are in a container whose parent contains only one
|
2010-11-14 23:18:39 +01:00
|
|
|
|
* child (its split functionality is unused so far), we just change the
|
|
|
|
|
* orientation (more intuitive than splitting again) */
|
2012-01-30 17:13:44 +01:00
|
|
|
|
if (con_num_children(parent) == 1 &&
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
(parent->layout == L_SPLITH ||
|
|
|
|
|
parent->layout == L_SPLITV)) {
|
|
|
|
|
parent->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
|
2010-11-14 23:18:39 +01:00
|
|
|
|
DLOG("Just changing orientation of existing container\n");
|
2010-06-01 22:45:18 +02:00
|
|
|
|
return;
|
2010-06-02 23:32:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DLOG("Splitting in orientation %d\n", orientation);
|
2010-06-01 22:45:18 +02:00
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/* 2: replace it with a new Con */
|
2011-06-02 17:21:38 +02:00
|
|
|
|
Con *new = con_new(NULL, NULL);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes);
|
|
|
|
|
TAILQ_REPLACE(&(parent->focus_head), con, new, focused);
|
|
|
|
|
new->parent = parent;
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
new->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2010-11-29 11:24:12 +01:00
|
|
|
|
/* 3: swap 'percent' (resize factor) */
|
|
|
|
|
new->percent = con->percent;
|
|
|
|
|
con->percent = 0.0;
|
|
|
|
|
|
|
|
|
|
/* 4: add it as a child to the new Con */
|
2010-11-28 01:51:16 +01:00
|
|
|
|
con_attach(con, new, false);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:35:05 +02:00
|
|
|
|
/*
|
2012-05-26 23:36:25 +02:00
|
|
|
|
* Moves focus one level up. Returns true if focus changed.
|
2010-07-13 11:35:05 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2012-05-26 23:36:25 +02:00
|
|
|
|
bool level_up(void) {
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/* We can focus up to the workspace, but not any higher in the tree */
|
2011-03-03 14:30:13 +01:00
|
|
|
|
if ((focused->parent->type != CT_CON &&
|
2012-10-04 18:50:33 +02:00
|
|
|
|
focused->parent->type != CT_FLOATING_CON &&
|
|
|
|
|
focused->parent->type != CT_WORKSPACE) ||
|
2011-03-03 14:30:13 +01:00
|
|
|
|
focused->type == CT_WORKSPACE) {
|
2012-08-05 20:56:33 +02:00
|
|
|
|
ELOG("'focus parent': Focus is already on the workspace, cannot go higher than that.\n");
|
2012-05-26 23:36:25 +02:00
|
|
|
|
return false;
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
con_focus(focused->parent);
|
2012-05-26 23:36:25 +02:00
|
|
|
|
return true;
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:35:05 +02:00
|
|
|
|
/*
|
2012-05-26 23:36:25 +02:00
|
|
|
|
* Moves focus one level down. Returns true if focus changed.
|
2010-07-13 11:35:05 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2012-05-26 23:36:25 +02:00
|
|
|
|
bool level_down(void) {
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/* Go down the focus stack of the current node */
|
|
|
|
|
Con *next = TAILQ_FIRST(&(focused->focus_head));
|
|
|
|
|
if (next == TAILQ_END(&(focused->focus_head))) {
|
|
|
|
|
printf("cannot go down\n");
|
2012-05-26 23:36:25 +02:00
|
|
|
|
return false;
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
con_focus(next);
|
2012-05-26 23:36:25 +02:00
|
|
|
|
return true;
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mark_unmapped(Con *con) {
|
|
|
|
|
Con *current;
|
|
|
|
|
|
|
|
|
|
con->mapped = false;
|
|
|
|
|
TAILQ_FOREACH(current, &(con->nodes_head), nodes)
|
|
|
|
|
mark_unmapped(current);
|
2010-11-20 19:11:43 +01:00
|
|
|
|
if (con->type == CT_WORKSPACE) {
|
2011-01-17 14:11:56 +01:00
|
|
|
|
/* We need to call mark_unmapped on floating nodes aswell since we can
|
|
|
|
|
* make containers floating. */
|
|
|
|
|
TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
|
|
|
|
|
mark_unmapped(current);
|
2010-11-20 19:11:43 +01:00
|
|
|
|
}
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:35:05 +02:00
|
|
|
|
/*
|
|
|
|
|
* Renders the tree, that is rendering all outputs using render_con() and
|
|
|
|
|
* pushing the changes to X11 using x_push_changes().
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-03-31 10:53:04 +02:00
|
|
|
|
void tree_render(void) {
|
2010-03-27 15:25:51 +01:00
|
|
|
|
if (croot == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2011-01-07 20:58:58 +01:00
|
|
|
|
DLOG("-- BEGIN RENDERING --\n");
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/* Reset map state for all nodes in tree */
|
|
|
|
|
/* TODO: a nicer method to walk all nodes would be good, maybe? */
|
|
|
|
|
mark_unmapped(croot);
|
|
|
|
|
croot->mapped = true;
|
|
|
|
|
|
2011-06-10 18:27:20 +02:00
|
|
|
|
render_con(croot, false);
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
x_push_changes(croot);
|
2011-01-07 20:58:58 +01:00
|
|
|
|
DLOG("-- END RENDERING --\n");
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:35:05 +02:00
|
|
|
|
/*
|
2011-06-11 19:15:16 +02:00
|
|
|
|
* Recursive function to walk the tree until a con can be found to focus.
|
2010-07-13 11:35:05 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2011-06-11 19:15:16 +02:00
|
|
|
|
static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) {
|
2011-08-06 18:28:05 +02:00
|
|
|
|
/* Stop recursing at workspaces after attempting to switch to next
|
|
|
|
|
* workspace if possible. */
|
|
|
|
|
if (con->type == CT_WORKSPACE) {
|
|
|
|
|
Output *current_output = get_output_containing(con->rect.x, con->rect.y);
|
|
|
|
|
Output *next_output;
|
|
|
|
|
|
|
|
|
|
if (!current_output)
|
|
|
|
|
return false;
|
|
|
|
|
DLOG("Current output is %s\n", current_output->name);
|
|
|
|
|
|
|
|
|
|
/* Try to find next output */
|
|
|
|
|
direction_t direction;
|
|
|
|
|
if (way == 'n' && orientation == HORIZ)
|
|
|
|
|
direction = D_RIGHT;
|
|
|
|
|
else if (way == 'p' && orientation == HORIZ)
|
|
|
|
|
direction = D_LEFT;
|
|
|
|
|
else if (way == 'n' && orientation == VERT)
|
|
|
|
|
direction = D_DOWN;
|
2011-08-07 15:46:24 +02:00
|
|
|
|
else if (way == 'p' && orientation == VERT)
|
|
|
|
|
direction = D_UP;
|
2011-08-06 18:28:05 +02:00
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
|
2012-09-22 16:49:37 +02:00
|
|
|
|
next_output = get_output_next(direction, current_output, CLOSEST_OUTPUT);
|
2011-08-06 18:28:05 +02:00
|
|
|
|
if (!next_output)
|
|
|
|
|
return false;
|
|
|
|
|
DLOG("Next output is %s\n", next_output->name);
|
|
|
|
|
|
|
|
|
|
/* Find visible workspace on next output */
|
2011-08-07 15:24:51 +02:00
|
|
|
|
Con *workspace = NULL;
|
2011-08-06 18:28:05 +02:00
|
|
|
|
GREP_FIRST(workspace, output_get_content(next_output->con), workspace_is_visible(child));
|
|
|
|
|
|
|
|
|
|
/* Show next workspace and focus appropriate container if possible. */
|
2011-08-07 15:24:51 +02:00
|
|
|
|
if (!workspace)
|
|
|
|
|
return false;
|
2011-08-06 18:28:05 +02:00
|
|
|
|
|
2011-10-02 17:54:23 +02:00
|
|
|
|
workspace_show(workspace);
|
2011-08-07 15:24:51 +02:00
|
|
|
|
Con *focus = con_descend_direction(workspace, direction);
|
2011-08-12 03:54:59 +02:00
|
|
|
|
if (focus) {
|
2011-08-07 15:24:51 +02:00
|
|
|
|
con_focus(focus);
|
2011-08-12 03:54:59 +02:00
|
|
|
|
x_set_warp_to(&(focus->rect));
|
|
|
|
|
}
|
2011-08-07 15:24:51 +02:00
|
|
|
|
return true;
|
2011-08-06 18:28:05 +02:00
|
|
|
|
}
|
2011-06-11 19:15:16 +02:00
|
|
|
|
|
2011-09-17 20:28:41 +02:00
|
|
|
|
Con *parent = con->parent;
|
|
|
|
|
|
2011-06-11 19:15:16 +02:00
|
|
|
|
if (con->type == CT_FLOATING_CON) {
|
2011-09-17 20:28:41 +02:00
|
|
|
|
/* left/right focuses the previous/next floating container */
|
|
|
|
|
if (orientation == HORIZ) {
|
|
|
|
|
Con *next;
|
|
|
|
|
if (way == 'n')
|
|
|
|
|
next = TAILQ_NEXT(con, floating_windows);
|
|
|
|
|
else next = TAILQ_PREV(con, floating_head, floating_windows);
|
|
|
|
|
|
|
|
|
|
/* If there is no next/previous container, wrap */
|
|
|
|
|
if (!next) {
|
|
|
|
|
if (way == 'n')
|
|
|
|
|
next = TAILQ_FIRST(&(parent->floating_head));
|
|
|
|
|
else next = TAILQ_LAST(&(parent->floating_head), floating_head);
|
|
|
|
|
}
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2011-09-17 20:28:41 +02:00
|
|
|
|
/* Still no next/previous container? bail out */
|
|
|
|
|
if (!next)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
con_focus(con_descend_focused(next));
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
/* up/down cycles through the Z-index */
|
|
|
|
|
/* TODO: implement cycling through the z-index */
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-06-11 19:15:16 +02:00
|
|
|
|
|
|
|
|
|
/* If the orientation does not match or there is no other con to focus, we
|
|
|
|
|
* need to go higher in the hierarchy */
|
|
|
|
|
if (con_orientation(parent) != orientation ||
|
|
|
|
|
con_num_children(parent) == 1)
|
|
|
|
|
return _tree_next(parent, way, orientation, wrap);
|
|
|
|
|
|
|
|
|
|
Con *current = TAILQ_FIRST(&(parent->focus_head));
|
|
|
|
|
/* TODO: when can the following happen (except for floating windows, which
|
|
|
|
|
* are handled above)? */
|
2011-01-27 16:51:16 +01:00
|
|
|
|
if (TAILQ_EMPTY(&(parent->nodes_head))) {
|
2011-06-11 19:15:16 +02:00
|
|
|
|
DLOG("nothing to focus\n");
|
|
|
|
|
return false;
|
2011-01-27 16:51:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
Con *next;
|
2011-06-11 19:15:16 +02:00
|
|
|
|
if (way == 'n')
|
2010-03-27 15:25:51 +01:00
|
|
|
|
next = TAILQ_NEXT(current, nodes);
|
2011-06-11 19:15:16 +02:00
|
|
|
|
else next = TAILQ_PREV(current, nodes_head, nodes);
|
|
|
|
|
|
|
|
|
|
if (!next) {
|
|
|
|
|
if (!config.force_focus_wrapping) {
|
|
|
|
|
/* If there is no next/previous container, we check if we can focus one
|
|
|
|
|
* when going higher (without wrapping, though). If so, we are done, if
|
|
|
|
|
* not, we wrap */
|
|
|
|
|
if (_tree_next(parent, way, orientation, false))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (!wrap)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (way == 'n')
|
2010-03-27 15:25:51 +01:00
|
|
|
|
next = TAILQ_FIRST(&(parent->nodes_head));
|
2011-06-11 19:15:16 +02:00
|
|
|
|
else next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-27 00:53:02 +02:00
|
|
|
|
/* Don't violate fullscreen focus restrictions. */
|
|
|
|
|
if (!con_fullscreen_permits_focusing(next))
|
|
|
|
|
return false;
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
/* 3: focus choice comes in here. at the moment we will go down
|
|
|
|
|
* until we find a window */
|
|
|
|
|
/* TODO: check for window, atm we only go down as far as possible */
|
2011-01-27 16:08:25 +01:00
|
|
|
|
con_focus(con_descend_focused(next));
|
2011-06-11 19:15:16 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Changes focus in the given way (next/previous) and given orientation
|
|
|
|
|
* (horizontal/vertical).
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void tree_next(char way, orientation_t orientation) {
|
|
|
|
|
_tree_next(focused, way, orientation, true);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-07 22:21:41 +01:00
|
|
|
|
/*
|
|
|
|
|
* tree_flatten() removes pairs of redundant split containers, e.g.:
|
|
|
|
|
* [workspace, horizontal]
|
|
|
|
|
* [v-split] [child3]
|
|
|
|
|
* [h-split]
|
|
|
|
|
* [child1] [child2]
|
|
|
|
|
* In this example, the v-split and h-split container are redundant.
|
|
|
|
|
* Such a situation can be created by moving containers in a direction which is
|
|
|
|
|
* not the orientation of their parent container. i3 needs to create a new
|
|
|
|
|
* split container then and if you move containers this way multiple times,
|
|
|
|
|
* redundant chains of split-containers can be the result.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void tree_flatten(Con *con) {
|
|
|
|
|
Con *current, *child, *parent = con->parent;
|
|
|
|
|
DLOG("Checking if I can flatten con = %p / %s\n", con, con->name);
|
|
|
|
|
|
|
|
|
|
/* We only consider normal containers without windows */
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
if (con->type != CT_CON ||
|
|
|
|
|
parent->layout == L_OUTPUT || /* con == "content" */
|
|
|
|
|
con->window != NULL)
|
2011-01-07 22:21:41 +01:00
|
|
|
|
goto recurse;
|
|
|
|
|
|
|
|
|
|
/* Ensure it got only one child */
|
|
|
|
|
child = TAILQ_FIRST(&(con->nodes_head));
|
2011-01-19 09:31:31 +01:00
|
|
|
|
if (child == NULL || TAILQ_NEXT(child, nodes) != NULL)
|
2011-01-07 22:21:41 +01:00
|
|
|
|
goto recurse;
|
|
|
|
|
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
DLOG("child = %p, con = %p, parent = %p\n", child, con, parent);
|
|
|
|
|
|
2011-01-07 22:21:41 +01:00
|
|
|
|
/* The child must have a different orientation than the con but the same as
|
|
|
|
|
* the con’s parent to be redundant */
|
2012-10-03 19:10:48 +02:00
|
|
|
|
if (!con_is_split(con) ||
|
|
|
|
|
!con_is_split(child) ||
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
con_orientation(con) == con_orientation(child) ||
|
|
|
|
|
con_orientation(child) != con_orientation(parent))
|
2011-01-07 22:21:41 +01:00
|
|
|
|
goto recurse;
|
|
|
|
|
|
|
|
|
|
DLOG("Alright, I have to flatten this situation now. Stay calm.\n");
|
|
|
|
|
/* 1: save focus */
|
|
|
|
|
Con *focus_next = TAILQ_FIRST(&(child->focus_head));
|
|
|
|
|
|
|
|
|
|
DLOG("detaching...\n");
|
|
|
|
|
/* 2: re-attach the children to the parent before con */
|
|
|
|
|
while (!TAILQ_EMPTY(&(child->nodes_head))) {
|
|
|
|
|
current = TAILQ_FIRST(&(child->nodes_head));
|
|
|
|
|
DLOG("detaching current=%p / %s\n", current, current->name);
|
|
|
|
|
con_detach(current);
|
|
|
|
|
DLOG("re-attaching\n");
|
|
|
|
|
/* We don’t use con_attach() here because for a CT_CON, the special
|
|
|
|
|
* case handling of con_attach() does not trigger. So all it would do
|
|
|
|
|
* is calling TAILQ_INSERT_AFTER, but with the wrong container. So we
|
|
|
|
|
* directly use the TAILQ macros. */
|
|
|
|
|
current->parent = parent;
|
|
|
|
|
TAILQ_INSERT_BEFORE(con, current, nodes);
|
|
|
|
|
DLOG("attaching to focus list\n");
|
|
|
|
|
TAILQ_INSERT_TAIL(&(parent->focus_head), current, focused);
|
2011-01-27 23:58:49 +01:00
|
|
|
|
current->percent = con->percent;
|
2011-01-07 22:21:41 +01:00
|
|
|
|
}
|
|
|
|
|
DLOG("re-attached all\n");
|
|
|
|
|
|
|
|
|
|
/* 3: restore focus, if con was focused */
|
|
|
|
|
if (focus_next != NULL &&
|
|
|
|
|
TAILQ_FIRST(&(parent->focus_head)) == con) {
|
|
|
|
|
DLOG("restoring focus to focus_next=%p\n", focus_next);
|
|
|
|
|
TAILQ_REMOVE(&(parent->focus_head), focus_next, focused);
|
|
|
|
|
TAILQ_INSERT_HEAD(&(parent->focus_head), focus_next, focused);
|
|
|
|
|
DLOG("restored focus.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 4: close the redundant cons */
|
|
|
|
|
DLOG("closing redundant cons\n");
|
2011-09-22 00:28:01 +02:00
|
|
|
|
tree_close(con, DONT_KILL_WINDOW, true, false);
|
2011-01-07 22:21:41 +01:00
|
|
|
|
|
|
|
|
|
/* Well, we got to abort the recursion here because we destroyed the
|
|
|
|
|
* container. However, if tree_flatten() is called sufficiently often,
|
|
|
|
|
* there can’t be the situation of having two pairs of redundant containers
|
|
|
|
|
* at once. Therefore, we can safely abort the recursion on this level
|
|
|
|
|
* after flattening. */
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
recurse:
|
|
|
|
|
/* We cannot use normal foreach here because tree_flatten might close the
|
|
|
|
|
* current container. */
|
|
|
|
|
current = TAILQ_FIRST(&(con->nodes_head));
|
|
|
|
|
while (current != NULL) {
|
|
|
|
|
Con *next = TAILQ_NEXT(current, nodes);
|
|
|
|
|
tree_flatten(current);
|
|
|
|
|
current = next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current = TAILQ_FIRST(&(con->floating_head));
|
|
|
|
|
while (current != NULL) {
|
|
|
|
|
Con *next = TAILQ_NEXT(current, floating_windows);
|
|
|
|
|
tree_flatten(current);
|
|
|
|
|
current = next;
|
|
|
|
|
}
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|