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