wmii/cmd/wmii/float.c

245 lines
4.3 KiB
C

/* Copyright ©2006-2009 Kris Maglione <maglione.k at Gmail>
* See LICENSE file for license details.
*/
#include "dat.h"
#include <limits.h>
#include "fns.h"
static void float_placeframe(Frame*);
void
float_attach(Area *a, Frame *f) {
f->client->floating = true;
f->r = f->floatr;
float_placeframe(f);
assert(a->sel != f);
frame_insert(f, a->sel);
if(a->sel == nil)
area_setsel(a, f);
}
void
float_detach(Frame *f) {
Frame *pr;
Area *a, *sel, *oldsel;
View *v;
v = f->view;
a = f->area;
sel = view_findarea(v, v->selcol, false);
oldsel = v->oldsel;
pr = f->aprev;
frame_remove(f);
if(a->sel == f) {
if(!pr)
pr = a->frame;
a->sel = nil;
area_setsel(a, pr);
}
f->area = nil;
if(oldsel)
area_focus(oldsel);
else if(!a->frame)
if(sel && sel->frame)
area_focus(sel);
}
void
float_resizeframe(Frame *f, Rectangle r) {
if(f->area->view == selview)
client_resize(f->client, r);
else
frame_resize(f, r);
}
void
float_arrange(Area *a) {
Frame *f;
assert(a->floating);
switch(a->mode) {
case Coldefault:
for(f=a->frame; f; f=f->anext)
f->collapsed = false;
break;
case Colstack:
for(f=a->frame; f; f=f->anext)
f->collapsed = (f != a->sel);
break;
default:
die("not reached");
break;
}
for(f=a->frame; f; f=f->anext)
f->r = f->floatr;
view_update(a->view);
}
static void
rect_push(Vector_rect *vec, Rectangle r) {
Rectangle *rp;
int i;
for(i=0; i < vec->n; i++) {
rp = &vec->ary[i];
if(rect_contains_p(*rp, r))
return;
if(rect_contains_p(r, *rp)) {
*rp = r;
return;
}
}
vector_rpush(vec, r);
}
Vector_rect*
unique_rects(Vector_rect *vec, Rectangle orig) {
static Vector_rect vec1, vec2;
Vector_rect *v1, *v2, *v;
Rectangle r1, r2;
int i, j;
v1 = &vec1;
v2 = &vec2;
v1->n = 0;
vector_rpush(v1, orig);
for(i=0; i < vec->n; i++) {
v2->n = 0;
r1 = vec->ary[i];
for(j=0; j < v1->n; j++) {
r2 = v1->ary[j];
if(!rect_intersect_p(r1, r2)) {
rect_push(v2, r2);
continue;
}
if(r2.min.x < r1.min.x)
rect_push(v2, Rect(r2.min.x, r2.min.y, r1.min.x, r2.max.y));
if(r2.min.y < r1.min.y)
rect_push(v2, Rect(r2.min.x, r2.min.y, r2.max.x, r1.min.y));
if(r2.max.x > r1.max.x)
rect_push(v2, Rect(r1.max.x, r2.min.y, r2.max.x, r2.max.y));
if(r2.max.y > r1.max.y)
rect_push(v2, Rect(r2.min.x, r1.max.y, r2.max.x, r2.max.y));
}
v = v1;
v1 = v2;
v2 = v;
}
return v1;
}
Rectangle
max_rect(Vector_rect *vec) {
Rectangle *r, *rp;
int i, a, area;
area = 0;
r = 0;
for(i=0; i < vec->n; i++) {
rp = &vec->ary[i];
a = Dx(*rp) * Dy(*rp);
if(a > area) {
area = a;
r = rp;
}
}
return r ? *r : ZR;
}
static void
float_placeframe(Frame *f) {
static Vector_rect vec;
Vector_rect *vp;
Rectangle r;
Point dim, p;
Client *c;
Frame *ff;
Area *a, *sel;
long area, l;
int i, s;
a = f->area;
c = f->client;
/*
if(c->trans)
return;
*/
if(c->fullscreen >= 0 || c->w.hints->position || starting) {
f->r = f->floatr;
return;
}
/* Find all rectangles on the floating layer into which
* the new frame would fit.
*/
vec.n = 0;
for(ff=a->frame; ff; ff=ff->anext)
/* TODO: Find out why this check is needed.
* The frame hasn't been inserted yet, but somehow,
* its old rectangle winds up in the list.
*/
if(ff->client != f->client)
vector_rpush(&vec, ff->r);
/* Decide which screen we want to place this on.
* Ideally, it should probably Do the Right Thing
* when a screen fills, but what's the right thing?
* I think usage will show...
*/
s = -1;
ff = client_groupframe(c, f->view);
if (ff)
s = ownerscreen(ff->r);
else if (selclient())
s = ownerscreen(selclient()->sel->r);
else {
sel = view_findarea(a->view, a->view->selcol, false);
if (sel)
s = sel->screen;
}
r = a->r;
if (s > -1)
r = screens[s]->r;
vp = unique_rects(&vec, r);
area = LONG_MAX;
dim.x = Dx(f->r);
dim.y = Dy(f->r);
p = ZP;
for(i=0; i < vp->n; i++) {
r = vp->ary[i];
if(Dx(r) < dim.x || Dy(r) < dim.y)
continue;
l = Dx(r) * Dy(r);
if(l < area) {
area = l;
p = r.min;
}
}
if(area == LONG_MAX) {
/* Cascade. */
ff = a->sel;
if(ff)
p = addpt(ff->r.min, Pt(Dy(ff->titlebar), Dy(ff->titlebar)));
if(p.x + Dx(f->r) > Dx(screen->r) ||
p.y + Dy(f->r) > screen->brect.min.y)
p = ZP;
}
f->floatr = rectsetorigin(f->r, p);
}