xref: /plan9/sys/src/libcontrol/entry.c (revision ac57dd0bdfb9d49ce3ebb32937bb07f2cec3da6c)
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
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
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
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,
113*ac57dd0bSDavid 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
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
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
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
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;
28980ee5cbfSDavid du Colombier 	case 0x15:	/* control U: kill line */
29080ee5cbfSDavid du Colombier 		e->cursor = 0;
29180ee5cbfSDavid du Colombier 		e->ntext = 0;
29280ee5cbfSDavid du Colombier 		break;
29380ee5cbfSDavid du Colombier 	case 0x16:	/* control V: paste (append snarf buffer) */
29480ee5cbfSDavid du Colombier 		s = _ctlgetsnarf();
29580ee5cbfSDavid du Colombier 		if(s != nil){
29680ee5cbfSDavid du Colombier 			n = runestrlen(s);
29780ee5cbfSDavid du Colombier 			e->text = ctlrealloc(e->text, (e->ntext+n+1)*sizeof(Rune));
29880ee5cbfSDavid du Colombier 			memmove(e->text+e->cursor+n, e->text+e->cursor,
29980ee5cbfSDavid du Colombier 				(e->ntext+1-e->cursor)*sizeof(Rune));
30080ee5cbfSDavid du Colombier 			memmove(e->text+e->cursor, s, n*sizeof(Rune));
30180ee5cbfSDavid du Colombier 			e->cursor += n;
30280ee5cbfSDavid du Colombier 			e->ntext += n;
30380ee5cbfSDavid du Colombier 		}
30480ee5cbfSDavid du Colombier 		break;
30580ee5cbfSDavid du Colombier 	}
30680ee5cbfSDavid du Colombier 	e->text[e->ntext] = L'\0';
30780ee5cbfSDavid du Colombier }
3089a747e4fSDavid du Colombier 
3099a747e4fSDavid du Colombier static void
3109a747e4fSDavid du Colombier entrykeys(Control *c, Rune *rp)
3119a747e4fSDavid du Colombier {
3129a747e4fSDavid du Colombier 	Entry *e;
3139a747e4fSDavid du Colombier 	int i;
3149a747e4fSDavid du Colombier 
3159a747e4fSDavid du Colombier 	e = (Entry *)c;
3169a747e4fSDavid du Colombier 	for(i=0; rp[i]!=L'\0'; i++)
3179a747e4fSDavid du Colombier 		entrykey(e, rp[i]);
3189a747e4fSDavid du Colombier 	entryshow(e);
3199a747e4fSDavid du Colombier }
3209a747e4fSDavid du Colombier 
3219a747e4fSDavid du Colombier Control*
3229a747e4fSDavid du Colombier createentry(Controlset *cs, char *name)
3239a747e4fSDavid du Colombier {
3249a747e4fSDavid du Colombier 	Entry *e;
3259a747e4fSDavid du Colombier 
3269a747e4fSDavid du Colombier 	e = (Entry*) _createctl(cs, "entry", sizeof(Entry), name);
3279a747e4fSDavid du Colombier 	e->text = ctlmalloc(sizeof(Rune));
3289a747e4fSDavid du Colombier 	e->ntext = 0;
3299a747e4fSDavid du Colombier 	e->image = _getctlimage("white");
3309a747e4fSDavid du Colombier 	e->textcolor = _getctlimage("black");
3319a747e4fSDavid du Colombier 	e->bordercolor = _getctlimage("black");
3329a747e4fSDavid du Colombier 	e->font = _getctlfont("font");
3339a747e4fSDavid du Colombier 	e->format = ctlstrdup("%q: value %q");
3349a747e4fSDavid du Colombier 	e->border = 0;
3359a747e4fSDavid du Colombier 	e->ctl = entryctl;
3369a747e4fSDavid du Colombier 	e->mouse = entrymouse;
3379a747e4fSDavid du Colombier 	e->key = entrykeys;
3389a747e4fSDavid du Colombier 	e->exit = entryfree;
3399a747e4fSDavid du Colombier 	return (Control *)e;
3409a747e4fSDavid du Colombier }
341