menus.c 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840
  1. #include "lib9.h"
  2. #include "draw.h"
  3. #include "keyboard.h"
  4. #include "tk.h"
  5. #include "frame.h"
  6. #include "label.h"
  7. /*
  8. arrow annotation for choicebutton: how do we make sure
  9. the menu items come up the same size?
  10. - set menu items to same req.width & height as button itself.
  11. autorepeat:
  12. when we get mouse event at the edge of the screen
  13. and the menu overlaps that edge,
  14. start autorepeat timer to slide the menu the opposite direction.
  15. variable setting + command invocation:
  16. is the value of the variable the text or the index?
  17. same for the value appended to the command, text or index?
  18. if it's reimplemented as a custom widget, how does the custom widget
  19. get notified of variable changes?
  20. */
  21. /* Widget Commands (+ means implemented)
  22. +activate
  23. +add
  24. +cget
  25. +configure
  26. +delete
  27. +entrycget
  28. +entryconfigure
  29. +index
  30. +insert
  31. +invoke
  32. +post
  33. +postcascade
  34. +type
  35. +unpost
  36. +yposition
  37. */
  38. #define O(t, e) ((long)(&((t*)0)->e))
  39. /* Layout constants */
  40. enum {
  41. Sepheight = 6, /* Height of menu separator */
  42. };
  43. #define NOCHOICE "-----"
  44. enum {
  45. Startspeed = TKI2F(1),
  46. };
  47. static
  48. TkOption mbopts[] =
  49. {
  50. "text", OPTtext, O(TkLabel, text), nil,
  51. "anchor", OPTflag, O(TkLabel, anchor), tkanchor,
  52. "underline", OPTdist, O(TkLabel, ul), nil,
  53. "justify", OPTstab, O(TkLabel, justify), tkjustify,
  54. "menu", OPTtext, O(TkLabel, menu), nil,
  55. "bitmap", OPTbmap, O(TkLabel, bitmap), nil,
  56. "image", OPTimag, O(TkLabel, img), nil,
  57. nil
  58. };
  59. static
  60. TkOption choiceopts[] =
  61. {
  62. "variable", OPTtext, O(TkLabel, variable), nil,
  63. "values", OPTlist, O(TkLabel, values), nil,
  64. "command", OPTtext, O(TkLabel, command), nil,
  65. nil
  66. };
  67. static
  68. TkEbind mbbindings[] =
  69. {
  70. {TkEnter, "%W tkMBenter %s"},
  71. {TkLeave, "%W tkMBleave"},
  72. {TkButton1P, "%W tkMBpress 1"},
  73. {TkKey, "%W tkMBkey 0x%K"},
  74. {TkButton1P|TkMotion, "%W tkMBpress 0"},
  75. };
  76. extern Rectangle bbnil;
  77. static char* tkmpost(Tk*, int, int, int, int, int);
  78. static void menuclr(Tk*);
  79. static void freemenu(Tk*);
  80. static void appenditem(Tk*, Tk*, int);
  81. static void layout(Tk*);
  82. static Tk* tkmenuindex2ptr(Tk*, char**);
  83. static void activateitem(Tk*);
  84. /*
  85. * unmap menu cascade upto (but not including) tk
  86. */
  87. static void
  88. tkunmapmenus(TkTop *top, Tk *tk)
  89. {
  90. TkTop *t;
  91. Tk *menu;
  92. TkWin *tkw;
  93. menu = top->ctxt->tkmenu;
  94. if (menu == nil)
  95. return;
  96. t = menu->env->top;
  97. /* if something went wrong, clear down all menus */
  98. if (tk != nil && tk->env->top != t)
  99. tk = nil;
  100. while (menu != nil && menu != tk) {
  101. menuclr(menu);
  102. tkunmap(menu);
  103. tkcancelrepeat(menu);
  104. tkw = TKobj(TkWin, menu);
  105. if (tkw->cascade != nil) {
  106. menu = tklook(t, tkw->cascade, 0);
  107. free(tkw->cascade);
  108. tkw->cascade = nil;
  109. } else
  110. menu = nil;
  111. }
  112. top->ctxt->tkmenu = menu;
  113. tksetmgrab(top, menu);
  114. }
  115. static void
  116. tkunmapmenu(Tk *tk)
  117. {
  118. TkTop *t;
  119. TkWin *tkw;
  120. Tk *parent;
  121. parent = nil;
  122. tkw = TKobj(TkWin, tk);
  123. t = tk->env->top;
  124. if (tkw->cascade != nil)
  125. parent = tklook(t, tkw->cascade, 0);
  126. tkunmapmenus(t, parent);
  127. if (tkw->freeonunmap)
  128. freemenu(tk);
  129. }
  130. static void
  131. tksizemenubutton(Tk *tk)
  132. {
  133. int w, h;
  134. char **v, *cur;
  135. TkLabel *tkl = TKobj(TkLabel, tk);
  136. tksizelabel(tk);
  137. if(tk->type != TKchoicebutton)
  138. return;
  139. w = tk->req.width;
  140. h = tk->req.height;
  141. v = tkl->values;
  142. if (v == nil || *v == nil)
  143. return;
  144. cur = tkl->text;
  145. for (; *v; v++) {
  146. tkl->text = *v;
  147. tksizelabel(tk);
  148. if (tk->req.width > w)
  149. w = tk->req.width;
  150. if (tk->req.height > h)
  151. h = tk->req.height;
  152. }
  153. tkl->text = cur;
  154. tksizelabel(tk);
  155. tk->req.width = w;
  156. tk->req.height = h;
  157. }
  158. static char*
  159. tkmkmenubutton(TkTop *t, char *arg, char **ret, int type, TkOption *opts)
  160. {
  161. Tk *tk;
  162. char *e, **v;
  163. TkName *names;
  164. TkLabel *tkl;
  165. TkOptab tko[3];
  166. /* need to get the label from elsewhere */
  167. tk = tknewobj(t, type, sizeof(Tk)+sizeof(TkLabel));
  168. if(tk == nil)
  169. return TkNomem;
  170. tk->borderwidth = 2;
  171. tk->flag |= Tknograb;
  172. tkl = TKobj(TkLabel, tk);
  173. tkl->ul = -1;
  174. if(type == TKchoicebutton)
  175. tkl->anchor = Tknorth|Tkwest;
  176. tko[0].ptr = tk;
  177. tko[0].optab = tkgeneric;
  178. tko[1].ptr = tkl;
  179. tko[1].optab = opts;
  180. tko[2].ptr = nil;
  181. names = nil;
  182. e = tkparse(t, arg, tko, &names);
  183. if(e != nil) {
  184. tkfreeobj(tk);
  185. return e;
  186. }
  187. tkl->nvalues = 0;
  188. if (tkl->values != nil) {
  189. for (v = tkl->values; *v; v++)
  190. ;
  191. tkl->nvalues = v - tkl->values;
  192. }
  193. if(type == TKchoicebutton){
  194. if(tkl->nvalues > 0)
  195. tkl->text = strdup(tkl->values[0]);
  196. else
  197. tkl->text = strdup(NOCHOICE);
  198. }
  199. tksettransparent(tk,
  200. tkhasalpha(tk->env, TkCbackgnd) ||
  201. tkhasalpha(tk->env, TkCselectbgnd) ||
  202. tkhasalpha(tk->env, TkCactivebgnd));
  203. e = tkbindings(t, tk, mbbindings, nelem(mbbindings));
  204. if(e != nil) {
  205. tkfreeobj(tk);
  206. return e;
  207. }
  208. tksizemenubutton(tk);
  209. e = tkaddchild(t, tk, &names);
  210. tkfreename(names);
  211. if(e != nil) {
  212. tkfreeobj(tk);
  213. return e;
  214. }
  215. tk->name->link = nil;
  216. return tkvalue(ret, "%s", tk->name->name);
  217. }
  218. char*
  219. tkchoicebutton(TkTop *t, char *arg, char **ret)
  220. {
  221. return tkmkmenubutton(t, arg, ret, TKchoicebutton, choiceopts);
  222. }
  223. char*
  224. tkmenubutton(TkTop *t, char *arg, char **ret)
  225. {
  226. return tkmkmenubutton(t, arg, ret, TKmenubutton, mbopts);
  227. }
  228. static char*
  229. tkmenubutcget(Tk *tk, char *arg, char **val)
  230. {
  231. TkOptab tko[3];
  232. TkLabel *tkl = TKobj(TkLabel, tk);
  233. tko[0].ptr = tk;
  234. tko[0].optab = tkgeneric;
  235. tko[1].ptr = tkl;
  236. tko[1].optab = (tk->type == TKchoicebutton ? choiceopts : mbopts);
  237. tko[2].ptr = nil;
  238. return tkgencget(tko, arg, val, tk->env->top);
  239. }
  240. static char*
  241. tkmenubutconf(Tk *tk, char *arg, char **val)
  242. {
  243. char *e, **v;
  244. TkGeom g;
  245. int bd;
  246. TkOptab tko[3];
  247. TkLabel *tkl = TKobj(TkLabel, tk);
  248. tko[0].ptr = tk;
  249. tko[0].optab = tkgeneric;
  250. tko[1].ptr = tkl;
  251. tko[1].optab = (tk->type == TKchoicebutton ? choiceopts : mbopts);
  252. tko[2].ptr = nil;
  253. if(*arg == '\0')
  254. return tkconflist(tko, val);
  255. g = tk->req;
  256. bd = tk->borderwidth;
  257. e = tkparse(tk->env->top, arg, tko, nil);
  258. if (tk->type == TKchoicebutton) {
  259. tkl->nvalues = 0;
  260. if (tkl->values != nil) {
  261. for (v = tkl->values; *v; v++)
  262. ;
  263. tkl->nvalues = v - tkl->values;
  264. }
  265. if (tkl->check >= tkl->nvalues || strcmp(tkl->text, tkl->values[tkl->check])) {
  266. /*
  267. * try to keep selected value the same if possible
  268. */
  269. for (v = tkl->values; v && *v; v++)
  270. if (!strcmp(*v, tkl->text))
  271. break;
  272. free(tkl->text);
  273. if (v == nil || *v == nil) {
  274. tkl->text = strdup(tkl->nvalues > 0 ? tkl->values[0] : NOCHOICE);
  275. tkl->check = 0;
  276. } else {
  277. tkl->check = v - tkl->values;
  278. tkl->text = strdup(*v);
  279. }
  280. }
  281. }
  282. tksettransparent(tk,
  283. tkhasalpha(tk->env, TkCbackgnd) ||
  284. tkhasalpha(tk->env, TkCselectbgnd) ||
  285. tkhasalpha(tk->env, TkCactivebgnd));
  286. tksizemenubutton(tk);
  287. tkgeomchg(tk, &g, bd);
  288. tk->dirty = tkrect(tk, 1);
  289. return e;
  290. }
  291. static char*
  292. tkMBleave(Tk *tk, char *arg, char **val)
  293. {
  294. USED(arg);
  295. USED(val);
  296. tk->flag &= ~Tkactive;
  297. tk->dirty = tkrect(tk, 1);
  298. return nil;
  299. }
  300. static Tk*
  301. mkchoicemenu(Tk *tkb)
  302. {
  303. Tk *menu, *tkc;
  304. int i;
  305. TkLabel *tkl, *tkcl;
  306. TkWin *tkw;
  307. TkTop *t;
  308. tkl = TKobj(TkLabel, tkb);
  309. t = tkb->env->top;
  310. menu = tknewobj(t, TKmenu, sizeof(Tk)+sizeof(TkWin));
  311. if(menu == nil)
  312. return nil;
  313. menu->relief = TKraised;
  314. menu->flag |= Tknograb;
  315. menu->borderwidth = 1;
  316. tkputenv(menu->env);
  317. menu->env = tkb->env;
  318. menu->env->ref++;
  319. menu->flag |= Tkwindow;
  320. menu->geom = tkmoveresize;
  321. tkw = TKobj(TkWin, menu);
  322. tkw->cbname = strdup(tkb->name->name);
  323. tkw->di = (void*)-1; // XXX
  324. for(i = tkl->nvalues - 1; i >= 0; i--){
  325. tkc = tknewobj(t, TKlabel, sizeof(Tk)+sizeof(TkLabel));
  326. /* XXX recover from malloc failure */
  327. tkc->flag = Tkwest|Tkfillx|Tktop;
  328. tkc->highlightwidth = 0;
  329. tkc->borderwidth = 1;
  330. tkc->relief = TKflat;
  331. tkputenv(tkc->env);
  332. tkc->env = tkb->env;
  333. tkc->env->ref++;
  334. tkcl = TKobj(TkLabel, tkc);
  335. tkcl->anchor = Tkwest;
  336. tkcl->ul = -1;
  337. tkcl->justify = Tkleft;
  338. tkcl->text = strdup(tkl->values[i]);
  339. tkcl->command = smprint("%s invoke %d", tkb->name->name, i);
  340. /* XXX recover from malloc failure */
  341. tksizelabel(tkc);
  342. tkc->req.height = tkb->req.height;
  343. appenditem(menu, tkc, 0);
  344. }
  345. layout(menu);
  346. tkw->next = t->windows;
  347. tkw->freeonunmap = 1;
  348. t->windows = menu;
  349. return menu;
  350. }
  351. static char*
  352. tkMBpress(Tk *tk, char *arg, char **val)
  353. {
  354. Tk *menu, *item;
  355. TkLabel *tkl = TKobj(TkLabel, tk);
  356. Point g;
  357. char buf[12], *bufp, *e;
  358. USED(arg);
  359. USED(val);
  360. g = tkposn(tk);
  361. if (tk->type == TKchoicebutton) {
  362. menu = mkchoicemenu(tk);
  363. if (menu == nil)
  364. return TkNomem;
  365. sprint(buf, "%d", tkl->check);
  366. bufp = buf;
  367. item = tkmenuindex2ptr(menu, &bufp);
  368. if(item == nil)
  369. return nil;
  370. g.y -= item->act.y;
  371. e = tkmpost(menu, g.x, g.y, 0, 0, 0);
  372. activateitem(item);
  373. return e;
  374. } else {
  375. if (tkl->menu == nil)
  376. return nil;
  377. menu = tklook(tk->env->top, tkl->menu, 0);
  378. if(menu == nil || menu->type != TKmenu)
  379. return TkBadwp;
  380. if(menu->flag & Tkmapped) {
  381. if(atoi(arg))
  382. tkunmapmenu(menu);
  383. return nil;
  384. }
  385. return tkmpost(menu, g.x, g.y, 0, tk->act.height + 2*tk->borderwidth, 1);
  386. }
  387. }
  388. static char*
  389. tkMBkey(Tk *tk, char *arg, char **val)
  390. {
  391. int key;
  392. USED(val);
  393. if(tk->flag & Tkdisabled)
  394. return nil;
  395. key = strtol(arg, nil, 0);
  396. if (key == '\n' || key == ' ')
  397. return tkMBpress(tk, "1", nil);
  398. return nil;
  399. }
  400. static char*
  401. tkMBenter(Tk *tk, char *arg, char **val)
  402. {
  403. USED(arg);
  404. USED(val);
  405. tk->flag |= Tkactive;
  406. tk->dirty = tkrect(tk, 1);
  407. return nil;
  408. }
  409. static char*
  410. tkchoicebutset(Tk *tk, char *arg, char **val)
  411. {
  412. char buf[12], *e;
  413. int v;
  414. TkLabel *tkl = TKobj(TkLabel, tk);
  415. USED(val);
  416. tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  417. if (*buf == '\0')
  418. return TkBadvl;
  419. v = atoi(buf);
  420. if (v < 0 || v >= tkl->nvalues)
  421. return TkBadvl;
  422. if (v == tkl->check)
  423. return nil;
  424. free(tkl->text);
  425. tkl->text = strdup(tkl->values[v]);
  426. /* XXX recover from malloc error */
  427. tkl->check = v;
  428. sprint(buf, "%d", v);
  429. e = tksetvar(tk->env->top, tkl->variable, buf);
  430. if(e != nil)
  431. return e;
  432. tk->dirty = tkrect(tk, 1);
  433. return nil;
  434. }
  435. static char*
  436. tkchoicebutinvoke(Tk *tk, char *arg, char **val)
  437. {
  438. TkLabel *tkl = TKobj(TkLabel, tk);
  439. char *e;
  440. e = tkchoicebutset(tk, arg, val);
  441. if(e != nil)
  442. return e;
  443. if(tkl->command)
  444. return tkexec(tk->env->top, tkl->command, val);
  445. return nil;
  446. }
  447. static char*
  448. tkchoicebutgetvalue(Tk *tk, char *arg, char **val)
  449. {
  450. char buf[12];
  451. int gotarg, v;
  452. TkLabel *tkl = TKobj(TkLabel, tk);
  453. if (tkl->nvalues == 0)
  454. return nil;
  455. tkword(tk->env->top, arg, buf, buf+sizeof(buf), &gotarg);
  456. if (!gotarg)
  457. return tkvalue(val, "%s", tkl->values[tkl->check]);
  458. v = atoi(buf);
  459. if (buf[0] < '0' || buf[0] > '9' || v >= tkl->nvalues)
  460. return TkBadvl;
  461. return tkvalue(val, "%s", tkl->values[tkl->check]);
  462. }
  463. static char*
  464. tkchoicebutsetvalue(Tk *tk, char *arg, char **val)
  465. {
  466. char *buf;
  467. char **v;
  468. int gotarg;
  469. TkLabel *tkl = TKobj(TkLabel, tk);
  470. USED(val);
  471. if (tkl->nvalues == 0)
  472. return TkBadvl;
  473. buf = mallocz(Tkmaxitem, 0);
  474. if (buf == nil)
  475. return TkNomem;
  476. tkword(tk->env->top, arg, buf, buf+Tkmaxitem, &gotarg);
  477. if (!gotarg) {
  478. free(buf);
  479. return TkBadvl;
  480. }
  481. for (v = tkl->values; *v; v++)
  482. if (strcmp(*v, buf) == 0)
  483. break;
  484. free(buf);
  485. if (*v == nil)
  486. return TkBadvl;
  487. free(tkl->text);
  488. tkl->text = strdup(*v);
  489. /* XXX recover from malloc error */
  490. tkl->check = v - tkl->values;
  491. tk->dirty = tkrect(tk, 1);
  492. return nil;
  493. }
  494. static char*
  495. tkchoicebutget(Tk *tk, char *arg, char **val)
  496. {
  497. TkLabel *tkl = TKobj(TkLabel, tk);
  498. char *buf, **v;
  499. int gotarg;
  500. if (tkl->nvalues == 0)
  501. return nil;
  502. buf = mallocz(Tkmaxitem, 0);
  503. if (buf == nil)
  504. return TkNomem;
  505. tkword(tk->env->top, arg, buf, buf+Tkmaxitem, &gotarg);
  506. if (!gotarg) {
  507. free(buf);
  508. return tkvalue(val, "%d", tkl->check);
  509. }
  510. for (v = tkl->values; *v; v++)
  511. if (strcmp(*v, buf) == 0)
  512. break;
  513. free(buf);
  514. if (*v)
  515. return tkvalue(val, "%d", v - tkl->values);
  516. return nil;
  517. }
  518. static char*
  519. tkchoicebutvaluecount(Tk *tk, char *arg, char **val)
  520. {
  521. TkLabel *tkl = TKobj(TkLabel, tk);
  522. USED(arg);
  523. return tkvalue(val, "%d", tkl->nvalues);
  524. }
  525. static void
  526. tkchoicevarchanged(Tk *tk, char *var, char *value)
  527. {
  528. TkLabel *tkl = TKobj(TkLabel, tk);
  529. int v;
  530. if(tkl->variable != nil && strcmp(tkl->variable, var) == 0){
  531. if(value[0] < '0' || value[0] > '9')
  532. return;
  533. v = atoi(value);
  534. if(v < 0 || v >= tkl->nvalues)
  535. return; /* what else can we do? */
  536. free(tkl->text);
  537. tkl->text = strdup(tkl->values[v]);
  538. /* XXX recover from malloc error */
  539. tkl->check = v;
  540. tk->dirty = tkrect(tk, 0);
  541. tkdirty(tk);
  542. }
  543. }
  544. Tk *
  545. tkfindchoicemenu(Tk *tkb)
  546. {
  547. Tk *tk, *next;
  548. TkTop *top;
  549. TkWin *tkw;
  550. top = tkb->env->top;
  551. for (tk = top->windows; tk != nil; tk = next){
  552. tkw = TKobj(TkWin, tk);
  553. if(tk->name == nil){
  554. assert(strcmp(tkw->cbname, tkb->name->name) == 0);
  555. return tk;
  556. }
  557. next = tkw->next;
  558. }
  559. return nil;
  560. }
  561. static
  562. TkOption menuopt[] =
  563. {
  564. "postcommand", OPTtext, O(TkWin, postcmd), nil,
  565. nil,
  566. };
  567. char*
  568. tkmenu(TkTop *t, char *arg, char **ret)
  569. {
  570. Tk *tk;
  571. char *e;
  572. TkWin *tkw;
  573. TkName *names;
  574. TkOptab tko[3];
  575. tk = tknewobj(t, TKmenu, sizeof(Tk)+sizeof(TkWin));
  576. if(tk == nil)
  577. return TkNomem;
  578. tkw = TKobj(TkWin, tk);
  579. tkw->di = (void*)-1; // XXX
  580. tk->relief = TKraised;
  581. tk->flag |= Tknograb;
  582. tk->borderwidth = 1;
  583. tko[0].ptr = tk;
  584. tko[0].optab = tkgeneric;
  585. tko[1].ptr = tkw;
  586. tko[1].optab = menuopt;
  587. tko[2].ptr = nil;
  588. names = nil;
  589. e = tkparse(t, arg, tko, &names);
  590. if(e != nil) {
  591. tkfreeobj(tk);
  592. return e;
  593. }
  594. e = tkaddchild(t, tk, &names);
  595. tkfreename(names);
  596. if(e != nil) {
  597. tkfreeobj(tk);
  598. return e;
  599. }
  600. tk->name->link = nil;
  601. tk->flag |= Tkwindow;
  602. tk->geom = tkmoveresize;
  603. tkw->next = t->windows;
  604. t->windows = tk;
  605. return tkvalue(ret, "%s", tk->name->name);
  606. }
  607. static void
  608. freemenu(Tk *top)
  609. {
  610. Tk *tk, *f, *nexttk, *nextf;
  611. TkWin *tkw;
  612. tkunmapmenu(top);
  613. tkw = TKobj(TkWin, top);
  614. for(tk = tkw->slave; tk; tk = nexttk) {
  615. nexttk = tk->next;
  616. for(f = tk->slave; f; f = nextf) {
  617. nextf = f->next;
  618. tkfreeobj(f);
  619. }
  620. tkfreeobj(tk);
  621. }
  622. top->slave = nil;
  623. tkfreeframe(top);
  624. }
  625. static
  626. TkOption mopt[] =
  627. {
  628. "menu", OPTtext, O(TkLabel, menu), nil,
  629. nil,
  630. };
  631. static void
  632. tkbuildmopt(TkOptab *tko, int n, Tk *tk)
  633. {
  634. memset(tko, 0, n*sizeof(TkOptab));
  635. n = 0;
  636. tko[n].ptr = tk;
  637. tko[n++].optab = tkgeneric;
  638. switch(tk->type) {
  639. case TKcascade:
  640. tko[n].ptr = TKobj(TkLabel, tk);
  641. tko[n++].optab = mopt;
  642. goto norm;
  643. case TKradiobutton:
  644. tko[n].ptr = TKobj(TkLabel, tk);
  645. tko[n++].optab = tkradopts;
  646. goto norm;
  647. case TKcheckbutton:
  648. tko[n].ptr = TKobj(TkLabel, tk);
  649. tko[n++].optab = tkcbopts;
  650. /* fall through */
  651. case TKlabel:
  652. norm:
  653. tko[n].ptr = TKobj(TkLabel, tk);
  654. tko[n].optab = tkbutopts;
  655. break;
  656. }
  657. }
  658. static char*
  659. tkmenuentryconf(Tk *menu, Tk *tk, char *arg)
  660. {
  661. char *e;
  662. TkOptab tko[4];
  663. USED(menu);
  664. tkbuildmopt(tko, nelem(tko), tk);
  665. e = tkparse(tk->env->top, arg, tko, nil);
  666. switch (tk->type) {
  667. case TKlabel:
  668. case TKcascade:
  669. tksizelabel(tk);
  670. break;
  671. case TKradiobutton:
  672. case TKcheckbutton:
  673. tksizebutton(tk);
  674. }
  675. return e;
  676. }
  677. static void
  678. layout(Tk *menu)
  679. {
  680. TkWin *tkw;
  681. Tk *tk;
  682. int m, w, y, maxmargin, maxw;
  683. y = 0;
  684. maxmargin = 0;
  685. maxw = 0;
  686. tkw = TKobj(TkWin, menu);
  687. /* determine padding for item text alignment */
  688. for (tk = tkw->slave; tk != nil; tk = tk->next) {
  689. m = tkbuttonmargin(tk); /* TO DO: relies on buttonmargin defaulting to labelmargin */
  690. tk->act.x = m; /* temp store */
  691. if (m > maxmargin)
  692. maxmargin = m;
  693. }
  694. /* set x pos and determine max width */
  695. for (tk = tkw->slave; tk != nil; tk = tk->next) {
  696. tk->act.x = tk->borderwidth + maxmargin - tk->act.x;
  697. tk->act.y = y + tk->borderwidth;
  698. tk->act.height = tk->req.height;
  699. tk->act.width = tk->req.width;
  700. y += tk->act.height+2*tk->borderwidth;
  701. w = tk->act.x + tk->req.width + 2* tk->borderwidth;
  702. if (w > maxw)
  703. maxw = w;
  704. }
  705. /* expand separators and cascades and mark all as dirty */
  706. for (tk = tkw->slave; tk != nil; tk = tk->next) {
  707. switch (tk->type) {
  708. case TKseparator:
  709. tk->act.x = tk->borderwidth;
  710. /*FALLTHRU*/
  711. case TKcascade:
  712. tk->act.width = (maxw - tk->act.x) - tk->borderwidth;
  713. }
  714. tk->dirty = tkrect(tk, 1);
  715. }
  716. menu->dirty = tkrect(menu, 1);
  717. tkmoveresize(menu, 0, 0, maxw, y);
  718. }
  719. static void
  720. menuitemgeom(Tk *sub, int x, int y, int w, int h)
  721. {
  722. if (sub->parent == nil)
  723. return;
  724. if(w < 0)
  725. w = 0;
  726. if(h < 0)
  727. h = 0;
  728. sub->req.x = x;
  729. sub->req.y = y;
  730. sub->req.width = w;
  731. sub->req.height = h;
  732. layout(sub->parent);
  733. }
  734. static void
  735. appenditem(Tk *menu, Tk *item, int where)
  736. {
  737. TkWin *tkw;
  738. Tk *f, **l;
  739. tkw = TKobj(TkWin, menu);
  740. l = &tkw->slave;
  741. for (f = *l; f != nil; f = f->next) {
  742. if (where-- == 0)
  743. break;
  744. l = &f->next;
  745. }
  746. *l = item;
  747. item->next = f;
  748. item->parent = menu;
  749. item->geom = menuitemgeom;
  750. }
  751. static char*
  752. menuadd(Tk *menu, char *arg, int where)
  753. {
  754. Tk *tkc;
  755. int configure;
  756. char *e;
  757. TkTop *t;
  758. TkLabel *tkl;
  759. char buf[Tkmaxitem];
  760. t = menu->env->top;
  761. arg = tkword(t, arg, buf, buf+sizeof(buf), nil);
  762. configure = 1;
  763. e = nil;
  764. if(strcmp(buf, "checkbutton") == 0)
  765. tkc = tkmkbutton(t, TKcheckbutton);
  766. else if(strcmp(buf, "radiobutton") == 0)
  767. tkc = tkmkbutton(t, TKradiobutton);
  768. else if(strcmp(buf, "command") == 0)
  769. tkc = tknewobj(t, TKlabel, sizeof(Tk)+sizeof(TkLabel));
  770. else if(strcmp(buf, "cascade") == 0)
  771. tkc = tknewobj(t, TKcascade, sizeof(Tk)+sizeof(TkLabel));
  772. else if(strcmp(buf, "separator") == 0) {
  773. tkc = tknewobj(t, TKseparator, sizeof(Tk)); /* it's really a frame */
  774. if (tkc != nil) {
  775. tkc->flag = Tkfillx|Tktop;
  776. tkc->req.height = Sepheight;
  777. configure = 0;
  778. }
  779. }
  780. else
  781. return TkBadvl;
  782. if (tkc == nil)
  783. e = TkNomem;
  784. if (e == nil) {
  785. if(tkc->env == t->env && menu->env != t->env) {
  786. tkputenv(tkc->env);
  787. tkc->env = menu->env;
  788. tkc->env->ref++;
  789. }
  790. if (configure) {
  791. tkc->flag = Tkwest|Tkfillx|Tktop;
  792. tkc->highlightwidth = 0;
  793. tkc->borderwidth = 1;
  794. tkc->relief = TKflat;
  795. tkl = TKobj(TkLabel, tkc);
  796. tkl->anchor = Tkwest;
  797. tkl->ul = -1;
  798. tkl->justify = Tkleft;
  799. e = tkmenuentryconf(menu, tkc, arg);
  800. }
  801. }
  802. if(e != nil) {
  803. if (tkc != nil)
  804. tkfreeobj(tkc);
  805. return e;
  806. }
  807. appenditem(menu, tkc, where);
  808. layout(menu);
  809. return nil;
  810. }
  811. static int
  812. tkmindex(Tk *tk, char *p)
  813. {
  814. TkWin *tkw;
  815. int y, n;
  816. if(*p >= '0' && *p <= '9')
  817. return atoi(p);
  818. tkw = TKobj(TkWin, tk);
  819. n = 0;
  820. if(*p == '@') {
  821. y = atoi(p+1);
  822. for(tk = tkw->slave; tk; tk = tk->next) {
  823. if(y >= tk->act.y && y < tk->act.y+tk->act.height+2*tk->borderwidth )
  824. return n;
  825. n++;
  826. }
  827. }
  828. if(strcmp(p, "end") == 0 || strcmp(p, "last") == 0) {
  829. for(tk = tkw->slave; tk && tk->next; tk = tk->next)
  830. n++;
  831. return n;
  832. }
  833. if(strcmp(p, "active") == 0) {
  834. for(tk = tkw->slave; tk; tk = tk->next) {
  835. if(tk->flag & Tkactive)
  836. return n;
  837. n++;
  838. }
  839. return -2;
  840. }
  841. if(strcmp(p, "none") == 0)
  842. return -2;
  843. return -1;
  844. }
  845. static int
  846. tkmenudel(Tk *tk, int y)
  847. {
  848. TkWin *tkw;
  849. Tk *f, **l, *next;
  850. tkw = TKobj(TkWin, tk);
  851. l = &tkw->slave;
  852. for(tk = *l; tk; tk = tk->next) {
  853. if(y-- == 0) {
  854. *l = tk->next;
  855. for(f = tk->slave; f; f = next) {
  856. next = f->next;
  857. tkfreeobj(f);
  858. }
  859. tkfreeobj(tk);
  860. return 1;
  861. }
  862. l = &tk->next;
  863. }
  864. return 0;
  865. }
  866. static char*
  867. tkmpost(Tk *tk, int x, int y, int cascade, int bh, int adjust)
  868. {
  869. char *e;
  870. TkWin *w;
  871. TkTop *t;
  872. Rectangle *dr;
  873. t = tk->env->top;
  874. if(adjust){
  875. dr = &t->screenr;
  876. if(x+tk->act.width > dr->max.x)
  877. x = dr->max.x - tk->act.width;
  878. if(x < 0)
  879. x = 0;
  880. if(y+bh+tk->act.height > dr->max.y)
  881. y -= tk->act.height + 2* tk->borderwidth;
  882. else
  883. y += bh;
  884. if(y < 0)
  885. y = 0;
  886. }
  887. menuclr(tk);
  888. tkmovewin(tk, Pt(x, y));
  889. /* stop possible postcommand recursion */
  890. if (tk->flag & Tkmapped)
  891. return nil;
  892. w = TKobj(TkWin, tk);
  893. if(w->postcmd != nil) {
  894. e = tkexec(tk->env->top, w->postcmd, nil);
  895. if(e != nil) {
  896. print("%s: postcommand: %s: %s\n", tkname(tk), w->postcmd, e);
  897. return e;
  898. }
  899. }
  900. if (!cascade)
  901. tkunmapmenus(t, nil);
  902. e = tkmap(tk);
  903. if(e != nil)
  904. return e;
  905. if (t->ctxt->tkmenu != nil)
  906. w->cascade = strdup(t->ctxt->tkmenu->name->name);
  907. t->ctxt->tkmenu = tk;
  908. tksetmgrab(t, tk);
  909. /* Make sure slaves are redrawn */
  910. return tkupdate(tk->env->top);
  911. }
  912. static Tk*
  913. tkmenuindex2ptr(Tk *tk, char **arg)
  914. {
  915. TkWin *tkw;
  916. int index;
  917. char *buf;
  918. buf = mallocz(Tkmaxitem, 0);
  919. if(buf == nil)
  920. return nil;
  921. *arg = tkword(tk->env->top, *arg, buf, buf+Tkmaxitem, nil);
  922. index = tkmindex(tk, buf);
  923. free(buf);
  924. if(index < 0)
  925. return nil;
  926. tkw = TKobj(TkWin, tk);
  927. for(tk = tkw->slave; tk && index; tk = tk->next)
  928. index--;
  929. if(tk == nil)
  930. return nil;
  931. return tk;
  932. }
  933. static char*
  934. tkmenuentrycget(Tk *tk, char *arg, char **val)
  935. {
  936. Tk *etk;
  937. TkOptab tko[4];
  938. etk = tkmenuindex2ptr(tk, &arg);
  939. if(etk == nil)
  940. return TkBadix;
  941. tkbuildmopt(tko, nelem(tko), etk);
  942. return tkgencget(tko, arg, val, tk->env->top);
  943. }
  944. static char*
  945. tkmenucget(Tk *tk, char *arg, char **val)
  946. {
  947. TkWin *tkw;
  948. TkOptab tko[4];
  949. tkw = TKobj(TkWin, tk);
  950. tko[0].ptr = tk;
  951. tko[0].optab = tkgeneric;
  952. tko[1].ptr = tkw;
  953. tko[1].optab = tktop;
  954. tko[2].ptr = tkw;
  955. tko[2].optab = menuopt;
  956. tko[3].ptr = nil;
  957. return tkgencget(tko, arg, val, tk->env->top);
  958. }
  959. static char*
  960. tkmenuconf(Tk *tk, char *arg, char **val)
  961. {
  962. char *e;
  963. TkGeom g;
  964. int bd;
  965. TkWin *tkw;
  966. TkOptab tko[3];
  967. tkw = TKobj(TkWin, tk);
  968. tko[0].ptr = tk;
  969. tko[0].optab = tkgeneric;
  970. tko[1].ptr = tkw;
  971. tko[1].optab = menuopt;
  972. tko[2].ptr = nil;
  973. if(*arg == '\0')
  974. return tkconflist(tko, val);
  975. g = tk->req;
  976. bd = tk->borderwidth;
  977. e = tkparse(tk->env->top, arg, tko, nil);
  978. tkgeomchg(tk, &g, bd);
  979. tk->dirty = tkrect(tk, 1);
  980. return e;
  981. }
  982. static char*
  983. tkmenuadd(Tk *tk, char *arg, char **val)
  984. {
  985. USED(val);
  986. return menuadd(tk, arg, -1);
  987. }
  988. static char*
  989. tkmenuinsert(Tk *tk, char *arg, char **val)
  990. {
  991. int index;
  992. char *buf;
  993. USED(val);
  994. buf = mallocz(Tkmaxitem, 0);
  995. if(buf == nil)
  996. return TkNomem;
  997. arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
  998. index = tkmindex(tk, buf);
  999. free(buf);
  1000. if (index < 0)
  1001. return TkBadix;
  1002. return menuadd(tk, arg, index);
  1003. }
  1004. static void
  1005. menuitemdirty(Tk *item)
  1006. {
  1007. Tk *menu;
  1008. Rectangle r;
  1009. menu = item->parent;
  1010. if (menu == nil)
  1011. return;
  1012. item->dirty = tkrect(item, 1);
  1013. r = rectaddpt(item->dirty, Pt(item->act.x, item->act.y));
  1014. combinerect(&menu->dirty, r);
  1015. }
  1016. static void
  1017. menuclr(Tk *tk)
  1018. {
  1019. TkWin *tkw;
  1020. Tk *f;
  1021. tkw = TKobj(TkWin, tk);
  1022. for(f = tkw->slave; f; f = f->next) {
  1023. if(f->flag & Tkactive) {
  1024. f->flag &= ~Tkactive;
  1025. menuitemdirty(f);
  1026. }
  1027. }
  1028. }
  1029. static char*
  1030. tkpostcascade(Tk *parent, Tk *tk, int toggle)
  1031. {
  1032. Tk *tkm;
  1033. TkWin *tkw;
  1034. Point g;
  1035. TkTop *t;
  1036. TkLabel *tkl;
  1037. char *e;
  1038. if(tk->flag & Tkdisabled)
  1039. return nil;
  1040. tkl = TKobj(TkLabel, tk);
  1041. t = tk->env->top;
  1042. tkm = tklook(t, tkl->menu, 0);
  1043. if(tkm == nil || tkm->type != TKmenu)
  1044. return TkBadwp;
  1045. if((tkm->flag & Tkmapped)) {
  1046. if (toggle) {
  1047. tkunmapmenus(t, parent);
  1048. return nil;
  1049. } else {
  1050. /* check that it is immediate cascade */
  1051. tkw = TKobj(TkWin, t->ctxt->tkmenu);
  1052. if (strcmp(tkw->cascade, parent->name->name) == 0)
  1053. return nil;
  1054. }
  1055. }
  1056. tkunmapmenus(t, parent);
  1057. tkl = TKobj(TkLabel, tk);
  1058. if(tkl->command != nil) {
  1059. e = tkexec(t, tkl->command, nil);
  1060. if (e != nil)
  1061. return e;
  1062. }
  1063. g = tkposn(tk);
  1064. g.x += tk->act.width;
  1065. g.y -= tkm->borderwidth;
  1066. e = tkmpost(tkm, g.x, g.y, 1, 0, 1);
  1067. return e;
  1068. }
  1069. static void
  1070. activateitem(Tk *item)
  1071. {
  1072. Tk *menu;
  1073. if (item == nil || (menu = item->parent) == nil)
  1074. return;
  1075. menuclr(menu);
  1076. if (!(item->flag & Tkdisabled)) {
  1077. item->flag |= Tkactive;
  1078. menuitemdirty(item);
  1079. }
  1080. }
  1081. static char*
  1082. tkmenuactivate(Tk *tk, char *arg, char **val)
  1083. {
  1084. Tk *f;
  1085. TkWin *tkw;
  1086. int index;
  1087. char *buf;
  1088. USED(val);
  1089. buf = mallocz(Tkmaxitem, 0);
  1090. if(buf == nil)
  1091. return TkNomem;
  1092. tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
  1093. index = tkmindex(tk, buf);
  1094. free(buf);
  1095. if (index == -1)
  1096. return TkBadix;
  1097. if (index == -2) {
  1098. menuclr(tk);
  1099. return nil;
  1100. }
  1101. tkw = TKobj(TkWin, tk);
  1102. for(f = tkw->slave; f; f = f->next)
  1103. if(index-- == 0)
  1104. break;
  1105. if(f == nil || f->flag & Tkdisabled) {
  1106. menuclr(tk);
  1107. return nil;
  1108. }
  1109. if(f->flag & Tkactive)
  1110. return nil;
  1111. activateitem(f);
  1112. return nil;
  1113. }
  1114. static int
  1115. iteminvoke(Tk *tk, Tk *tki, char *arg)
  1116. {
  1117. int unmap = 0;
  1118. menuitemdirty(tki);
  1119. switch(tki->type) {
  1120. case TKlabel:
  1121. unmap = 1;
  1122. case TKcheckbutton:
  1123. case TKradiobutton:
  1124. tkbuttoninvoke(tki, arg, nil);
  1125. break;
  1126. case TKcascade:
  1127. tkpostcascade(tk, tki, 0);
  1128. break;
  1129. }
  1130. return unmap;
  1131. }
  1132. static char*
  1133. tkmenuinvoke(Tk *tk, char *arg, char **val)
  1134. {
  1135. Tk *tki;
  1136. USED(val);
  1137. tki = tkmenuindex2ptr(tk, &arg);
  1138. if(tki == nil)
  1139. return nil;
  1140. iteminvoke(tk, tki, arg);
  1141. return nil;
  1142. }
  1143. static char*
  1144. tkmenudelete(Tk *tk, char *arg, char **val)
  1145. {
  1146. int index1, index2;
  1147. char *buf;
  1148. USED(val);
  1149. buf = mallocz(Tkmaxitem, 0);
  1150. if(buf == nil)
  1151. return TkNomem;
  1152. arg = tkitem(buf, arg);
  1153. index1 = tkmindex(tk, buf);
  1154. if(index1 < 0) {
  1155. free(buf);
  1156. return TkBadix;
  1157. }
  1158. index2 = index1;
  1159. if(*arg != '\0') {
  1160. tkitem(buf, arg);
  1161. index2 = tkmindex(tk, buf);
  1162. }
  1163. free(buf);
  1164. if(index2 < 0)
  1165. return TkBadix;
  1166. while(index2 >= index1 && tkmenudel(tk, index2))
  1167. index2--;
  1168. layout(tk);
  1169. return nil;
  1170. }
  1171. static char*
  1172. tkmenupost(Tk *tk, char *arg, char **val)
  1173. {
  1174. int x, y;
  1175. TkTop *t;
  1176. char *buf;
  1177. USED(val);
  1178. buf = mallocz(Tkmaxitem, 0);
  1179. if(buf == nil)
  1180. return TkNomem;
  1181. t = tk->env->top;
  1182. arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
  1183. if(buf[0] == '\0') {
  1184. free(buf);
  1185. return TkBadvl;
  1186. }
  1187. x = atoi(buf);
  1188. tkword(t, arg, buf, buf+Tkmaxitem, nil);
  1189. if(buf[0] == '\0') {
  1190. free(buf);
  1191. return TkBadvl;
  1192. }
  1193. y = atoi(buf);
  1194. free(buf);
  1195. return tkmpost(tk, x, y, 0, 0, 1);
  1196. }
  1197. static char*
  1198. tkmenuunpost(Tk *tk, char *arg, char **val)
  1199. {
  1200. USED(arg);
  1201. USED(val);
  1202. tkunmapmenu(tk);
  1203. return nil;
  1204. }
  1205. static char*
  1206. tkmenuindex(Tk *tk, char *arg, char **val)
  1207. {
  1208. char *buf;
  1209. int index;
  1210. buf = mallocz(Tkmaxitem, 0);
  1211. if(buf == nil)
  1212. return TkNomem;
  1213. tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
  1214. index = tkmindex(tk, buf);
  1215. free(buf);
  1216. if (index == -1)
  1217. return TkBadix;
  1218. if (index == -2)
  1219. return "none";
  1220. return tkvalue(val, "%d", index);
  1221. }
  1222. static char*
  1223. tkmenuyposn(Tk *tk, char *arg, char **val)
  1224. {
  1225. tk = tkmenuindex2ptr(tk, &arg);
  1226. if(tk == nil)
  1227. return TkBadix;
  1228. return tkvalue(val, "%d", tk->act.y);
  1229. }
  1230. static char*
  1231. tkmenupostcascade(Tk *tk, char *arg, char **val)
  1232. {
  1233. Tk *tki;
  1234. USED(val);
  1235. tki = tkmenuindex2ptr(tk, &arg);
  1236. if(tki == nil || tki->type != TKcascade)
  1237. return nil;
  1238. return tkpostcascade(tk, tki, 0);
  1239. }
  1240. static char*
  1241. tkmenutype(Tk *tk, char *arg, char **val)
  1242. {
  1243. tk = tkmenuindex2ptr(tk, &arg);
  1244. if(tk == nil)
  1245. return TkBadix;
  1246. return tkvalue(val, tk->type == TKlabel ? "command" : tkmethod[tk->type]->name);
  1247. }
  1248. static char*
  1249. tkmenususpend(Tk *tk, char *arg, char **val)
  1250. {
  1251. USED(arg);
  1252. USED(val);
  1253. if(tk->type == TKchoicebutton){
  1254. tk = tkfindchoicemenu(tk);
  1255. if(tk == nil)
  1256. return TkNotwm;
  1257. }
  1258. tk->flag |= Tksuspended;
  1259. return nil;
  1260. }
  1261. static char*
  1262. tkmenuentryconfig(Tk *tk, char *arg, char **val)
  1263. {
  1264. Tk *etk;
  1265. char *e;
  1266. USED(val);
  1267. etk = tkmenuindex2ptr(tk, &arg);
  1268. if(etk == nil)
  1269. return TkBadix;
  1270. e = tkmenuentryconf(tk, etk, arg);
  1271. layout(tk);
  1272. return e;
  1273. }
  1274. static Tk*
  1275. xymenuitem(Tk *tk, int x, int y)
  1276. {
  1277. TkWin *tkw = TKobj(TkWin, tk);
  1278. x -= tkw->act.x;
  1279. y -= tkw->act.y;
  1280. x -= tk->borderwidth;
  1281. y -= tk->act.y + tk->borderwidth;
  1282. if (x < tk->act.x || x > tk->act.x+tk->act.width)
  1283. return nil;
  1284. for(tk = tkw->slave; tk; tk = tk->next) {
  1285. if(y >= tk->act.y && y < tk->act.y+tk->act.height+2*tk->borderwidth)
  1286. return tk;
  1287. }
  1288. return nil;
  1289. }
  1290. static char *
  1291. menukey(Tk *tk, int key)
  1292. {
  1293. Tk *scan, *active, *first, *last, *prev, *next;
  1294. TkWin *tkw;
  1295. TkTop *top;
  1296. top = tk->env->top;
  1297. active = first = last = prev = next = nil;
  1298. tkw = TKobj(TkWin, tk);
  1299. for(scan = tkw->slave; scan != nil; scan = scan->next) {
  1300. if(scan->type == TKseparator)
  1301. continue;
  1302. if(first == nil)
  1303. first = scan;
  1304. if (active != nil && next == nil)
  1305. next = scan;
  1306. if(active == nil && scan->flag & Tkactive)
  1307. active = scan;
  1308. if (active == nil)
  1309. prev = scan;
  1310. last = scan;
  1311. }
  1312. if (next == nil)
  1313. next = first;
  1314. if (prev == nil)
  1315. prev = last;
  1316. switch (key) {
  1317. case Esc:
  1318. tkunmapmenus(top, nil);
  1319. break;
  1320. case Left:
  1321. if (tkw->cascade != nil)
  1322. tkunmapmenu(tk);
  1323. break;
  1324. case Right:
  1325. if (active == nil || active->type != TKcascade)
  1326. break;
  1327. case ' ':
  1328. case '\n':
  1329. if (active != nil) {
  1330. if (iteminvoke(tk, active, nil))
  1331. tkunmapmenus(top, nil);
  1332. }
  1333. break;
  1334. case Up:
  1335. next = prev;
  1336. case Down:
  1337. if (next != nil)
  1338. activateitem(next);
  1339. }
  1340. return nil;
  1341. }
  1342. static char*
  1343. drawmenu(Tk *tk, Point orig)
  1344. {
  1345. Image *dst;
  1346. TkWin *tkw;
  1347. Tk *sub;
  1348. Point p, bd;
  1349. int bg;
  1350. Rectangle mainr, clientr, subr;
  1351. tkw = TKobj(TkWin, tk);
  1352. dst = tkimageof(tk);
  1353. bd = Pt(tk->borderwidth, tk->borderwidth);
  1354. mainr.min = addpt(orig, Pt(tk->act.x, tk->act.y));
  1355. clientr.min = addpt(mainr.min, bd);
  1356. clientr.max = addpt(clientr.min, Pt(tk->act.width, tk->act.height));
  1357. mainr.max = addpt(clientr.max, bd);
  1358. /*
  1359. * note that we draw item background to get full menu width
  1360. * active indicator, this means we must dirty the entire
  1361. * item rectangle to ensure it is fully redrawn
  1362. */
  1363. p = clientr.min;
  1364. subr = clientr;
  1365. for (sub = tkw->slave; sub != nil; sub = sub->next) {
  1366. if (Dx(sub->dirty) == 0)
  1367. continue;
  1368. subr.min.y = p.y + sub->act.y - sub->borderwidth;
  1369. subr.max.y = p.y + sub->act.y + sub->act.height + sub->borderwidth;
  1370. bg = TkCbackgnd;
  1371. if (sub->flag & Tkactive)
  1372. bg = TkCactivebgnd;
  1373. draw(dst, subr, tkgc(sub->env, bg), nil, ZP);
  1374. sub->dirty = tkrect(sub, 1);
  1375. sub->flag |= Tkrefresh;
  1376. tkmethod[sub->type]->draw(sub, p);
  1377. sub->dirty = bbnil;
  1378. sub->flag &= ~Tkrefresh;
  1379. }
  1380. /* todo: dirty check */
  1381. tkdrawrelief(dst, tk, mainr.min, TkCbackgnd, tk->relief);
  1382. return nil;
  1383. }
  1384. static void
  1385. menudirty(Tk *sub)
  1386. {
  1387. menuitemdirty(sub);
  1388. }
  1389. static Point
  1390. menurelpos(Tk *sub)
  1391. {
  1392. return Pt(sub->act.x-sub->borderwidth, sub->act.y-sub->borderwidth);
  1393. }
  1394. static void
  1395. autoscroll(Tk *tk, void *v, int cancelled)
  1396. {
  1397. TkWin *tkw;
  1398. Rectangle r, dr;
  1399. Point delta, od;
  1400. TkMouse *m;
  1401. Tk *item;
  1402. USED(v);
  1403. tkw = TKobj(TkWin, tk);
  1404. if (cancelled) {
  1405. tkw->speed = 0;
  1406. return;
  1407. }
  1408. if(!eqpt(tkw->act, tkw->req)){
  1409. print("not autoscrolling, act: %P, req: %P\n", tkw->act, tkw->req);
  1410. return;
  1411. }
  1412. dr = tk->env->top->screenr;
  1413. delta.x = TKF2I(tkw->delta.x * tkw->speed);
  1414. delta.y = TKF2I(tkw->delta.y * tkw->speed);
  1415. r = rectaddpt(tkrect(tk, 1), Pt(tk->borderwidth + tkw->act.x, tk->borderwidth + tkw->act.y));
  1416. od = delta;
  1417. /* make sure we don't go too far */
  1418. if (delta.x > 0 && r.min.x + delta.x > dr.min.x)
  1419. delta.x = dr.min.x - r.min.x;
  1420. else if (delta.x < 0 && r.max.x + delta.x < dr.max.x)
  1421. delta.x = dr.max.x - r.max.x;
  1422. if (delta.y > 0 && r.min.y + delta.y > dr.min.y)
  1423. delta.y = dr.min.y - r.min.y;
  1424. else if (delta.y < 0 && r.max.y + delta.y < dr.max.y)
  1425. delta.y = dr.max.y - r.max.y;
  1426. m = &tk->env->top->ctxt->mstate;
  1427. item = xymenuitem(tk, m->x - delta.x, m->y - delta.y);
  1428. if (item == nil)
  1429. menuclr(tk);
  1430. else
  1431. activateitem(item);
  1432. tkmovewin(tk, Pt(tkw->req.x + delta.x, tkw->req.y + delta.y));
  1433. tkupdate(tk->env->top);
  1434. /* tkenterleave won't do this for us, so we have to do it ourselves */
  1435. tkw->speed += tkw->speed / 3;
  1436. r = rectaddpt(tkrect(tk, 1), Pt(tk->borderwidth + tkw->act.x, tk->borderwidth + tkw->act.y));
  1437. if((delta.y > 0 && r.min.x >= dr.min.x) || (delta.x < 0 && r.max.x <= dr.max.x))
  1438. tkw->delta.x = 0;
  1439. if((delta.y > 0 && r.min.y >= dr.min.y) || (delta.y < 0 && r.max.y <= dr.max.y))
  1440. tkw->delta.y = 0;
  1441. if (eqpt(tkw->delta, ZP)) {
  1442. tkcancelrepeat(tk);
  1443. tkw->speed = 0;
  1444. }
  1445. }
  1446. static void
  1447. startautoscroll(Tk *tk, TkMouse *m)
  1448. {
  1449. Rectangle dr, r;
  1450. Point d;
  1451. TkWin *tkw;
  1452. tkw = TKobj(TkWin, tk);
  1453. dr = tk->env->top->screenr;
  1454. r = rectaddpt(tkrect(tk, 1), Pt(tk->borderwidth + tkw->act.x, tk->borderwidth + tkw->act.y));
  1455. d = Pt(0, 0);
  1456. if(m->x <= 0 && r.min.x < dr.min.x)
  1457. d.x = 1;
  1458. else if (m->x >= dr.max.x - 1 && r.max.x >= dr.max.x)
  1459. d.x = -1;
  1460. if(m->y <= 0 && r.min.y < dr.min.y)
  1461. d.y = 1;
  1462. else if (m->y >= dr.max.y - 1 && r.max.y >= dr.max.y)
  1463. d.y = -1;
  1464. //print("startautoscroll, delta %P\n", d);
  1465. if (d.x == 0 && d.y == 0){
  1466. if (tkw->speed > 0){
  1467. tkcancelrepeat(tk);
  1468. tkw->speed = 0;
  1469. }
  1470. return;
  1471. }
  1472. if (tkw->speed == 0) {
  1473. tkw->speed = TKI2F(Dy(r)) / 100;
  1474. tkrepeat(tk, autoscroll, nil, 0, TkRptinterval/2);
  1475. }
  1476. tkw->delta = d;
  1477. }
  1478. static void
  1479. menuevent1(Tk *tk, int event, void *a)
  1480. {
  1481. TkMouse *m;
  1482. Tk *item;
  1483. if (event & TkKey) {
  1484. menukey(tk, event & 0xffff);
  1485. return;
  1486. }
  1487. if (event & TkLeave) {
  1488. menuclr(tk);
  1489. return;
  1490. }
  1491. if ((!(event & TkEmouse) || (event & TkTakefocus)) && !(event & TkEnter))
  1492. return;
  1493. m = (TkMouse*)a;
  1494. startautoscroll(tk, m);
  1495. item = xymenuitem(tk, m->x, m->y);
  1496. if (item == nil)
  1497. menuclr(tk);
  1498. else
  1499. activateitem(item);
  1500. if ((event & (TkMotion|TkEnter)) && item == nil)
  1501. return;
  1502. if (event & TkEpress) {
  1503. if (item == nil) {
  1504. tkunmapmenus(tk->env->top, nil);
  1505. return;
  1506. }
  1507. if (item->type == TKcascade)
  1508. tkpostcascade(tk, item, !(event & TkMotion));
  1509. else
  1510. tkunmapmenus(tk->env->top, tk);
  1511. return;
  1512. }
  1513. if ((event & TkErelease) && m->b == 0) {
  1514. if (item != nil) {
  1515. if (item->type == TKcascade)
  1516. return;
  1517. if (!iteminvoke(tk, item, nil))
  1518. return;
  1519. }
  1520. tkunmapmenus(tk->env->top, nil);
  1521. }
  1522. }
  1523. static Tk*
  1524. menuevent(Tk *tk, int event, void *a)
  1525. {
  1526. menuevent1(tk, event, a);
  1527. tksubdeliver(tk, tk->binds, event, a, 0);
  1528. return nil;
  1529. }
  1530. static
  1531. TkCmdtab menucmd[] =
  1532. {
  1533. "activate", tkmenuactivate,
  1534. "add", tkmenuadd,
  1535. "cget", tkmenucget,
  1536. "configure", tkmenuconf,
  1537. "delete", tkmenudelete,
  1538. "entryconfigure", tkmenuentryconfig,
  1539. "entrycget", tkmenuentrycget,
  1540. "index", tkmenuindex,
  1541. "insert", tkmenuinsert,
  1542. "invoke", tkmenuinvoke,
  1543. "post", tkmenupost,
  1544. "postcascade", tkmenupostcascade,
  1545. "type", tkmenutype,
  1546. "unpost", tkmenuunpost,
  1547. "yposition", tkmenuyposn,
  1548. "suspend", tkmenususpend,
  1549. nil
  1550. };
  1551. static
  1552. TkCmdtab menubutcmd[] =
  1553. {
  1554. "cget", tkmenubutcget,
  1555. "configure", tkmenubutconf,
  1556. "tkMBenter", tkMBenter,
  1557. "tkMBleave", tkMBleave,
  1558. "tkMBpress", tkMBpress,
  1559. "tkMBkey", tkMBkey,
  1560. nil
  1561. };
  1562. static
  1563. TkCmdtab choicebutcmd[] =
  1564. {
  1565. "cget", tkmenubutcget,
  1566. "configure", tkmenubutconf,
  1567. "set", tkchoicebutset,
  1568. "get", tkchoicebutget,
  1569. "setvalue", tkchoicebutsetvalue,
  1570. "getvalue", tkchoicebutgetvalue,
  1571. "invoke", tkchoicebutinvoke,
  1572. "valuecount", tkchoicebutvaluecount,
  1573. "tkMBenter", tkMBenter,
  1574. "tkMBleave", tkMBleave,
  1575. "tkMBpress", tkMBpress,
  1576. "tkMBkey", tkMBkey,
  1577. "suspend", tkmenususpend,
  1578. nil
  1579. };
  1580. TkMethod menumethod = {
  1581. "menu",
  1582. menucmd,
  1583. freemenu,
  1584. drawmenu,
  1585. nil,
  1586. nil,
  1587. nil,
  1588. menudirty,
  1589. menurelpos,
  1590. menuevent
  1591. };
  1592. TkMethod menubuttonmethod = {
  1593. "menubutton",
  1594. menubutcmd,
  1595. tkfreelabel,
  1596. tkdrawlabel
  1597. };
  1598. TkMethod choicebuttonmethod = {
  1599. "choicebutton",
  1600. choicebutcmd,
  1601. tkfreelabel,
  1602. tkdrawlabel,
  1603. nil,
  1604. nil,
  1605. nil,
  1606. nil,
  1607. nil,
  1608. nil,
  1609. nil,
  1610. nil,
  1611. tkchoicevarchanged
  1612. };
  1613. TkMethod separatormethod = {
  1614. "separator",
  1615. nil,
  1616. tkfreeframe,
  1617. tkdrawframe
  1618. };
  1619. TkMethod cascademethod = {
  1620. "cascade",
  1621. nil,
  1622. tkfreelabel,
  1623. tkdrawlabel
  1624. };