enlightenment/src/modules/tiling/window_tree.c

255 lines
5.9 KiB
C

#include "e.h"
#include "window_tree.h"
void
tiling_window_tree_walk(Window_Tree *root, void (*func)(void *))
{
Eina_Inlist *itr_safe;
Window_Tree *itr;
if (!root)
return;
EINA_INLIST_FOREACH_SAFE(root->children, itr_safe, itr)
{
tiling_window_tree_free(itr);
}
func(root);
}
void
tiling_window_tree_free(Window_Tree *root)
{
tiling_window_tree_walk(root, free);
}
static void
_tiling_window_tree_split_add(Window_Tree *parent, Window_Tree *new_node)
{
/* Make a new node for the parent client and split the weights in half. */
Window_Tree *new_parent_client = calloc(1, sizeof(*new_node));
new_node->parent = parent;
new_parent_client->parent = parent;
new_parent_client->client = parent->client;
parent->client = NULL;
new_parent_client->weight = 0.5;
new_node->weight = 0.5;
parent->children = eina_inlist_append(parent->children,
EINA_INLIST_GET(new_parent_client));
parent->children = eina_inlist_append(parent->children,
EINA_INLIST_GET(new_node));
}
static void
_tiling_window_tree_parent_add(Window_Tree *parent, Window_Tree *new_node)
{
/* Adjust existing children's weights */
Window_Tree *itr;
int children_count = eina_inlist_count(parent->children);
float weight = 1.0 / (children_count + 1);
new_node->parent = parent;
new_node->weight = weight;
weight *= children_count;
EINA_INLIST_FOREACH(parent->children, itr)
{
itr->weight *= weight;
}
parent->children = eina_inlist_append(parent->children,
EINA_INLIST_GET(new_node));
}
static int
_tiling_window_tree_split_type_get(Window_Tree *node)
{
int ret = 0;
while (node->parent)
{
ret++;
node = node->parent;
}
return ret % 2;
}
Window_Tree *
tiling_window_tree_add(Window_Tree *root, Window_Tree *parent, E_Client *client, Tiling_Split_Type split_type)
{
Window_Tree *new_node = calloc(1, sizeof(*new_node));
new_node->client = client;
Tiling_Split_Type parent_split_type;
if (!root)
{
new_node->weight = 1.0;
return new_node;
}
else if (!parent)
{
parent = root;
}
parent_split_type = _tiling_window_tree_split_type_get(parent);
if (parent_split_type == split_type)
{
if (parent->children)
{
_tiling_window_tree_parent_add(parent, new_node);
}
else
{
_tiling_window_tree_split_add(parent, new_node);
}
}
else
{
Window_Tree *grand_parent = parent->parent;
if (grand_parent && grand_parent->children)
{
_tiling_window_tree_parent_add(grand_parent, new_node);
}
else
{
_tiling_window_tree_split_add(parent, new_node);
}
}
return new_node;
}
Window_Tree *
tiling_window_tree_remove(Window_Tree *root, Window_Tree *item)
{
if (root == item)
{
free(item);
return NULL;
}
else if (!item->client)
{
ERR("Tried deleting node %p that doesn't have a client.", item);
return root;
}
int children_count = eina_inlist_count(item->parent->children);
if (children_count <= 2)
{
Window_Tree *item_keep = NULL;
/* Adjust existing children's weights */
EINA_INLIST_FOREACH(item->parent->children, item_keep)
{
if (item_keep != item)
break;
}
item->parent->client = item_keep->client;
item->parent->children = item_keep->children;
/* Update the children's parent. */
{
Window_Tree *itr;
EINA_INLIST_FOREACH(item->parent->children, itr)
{
itr->parent = item->parent;
}
}
free(item_keep);
}
else
{
Window_Tree *itr;
float weight = (((float) children_count) - 1.0) / children_count;
item->parent->children = eina_inlist_remove(item->parent->children,
EINA_INLIST_GET(item));
/* Adjust existing children's weights */
EINA_INLIST_FOREACH(item->parent->children, itr)
{
itr->weight /= weight;
}
}
free(item);
return root;
}
Window_Tree *
tiling_window_tree_client_find(Window_Tree *root, E_Client *client)
{
Window_Tree *itr;
if (!client)
return NULL;
if (!root || (root->client == client))
return root;
EINA_INLIST_FOREACH(root->children, itr)
{
Window_Tree *ret;
ret = tiling_window_tree_client_find(itr, client);
if (ret)
return ret;
}
return NULL;
}
/* FIXME: Deduplicate this func. */
static void
_e_client_move_resize(E_Client *ec,
int x,
int y,
int w,
int h)
{
DBG("%p -> %dx%d+%d+%d", ec, w, h, x, y);
evas_object_geometry_set(ec->frame, x, y, w, h);
}
void
_tiling_window_tree_level_apply(Window_Tree *root, Evas_Coord x, Evas_Coord y,
Evas_Coord w, Evas_Coord h, int level)
{
Window_Tree *itr;
Tiling_Split_Type split_type = level % 2;
if (root->client)
_e_client_move_resize(root->client, x, y, w, h);
if (split_type == TILING_SPLIT_HORIZONTAL)
{
EINA_INLIST_FOREACH(root->children, itr)
{
Evas_Coord itw = w * itr->weight;
_tiling_window_tree_level_apply(itr, x, y, itw, h, level + 1);
x += itw;
}
}
else if (split_type == TILING_SPLIT_VERTICAL)
{
EINA_INLIST_FOREACH(root->children, itr)
{
Evas_Coord ith = h * itr->weight;
_tiling_window_tree_level_apply(itr, x, y, w, ith, level + 1);
y += ith;
}
}
}
void
tiling_window_tree_apply(Window_Tree *root, Evas_Coord x, Evas_Coord y,
Evas_Coord w, Evas_Coord h)
{
_tiling_window_tree_level_apply(root, x, y, w, h, 0);
}