#include "dat.h" #include "fn.h" enum { Asciitofull = 0xFEE0, }; int popupscale = 1; static void fill(u32int *buf, int n, u32int color) { int i; for(i = 0; i < n; i++) buf[i] = color; } static void fillrect(u32int *buf, int w, int h, int x, int y, int rw, int rh, u32int color) { int x0, x1, y0, y1; x0 = min(max(x, 0), w); x1 = min(max(x + max(rw, 0), 0), w); y0 = min(max(y, 0), h); y1 = min(max(y + max(rh, 0), 0), h); for(; y0 < y1; y0++) fill(buf + y0*w + x0, max(x1 - x0, 0), color); } static int validarea(Area *a) { return a != nil && a->w > 0 && a->h > 0; } static vlong areadistance(Area *a, int x, int y) { vlong dx, dy, right, bottom; right = (vlong)a->x + a->w; bottom = (vlong)a->y + a->h; dx = x < a->x ? (vlong)a->x - x : x >= right ? x - right : 0; dy = y < a->y ? (vlong)a->y - y : y >= bottom ? y - bottom : 0; return dx + dy; } void popuparea(Area *mon, int nmon, Area *work, int x, int y, Area *out) { vlong best, bottom, d, right, x0, x1, y0, y1; int i, pick; memset(out, 0, sizeof *out); pick = -1; best = (vlong)1 << 62; for(i = 0; i < nmon; i++){ if(!validarea(&mon[i])) continue; right = (vlong)mon[i].x + mon[i].w; bottom = (vlong)mon[i].y + mon[i].h; if(x >= mon[i].x && x < right && y >= mon[i].y && y < bottom){ pick = i; break; } d = areadistance(&mon[i], x, y); if(d < best){ best = d; pick = i; } } if(pick < 0) return; *out = mon[pick]; if(!validarea(work)) return; x0 = max((vlong)out->x, work->x); y0 = max((vlong)out->y, work->y); x1 = min((vlong)out->x + out->w, (vlong)work->x + work->w); y1 = min((vlong)out->y + out->h, (vlong)work->y + work->h); if(x1 <= x0 || y1 <= y0) return; out->x = x0; out->y = y0; out->w = x1 - x0; out->h = y1 - y0; } int pagemarker(char *buf, int nbuf, int first, int shown, int total) { if(nbuf <= 0) return 0; buf[0] = 0; if(shown <= 0 || (first == 0 && shown >= total)) return 0; return snprint(buf, nbuf, "%d-%d/%d", first + 1, first + shown, total); } static int fitrows(int h, int pre, int mark, int n) { int fixed; fixed = 2*PopupPad + mark*Fontsz; if(pre) fixed += Fontsz + (n != 0 ? PopupSep : 0); if(h <= fixed) return 0; return min(n, (h - fixed) / Fontsz); } void popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p) { char buf[32]; int first, i, markrow, nall, nmark, npre, total, width, y; memset(p, 0, sizeof *p); p->prey = -1; p->sepy = -1; p->rowsy = -1; p->marky = -1; p->sely = -1; nall = min(max(dc->nkouho, 0), Maxdisp); npre = dc->pre.n != 0; if((nall == 0 && !npre) || areaw <= 0 || areah <= 0) return; /* As many rows as fit, and a page marker when they do not all. */ total = max(dc->total, dc->first + nall); markrow = dc->first > 0 || nall < total; p->n = fitrows(areah, npre, markrow, nall); if(p->n < nall && !markrow){ markrow = 1; p->n = fitrows(areah, npre, markrow, nall); } if(p->n == 0 && !npre) return; if(p->n != 0 && dc->sel >= p->n) p->row0 = min(dc->sel - p->n + 1, nall - p->n); first = dc->first + p->row0; nmark = markrow ? pagemarker(buf, sizeof buf, first, p->n, total) : 0; if(nmark == 0) markrow = 0; width = PopupBasew; if(npre) width = max(width, textwidth(&dc->pre) + 2*PopupPad); for(i = 0; i < p->n; i++) width = max(width, textwidth(&dc->kouho[p->row0+i]) + PopupNumw + 2*PopupPad); if(markrow){ sinit(&p->mark, buf, nmark); p->markw = textwidth(&p->mark); width = max(width, p->markw + 2*PopupPad); } p->w = min(width, areaw); y = PopupPad; if(npre){ p->prey = y; y += Fontsz; } if(npre && p->n != 0){ p->sepy = y; y += PopupSep; } if(p->n != 0){ p->rowsy = y; y += p->n*Fontsz; } if(markrow){ p->marky = y; y += Fontsz; } p->h = min(y + PopupPad, areah); p->textw = max(p->w - 2*PopupPad - PopupNumw, 0); if(markrow){ p->markw = min(p->markw, max(p->w - 2*PopupPad, 0)); p->markx = p->w - PopupPad - p->markw; } p->selw = max(p->w - 2*PopupPad, 0); if(dc->sel >= p->row0 && dc->sel < p->row0 + p->n) p->sely = p->rowsy + (dc->sel - p->row0)*Fontsz; } void popupdraw(u32int *img, Drawcmd *dc, Popup *p) { Str num; u32int color; int i, j, y; fill(img, p->w*p->h, Colbg); if(p->prey >= 0) textdraw(img, p->w, p->h, PopupPad, p->prey, max(p->w - 2*PopupPad, 0), Colfg, &dc->pre); if(p->sepy >= 0) fillrect(img, p->w, p->h, PopupPad, p->sepy, p->w - 2*PopupPad, PopupSep, Colsep); if(p->sely >= 0) fillrect(img, p->w, p->h, PopupPad, p->sely, p->selw, Fontsz, Colsel); for(i = 0, y = p->rowsy; i < p->n; i++, y += Fontsz){ j = p->row0 + i; color = j == dc->sel ? Colselfg : Colfg; sclear(&num); sputr(&num, '1' + j + Asciitofull); textdraw(img, p->w, p->h, PopupPad, y, min(PopupNumw, max(p->w - 2*PopupPad, 0)), color, &num); textdraw(img, p->w, p->h, PopupPad + PopupNumw, y, p->textw, color, &dc->kouho[j]); } if(p->marky >= 0) textdraw(img, p->w, p->h, p->markx, p->marky, p->markw, Colfg, &p->mark); } void popupposition(Caret *caret, int pointerx, int pointery, Area *area, int w, int h, int *x, int *y) { vlong below, bottom, px, py, right, xmax, ymax; if(!validarea(area)){ *x = 0; *y = 0; return; } right = (vlong)area->x + area->w; bottom = (vlong)area->y + area->h; if(caret->valid){ px = caret->x; below = (vlong)caret->y + max(caret->h, 0); if(below >= area->y && below + h <= bottom) py = below; else py = (vlong)caret->y - h; }else{ px = (vlong)pointerx + 10; py = (vlong)pointery + 10; if(py + h > bottom) py = (vlong)pointery - 10 - h; } xmax = max(right - w, area->x); ymax = max(bottom - h, area->y); *x = max((vlong)area->x, min(px, xmax)); *y = max((vlong)area->y, min(py, ymax)); }