#include #include #include #include #include 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; ilasthit = hit; return hit; }