xref: /plan9/sys/src/libcontrol/menu.c (revision ac57dd0bdfb9d49ce3ebb32937bb07f2cec3da6c)
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 Menu0 Menu0;	/* Menu is taken by mouse.h */
10 
11 struct Menu0
12 {
13 	Control;
14 	CImage	*image;
15 	CImage	*bordercolor;
16 	CImage	*textcolor;
17 	CImage	*selectcolor;
18 	CImage	*selecttextcolor;
19 	CFont	*font;
20 	char		**line;
21 	int		nline;
22 	int		border;
23 	int		align;
24 	Image	*window;
25 	int		visible;	/* state of menu */
26 	int		selection;		/* currently selected line; -1 == none */
27 	int		prevsel;	/* previous selection */
28 	int		lastbut;	/* previous state of mouse button */
29 };
30 
31 enum{
32 	EAdd,
33 	EAlign,
34 	EBorder,
35 	EBordercolor,
36 	EFocus,
37 	EFont,
38 	EFormat,
39 	EHide,
40 	EImage,
41 	ERect,
42 	EReveal,
43 	ESelectcolor,
44 	ESelecttextcolor,
45 	EShow,
46 	ESize,
47 	ETextcolor,
48 	EWindow,
49 };
50 
51 static char *cmds[] = {
52 	[EAdd] = 			"add",
53 	[EAlign] = 			"align",
54 	[EBorder] = 		"border",
55 	[EBordercolor] =	"bordercolor",
56 	[EFocus] = 		"focus",
57 	[EFont] =			"font",
58 	[EFormat] = 		"format",
59 	[EHide] =			"hide",
60 	[EImage] =		"image",
61 	[ERect] =			"rect",
62 	[EReveal] =		"reveal",
63 	[ESelectcolor] =	"selectcolor",
64 	[ESelecttextcolor] =	"selecttextcolor",
65 	[EShow] =			"show",
66 	[ESize] =			"size",
67 	[ETextcolor] =		"textcolor",
68 	[EWindow] =		"window",
69 	nil
70 };
71 
72 static void	menushow(Menu0*);
73 static void menuhide(Menu0*);
74 
75 static void
menufree(Control * c)76 menufree(Control *c)
77 {
78 	Menu0 *m;
79 
80 	m = (Menu0*)c;
81 	_putctlfont(m->font);
82 	_putctlimage(m->image);
83 	_putctlimage(m->textcolor);
84 	_putctlimage(m->bordercolor);
85 	_putctlimage(m->selectcolor);
86 	_putctlimage(m->selecttextcolor);
87 }
88 
89 static void
menushow(Menu0 * m)90 menushow(Menu0 *m)
91 {
92 	Rectangle r, clipr;
93 	int i, dx, dy, w;
94 	Font *f;
95 	Point p, q;
96 	Image *im, *c;
97 
98 	if(m->hidden || m->window == nil)
99 		return;
100 
101 	m->visible = 1;
102 	f = m->font->font;
103 	draw(m->window, m->rect, m->image->image, nil, m->image->image->r.min);
104 	if(m->border > 0)
105 		border(m->window, m->rect, m->border, m->bordercolor->image, ZP);
106 	/* text goes here */
107 	dx = 0;
108 	for(i=0; i<m->nline; i++){
109 		w = stringwidth(f, m->line[i]);
110 		if(dx < w)
111 			dx = w;
112 	}
113 	dy = m->nline*f->height;
114 	clipr = insetrect(m->rect, m->border);
115 	p = _ctlalignpoint(clipr, dx, dy, m->align);
116 	im = m->textcolor->image;
117 //	if(m->pressed)
118 //		im = m->pressedtextcolor->image;
119 	for(i=0; i<m->nline; i++){
120 		r.min = p;
121 		r.max.x = p.x+dx;
122 		r.max.y = p.y+f->height;
123 		c = im;
124 		if(i == m->selection){
125 			draw(m->window, r, m->selectcolor->image, nil, ZP);
126 			c = m->selecttextcolor->image;
127 		}
128 		q = _ctlalignpoint(r, stringwidth(f, m->line[i]), f->height, m->align%3);
129 		_string(m->window, q, c,
130 			ZP, f, m->line[i], nil, strlen(m->line[i]),
131 			clipr, nil, ZP, SoverD);
132 		p.y += f->height;
133 	}
134 //	if(m->pressed)
135 //		draw(m->screen, m->rect, m->lighm->image, m->mask->image, m->mask->image->r.min);
136 	flushimage(display, 1);
137 }
138 
139 static Point
menusize(Menu0 * m)140 menusize(Menu0 *m)
141 {
142 	int x, y;
143 	int i;
144 	Point p;
145 	Font *f;
146 
147 	x = 0;
148 	y = 0;
149 	f = m->font->font;
150 	for(i=0; i<m->nline; i++){
151 		p = stringsize(f, m->line[i]);
152 		if(p.x > x)
153 			x = p.x;
154 		y += f->height;
155 	}
156 
157 	return Pt(x+2*m->border, y+2*m->border);
158 }
159 
160 static void
menuhide(Menu0 * m)161 menuhide(Menu0 *m)
162 {
163 	freeimage(m->window);
164 	m->window = nil;
165 	m->rect.max.y = m->rect.min.y;	/* go to zero size */
166 	m->lastbut = 0;
167 	m->visible = 0;
168 	if(m->selection >= 0)
169 		m->prevsel = m->selection;
170 	m->selection = -1;
171 	_ctlfocus(m, 0);
172 }
173 
174 static void
menutrack(Control * c,Mouse * ms)175 menutrack(Control *c, Mouse *ms)
176 {
177 	Rectangle r;
178 	int s;
179 	Menu0 *m;
180 
181 	m = (Menu0*)c;
182 	if(m->window == nil)
183 		return;
184 	if(m->lastbut && ms->buttons==0){	/* menu was released */
185 		chanprint(m->event, "%q: value %d", m->name, m->selection);
186 		menuhide(m);
187 		return;
188 	}
189 	m->lastbut = ms->buttons;
190 	r = insetrect(m->rect, m->border);
191 	if(!ptinrect(ms->xy, r))
192 		s = -1;
193 	else{
194 		s = (ms->xy.y - r.min.y)/m->font->font->height;
195 		if(s < 0 || s >= m->nline)
196 			s = -1;
197 	}
198 	if(m->visible== 0 || s!=m->selection){
199 		m->selection = s;
200 		menushow(m);
201 	}
202 }
203 
204 static void
menuctl(Control * c,CParse * cp)205 menuctl(Control *c, CParse *cp)
206 {
207 	int up, cmd, h;
208 	Rectangle r;
209 	Menu0 *m;
210 	Point diag;
211 
212 	m = (Menu0*)c;
213 	cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
214 	switch(cmd){
215 	default:
216 		ctlerror("%q: unrecognized message '%s'", m->name, cp->str);
217 		break;
218 	case EAdd:
219 		_ctlargcount(m, cp, 2);
220 		m->line = ctlrealloc(m->line, (m->nline+1)*sizeof(char*));
221 		m->line[m->nline++] = ctlstrdup(cp->args[1]);
222 		menushow(m);
223 		break;
224 	case EAlign:
225 		_ctlargcount(m, cp, 2);
226 		m->align = _ctlalignment(cp->args[1]);
227 		menushow(m);
228 		break;
229 	case EBorder:
230 		_ctlargcount(m, cp, 2);
231 		m->border = cp->iargs[1];
232 		menushow(m);
233 		break;
234 	case EBordercolor:
235 		_ctlargcount(m, cp, 2);
236 		_setctlimage(m, &m->bordercolor, cp->args[1]);
237 		menushow(m);
238 		break;
239 	case EFocus:
240 		_ctlargcount(m, cp, 2);
241 		if(atoi(cp->args[1]) == 0)
242 			menuhide(m);
243 		break;
244 	case EFont:
245 		_ctlargcount(m, cp, 2);
246 		_setctlfont(m, &m->font, cp->args[1]);
247 		break;
248 	case EFormat:
249 		_ctlargcount(m, cp, 2);
250 		m->format = ctlstrdup(cp->args[1]);
251 		break;
252 	case EHide:
253 		_ctlargcount(m, cp, 1);
254 		m->hidden = 1;
255 		break;
256 	case EImage:
257 		_ctlargcount(m, cp, 2);
258 		_setctlimage(m, &m->image, cp->args[1]);
259 		menushow(m);
260 		break;
261 	case ERect:
262 		_ctlargcount(m, cp, 5);
263 		r.min.x = cp->iargs[1];
264 		r.min.y = cp->iargs[2];
265 		r.max.x = cp->iargs[3];
266 		r.max.y = cp->iargs[4];
267 		if(Dx(r)<0 || Dy(r)<0)
268 			ctlerror("%q: bad rectangle: %s", m->name, cp->str);
269 		m->rect = r;
270 		menushow(m);
271 		break;
272 	case EReveal:
273 		_ctlargcount(m, cp, 1);
274 		m->hidden = 0;
275 		menushow(m);
276 		break;
277 	case ESelectcolor:
278 		_ctlargcount(m, cp, 2);
279 		_setctlimage(m, &m->selectcolor, cp->args[1]);
280 		menushow(m);
281 		break;
282 	case ESelecttextcolor:
283 		_ctlargcount(m, cp, 2);
284 		_setctlimage(m, &m->selecttextcolor, cp->args[1]);
285 		menushow(m);
286 		break;
287 	case EShow:
288 		_ctlargcount(m, cp, 1);
289 		menushow(m);
290 		break;
291 	case ESize:
292 		if (cp->nargs == 3)
293 			r.max = Pt(0x7fffffff, 0x7fffffff);
294 		else{
295 			_ctlargcount(m, cp, 5);
296 			r.max.x = cp->iargs[3];
297 			r.max.y = cp->iargs[4];
298 		}
299 		r.min.x = cp->iargs[1];
300 		r.min.y = cp->iargs[2];
301 		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)
302 			ctlerror("%q: bad sizes: %s", m->name, cp->str);
303 		m->size.min = r.min;
304 		m->size.max = r.max;
305 		break;
306 	case ETextcolor:
307 		_ctlargcount(m, cp, 2);
308 		_setctlimage(m, &m->textcolor, cp->args[1]);
309 		menushow(m);
310 		break;
311 	case EWindow:
312 		/* no args == toggle; otherwise 0 or 1 for state of window */
313 		if(cp->nargs >= 2)
314 			up = cp->iargs[1];
315 		else
316 			up = (m->window == nil);
317 		if(!up){	/* take window down */
318 			if(m->window)
319 				menuhide(m);
320 			break;
321 		}
322 		if(m->window != nil)
323 			break;
324 		diag = menusize(m);
325 		m->rect.max.x = m->rect.min.x + diag.x;
326 		m->rect.max.y = m->rect.min.y + diag.y;
327 		m->window = allocwindow(_screen, m->rect, Refbackup, DWhite);
328 		if(m->window == nil)
329 			m->window = m->screen;
330 		up = m->prevsel;
331 		if(up<0 || up>=m->nline)
332 			up = 0;
333 		m->selection = up;
334 		menushow(m);
335 		h = m->font->font->height;
336 		moveto(m->controlset->mousectl,
337 			Pt(m->rect.min.x+Dx(m->rect)/2, m->rect.min.y+up*h+h/2));
338 //		_ctlfocus(m, 1);
339 		break;
340 	}
341 }
342 
343 Control*
createmenu(Controlset * cs,char * name)344 createmenu(Controlset *cs, char *name)
345 {
346 	Menu0 *m;
347 
348 	m = (Menu0*)_createctl(cs, "menu", sizeof(Menu0), name);
349 	m->font = _getctlfont("font");
350 	m->image = _getctlimage("white");
351 	m->textcolor = _getctlimage("black");
352 	m->selectcolor = _getctlimage("yellow");
353 	m->selecttextcolor = _getctlimage("black");
354 	m->bordercolor = _getctlimage("black");
355 	m->format = ctlstrdup("%q: value %d");
356 	m->border = 0;
357 	m->align = Aupperleft;
358 	m->visible = 0;
359 	m->window = nil;
360 	m->lastbut = 0;
361 	m->selection = -1;
362 	m->mouse = menutrack;
363 	m->ctl = menuctl;
364 	m->exit = menufree;
365 	return (Control *)m;
366 }
367