enlightenment_my/src/modules/tiling/window_tree.c

408 lines
10 KiB
C
Raw Normal View History

#include "e.h"
2014-01-13 04:18:48 -08:00
#include "window_tree.h"
#include "e_mod_tiling.h"
2014-01-13 04:18:48 -08:00
void
2014-01-13 09:18:08 -08:00
tiling_window_tree_walk(Window_Tree *root, void (*func)(void *))
2014-01-13 04:18:48 -08:00
{
Eina_Inlist *itr_safe;
Window_Tree *itr;
2014-01-13 09:28:46 -08:00
if (!root)
return;
2014-01-13 04:18:48 -08:00
EINA_INLIST_FOREACH_SAFE(root->children, itr_safe, itr)
{
tiling_window_tree_free(itr);
}
2014-01-13 09:18:08 -08:00
func(root);
}
void
tiling_window_tree_free(Window_Tree *root)
{
tiling_window_tree_walk(root, free);
2014-01-13 04:18:48 -08:00
}
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;
}
2014-01-13 04:18:48 -08:00
Window_Tree *
tiling_window_tree_add(Window_Tree *root, Window_Tree *parent, E_Client *client, Tiling_Split_Type split_type)
2014-01-13 04:18:48 -08:00
{
Window_Tree *new_node = calloc(1, sizeof(*new_node));
new_node->client = client;
Tiling_Split_Type parent_split_type;
if (!root)
2014-01-13 04:18:48 -08:00
{
new_node->weight = 1.0;
return new_node;
}
else if (!parent)
2014-01-13 04:18:48 -08:00
{
parent = root;
parent_split_type = _tiling_window_tree_split_type_get(parent);
if ((parent_split_type != split_type) && (parent->children))
{
parent = (Window_Tree *) parent->children;
}
}
2014-01-13 04:18:48 -08:00
parent_split_type = _tiling_window_tree_split_type_get(parent);
2014-01-13 04:18:48 -08:00
if (parent_split_type == split_type)
{
if (parent->children)
2014-01-13 04:18:48 -08:00
{
_tiling_window_tree_parent_add(parent, new_node);
}
else
{
_tiling_window_tree_split_add(parent, new_node);
2014-01-13 04:18:48 -08:00
}
}
else
{
Window_Tree *grand_parent = parent->parent;
if (grand_parent && grand_parent->children)
{
_tiling_window_tree_parent_add(grand_parent, new_node);
}
else
{
root = calloc(1, sizeof(*root));
_tiling_window_tree_split_add(parent, new_node);
root->weight = 1.0;
root->children = eina_inlist_append(root->children,
EINA_INLIST_GET(parent));
parent->parent = root;
}
2014-01-13 04:18:48 -08:00
}
return root;
2014-01-13 04:18:48 -08:00
}
Window_Tree *
tiling_window_tree_remove(Window_Tree *root, Window_Tree *item)
2014-01-13 04:18:48 -08:00
{
if (root == item)
{
free(item);
return NULL;
}
2014-01-13 09:48:17 -08:00
else if (!item->client)
{
ERR("Tried deleting node %p that doesn't have a client.", item);
return root;
}
2014-01-13 04:18:48 -08:00
2014-01-14 06:03:18 -08:00
Window_Tree *parent = item->parent;
2014-01-13 04:18:48 -08:00
int children_count = eina_inlist_count(item->parent->children);
if (children_count <= 2)
2014-01-13 04:18:48 -08:00
{
2014-01-14 05:50:48 -08:00
Window_Tree *grand_parent = parent->parent;
Window_Tree *item_keep = NULL;
/* Adjust existing children's weights */
2014-01-14 05:50:48 -08:00
EINA_INLIST_FOREACH(parent->children, item_keep)
{
if (item_keep != item)
break;
}
if (!item_keep)
{
/* Special case of deleting the last vertical split item. */
free(item);
free(root);
return NULL;
}
else if (!item_keep->children)
2014-01-14 05:50:48 -08:00
{
parent->client = item_keep->client;
parent->children = NULL;
2014-01-13 09:48:17 -08:00
2014-01-14 05:50:48 -08:00
free(item_keep);
}
else
2014-01-13 09:48:17 -08:00
{
if (grand_parent)
2014-01-13 09:48:17 -08:00
{
/* Update the children's parent. */
2014-01-14 05:50:48 -08:00
{
Eina_Inlist *itr_safe;
Window_Tree *itr;
EINA_INLIST_FOREACH_SAFE(item_keep->children, itr_safe, itr)
{
/* We are prepending to double-reverse the order. */
grand_parent->children =
eina_inlist_prepend_relative(grand_parent->children,
EINA_INLIST_GET(itr), EINA_INLIST_GET(parent));
itr->weight *= parent->weight;
itr->parent = grand_parent;
}
grand_parent->children = eina_inlist_remove(grand_parent->children,
EINA_INLIST_GET(parent));
free(parent);
2014-01-14 05:50:48 -08:00
}
2014-01-13 09:48:17 -08:00
}
else
{
/* This is fine, as this is a child of the root so we allow two
* levels. */
item_keep->weight = 1.0;
}
2014-01-14 06:03:18 -08:00
parent->children = eina_inlist_remove(parent->children, EINA_INLIST_GET(item));
}
2014-01-13 04:18:48 -08:00
}
else
{
Window_Tree *itr;
2014-01-14 05:50:48 -08:00
float weight = 1.0 - item->weight;
2014-01-13 04:18:48 -08:00
2014-01-14 06:03:18 -08:00
parent->children = eina_inlist_remove(parent->children,
2014-01-13 04:18:48 -08:00
EINA_INLIST_GET(item));
/* Adjust existing children's weights */
2014-01-14 06:03:18 -08:00
EINA_INLIST_FOREACH(parent->children, itr)
2014-01-13 04:18:48 -08:00
{
itr->weight /= weight;
}
}
free(item);
return root;
2014-01-13 04:18:48 -08:00
}
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;
}
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)
tiling_e_client_move_resize_extra(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);
}
static Window_Tree *
_inlist_next(Window_Tree *it)
{
return (Window_Tree *) EINA_INLIST_GET(it)->next;
}
static Window_Tree *
_inlist_prev(Window_Tree *it)
{
return (Window_Tree *) EINA_INLIST_GET(it)->prev;
}
static void
_tiling_window_tree_node_resize_direction(Window_Tree *node, Window_Tree *parent,
double dir_diff, int dir)
{
double weight = 0.0;
double weight_diff;
Window_Tree *children_start;
Window_Tree *itr;
Window_Tree *(*itr_func)(Window_Tree *);
if (dir > 0)
{
itr_func = _inlist_prev;
children_start = (Window_Tree *) parent->children->last;
}
else
{
itr_func = _inlist_next;
children_start = (Window_Tree *) parent->children;
}
itr = (Window_Tree *) children_start;
while (itr != node)
{
weight += itr->weight;
itr = itr_func(itr);
}
if (weight == 0.0)
return;
weight_diff = itr->weight;
itr->weight *= dir_diff;
weight_diff -= itr->weight;
weight_diff /= weight;
for (itr = children_start ; itr != node ; itr = itr_func(itr))
{
itr->weight += itr->weight * weight_diff;
}
}
void
tiling_window_tree_node_resize(Window_Tree *node, int w_dir, double w_diff, int h_dir, double h_diff)
{
Window_Tree *parent = node->parent;
Window_Tree *w_parent, *h_parent;
/* If we have no parent, means we need to be full screen anyway. */
if (!parent)
return;
Window_Tree *grand_parent = parent->parent;
Tiling_Split_Type parent_split_type = _tiling_window_tree_split_type_get(parent);
/* w_diff related changes. */
if (parent_split_type == TILING_SPLIT_HORIZONTAL)
{
w_parent = parent;
h_parent = grand_parent;
}
else
{
w_parent = grand_parent;
h_parent = parent;
}
if ((h_diff != 1.0) && h_parent)
{
Window_Tree *tmp_node = (h_parent == parent) ? node : parent;
_tiling_window_tree_node_resize_direction(tmp_node, h_parent, h_diff, h_dir);
}
if ((w_diff != 1.0) && w_parent)
{
Window_Tree *tmp_node = (w_parent == parent) ? node : parent;
_tiling_window_tree_node_resize_direction(tmp_node, w_parent, w_diff, w_dir);
}
}
void
tiling_window_tree_dump(Window_Tree *root, int level)
{
int i;
if (!root)
return;
for (i = 0 ; i < level ; i++)
printf(" ");
if (root->children)
printf("\\-");
else
printf("|-");
printf("%f\n", root->weight);
Window_Tree *itr;
EINA_INLIST_FOREACH(root->children, itr)
{
tiling_window_tree_dump(itr, level + 1);
}
}