#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
static double
dist(Point p0, Point p1)
{
int dx, dy;
dx = p1.x - p0.x;
dy = p1.y - p0.y;
return sqrt(dx*dx + dy*dy);
}
static int
pie(int but, Mousectl *mc, Screen *scr, char *item[], int nitem, int nsub)
{
Rectangle r1, r2, sc, menur;
Point p, p0, pz;
int i, hit, r, l, rmin, rmax;
Image *b, *backup, *color[3];
char *s;
double a0, a, e;
hit = -1;
e = nitem > 0 ? 2*PI/nitem : 2*PI;
// the 1st at 12 o'clock
a0 = 2*PI-PI/2;
rmin = 16;
rmax = rmin * 2;
p = mc->xy;
Resize:
r2 = Rect(0, 0, 0, 0);
for(i = 0, a = a0; i < nitem; i++, a += e, r2 = r1){
/* middle ring radius where the label is placed */
r = rmin + (rmax - rmin)/2;
p0 = addpt(p, Pt(cos(a)*r, sin(a)*r));
pz = stringsize(font, item[i]);
r1.min = subpt(p0, Pt(pz.x/2, pz.y/2));
r1.max = addpt(p0, Pt(pz.x/2, pz.y/2));
r1 = insetrect(r1, -1);
/*
* if the label is overlapping with its previous one,
* increase the inner ring radius
*/
if(rectXrect(r2, r1)){
rmin++;
if(rmax < rmin)
rmax = rmin;
goto Resize;
}
/*
* if any corner of the label crosses the outer radius,
* increase the outer ring radius.
*/
l = dist(p, Pt(r1.min.x, r1.min.y));
if(l > rmax){
rmax++;
goto Resize;
}
l = dist(p, Pt(r1.min.x, r1.max.y));
if(l > rmax){
rmax++;
goto Resize;
}
l = dist(p, Pt(r1.max.x, r1.min.y));
if(l > rmax){
rmax++;
goto Resize;
}
l = dist(p, Pt(r1.max.x, r1.max.y));
if(l > rmax){
rmax++;
goto Resize;
}
}
rmin -= 2;
rmax += 2;
sc = screen->clipr;
replclipr(screen, 0, screen->r);
/* as we know the size now, translate to screen */
if(p.x + rmax > screen->r.max.x)
p.x = screen->r.max.x - rmax;
if(p.x - rmax < screen->r.min.x)
p.x = screen->r.min.x + rmax;
if(p.y + rmax > screen->r.max.y)
p.y = screen->r.max.y - rmax;
if(p.y - rmax < screen->r.min.y)
p.y = screen->r.min.y + rmax;
moveto(mc, p);
menur.min = Pt(p.x - rmax-2, p.y - rmax-2);
menur.max = Pt(p.x + rmax+2, p.y + rmax+2);
if(scr){
b = allocwindow(scr, menur, Refbackup, DNofill);
if(b == nil)
b = screen;
backup = nil;
}else{
b = screen;
backup = allocimage(display, menur, screen->chan, 0, DNofill);
if(backup)
draw(backup, menur, screen, nil, menur.min);
}
color[0] = allocimagemix(display, DPalegreen, DWhite);
color[1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen);
color[2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen);
Redraw:
fillellipse(b, p, rmax, rmax, color[0], ZP);
ellipse(b, p, rmin, rmin, 1, color[1], ZP);
ellipse(b, p, rmax, rmax, 1, color[1], ZP);
for(i = 0, a = a0; i < nitem; i++, a += e){
s = item[i];
pz = stringsize(font, s);
r = rmin + (rmax - rmin)/2;
p0 = addpt(p, Pt(cos(a)*r, sin(a)*r));
if(i == hit){
Point ap[3];
r = (pz.x < pz.y ? pz.x : pz.y) / 2;
ap[0] = p;
ap[1] = addpt(p0, Pt(cos(a - PI/2)*r, sin(a - PI/2)*r));
ap[2] = addpt(p0, Pt(cos(a + PI/2)*r, sin(a + PI/2)*r));
fillpoly(b, ap, nelem(ap), 0, color[2], ZP);
r1.min = subpt(p0, Pt(pz.x/2, pz.y/2));
r1.max = addpt(p0, Pt(pz.x/2, pz.y/2));
r1 = insetrect(r1, -1);
draw(b, r1, color[2], nil, ZP);
string(b, subpt(p0, Pt(pz.x/2, pz.y/2)), color[0], ZP, font, s);
} else {
string(b, subpt(p0, Pt(pz.x/2, pz.y/2)), display->black, ZP, font, s);
}
}
flushimage(display, 1);
while(mc->buttons & (1<<(but-1))){
l = dist(p, mc->xy);
i = -1;
if(l > 4){
for(i = 0, a = a0; i < nitem; i++, a += e){
double b;
b = atan2((mc->xy.y - p.y),(mc->xy.x - p.x)) - a + e/2;
while(b < 0)
b += 2*PI;
while(b >= 2*PI)
b -= 2*PI;
if(b < e)
break;
}
if(i == nitem)
i = -1;
}
if(l >= rmax){
if(nsub < 0){
hit = -1;
break;
}
if(nsub>0 && nitem>0 && i == nitem-1){
int k;
k = pie(but, mc, scr, item + nitem, nsub, -1);
if(k != -1){
hit = k + nitem;
break;
}
}
i = -1;
}
if(hit != i){
hit = i;
goto Redraw;
}
readmouse(mc);
}
if(b != screen)
freeimage(b);
if(backup){
draw(screen, menur, backup, nil, menur.min);
freeimage(backup);
}
replclipr(screen, 0, sc);
flushimage(display, 1);
for(i = 0; i < nelem(color); i++)
freeimage(color[i]);
return hit;
}
int
piemenuhit(int but, Mousectl *mc, Menu *menu, Screen *scr)
{
char *s, **item;
int i, n, nitem, nsub, hit;
item = nil;
i = n = 0;
while(s = menu->item ? menu->item[i] : menu->gen(i)){
i++;
if(n % 16 == 0)
item = realloc(item, (n + 16) * sizeof(item[0]));
if(s[0] == '[' && s[strlen(s)-1] == ']'){
s = strdup(s+1);
s[strlen(s)-1] = 0;
item[n++] = s;
break;
} else {
item[n++] = strdup(s);
}
}
nitem = n;
while(s = menu->item ? menu->item[i] : menu->gen(i)){
i++;
if(n % 16 == 0)
item = realloc(item, (n + 16) * sizeof(item[0]));
item[n++] = strdup(s);
}
nsub = n - nitem;
hit = pie(but, mc, scr, item, nitem, nsub);
for(i = 0; i<n; i++)
free(item[i]);
free(item);
if(hit != -1)
menu->lasthit = hit;
return hit;
}
|