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