2006-02-03 18:15:36 +03:00
|
|
|
/*
|
|
|
|
* (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
|
|
|
|
* See LICENSE file for license details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2006-02-19 18:21:01 +03:00
|
|
|
#include <string.h>
|
2006-02-03 18:15:36 +03:00
|
|
|
|
|
|
|
#include "wm.h"
|
|
|
|
|
|
|
|
Area *
|
2006-03-04 11:23:17 +03:00
|
|
|
alloc_area(Tag *t)
|
2006-02-03 18:15:36 +03:00
|
|
|
{
|
2006-02-03 20:11:22 +03:00
|
|
|
static unsigned short id = 1;
|
2006-02-03 18:15:36 +03:00
|
|
|
Area *a = cext_emallocz(sizeof(Area));
|
2006-03-04 11:23:17 +03:00
|
|
|
a->tag = t;
|
2006-02-03 18:15:36 +03:00
|
|
|
a->id = id++;
|
2006-02-19 17:40:44 +03:00
|
|
|
update_area_geometry(a);
|
2006-03-04 11:23:17 +03:00
|
|
|
t->area = (Area **)cext_array_attach((void **)t->area, a, sizeof(Area *), &t->areasz);
|
|
|
|
t->sel = t->narea;
|
|
|
|
fprintf(stderr, "alloc_area: t->sel == %d\n", t->sel);
|
|
|
|
t->narea++;
|
2006-02-03 18:15:36 +03:00
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2006-02-19 17:40:44 +03:00
|
|
|
void
|
|
|
|
update_area_geometry(Area *a)
|
|
|
|
{
|
|
|
|
a->rect = rect;
|
|
|
|
a->rect.height -= brect.height;
|
|
|
|
}
|
|
|
|
|
2006-02-03 18:15:36 +03:00
|
|
|
void
|
|
|
|
destroy_area(Area *a)
|
|
|
|
{
|
2006-03-04 11:23:17 +03:00
|
|
|
Tag *t = a->tag;
|
2006-03-02 21:58:54 +03:00
|
|
|
if(a->nclient)
|
|
|
|
return;
|
2006-02-28 12:04:48 +03:00
|
|
|
if(a->client)
|
|
|
|
free(a->client);
|
2006-03-04 11:23:17 +03:00
|
|
|
cext_array_detach((void **)t->area, a, &t->areasz);
|
|
|
|
t->narea--;
|
|
|
|
if(t->sel == t->narea) {
|
|
|
|
if(t->narea)
|
|
|
|
t->sel = t->narea - 1;
|
2006-03-02 19:38:15 +03:00
|
|
|
else
|
2006-03-04 11:23:17 +03:00
|
|
|
t->sel = 0;
|
2006-03-02 19:38:15 +03:00
|
|
|
}
|
2006-02-03 18:15:36 +03:00
|
|
|
free(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2006-03-01 09:51:04 +03:00
|
|
|
area2index(Area *a)
|
2006-02-03 18:15:36 +03:00
|
|
|
{
|
|
|
|
int i;
|
2006-03-04 11:23:17 +03:00
|
|
|
Tag *t = a->tag;
|
|
|
|
for(i = 0; i < t->narea; i++)
|
|
|
|
if(t->area[i] == a)
|
2006-02-03 18:15:36 +03:00
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2006-03-04 11:23:17 +03:00
|
|
|
aid2index(Tag *t, unsigned short id)
|
2006-02-03 18:15:36 +03:00
|
|
|
{
|
|
|
|
int i;
|
2006-03-04 11:23:17 +03:00
|
|
|
for(i = 0; i < t->narea; i++)
|
|
|
|
if(t->area[i]->id == id)
|
2006-02-03 18:15:36 +03:00
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
2006-02-19 18:21:01 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
select_area(Area *a, char *arg)
|
|
|
|
{
|
|
|
|
Area *new;
|
2006-03-04 11:23:17 +03:00
|
|
|
Tag *t = a->tag;
|
2006-03-01 09:51:04 +03:00
|
|
|
int i = area2index(a);
|
2006-02-19 18:21:01 +03:00
|
|
|
if(i == -1)
|
|
|
|
return;
|
|
|
|
if(!strncmp(arg, "prev", 5)) {
|
2006-02-24 11:52:20 +03:00
|
|
|
if(i == 1)
|
2006-03-04 11:23:17 +03:00
|
|
|
i = t->narea - 1;
|
2006-02-19 18:21:01 +03:00
|
|
|
else
|
|
|
|
i--;
|
|
|
|
} else if(!strncmp(arg, "next", 5)) {
|
2006-03-04 11:23:17 +03:00
|
|
|
if(i + 1 < t->narea)
|
2006-02-19 18:21:01 +03:00
|
|
|
i++;
|
|
|
|
else
|
2006-02-24 11:52:20 +03:00
|
|
|
i = 1;
|
2006-02-19 18:21:01 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const char *errstr;
|
2006-03-04 11:23:17 +03:00
|
|
|
i = cext_strtonum(arg, 0, t->narea - 1, &errstr);
|
2006-02-19 18:21:01 +03:00
|
|
|
if(errstr)
|
|
|
|
return;
|
|
|
|
}
|
2006-03-04 11:23:17 +03:00
|
|
|
new = t->area[i];
|
2006-02-19 18:21:01 +03:00
|
|
|
if(new->nclient)
|
2006-02-28 10:51:53 +03:00
|
|
|
focus_client(new->client[new->sel]);
|
2006-03-04 11:23:17 +03:00
|
|
|
t->sel = i;
|
|
|
|
fprintf(stderr, "select_area: t->sel == %d\n", t->sel);
|
2006-02-19 18:21:01 +03:00
|
|
|
}
|
2006-02-24 12:06:02 +03:00
|
|
|
|
|
|
|
void
|
2006-03-02 19:38:15 +03:00
|
|
|
send_toarea(Area *to, Client *c)
|
2006-02-24 12:06:02 +03:00
|
|
|
{
|
2006-03-02 19:38:15 +03:00
|
|
|
detach_fromarea(c);
|
|
|
|
attach_toarea(to, c);
|
2006-02-28 10:51:53 +03:00
|
|
|
focus_client(c);
|
2006-02-24 12:06:02 +03:00
|
|
|
}
|
2006-03-02 12:26:30 +03:00
|
|
|
|
|
|
|
void
|
2006-03-02 19:38:15 +03:00
|
|
|
attach_toarea(Area *a, Client *c)
|
2006-03-02 12:26:30 +03:00
|
|
|
{
|
|
|
|
a->client = (Client **)cext_array_attach(
|
|
|
|
(void **)a->client, c, sizeof(Client *), &a->clientsz);
|
|
|
|
a->nclient++;
|
|
|
|
c->area = a;
|
2006-03-02 21:58:54 +03:00
|
|
|
if(area2index(a)) /* column */
|
2006-03-02 17:28:55 +03:00
|
|
|
arrange_area(a);
|
2006-03-02 21:58:54 +03:00
|
|
|
else /* floating */
|
2006-03-02 12:26:30 +03:00
|
|
|
resize_client(c, &c->frame.rect, nil, False);
|
|
|
|
}
|
|
|
|
|
2006-03-02 15:37:52 +03:00
|
|
|
void
|
2006-03-02 19:38:15 +03:00
|
|
|
detach_fromarea(Client *c)
|
2006-03-02 15:37:52 +03:00
|
|
|
{
|
|
|
|
Area *a = c->area;
|
|
|
|
cext_array_detach((void **)a->client, c, &a->clientsz);
|
|
|
|
a->nclient--;
|
2006-03-04 11:23:17 +03:00
|
|
|
if(a->nclient) {
|
|
|
|
if(a->sel >= a->nclient)
|
|
|
|
a->sel = 0;
|
|
|
|
arrange_area(a);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Tag *t = a->tag;
|
|
|
|
destroy_area(a);
|
|
|
|
arrange_tag(t, True);
|
|
|
|
}
|
2006-03-02 17:28:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
colmode2str(ColumnMode mode)
|
|
|
|
{
|
|
|
|
switch(mode) {
|
|
|
|
case COL_EQUAL: return "equal"; break;
|
|
|
|
case COL_STACK: return "stack"; break;
|
|
|
|
case COL_MAX: return "max"; break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
ColumnMode
|
|
|
|
str2colmode(char *arg)
|
|
|
|
{
|
|
|
|
if(!strncmp("equal", arg, 6))
|
|
|
|
return COL_EQUAL;
|
|
|
|
if(!strncmp("stack", arg, 6))
|
|
|
|
return COL_STACK;
|
|
|
|
if(!strncmp("max", arg, 4))
|
|
|
|
return COL_MAX;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
relax_area(Area *a)
|
|
|
|
{
|
2006-03-04 11:23:17 +03:00
|
|
|
unsigned int i, yoff, h, hdiff;
|
2006-03-02 17:28:55 +03:00
|
|
|
|
|
|
|
if(!a->nclient)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* some relaxing from potential increment gaps */
|
2006-03-04 11:23:17 +03:00
|
|
|
h = 0;
|
2006-03-02 17:28:55 +03:00
|
|
|
for(i = 0; i < a->nclient; i++) {
|
|
|
|
Client *c = a->client[i];
|
|
|
|
if(a->mode == COL_MAX) {
|
|
|
|
if(h < c->frame.rect.height)
|
|
|
|
h = c->frame.rect.height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
h += c->frame.rect.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* try to add rest space to all clients if not COL_STACK mode */
|
|
|
|
if(a->mode != COL_STACK) {
|
|
|
|
for(i = 0; (h < a->rect.height) && (i < a->nclient); i++) {
|
|
|
|
Client *c = a->client[i];
|
|
|
|
unsigned int tmp = c->frame.rect.height;
|
|
|
|
c->frame.rect.height += (a->rect.height - h);
|
|
|
|
resize_client(c, &c->frame.rect, 0, True);
|
|
|
|
h += (c->frame.rect.height - tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hdiff = (a->rect.height - h) / a->nclient;
|
|
|
|
yoff = a->rect.y + hdiff / 2;
|
|
|
|
for(i = 0; i < a->nclient; i++) {
|
|
|
|
Client *c = a->client[i];
|
2006-03-04 11:23:17 +03:00
|
|
|
c->frame.rect.x = a->rect.x + (a->rect.width - c->frame.rect.width) / 2;
|
2006-03-02 17:28:55 +03:00
|
|
|
c->frame.rect.y = yoff;
|
|
|
|
if(a->mode != COL_MAX)
|
|
|
|
yoff = c->frame.rect.y + c->frame.rect.height + hdiff;
|
|
|
|
resize_client(c, &c->frame.rect, 0, False);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
arrange_area(Area *a)
|
|
|
|
{
|
|
|
|
unsigned int i, yoff, h;
|
|
|
|
|
|
|
|
if(!a->nclient)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch(a->mode) {
|
|
|
|
case COL_EQUAL:
|
|
|
|
h = a->rect.height;
|
|
|
|
h /= a->nclient;
|
|
|
|
for(i = 0; i < a->nclient; i++) {
|
|
|
|
Client *c = a->client[i];
|
|
|
|
c->frame.rect = a->rect;
|
|
|
|
c->frame.rect.y += i * h;
|
|
|
|
if(i + 1 < a->nclient)
|
|
|
|
c->frame.rect.height = h;
|
|
|
|
else
|
|
|
|
c->frame.rect.height =
|
|
|
|
a->rect.height - c->frame.rect.y + a->rect.y;
|
|
|
|
resize_client(c, &c->frame.rect, 0, True);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case COL_STACK:
|
|
|
|
yoff = a->rect.y;
|
|
|
|
h = a->rect.height - (a->nclient - 1) * bar_height();
|
|
|
|
for(i = 0; i < a->nclient; i++) {
|
|
|
|
Client *c = a->client[i];
|
|
|
|
c->frame.rect = a->rect;
|
|
|
|
c->frame.rect.y = yoff;
|
|
|
|
if(i == a->sel)
|
|
|
|
c->frame.rect.height = h;
|
|
|
|
else
|
|
|
|
c->frame.rect.height = bar_height();
|
|
|
|
yoff += c->frame.rect.height;
|
|
|
|
resize_client(c, &c->frame.rect, 0, True);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case COL_MAX:
|
|
|
|
for(i = 0; i < a->nclient; i++) {
|
|
|
|
Client *c = a->client[i];
|
|
|
|
c->frame.rect = a->rect;
|
|
|
|
resize_client(c, &c->frame.rect, 0, True);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
relax_area(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-03-04 11:23:17 +03:00
|
|
|
arrange_tag(Tag *t, Bool updategeometry)
|
2006-03-02 17:28:55 +03:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
unsigned int width;
|
|
|
|
|
2006-03-04 11:23:17 +03:00
|
|
|
if(t->narea == 1)
|
2006-03-02 17:28:55 +03:00
|
|
|
return;
|
|
|
|
|
2006-03-04 11:23:17 +03:00
|
|
|
width = rect.width / (t->narea - 1);
|
|
|
|
for(i = 1; i < t->narea; i++) {
|
|
|
|
Area *a = t->area[i];
|
2006-03-02 19:38:15 +03:00
|
|
|
if(updategeometry) {
|
2006-03-02 17:28:55 +03:00
|
|
|
update_area_geometry(a);
|
|
|
|
a->rect.x = (i - 1) * width;
|
|
|
|
a->rect.width = width;
|
|
|
|
}
|
|
|
|
arrange_area(a);
|
2006-03-02 15:37:52 +03:00
|
|
|
}
|
|
|
|
}
|
2006-03-02 17:28:55 +03:00
|
|
|
|
|
|
|
static void
|
|
|
|
match_horiz(Area *a, XRectangle *r)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for(i = 0; i < a->nclient; i++) {
|
|
|
|
Client *c = a->client[i];
|
|
|
|
c->frame.rect.x = r->x;
|
|
|
|
c->frame.rect.width = r->width;
|
|
|
|
resize_client(c, &c->frame.rect, nil, False);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
drop_resize(Client *c, XRectangle *new)
|
|
|
|
{
|
|
|
|
Area *west = nil, *east = nil, *a = c->area;
|
2006-03-04 11:23:17 +03:00
|
|
|
Tag *t = a->tag;
|
2006-03-02 17:28:55 +03:00
|
|
|
Client *north = nil, *south = nil;
|
|
|
|
unsigned int i;
|
|
|
|
|
2006-03-04 11:23:17 +03:00
|
|
|
for(i = 1; (i < t->narea) && (t->area[i] != a); i++);
|
2006-03-02 17:28:55 +03:00
|
|
|
/* first managed area is indexed 1, thus (i > 1) ? ... */
|
2006-03-04 11:23:17 +03:00
|
|
|
west = (i > 1) ? t->area[i - 1] : nil;
|
|
|
|
east = i + 1 < t->narea ? t->area[i + 1] : nil;
|
2006-03-02 17:28:55 +03:00
|
|
|
|
|
|
|
for(i = 1; (i < a->nclient) && (a->client[i] != c); i++);
|
|
|
|
north = i ? a->client[i - 1] : nil;
|
|
|
|
south = i + 1 < a->nclient ? a->client[i + 1] : nil;
|
|
|
|
|
|
|
|
/* horizontal resize */
|
|
|
|
if(west && (new->x != c->frame.rect.x)) {
|
|
|
|
west->rect.width = new->x - west->rect.x;
|
|
|
|
a->rect.width += c->frame.rect.x - new->x;
|
|
|
|
a->rect.x = new->x;
|
|
|
|
match_horiz(west, &west->rect);
|
|
|
|
match_horiz(a, &a->rect);
|
|
|
|
relax_area(west);
|
|
|
|
}
|
|
|
|
if(east && (new->x + new->width != c->frame.rect.x + c->frame.rect.width)) {
|
|
|
|
east->rect.width -= new->x + new->width - east->rect.x;
|
|
|
|
east->rect.x = new->x + new->width;
|
|
|
|
a->rect.x = new->x;
|
|
|
|
a->rect.width = new->width;
|
|
|
|
match_horiz(a, &a->rect);
|
|
|
|
match_horiz(east, &east->rect);
|
|
|
|
relax_area(east);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vertical resize */
|
|
|
|
if(north && (new->y != c->frame.rect.y)) {
|
|
|
|
north->frame.rect.height = new->y - north->frame.rect.y;
|
|
|
|
c->frame.rect.height += c->frame.rect.y - new->y;
|
|
|
|
c->frame.rect.y = new->y;
|
|
|
|
resize_client(north, &north->frame.rect, nil, False);
|
|
|
|
resize_client(c, &c->frame.rect, nil, False);
|
|
|
|
}
|
|
|
|
if(south && (new->y + new->height != c->frame.rect.y + c->frame.rect.height)) {
|
|
|
|
south->frame.rect.height -= new->y + new->height - south->frame.rect.y;
|
|
|
|
south->frame.rect.y = new->y + new->height;
|
|
|
|
c->frame.rect.y = new->y;
|
|
|
|
c->frame.rect.height = new->height;
|
|
|
|
resize_client(c, &c->frame.rect, nil, False);
|
|
|
|
resize_client(south, &south->frame.rect, nil, False);
|
|
|
|
}
|
|
|
|
relax_area(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
drop_moving(Client *c, XRectangle *new, XPoint * pt)
|
|
|
|
{
|
|
|
|
Area *tgt = nil, *src = c->area;
|
2006-03-04 11:23:17 +03:00
|
|
|
Tag *t = src->tag;
|
2006-03-02 17:28:55 +03:00
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if(!pt || src->nclient < 2)
|
|
|
|
return;
|
|
|
|
|
2006-03-04 11:23:17 +03:00
|
|
|
for(i = 1; (i < t->narea) &&
|
|
|
|
!blitz_ispointinrect(pt->x, pt->y, &t->area[i]->rect); i++);
|
|
|
|
if((tgt = ((i < t->narea) ? t->area[i] : nil))) {
|
2006-03-02 17:28:55 +03:00
|
|
|
if(tgt != src) {
|
2006-03-02 19:38:15 +03:00
|
|
|
send_toarea(tgt, c);
|
2006-03-02 17:28:55 +03:00
|
|
|
arrange_area(tgt);
|
|
|
|
}
|
|
|
|
else {
|
2006-03-02 19:38:15 +03:00
|
|
|
for(i = 0; (i < src->nclient) && !blitz_ispointinrect(
|
|
|
|
pt->x, pt->y, &src->client[i]->frame.rect); i++);
|
2006-03-02 17:28:55 +03:00
|
|
|
if((i < src->nclient) && (c != src->client[i])) {
|
|
|
|
unsigned int j = client2index(c);
|
|
|
|
Client *tmp = src->client[j];
|
|
|
|
src->client[j] = src->client[i];
|
|
|
|
src->client[i] = tmp;
|
|
|
|
arrange_area(src);
|
|
|
|
focus_client(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
resize_area(Client *c, XRectangle *r, XPoint *pt)
|
|
|
|
{
|
|
|
|
if((c->frame.rect.width == r->width)
|
|
|
|
&& (c->frame.rect.height == r->height))
|
|
|
|
drop_moving(c, r, pt);
|
|
|
|
else
|
|
|
|
drop_resize(c, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
Area *
|
2006-03-04 11:23:17 +03:00
|
|
|
new_area(Tag *t)
|
2006-03-02 17:28:55 +03:00
|
|
|
{
|
2006-03-04 11:23:17 +03:00
|
|
|
Area *a = alloc_area(t);
|
|
|
|
arrange_tag(t, True);
|
2006-03-02 17:28:55 +03:00
|
|
|
return a;
|
|
|
|
}
|
2006-03-02 21:58:54 +03:00
|
|
|
|