180ee5cbfSDavid du Colombier #include <u.h>
280ee5cbfSDavid du Colombier #include <libc.h>
380ee5cbfSDavid du Colombier #include <draw.h>
480ee5cbfSDavid du Colombier #include <thread.h>
580ee5cbfSDavid du Colombier #include <mouse.h>
680ee5cbfSDavid du Colombier #include <keyboard.h>
780ee5cbfSDavid du Colombier #include <control.h>
880ee5cbfSDavid du Colombier
980ee5cbfSDavid du Colombier typedef struct Entry Entry;
1080ee5cbfSDavid du Colombier
1180ee5cbfSDavid du Colombier struct Entry
1280ee5cbfSDavid du Colombier {
1380ee5cbfSDavid du Colombier Control;
1480ee5cbfSDavid du Colombier int border;
1580ee5cbfSDavid du Colombier CFont *font;
1680ee5cbfSDavid du Colombier CImage *image;
1780ee5cbfSDavid du Colombier CImage *textcolor;
1880ee5cbfSDavid du Colombier CImage *bordercolor;
1980ee5cbfSDavid du Colombier Rune *text;
2080ee5cbfSDavid du Colombier int ntext;
2180ee5cbfSDavid du Colombier int cursor;
2280ee5cbfSDavid du Colombier int align;
2380ee5cbfSDavid du Colombier int hasfocus;
2480ee5cbfSDavid du Colombier int lastbut;
2580ee5cbfSDavid du Colombier };
2680ee5cbfSDavid du Colombier
2780ee5cbfSDavid du Colombier enum{
2880ee5cbfSDavid du Colombier EAlign,
2980ee5cbfSDavid du Colombier EBorder,
3080ee5cbfSDavid du Colombier EBordercolor,
3180ee5cbfSDavid du Colombier EData,
3280ee5cbfSDavid du Colombier EFocus,
3380ee5cbfSDavid du Colombier EFont,
3480ee5cbfSDavid du Colombier EFormat,
359a747e4fSDavid du Colombier EHide,
3680ee5cbfSDavid du Colombier EImage,
3780ee5cbfSDavid du Colombier ERect,
389a747e4fSDavid du Colombier EReveal,
3980ee5cbfSDavid du Colombier EShow,
409a747e4fSDavid du Colombier ESize,
4180ee5cbfSDavid du Colombier ETextcolor,
4280ee5cbfSDavid du Colombier EValue,
4380ee5cbfSDavid du Colombier };
4480ee5cbfSDavid du Colombier
4580ee5cbfSDavid du Colombier static char *cmds[] = {
4680ee5cbfSDavid du Colombier [EAlign] = "align",
4780ee5cbfSDavid du Colombier [EBorder] = "border",
4880ee5cbfSDavid du Colombier [EBordercolor] = "bordercolor",
4980ee5cbfSDavid du Colombier [EData] = "data",
5080ee5cbfSDavid du Colombier [EFocus] = "focus",
5180ee5cbfSDavid du Colombier [EFont] = "font",
5280ee5cbfSDavid du Colombier [EFormat] = "format",
539a747e4fSDavid du Colombier [EHide] = "hide",
5480ee5cbfSDavid du Colombier [EImage] = "image",
5580ee5cbfSDavid du Colombier [ERect] = "rect",
569a747e4fSDavid du Colombier [EReveal] = "reveal",
5780ee5cbfSDavid du Colombier [EShow] = "show",
589a747e4fSDavid du Colombier [ESize] = "size",
5980ee5cbfSDavid du Colombier [ETextcolor] = "textcolor",
6080ee5cbfSDavid du Colombier [EValue] = "value",
6180ee5cbfSDavid du Colombier nil
6280ee5cbfSDavid du Colombier };
6380ee5cbfSDavid du Colombier
6480ee5cbfSDavid du Colombier static void
entryfree(Control * c)659a747e4fSDavid du Colombier entryfree(Control *c)
6680ee5cbfSDavid du Colombier {
6780ee5cbfSDavid du Colombier Entry *e;
6880ee5cbfSDavid du Colombier
699a747e4fSDavid du Colombier e = (Entry *)c;
7080ee5cbfSDavid du Colombier _putctlfont(e->font);
7180ee5cbfSDavid du Colombier _putctlimage(e->image);
7280ee5cbfSDavid du Colombier _putctlimage(e->textcolor);
7380ee5cbfSDavid du Colombier _putctlimage(e->bordercolor);
7480ee5cbfSDavid du Colombier free(e->text);
7580ee5cbfSDavid du Colombier }
7680ee5cbfSDavid du Colombier
7780ee5cbfSDavid du Colombier static Point
entrypoint(Entry * e,int c)7880ee5cbfSDavid du Colombier entrypoint(Entry *e, int c)
7980ee5cbfSDavid du Colombier {
8080ee5cbfSDavid du Colombier Point p;
8180ee5cbfSDavid du Colombier Rectangle r;
8280ee5cbfSDavid du Colombier
8380ee5cbfSDavid du Colombier r = e->rect;
8480ee5cbfSDavid du Colombier if(e->border > 0)
8580ee5cbfSDavid du Colombier r = insetrect(r, e->border);
8680ee5cbfSDavid du Colombier p = _ctlalignpoint(r,
8780ee5cbfSDavid du Colombier runestringnwidth(e->font->font, e->text, e->ntext),
8880ee5cbfSDavid du Colombier e->font->font->height, e->align);
8980ee5cbfSDavid du Colombier if(c > e->ntext)
9080ee5cbfSDavid du Colombier c = e->ntext;
9180ee5cbfSDavid du Colombier p.x += runestringnwidth(e->font->font, e->text, c);
9280ee5cbfSDavid du Colombier return p;
9380ee5cbfSDavid du Colombier }
9480ee5cbfSDavid du Colombier
9580ee5cbfSDavid du Colombier static void
entryshow(Entry * e)9680ee5cbfSDavid du Colombier entryshow(Entry *e)
9780ee5cbfSDavid du Colombier {
9880ee5cbfSDavid du Colombier Rectangle r, dr;
9980ee5cbfSDavid du Colombier Point p;
10080ee5cbfSDavid du Colombier
1019a747e4fSDavid du Colombier if (e->hidden)
1029a747e4fSDavid du Colombier return;
10380ee5cbfSDavid du Colombier r = e->rect;
10480ee5cbfSDavid du Colombier draw(e->screen, r, e->image->image, nil, e->image->image->r.min);
10580ee5cbfSDavid du Colombier if(e->border > 0){
10680ee5cbfSDavid du Colombier border(e->screen, r, e->border, e->bordercolor->image, e->bordercolor->image->r.min);
10780ee5cbfSDavid du Colombier dr = insetrect(r, e->border);
10880ee5cbfSDavid du Colombier }else
10980ee5cbfSDavid du Colombier dr = r;
11080ee5cbfSDavid du Colombier p = entrypoint(e, 0);
11180ee5cbfSDavid du Colombier _string(e->screen, p, e->textcolor->image,
11280ee5cbfSDavid du Colombier ZP, e->font->font, nil, e->text, e->ntext,
113ac57dd0bSDavid du Colombier dr, nil, ZP, SoverD);
11480ee5cbfSDavid du Colombier if(e->hasfocus){
11580ee5cbfSDavid du Colombier p = entrypoint(e, e->cursor);
11680ee5cbfSDavid du Colombier r.min = p;
11780ee5cbfSDavid du Colombier r.max.x = p.x+1;
11880ee5cbfSDavid du Colombier r.max.y = p.y+e->font->font->height;
11980ee5cbfSDavid du Colombier if(rectclip(&r, dr))
12080ee5cbfSDavid du Colombier draw(e->screen, r, e->textcolor->image, nil, ZP);
12180ee5cbfSDavid du Colombier }
12280ee5cbfSDavid du Colombier flushimage(display, 1);
12380ee5cbfSDavid du Colombier }
12480ee5cbfSDavid du Colombier
12580ee5cbfSDavid du Colombier static void
entrysetpoint(Entry * e,Point cp)1269a747e4fSDavid du Colombier entrysetpoint(Entry *e, Point cp)
1279a747e4fSDavid du Colombier {
1289a747e4fSDavid du Colombier Point p;
1299a747e4fSDavid du Colombier int i;
1309a747e4fSDavid du Colombier
1319a747e4fSDavid du Colombier if(!ptinrect(cp, insetrect(e->rect, e->border)))
1329a747e4fSDavid du Colombier return;
1339a747e4fSDavid du Colombier p = entrypoint(e, 0);
1349a747e4fSDavid du Colombier for(i=0; i<e->ntext; i++){
1359a747e4fSDavid du Colombier p.x += runestringnwidth(e->font->font, e->text+i, 1);
1369a747e4fSDavid du Colombier if(p.x > cp.x)
1379a747e4fSDavid du Colombier break;
1389a747e4fSDavid du Colombier }
1399a747e4fSDavid du Colombier e->cursor = i;
1409a747e4fSDavid du Colombier entryshow(e);
1419a747e4fSDavid du Colombier }
1429a747e4fSDavid du Colombier
1439a747e4fSDavid du Colombier static void
entrymouse(Control * c,Mouse * m)1449a747e4fSDavid du Colombier entrymouse(Control *c, Mouse *m)
1459a747e4fSDavid du Colombier {
1469a747e4fSDavid du Colombier Entry *e;
1479a747e4fSDavid du Colombier
1489a747e4fSDavid du Colombier e = (Entry*)c;
1499a747e4fSDavid du Colombier if(m->buttons==1 && e->lastbut==0)
1509a747e4fSDavid du Colombier entrysetpoint(e, m->xy);
1519a747e4fSDavid du Colombier e->lastbut = m->buttons;
1529a747e4fSDavid du Colombier }
1539a747e4fSDavid du Colombier
1549a747e4fSDavid du Colombier static void
entryctl(Control * c,CParse * cp)1559a747e4fSDavid du Colombier entryctl(Control *c, CParse *cp)
15680ee5cbfSDavid du Colombier {
15780ee5cbfSDavid du Colombier int cmd;
15880ee5cbfSDavid du Colombier Rectangle r;
15980ee5cbfSDavid du Colombier Entry *e;
16080ee5cbfSDavid du Colombier Rune *rp;
16180ee5cbfSDavid du Colombier
16280ee5cbfSDavid du Colombier e = (Entry*)c;
1639a747e4fSDavid du Colombier cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
16480ee5cbfSDavid du Colombier switch(cmd){
16580ee5cbfSDavid du Colombier default:
1669a747e4fSDavid du Colombier ctlerror("%q: unrecognized message '%s'", e->name, cp->str);
16780ee5cbfSDavid du Colombier break;
16880ee5cbfSDavid du Colombier case EAlign:
1699a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
1709a747e4fSDavid du Colombier e->align = _ctlalignment(cp->args[1]);
17180ee5cbfSDavid du Colombier break;
17280ee5cbfSDavid du Colombier case EBorder:
1739a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
1749a747e4fSDavid du Colombier if(cp->iargs[1] < 0)
1759a747e4fSDavid du Colombier ctlerror("%q: bad border: %c", e->name, cp->str);
1769a747e4fSDavid du Colombier e->border = cp->iargs[1];
17780ee5cbfSDavid du Colombier break;
17880ee5cbfSDavid du Colombier case EBordercolor:
1799a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
1809a747e4fSDavid du Colombier _setctlimage(e, &e->bordercolor, cp->args[1]);
18180ee5cbfSDavid du Colombier break;
18280ee5cbfSDavid du Colombier case EData:
1839a747e4fSDavid du Colombier _ctlargcount(e, cp, 1);
1849a747e4fSDavid du Colombier chanprint(e->data, "%S", e->text);
18580ee5cbfSDavid du Colombier break;
18680ee5cbfSDavid du Colombier case EFocus:
1879a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
1889a747e4fSDavid du Colombier e->hasfocus = cp->iargs[1];
18980ee5cbfSDavid du Colombier e->lastbut = 0;
19080ee5cbfSDavid du Colombier entryshow(e);
19180ee5cbfSDavid du Colombier break;
19280ee5cbfSDavid du Colombier case EFont:
1939a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
1949a747e4fSDavid du Colombier _setctlfont(e, &e->font, cp->args[1]);
19580ee5cbfSDavid du Colombier break;
19680ee5cbfSDavid du Colombier case EFormat:
1979a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
1989a747e4fSDavid du Colombier e->format = ctlstrdup(cp->args[1]);
1999a747e4fSDavid du Colombier break;
2009a747e4fSDavid du Colombier case EHide:
2019a747e4fSDavid du Colombier _ctlargcount(e, cp, 1);
2029a747e4fSDavid du Colombier e->hidden = 1;
20380ee5cbfSDavid du Colombier break;
20480ee5cbfSDavid du Colombier case EImage:
2059a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
2069a747e4fSDavid du Colombier _setctlimage(e, &e->image, cp->args[1]);
20780ee5cbfSDavid du Colombier break;
20880ee5cbfSDavid du Colombier case ERect:
2099a747e4fSDavid du Colombier _ctlargcount(e, cp, 5);
2109a747e4fSDavid du Colombier r.min.x = cp->iargs[1];
2119a747e4fSDavid du Colombier r.min.y = cp->iargs[2];
2129a747e4fSDavid du Colombier r.max.x = cp->iargs[3];
2139a747e4fSDavid du Colombier r.max.y = cp->iargs[4];
21480ee5cbfSDavid du Colombier if(Dx(r)<=0 || Dy(r)<=0)
2159a747e4fSDavid du Colombier ctlerror("%q: bad rectangle: %s", e->name, cp->str);
21680ee5cbfSDavid du Colombier e->rect = r;
21780ee5cbfSDavid du Colombier break;
2189a747e4fSDavid du Colombier case EReveal:
2199a747e4fSDavid du Colombier _ctlargcount(e, cp, 1);
2209a747e4fSDavid du Colombier e->hidden = 0;
22180ee5cbfSDavid du Colombier entryshow(e);
22280ee5cbfSDavid du Colombier break;
2239a747e4fSDavid du Colombier case EShow:
2249a747e4fSDavid du Colombier _ctlargcount(e, cp, 1);
2259a747e4fSDavid du Colombier entryshow(e);
2269a747e4fSDavid du Colombier break;
2279a747e4fSDavid du Colombier case ESize:
2289a747e4fSDavid du Colombier if (cp->nargs == 3)
2299a747e4fSDavid du Colombier r.max = Pt(0x7fffffff, 0x7fffffff);
2309a747e4fSDavid du Colombier else{
2319a747e4fSDavid du Colombier _ctlargcount(e, cp, 5);
2329a747e4fSDavid du Colombier r.max.x = cp->iargs[3];
2339a747e4fSDavid du Colombier r.max.y = cp->iargs[4];
2349a747e4fSDavid du Colombier }
2359a747e4fSDavid du Colombier r.min.x = cp->iargs[1];
2369a747e4fSDavid du Colombier r.min.y = cp->iargs[2];
2379a747e4fSDavid du Colombier if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
2389a747e4fSDavid du Colombier ctlerror("%q: bad sizes: %s", e->name, cp->str);
2399a747e4fSDavid du Colombier e->size.min = r.min;
2409a747e4fSDavid du Colombier e->size.max = r.max;
24180ee5cbfSDavid du Colombier break;
24280ee5cbfSDavid du Colombier case ETextcolor:
2439a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
2449a747e4fSDavid du Colombier _setctlimage(e, &e->textcolor, cp->args[1]);
24580ee5cbfSDavid du Colombier break;
24680ee5cbfSDavid du Colombier case EValue:
2479a747e4fSDavid du Colombier _ctlargcount(e, cp, 2);
2489a747e4fSDavid du Colombier rp = _ctlrunestr(cp->args[1]);
24980ee5cbfSDavid du Colombier if(runestrcmp(rp, e->text) != 0){
25080ee5cbfSDavid du Colombier free(e->text);
25180ee5cbfSDavid du Colombier e->text = rp;
25280ee5cbfSDavid du Colombier e->ntext = runestrlen(e->text);
2539a747e4fSDavid du Colombier e->cursor = e->ntext;
25480ee5cbfSDavid du Colombier entryshow(e);
25580ee5cbfSDavid du Colombier }else
25680ee5cbfSDavid du Colombier free(rp);
25780ee5cbfSDavid du Colombier break;
25880ee5cbfSDavid du Colombier }
25980ee5cbfSDavid du Colombier }
26080ee5cbfSDavid du Colombier
26180ee5cbfSDavid du Colombier static void
entrykey(Entry * e,Rune r)26280ee5cbfSDavid du Colombier entrykey(Entry *e, Rune r)
26380ee5cbfSDavid du Colombier {
26480ee5cbfSDavid du Colombier Rune *s;
26580ee5cbfSDavid du Colombier int n;
26680ee5cbfSDavid du Colombier char *p;
26780ee5cbfSDavid du Colombier
26880ee5cbfSDavid du Colombier switch(r){
26980ee5cbfSDavid du Colombier default:
27080ee5cbfSDavid du Colombier e->text = ctlrealloc(e->text, (e->ntext+1+1)*sizeof(Rune));
27180ee5cbfSDavid du Colombier memmove(e->text+e->cursor+1, e->text+e->cursor,
27280ee5cbfSDavid du Colombier (e->ntext+1-e->cursor)*sizeof(Rune));
27380ee5cbfSDavid du Colombier e->text[e->cursor++] = r;
27480ee5cbfSDavid du Colombier e->ntext++;
27580ee5cbfSDavid du Colombier break;
27680ee5cbfSDavid du Colombier case L'\n': /* newline: return value */
27780ee5cbfSDavid du Colombier p = _ctlstrrune(e->text);
2789a747e4fSDavid du Colombier chanprint(e->event, e->format, e->name, p);
27980ee5cbfSDavid du Colombier free(p);
28080ee5cbfSDavid du Colombier return;
28180ee5cbfSDavid du Colombier case L'\b':
28280ee5cbfSDavid du Colombier if(e->cursor > 0){
28380ee5cbfSDavid du Colombier memmove(e->text+e->cursor-1, e->text+e->cursor,
28480ee5cbfSDavid du Colombier (e->ntext+1-e->cursor)*sizeof(Rune));
28580ee5cbfSDavid du Colombier e->cursor--;
28680ee5cbfSDavid du Colombier e->ntext--;
28780ee5cbfSDavid du Colombier }
28880ee5cbfSDavid du Colombier break;
289*42dedc50SDavid du Colombier case Kright:
290*42dedc50SDavid du Colombier if(e->cursor < e->ntext)
291*42dedc50SDavid du Colombier e->cursor++;
292*42dedc50SDavid du Colombier break;
293*42dedc50SDavid du Colombier case Kleft:
294*42dedc50SDavid du Colombier if(e->cursor > 0)
295*42dedc50SDavid du Colombier e->cursor--;
296*42dedc50SDavid du Colombier break;
297*42dedc50SDavid du Colombier case 0x01: /* control A: beginning of line */
298*42dedc50SDavid du Colombier e->cursor = 0;
299*42dedc50SDavid du Colombier break;
300*42dedc50SDavid du Colombier case 0x05: /* control E: end of line */
301*42dedc50SDavid du Colombier e->cursor = e->ntext;
302*42dedc50SDavid du Colombier break;
30380ee5cbfSDavid du Colombier case 0x15: /* control U: kill line */
30480ee5cbfSDavid du Colombier e->cursor = 0;
30580ee5cbfSDavid du Colombier e->ntext = 0;
30680ee5cbfSDavid du Colombier break;
30780ee5cbfSDavid du Colombier case 0x16: /* control V: paste (append snarf buffer) */
30880ee5cbfSDavid du Colombier s = _ctlgetsnarf();
30980ee5cbfSDavid du Colombier if(s != nil){
31080ee5cbfSDavid du Colombier n = runestrlen(s);
31180ee5cbfSDavid du Colombier e->text = ctlrealloc(e->text, (e->ntext+n+1)*sizeof(Rune));
31280ee5cbfSDavid du Colombier memmove(e->text+e->cursor+n, e->text+e->cursor,
31380ee5cbfSDavid du Colombier (e->ntext+1-e->cursor)*sizeof(Rune));
31480ee5cbfSDavid du Colombier memmove(e->text+e->cursor, s, n*sizeof(Rune));
31580ee5cbfSDavid du Colombier e->cursor += n;
31680ee5cbfSDavid du Colombier e->ntext += n;
31780ee5cbfSDavid du Colombier }
31880ee5cbfSDavid du Colombier break;
31980ee5cbfSDavid du Colombier }
32080ee5cbfSDavid du Colombier e->text[e->ntext] = L'\0';
32180ee5cbfSDavid du Colombier }
3229a747e4fSDavid du Colombier
3239a747e4fSDavid du Colombier static void
entrykeys(Control * c,Rune * rp)3249a747e4fSDavid du Colombier entrykeys(Control *c, Rune *rp)
3259a747e4fSDavid du Colombier {
3269a747e4fSDavid du Colombier Entry *e;
3279a747e4fSDavid du Colombier int i;
3289a747e4fSDavid du Colombier
3299a747e4fSDavid du Colombier e = (Entry *)c;
3309a747e4fSDavid du Colombier for(i=0; rp[i]!=L'\0'; i++)
3319a747e4fSDavid du Colombier entrykey(e, rp[i]);
3329a747e4fSDavid du Colombier entryshow(e);
3339a747e4fSDavid du Colombier }
3349a747e4fSDavid du Colombier
3359a747e4fSDavid du Colombier Control*
createentry(Controlset * cs,char * name)3369a747e4fSDavid du Colombier createentry(Controlset *cs, char *name)
3379a747e4fSDavid du Colombier {
3389a747e4fSDavid du Colombier Entry *e;
3399a747e4fSDavid du Colombier
3409a747e4fSDavid du Colombier e = (Entry*) _createctl(cs, "entry", sizeof(Entry), name);
3419a747e4fSDavid du Colombier e->text = ctlmalloc(sizeof(Rune));
3429a747e4fSDavid du Colombier e->ntext = 0;
3439a747e4fSDavid du Colombier e->image = _getctlimage("white");
3449a747e4fSDavid du Colombier e->textcolor = _getctlimage("black");
3459a747e4fSDavid du Colombier e->bordercolor = _getctlimage("black");
3469a747e4fSDavid du Colombier e->font = _getctlfont("font");
3479a747e4fSDavid du Colombier e->format = ctlstrdup("%q: value %q");
3489a747e4fSDavid du Colombier e->border = 0;
3499a747e4fSDavid du Colombier e->ctl = entryctl;
3509a747e4fSDavid du Colombier e->mouse = entrymouse;
3519a747e4fSDavid du Colombier e->key = entrykeys;
3529a747e4fSDavid du Colombier e->exit = entryfree;
3539a747e4fSDavid du Colombier return (Control *)e;
3549a747e4fSDavid du Colombier }
355