xref: /plan9/sys/src/libcontrol/entry.c (revision 80ee5cbfe36716af62da8896207e9763b8e3d760)
1*80ee5cbfSDavid du Colombier #include <u.h>
2*80ee5cbfSDavid du Colombier #include <libc.h>
3*80ee5cbfSDavid du Colombier #include <draw.h>
4*80ee5cbfSDavid du Colombier #include <thread.h>
5*80ee5cbfSDavid du Colombier #include <mouse.h>
6*80ee5cbfSDavid du Colombier #include <keyboard.h>
7*80ee5cbfSDavid du Colombier #include <control.h>
8*80ee5cbfSDavid du Colombier 
9*80ee5cbfSDavid du Colombier typedef struct Entry Entry;
10*80ee5cbfSDavid du Colombier 
11*80ee5cbfSDavid du Colombier struct Entry
12*80ee5cbfSDavid du Colombier {
13*80ee5cbfSDavid du Colombier 	Control;
14*80ee5cbfSDavid du Colombier 	int		border;
15*80ee5cbfSDavid du Colombier 	CFont	*font;
16*80ee5cbfSDavid du Colombier 	CImage	*image;
17*80ee5cbfSDavid du Colombier 	CImage	*textcolor;
18*80ee5cbfSDavid du Colombier 	CImage	*bordercolor;
19*80ee5cbfSDavid du Colombier 	Rune		*text;
20*80ee5cbfSDavid du Colombier 	int		ntext;
21*80ee5cbfSDavid du Colombier 	int		cursor;
22*80ee5cbfSDavid du Colombier 	int		align;
23*80ee5cbfSDavid du Colombier 	int		hasfocus;
24*80ee5cbfSDavid du Colombier 	int		lastbut;
25*80ee5cbfSDavid du Colombier };
26*80ee5cbfSDavid du Colombier 
27*80ee5cbfSDavid du Colombier enum{
28*80ee5cbfSDavid du Colombier 	EAlign,
29*80ee5cbfSDavid du Colombier 	EBorder,
30*80ee5cbfSDavid du Colombier 	EBordercolor,
31*80ee5cbfSDavid du Colombier 	EData,
32*80ee5cbfSDavid du Colombier 	EFocus,
33*80ee5cbfSDavid du Colombier 	EFont,
34*80ee5cbfSDavid du Colombier 	EFormat,
35*80ee5cbfSDavid du Colombier 	EImage,
36*80ee5cbfSDavid du Colombier 	ERect,
37*80ee5cbfSDavid du Colombier 	EShow,
38*80ee5cbfSDavid du Colombier 	ETextcolor,
39*80ee5cbfSDavid du Colombier 	EValue,
40*80ee5cbfSDavid du Colombier };
41*80ee5cbfSDavid du Colombier 
42*80ee5cbfSDavid du Colombier static char *cmds[] = {
43*80ee5cbfSDavid du Colombier 	[EAlign] =			"align",
44*80ee5cbfSDavid du Colombier 	[EBorder] =		"border",
45*80ee5cbfSDavid du Colombier 	[EBordercolor] =	"bordercolor",
46*80ee5cbfSDavid du Colombier 	[EData] = 			"data",
47*80ee5cbfSDavid du Colombier 	[EFocus] = 		"focus",
48*80ee5cbfSDavid du Colombier 	[EFont] =			"font",
49*80ee5cbfSDavid du Colombier 	[EFormat] = 		"format",
50*80ee5cbfSDavid du Colombier 	[EImage] =		"image",
51*80ee5cbfSDavid du Colombier 	[ERect] =			"rect",
52*80ee5cbfSDavid du Colombier 	[EShow] =			"show",
53*80ee5cbfSDavid du Colombier 	[ETextcolor] =		"textcolor",
54*80ee5cbfSDavid du Colombier 	[EValue] =			"value",
55*80ee5cbfSDavid du Colombier 	nil
56*80ee5cbfSDavid du Colombier };
57*80ee5cbfSDavid du Colombier 
58*80ee5cbfSDavid du Colombier static void	entryctl(Control*, char*);
59*80ee5cbfSDavid du Colombier static void	entryshow(Entry*);
60*80ee5cbfSDavid du Colombier static void	entrykey(Entry*, Rune);
61*80ee5cbfSDavid du Colombier static void	entryfree(Entry*);
62*80ee5cbfSDavid du Colombier static Point	entrypoint(Entry*, int);
63*80ee5cbfSDavid du Colombier static void	entrysetpoint(Entry*, Point);
64*80ee5cbfSDavid du Colombier 
65*80ee5cbfSDavid du Colombier static void
66*80ee5cbfSDavid du Colombier entrythread(void *v)
67*80ee5cbfSDavid du Colombier {
68*80ee5cbfSDavid du Colombier 	char buf[32];
69*80ee5cbfSDavid du Colombier 	Entry *e;
70*80ee5cbfSDavid du Colombier 	int i;
71*80ee5cbfSDavid du Colombier 
72*80ee5cbfSDavid du Colombier 	e = v;
73*80ee5cbfSDavid du Colombier 
74*80ee5cbfSDavid du Colombier 	snprint(buf, sizeof buf, "entry-%s-0x%p", e->name, e);
75*80ee5cbfSDavid du Colombier 	threadsetname(buf);
76*80ee5cbfSDavid du Colombier 
77*80ee5cbfSDavid du Colombier 	e->text = ctlmalloc(sizeof(Rune));
78*80ee5cbfSDavid du Colombier 	e->ntext = 0;
79*80ee5cbfSDavid du Colombier 	e->image = _getctlimage("white");
80*80ee5cbfSDavid du Colombier 	e->textcolor = _getctlimage("black");
81*80ee5cbfSDavid du Colombier 	e->bordercolor = _getctlimage("black");
82*80ee5cbfSDavid du Colombier 	e->font = _getctlfont("font");
83*80ee5cbfSDavid du Colombier 	e->format = ctlstrdup("%q: value %q");
84*80ee5cbfSDavid du Colombier 	e->border = 0;
85*80ee5cbfSDavid du Colombier 
86*80ee5cbfSDavid du Colombier 	for(;;){
87*80ee5cbfSDavid du Colombier 		switch(alt(e->alts)){
88*80ee5cbfSDavid du Colombier 		default:
89*80ee5cbfSDavid du Colombier 			ctlerror("%q: unknown message", e->name);
90*80ee5cbfSDavid du Colombier 		case AKey:
91*80ee5cbfSDavid du Colombier 			for(i=0; e->kbdr[i]!=L'\0'; i++)
92*80ee5cbfSDavid du Colombier 				entrykey(e, e->kbdr[i]);
93*80ee5cbfSDavid du Colombier 			entryshow(e);
94*80ee5cbfSDavid du Colombier 			break;
95*80ee5cbfSDavid du Colombier 		case AMouse:
96*80ee5cbfSDavid du Colombier 			if(e->m.buttons==1 && e->lastbut==0)
97*80ee5cbfSDavid du Colombier 				entrysetpoint(e, e->m.xy);
98*80ee5cbfSDavid du Colombier 			e->lastbut = e->m.buttons;
99*80ee5cbfSDavid du Colombier 			break;
100*80ee5cbfSDavid du Colombier 		case ACtl:
101*80ee5cbfSDavid du Colombier 			_ctlcontrol(e, e->str, entryctl);
102*80ee5cbfSDavid du Colombier 			free(e->str);
103*80ee5cbfSDavid du Colombier 			break;
104*80ee5cbfSDavid du Colombier 		case AWire:
105*80ee5cbfSDavid du Colombier 			_ctlrewire(e);
106*80ee5cbfSDavid du Colombier 			break;
107*80ee5cbfSDavid du Colombier 		case AExit:
108*80ee5cbfSDavid du Colombier 			entryfree(e);
109*80ee5cbfSDavid du Colombier 			sendul(e->exit, 0);
110*80ee5cbfSDavid du Colombier 			return;
111*80ee5cbfSDavid du Colombier 		}
112*80ee5cbfSDavid du Colombier 	}
113*80ee5cbfSDavid du Colombier }
114*80ee5cbfSDavid du Colombier 
115*80ee5cbfSDavid du Colombier Control*
116*80ee5cbfSDavid du Colombier createentry(Controlset *cs, char *name)
117*80ee5cbfSDavid du Colombier {
118*80ee5cbfSDavid du Colombier 	return _createctl(cs, "entry", sizeof(Entry), name, entrythread, 0);
119*80ee5cbfSDavid du Colombier }
120*80ee5cbfSDavid du Colombier 
121*80ee5cbfSDavid du Colombier static void
122*80ee5cbfSDavid du Colombier entryfree(Entry *e)
123*80ee5cbfSDavid du Colombier {
124*80ee5cbfSDavid du Colombier 	_putctlfont(e->font);
125*80ee5cbfSDavid du Colombier 	_putctlimage(e->image);
126*80ee5cbfSDavid du Colombier 	_putctlimage(e->textcolor);
127*80ee5cbfSDavid du Colombier 	_putctlimage(e->bordercolor);
128*80ee5cbfSDavid du Colombier 	free(e->text);
129*80ee5cbfSDavid du Colombier }
130*80ee5cbfSDavid du Colombier 
131*80ee5cbfSDavid du Colombier static Point
132*80ee5cbfSDavid du Colombier entrypoint(Entry *e, int c)
133*80ee5cbfSDavid du Colombier {
134*80ee5cbfSDavid du Colombier 	Point p;
135*80ee5cbfSDavid du Colombier 	Rectangle r;
136*80ee5cbfSDavid du Colombier 
137*80ee5cbfSDavid du Colombier 	r = e->rect;
138*80ee5cbfSDavid du Colombier 	if(e->border > 0)
139*80ee5cbfSDavid du Colombier 		r = insetrect(r, e->border);
140*80ee5cbfSDavid du Colombier 	p = _ctlalignpoint(r,
141*80ee5cbfSDavid du Colombier 		runestringnwidth(e->font->font, e->text, e->ntext),
142*80ee5cbfSDavid du Colombier 		e->font->font->height, e->align);
143*80ee5cbfSDavid du Colombier 	if(c > e->ntext)
144*80ee5cbfSDavid du Colombier 		c = e->ntext;
145*80ee5cbfSDavid du Colombier 	p.x += runestringnwidth(e->font->font, e->text, c);
146*80ee5cbfSDavid du Colombier 	return p;
147*80ee5cbfSDavid du Colombier }
148*80ee5cbfSDavid du Colombier 
149*80ee5cbfSDavid du Colombier static void
150*80ee5cbfSDavid du Colombier entrysetpoint(Entry *e, Point cp)
151*80ee5cbfSDavid du Colombier {
152*80ee5cbfSDavid du Colombier 	Point p;
153*80ee5cbfSDavid du Colombier 	int i;
154*80ee5cbfSDavid du Colombier 
155*80ee5cbfSDavid du Colombier 	if(!ptinrect(cp, insetrect(e->rect, e->border)))
156*80ee5cbfSDavid du Colombier 		return;
157*80ee5cbfSDavid du Colombier 	p = entrypoint(e, 0);
158*80ee5cbfSDavid du Colombier 	for(i=0; i<e->ntext; i++){
159*80ee5cbfSDavid du Colombier 		p.x += runestringnwidth(e->font->font, e->text+i, 1);
160*80ee5cbfSDavid du Colombier 		if(p.x > cp.x)
161*80ee5cbfSDavid du Colombier 			break;
162*80ee5cbfSDavid du Colombier 	}
163*80ee5cbfSDavid du Colombier 	e->cursor = i;
164*80ee5cbfSDavid du Colombier 	entryshow(e);
165*80ee5cbfSDavid du Colombier }
166*80ee5cbfSDavid du Colombier 
167*80ee5cbfSDavid du Colombier static void
168*80ee5cbfSDavid du Colombier entryshow(Entry *e)
169*80ee5cbfSDavid du Colombier {
170*80ee5cbfSDavid du Colombier 	Rectangle r, dr;
171*80ee5cbfSDavid du Colombier 	Point p;
172*80ee5cbfSDavid du Colombier 
173*80ee5cbfSDavid du Colombier 	r = e->rect;
174*80ee5cbfSDavid du Colombier 	draw(e->screen, r, e->image->image, nil, e->image->image->r.min);
175*80ee5cbfSDavid du Colombier 	if(e->border > 0){
176*80ee5cbfSDavid du Colombier 		border(e->screen, r, e->border, e->bordercolor->image, e->bordercolor->image->r.min);
177*80ee5cbfSDavid du Colombier 		dr = insetrect(r, e->border);
178*80ee5cbfSDavid du Colombier 	}else
179*80ee5cbfSDavid du Colombier 		dr = r;
180*80ee5cbfSDavid du Colombier 	p = entrypoint(e, 0);
181*80ee5cbfSDavid du Colombier 	_string(e->screen, p, e->textcolor->image,
182*80ee5cbfSDavid du Colombier 		ZP, e->font->font, nil, e->text, e->ntext,
183*80ee5cbfSDavid du Colombier 		dr, nil, ZP);
184*80ee5cbfSDavid du Colombier 	if(e->hasfocus){
185*80ee5cbfSDavid du Colombier 		p = entrypoint(e, e->cursor);
186*80ee5cbfSDavid du Colombier 		r.min = p;
187*80ee5cbfSDavid du Colombier 		r.max.x = p.x+1;
188*80ee5cbfSDavid du Colombier 		r.max.y = p.y+e->font->font->height;
189*80ee5cbfSDavid du Colombier 		if(rectclip(&r, dr))
190*80ee5cbfSDavid du Colombier 			draw(e->screen, r, e->textcolor->image, nil, ZP);
191*80ee5cbfSDavid du Colombier 	}
192*80ee5cbfSDavid du Colombier 	flushimage(display, 1);
193*80ee5cbfSDavid du Colombier }
194*80ee5cbfSDavid du Colombier 
195*80ee5cbfSDavid du Colombier static void
196*80ee5cbfSDavid du Colombier entryctl(Control *c, char *str)
197*80ee5cbfSDavid du Colombier {
198*80ee5cbfSDavid du Colombier 	int cmd;
199*80ee5cbfSDavid du Colombier 	CParse cp;
200*80ee5cbfSDavid du Colombier 	Rectangle r;
201*80ee5cbfSDavid du Colombier 	Entry *e;
202*80ee5cbfSDavid du Colombier 	Rune *rp;
203*80ee5cbfSDavid du Colombier 
204*80ee5cbfSDavid du Colombier 	e = (Entry*)c;
205*80ee5cbfSDavid du Colombier 	cmd = _ctlparse(&cp, str, cmds);
206*80ee5cbfSDavid du Colombier 	switch(cmd){
207*80ee5cbfSDavid du Colombier 	default:
208*80ee5cbfSDavid du Colombier 		ctlerror("%q: unrecognized message '%s'", e->name, cp.str);
209*80ee5cbfSDavid du Colombier 		break;
210*80ee5cbfSDavid du Colombier 	case EAlign:
211*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
212*80ee5cbfSDavid du Colombier 		e->align = _ctlalignment(cp.args[1]);
213*80ee5cbfSDavid du Colombier 		break;
214*80ee5cbfSDavid du Colombier 	case EBorder:
215*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
216*80ee5cbfSDavid du Colombier 		if(cp.iargs[1] < 0)
217*80ee5cbfSDavid du Colombier 			ctlerror("%q: bad border: %c", e->name, cp.str);
218*80ee5cbfSDavid du Colombier 		e->border = cp.iargs[1];
219*80ee5cbfSDavid du Colombier 		break;
220*80ee5cbfSDavid du Colombier 	case EBordercolor:
221*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
222*80ee5cbfSDavid du Colombier 		_setctlimage(e, &e->bordercolor, cp.args[1]);
223*80ee5cbfSDavid du Colombier 		break;
224*80ee5cbfSDavid du Colombier 	case EData:
225*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 1);
226*80ee5cbfSDavid du Colombier 		printctl(e->data, "%S", e->text);
227*80ee5cbfSDavid du Colombier 		break;
228*80ee5cbfSDavid du Colombier 	case EFocus:
229*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
230*80ee5cbfSDavid du Colombier 		e->hasfocus = cp.iargs[1];
231*80ee5cbfSDavid du Colombier 		e->lastbut = 0;
232*80ee5cbfSDavid du Colombier 		entryshow(e);
233*80ee5cbfSDavid du Colombier 		break;
234*80ee5cbfSDavid du Colombier 	case EFont:
235*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
236*80ee5cbfSDavid du Colombier 		_setctlfont(e, &e->font, cp.args[1]);
237*80ee5cbfSDavid du Colombier 		break;
238*80ee5cbfSDavid du Colombier 	case EFormat:
239*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
240*80ee5cbfSDavid du Colombier 		e->format = ctlstrdup(cp.args[1]);
241*80ee5cbfSDavid du Colombier 		break;
242*80ee5cbfSDavid du Colombier 	case EImage:
243*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
244*80ee5cbfSDavid du Colombier 		_setctlimage(e, &e->image, cp.args[1]);
245*80ee5cbfSDavid du Colombier 		break;
246*80ee5cbfSDavid du Colombier 	case ERect:
247*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 5);
248*80ee5cbfSDavid du Colombier 		r.min.x = cp.iargs[1];
249*80ee5cbfSDavid du Colombier 		r.min.y = cp.iargs[2];
250*80ee5cbfSDavid du Colombier 		r.max.x = cp.iargs[3];
251*80ee5cbfSDavid du Colombier 		r.max.y = cp.iargs[4];
252*80ee5cbfSDavid du Colombier 		if(Dx(r)<=0 || Dy(r)<=0)
253*80ee5cbfSDavid du Colombier 			ctlerror("%q: bad rectangle: %s", e->name, cp.str);
254*80ee5cbfSDavid du Colombier 		e->rect = r;
255*80ee5cbfSDavid du Colombier 		break;
256*80ee5cbfSDavid du Colombier 	case EShow:
257*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 1);
258*80ee5cbfSDavid du Colombier 		entryshow(e);
259*80ee5cbfSDavid du Colombier 		break;
260*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
261*80ee5cbfSDavid du Colombier 		_setctlimage(e, &e->textcolor, cp.args[1]);
262*80ee5cbfSDavid du Colombier 		break;
263*80ee5cbfSDavid du Colombier 	case ETextcolor:
264*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
265*80ee5cbfSDavid du Colombier 		_setctlimage(e, &e->textcolor, cp.args[1]);
266*80ee5cbfSDavid du Colombier 		break;
267*80ee5cbfSDavid du Colombier 	case EValue:
268*80ee5cbfSDavid du Colombier 		_ctlargcount(e, &cp, 2);
269*80ee5cbfSDavid du Colombier 		rp = _ctlrunestr(cp.args[1]);
270*80ee5cbfSDavid du Colombier 		if(runestrcmp(rp, e->text) != 0){
271*80ee5cbfSDavid du Colombier 			free(e->text);
272*80ee5cbfSDavid du Colombier 			e->text = rp;
273*80ee5cbfSDavid du Colombier 			e->ntext = runestrlen(e->text);
274*80ee5cbfSDavid du Colombier 			entryshow(e);
275*80ee5cbfSDavid du Colombier 		}else
276*80ee5cbfSDavid du Colombier 			free(rp);
277*80ee5cbfSDavid du Colombier 		break;
278*80ee5cbfSDavid du Colombier 	}
279*80ee5cbfSDavid du Colombier }
280*80ee5cbfSDavid du Colombier 
281*80ee5cbfSDavid du Colombier static void
282*80ee5cbfSDavid du Colombier entrykey(Entry *e, Rune r)
283*80ee5cbfSDavid du Colombier {
284*80ee5cbfSDavid du Colombier 	Rune *s;
285*80ee5cbfSDavid du Colombier 	int n;
286*80ee5cbfSDavid du Colombier 	char *p;
287*80ee5cbfSDavid du Colombier 
288*80ee5cbfSDavid du Colombier 	switch(r){
289*80ee5cbfSDavid du Colombier 	default:
290*80ee5cbfSDavid du Colombier 		e->text = ctlrealloc(e->text, (e->ntext+1+1)*sizeof(Rune));
291*80ee5cbfSDavid du Colombier 		memmove(e->text+e->cursor+1, e->text+e->cursor,
292*80ee5cbfSDavid du Colombier 			(e->ntext+1-e->cursor)*sizeof(Rune));
293*80ee5cbfSDavid du Colombier 		e->text[e->cursor++] = r;
294*80ee5cbfSDavid du Colombier 		e->ntext++;
295*80ee5cbfSDavid du Colombier 		break;
296*80ee5cbfSDavid du Colombier 	case L'\n':	/* newline: return value */
297*80ee5cbfSDavid du Colombier 		p = _ctlstrrune(e->text);
298*80ee5cbfSDavid du Colombier 		printctl(e->event, e->format, e->name, p);
299*80ee5cbfSDavid du Colombier 		free(p);
300*80ee5cbfSDavid du Colombier 		return;
301*80ee5cbfSDavid du Colombier 	case L'\b':
302*80ee5cbfSDavid du Colombier 		if(e->cursor > 0){
303*80ee5cbfSDavid du Colombier 			memmove(e->text+e->cursor-1, e->text+e->cursor,
304*80ee5cbfSDavid du Colombier 				(e->ntext+1-e->cursor)*sizeof(Rune));
305*80ee5cbfSDavid du Colombier 			e->cursor--;
306*80ee5cbfSDavid du Colombier 			e->ntext--;
307*80ee5cbfSDavid du Colombier 		}
308*80ee5cbfSDavid du Colombier 		break;
309*80ee5cbfSDavid du Colombier 	case 0x15:	/* control U: kill line */
310*80ee5cbfSDavid du Colombier 		e->cursor = 0;
311*80ee5cbfSDavid du Colombier 		e->ntext = 0;
312*80ee5cbfSDavid du Colombier 		break;
313*80ee5cbfSDavid du Colombier 	case 0x16:	/* control V: paste (append snarf buffer) */
314*80ee5cbfSDavid du Colombier 		s = _ctlgetsnarf();
315*80ee5cbfSDavid du Colombier 		if(s != nil){
316*80ee5cbfSDavid du Colombier 			n = runestrlen(s);
317*80ee5cbfSDavid du Colombier 			e->text = ctlrealloc(e->text, (e->ntext+n+1)*sizeof(Rune));
318*80ee5cbfSDavid du Colombier 			memmove(e->text+e->cursor+n, e->text+e->cursor,
319*80ee5cbfSDavid du Colombier 				(e->ntext+1-e->cursor)*sizeof(Rune));
320*80ee5cbfSDavid du Colombier 			memmove(e->text+e->cursor, s, n*sizeof(Rune));
321*80ee5cbfSDavid du Colombier 			e->cursor += n;
322*80ee5cbfSDavid du Colombier 			e->ntext += n;
323*80ee5cbfSDavid du Colombier 		}
324*80ee5cbfSDavid du Colombier 		break;
325*80ee5cbfSDavid du Colombier 	}
326*80ee5cbfSDavid du Colombier 	e->text[e->ntext] = L'\0';
327*80ee5cbfSDavid du Colombier }
328