menu.c 7.4 KB

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