widget.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478
  1. /*
  2. * BURG - Brand-new Universal loadeR from GRUB
  3. * Copyright 2009 Bean Lee - All Rights Reserved
  4. *
  5. * BURG is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * BURG is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with BURG. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/mm.h>
  19. #include <grub/env.h>
  20. #include <grub/err.h>
  21. #include <grub/misc.h>
  22. #include <grub/time.h>
  23. #include <grub/widget.h>
  24. #include <grub/dialog.h>
  25. #include <grub/menu_data.h>
  26. #include <grub/term.h>
  27. #include <grub/loader.h>
  28. #include <grub/parser.h>
  29. #include <grub/command.h>
  30. GRUB_EXPORT(grub_widget_class_list);
  31. GRUB_EXPORT(grub_widget_create);
  32. GRUB_EXPORT(grub_widget_init);
  33. GRUB_EXPORT(grub_widget_draw);
  34. GRUB_EXPORT(grub_widget_draw_region);
  35. GRUB_EXPORT(grub_widget_free);
  36. GRUB_EXPORT(grub_widget_select_node);
  37. GRUB_EXPORT(grub_widget_input);
  38. GRUB_EXPORT(grub_widget_get_prop);
  39. GRUB_EXPORT(grub_widget_current_node);
  40. GRUB_EXPORT(grub_widget_refresh);
  41. GRUB_EXPORT(grub_widget_screen);
  42. grub_widget_class_t grub_widget_class_list;
  43. grub_uitree_t grub_widget_current_node;
  44. int grub_widget_refresh;
  45. grub_uitree_t grub_widget_screen;
  46. grub_err_t
  47. grub_widget_create (grub_uitree_t node)
  48. {
  49. grub_uitree_t child;
  50. child = node;
  51. while (child)
  52. {
  53. grub_widget_class_t class;
  54. grub_widget_t widget;
  55. int size;
  56. class = grub_widget_class_list;
  57. while (class)
  58. {
  59. if (! grub_strcmp (child->name, class->name))
  60. break;
  61. class = class->next;
  62. }
  63. if (! class)
  64. return grub_error (GRUB_ERR_BAD_ARGUMENT, "class not found");
  65. size = (class->get_data_size) ? class->get_data_size () : 0;
  66. widget = grub_zalloc (sizeof (struct grub_widget) + size);
  67. if (! widget)
  68. break;
  69. widget->class = class;
  70. widget->data = (char *) widget + sizeof (struct grub_widget);
  71. widget->node = child;
  72. child->data = widget;
  73. child = grub_tree_next_node (GRUB_AS_TREE (node), GRUB_AS_TREE (child));
  74. }
  75. return grub_errno;
  76. }
  77. static void
  78. init_size (grub_widget_t widget, int size_only)
  79. {
  80. grub_widget_t parent;
  81. grub_uitree_t node;
  82. int pw, ph;
  83. char *p, *m1, *m2;
  84. node = widget->node;
  85. if ((! node->parent) || (! node->parent->data))
  86. return;
  87. parent = widget->node->parent->data;
  88. pw = parent->inner_width;
  89. ph = parent->inner_height;
  90. p = grub_widget_get_prop (node, "width");
  91. if (p)
  92. {
  93. node->flags |= GRUB_WIDGET_FLAG_FIXED_WIDTH;
  94. widget->width = grub_menu_parse_size (p, pw, 1);
  95. }
  96. p = grub_widget_get_prop (node, "height");
  97. if (p)
  98. {
  99. node->flags |= GRUB_WIDGET_FLAG_FIXED_HEIGHT;
  100. widget->height = grub_menu_parse_size (p, ph, 0);
  101. }
  102. m1 = grub_widget_get_prop (node, "attach_left");
  103. m2 = grub_widget_get_prop (node, "attach_right");
  104. if ((m1) && (m2))
  105. {
  106. node->flags |= GRUB_WIDGET_FLAG_FIXED_WIDTH;
  107. widget->width = pw - grub_menu_parse_size (m1, pw, 1)
  108. - grub_menu_parse_size (m2, pw, 1);
  109. }
  110. m1 = grub_widget_get_prop (node, "attach_top");
  111. m2 = grub_widget_get_prop (node, "attach_bottom");
  112. if ((m1) && (m2))
  113. {
  114. node->flags |= GRUB_WIDGET_FLAG_FIXED_HEIGHT;
  115. widget->height = ph - grub_menu_parse_size (m1, ph, 0)
  116. - grub_menu_parse_size (m2, ph, 0);
  117. }
  118. if (size_only)
  119. return;
  120. p = grub_widget_get_prop (node, "attach_left");
  121. if (p)
  122. {
  123. node->flags |= GRUB_WIDGET_FLAG_FIXED_X;
  124. widget->x = grub_menu_parse_size (p, pw, 1);
  125. }
  126. p = grub_widget_get_prop (node, "attach_right");
  127. if (p)
  128. {
  129. node->flags |= GRUB_WIDGET_FLAG_FIXED_X;
  130. widget->x = pw - grub_menu_parse_size (p, pw, 1) - widget->width;
  131. }
  132. p = grub_widget_get_prop (node, "attach_hcenter");
  133. if (p)
  134. {
  135. node->flags |= GRUB_WIDGET_FLAG_FIXED_X;
  136. widget->x = (pw - widget->width) / 2 + grub_menu_parse_size (p, pw, 1);
  137. }
  138. p = grub_widget_get_prop (node, "attach_top");
  139. if (p)
  140. {
  141. node->flags |= GRUB_WIDGET_FLAG_FIXED_Y;
  142. widget->y = grub_menu_parse_size (p, ph, 0);
  143. }
  144. p = grub_widget_get_prop (node, "attach_bottom");
  145. if (p)
  146. {
  147. node->flags |= GRUB_WIDGET_FLAG_FIXED_Y;
  148. widget->y = ph - grub_menu_parse_size (p, ph, 0) - widget->height;
  149. }
  150. p = grub_widget_get_prop (node, "attach_vcenter");
  151. if (p)
  152. {
  153. node->flags |= GRUB_WIDGET_FLAG_FIXED_Y;
  154. widget->y = (ph - widget->height) / 2 + grub_menu_parse_size (p, ph, 0);
  155. }
  156. }
  157. #define HALIGN_LEFT 0
  158. #define HALIGN_CENTER 1
  159. #define HALIGN_RIGHT 2
  160. #define HALIGN_EXTEND 3
  161. #define VALIGN_TOP 0
  162. #define VALIGN_CENTER 1
  163. #define VALIGN_BOTTOM 2
  164. #define VALIGN_EXTEND 3
  165. static void
  166. align_x (grub_widget_t child, int halign, int width)
  167. {
  168. int delta;
  169. delta = width - child->width;
  170. if (delta <= 0)
  171. return;
  172. if (halign == HALIGN_EXTEND)
  173. child->width += delta;
  174. else if (halign == HALIGN_CENTER)
  175. child->x += delta >> 1;
  176. else if (halign == HALIGN_RIGHT)
  177. child->x += delta;
  178. }
  179. static void
  180. align_y (grub_widget_t child, int valign, int height)
  181. {
  182. int delta;
  183. delta = height - child->height;
  184. if (delta <= 0)
  185. return;
  186. if (valign == VALIGN_EXTEND)
  187. child->height += delta;
  188. else if (valign == VALIGN_CENTER)
  189. child->y += delta >> 1;
  190. else if (valign == VALIGN_BOTTOM)
  191. child->y += delta;
  192. }
  193. static void
  194. get_direction (grub_uitree_t node, int *horizontal, int *reverse)
  195. {
  196. char *p;
  197. *horizontal = 0;
  198. *reverse = 0;
  199. p = grub_widget_get_prop (node, "direction");
  200. if (p)
  201. {
  202. if (! grub_strcmp (p, "left_to_right"))
  203. *horizontal = 1;
  204. else if (! grub_strcmp (p, "right_to_left"))
  205. *reverse = *horizontal = 1;
  206. else if (! grub_strcmp (p, "bottom_to_top"))
  207. *reverse = 1;
  208. }
  209. }
  210. static int screen_width, screen_height;
  211. static void
  212. adjust_layout (grub_widget_t widget, int calc_mode)
  213. {
  214. grub_uitree_t node;
  215. int space, extra, count, width, height, max_items, index, max_width, max_height;
  216. int horizontal, reverse;
  217. char *p;
  218. node = widget->node;
  219. p = grub_widget_get_prop (node, "space");
  220. space = (p) ? grub_menu_parse_size (p, 0, 1) : 0;
  221. p = grub_widget_get_prop (node, "max_items");
  222. max_items = (p) ? grub_strtoul (p, 0, 0) : 0;
  223. get_direction (node, &horizontal, &reverse);
  224. width = 0;
  225. height = 0;
  226. count = 0;
  227. index = 0;
  228. max_width = screen_width;
  229. max_height = screen_height;
  230. for (node = node->child; node; node = node->next)
  231. {
  232. grub_widget_t child;
  233. int skip;
  234. child = node->data;
  235. if (! child)
  236. continue;
  237. if (calc_mode)
  238. skip = (horizontal) ? (child->width <= 0) : (child->height <= 0);
  239. else
  240. {
  241. init_size (child, 0);
  242. skip = ((child->width <= 0) || (child->height <= 0) ||
  243. (node->flags & GRUB_WIDGET_FLAG_FIXED_XY));
  244. }
  245. if (skip)
  246. continue;
  247. if (horizontal)
  248. {
  249. width += child->width + space;
  250. if (child->height > height)
  251. height = child->height;
  252. }
  253. else
  254. {
  255. height += child->height + space;
  256. if (child->width > width)
  257. width = child->width;
  258. }
  259. if (grub_widget_get_prop (node, "extend"))
  260. {
  261. node->flags |= GRUB_WIDGET_FLAG_EXTEND;
  262. count++;
  263. }
  264. index++;
  265. if (index == max_items)
  266. {
  267. if (horizontal)
  268. {
  269. if (width - space < max_width)
  270. max_width = width - space;
  271. }
  272. else
  273. {
  274. if (height - space < max_height)
  275. max_height = height - space;
  276. }
  277. }
  278. }
  279. if (horizontal)
  280. {
  281. if (width > 0)
  282. width -= space;
  283. }
  284. else
  285. {
  286. if (height > 0)
  287. height -= space;
  288. }
  289. node = widget->node;
  290. if (calc_mode)
  291. {
  292. init_size (widget, 1);
  293. if (! (node->flags & GRUB_WIDGET_FLAG_FIXED_WIDTH))
  294. {
  295. p = grub_widget_get_prop (node, "max_width");
  296. if (p)
  297. {
  298. int w;
  299. w = grub_menu_parse_size (p, 0, 1);
  300. if (width > w)
  301. width = w;
  302. }
  303. if (width > max_width)
  304. width = max_width;
  305. p = grub_widget_get_prop (node, "min_width");
  306. if (p)
  307. {
  308. int w;
  309. w = grub_menu_parse_size (p, 0, 1);
  310. if (width < w)
  311. width = w;
  312. }
  313. widget->width = width;
  314. }
  315. if (! (node->flags & GRUB_WIDGET_FLAG_FIXED_HEIGHT))
  316. {
  317. p = grub_widget_get_prop (node, "max_height");
  318. if (p)
  319. {
  320. int h;
  321. h = grub_menu_parse_size (p, 0, 0);
  322. if (height > h)
  323. height = h;
  324. }
  325. if (height > max_height)
  326. height = max_height;
  327. p = grub_widget_get_prop (node, "min_height");
  328. if (p)
  329. {
  330. int h;
  331. h = grub_menu_parse_size (p, 0, 0);
  332. if (height < h)
  333. height = h;
  334. }
  335. widget->height = height;
  336. }
  337. return;
  338. }
  339. if (horizontal)
  340. {
  341. extra = widget->inner_width - width;
  342. if (reverse)
  343. {
  344. if ((extra > 0) && (count > 0))
  345. width = widget->inner_width;
  346. }
  347. else
  348. width = 0;
  349. height = 0;
  350. }
  351. else
  352. {
  353. extra = widget->inner_height - height;
  354. if (reverse)
  355. {
  356. if ((extra > 0) && (count > 0))
  357. height = widget->inner_height;
  358. }
  359. else
  360. height = 0;
  361. width = 0;
  362. }
  363. if (extra < 0)
  364. extra = 0;
  365. if (count > 1)
  366. extra = extra / count;
  367. for (node = node->child; node; node = node->next)
  368. {
  369. grub_widget_t child;
  370. int flags, valign, halign;
  371. child = node->data;
  372. flags = (node->flags & GRUB_WIDGET_FLAG_FIXED_XY);
  373. if ((! child) || (child->width <= 0) || (child->height <= 0) ||
  374. (flags == GRUB_WIDGET_FLAG_FIXED_XY))
  375. continue;
  376. halign = HALIGN_EXTEND;
  377. p = grub_widget_get_prop (node, "halign");
  378. if (p)
  379. {
  380. if (! grub_strcmp (p, "left"))
  381. halign = HALIGN_LEFT;
  382. else if (! grub_strcmp (p, "center"))
  383. halign = HALIGN_CENTER;
  384. else if (! grub_strcmp (p, "right"))
  385. halign = HALIGN_RIGHT;
  386. }
  387. valign = VALIGN_EXTEND;
  388. p = grub_widget_get_prop (node, "valign");
  389. if (p)
  390. {
  391. if (! grub_strcmp (p, "top"))
  392. valign = VALIGN_TOP;
  393. else if (! grub_strcmp (p, "center"))
  394. valign = VALIGN_CENTER;
  395. else if (! grub_strcmp (p, "bottom"))
  396. valign = VALIGN_BOTTOM;
  397. }
  398. if (flags == GRUB_WIDGET_FLAG_FIXED_X)
  399. align_y (child, valign, widget->inner_height);
  400. else if (flags == GRUB_WIDGET_FLAG_FIXED_Y)
  401. align_x (child, halign, widget->inner_width);
  402. else
  403. {
  404. if (horizontal)
  405. {
  406. int w;
  407. w = child->width;
  408. if (node->flags & GRUB_WIDGET_FLAG_EXTEND)
  409. w += extra;
  410. if (reverse)
  411. {
  412. child->x = width - w;
  413. width -= w + space;
  414. }
  415. else
  416. {
  417. child->x = width;
  418. width += w + space;
  419. }
  420. if (node->flags & GRUB_WIDGET_FLAG_EXTEND)
  421. align_x (child, halign, w);
  422. align_y (child, valign, widget->inner_height);
  423. }
  424. else
  425. {
  426. int h;
  427. h = child->height;
  428. if (node->flags & GRUB_WIDGET_FLAG_EXTEND)
  429. h += extra;
  430. if (reverse)
  431. {
  432. child->y = height - h;
  433. height -= h + space;
  434. }
  435. else
  436. {
  437. child->y = height;
  438. height += h + space;
  439. }
  440. if (node->flags & GRUB_WIDGET_FLAG_EXTEND)
  441. align_y (child, valign, h);
  442. align_x (child, halign, widget->inner_width);
  443. }
  444. }
  445. }
  446. }
  447. static void
  448. init_widget (grub_uitree_t node)
  449. {
  450. grub_uitree_t child;
  451. grub_widget_t widget;
  452. child = node->child;
  453. while (child)
  454. {
  455. init_widget (child);
  456. child = child->next;
  457. }
  458. widget = node->data;
  459. adjust_layout (widget, 1);
  460. if (widget->class->init_size)
  461. widget->class->init_size (widget);
  462. }
  463. void
  464. grub_widget_init (grub_uitree_t node)
  465. {
  466. grub_uitree_t child;
  467. grub_menu_region_get_screen_size (&screen_width, &screen_height);
  468. init_widget (node);
  469. init_size (node->data, 0);
  470. child = node;
  471. while (child)
  472. {
  473. grub_widget_t widget;
  474. widget = child->data;
  475. widget->inner_width = widget->width;
  476. widget->inner_height = widget->height;
  477. if (child->parent)
  478. {
  479. grub_widget_t parent = child->parent->data;
  480. widget->org_x = parent->org_x + parent->inner_x + widget->x;
  481. widget->org_y = parent->org_y + parent->inner_y + widget->y;
  482. }
  483. if (widget->class->fini_size)
  484. widget->class->fini_size (widget);
  485. adjust_layout (widget, 0);
  486. if ((widget->width > 0) && (widget->height > 0))
  487. {
  488. if (grub_widget_get_prop (child, "anchor"))
  489. child->flags |= GRUB_WIDGET_FLAG_ANCHOR;
  490. if (grub_widget_get_prop (child, "command"))
  491. child->flags |= GRUB_WIDGET_FLAG_NODE;
  492. }
  493. child = grub_tree_next_node (GRUB_AS_TREE (node), GRUB_AS_TREE (child));
  494. }
  495. }
  496. void
  497. grub_widget_free (grub_uitree_t node)
  498. {
  499. grub_uitree_t child;
  500. child = node;
  501. while (child)
  502. {
  503. grub_widget_t widget;
  504. widget = child->data;
  505. if (widget)
  506. {
  507. if (widget->class->free)
  508. widget->class->free (widget);
  509. grub_free (widget);
  510. child->data = 0;
  511. }
  512. child = grub_tree_next_node (GRUB_AS_TREE (node), GRUB_AS_TREE (child));
  513. }
  514. }
  515. static void
  516. draw_child (grub_menu_region_update_list_t *head, grub_uitree_t node,
  517. int x, int y, int width, int height)
  518. {
  519. grub_widget_t widget;
  520. grub_uitree_t child;
  521. widget = node->data;
  522. for (child = node->child; child; child = child->next)
  523. {
  524. grub_widget_t c;
  525. int cx, cy, cw, ch;
  526. if (child->flags & GRUB_WIDGET_FLAG_HIDDEN)
  527. continue;
  528. c = child->data;
  529. cx = widget->org_x + x - c->org_x;
  530. cy = widget->org_y + y - c->org_y;
  531. cw = width;
  532. ch = height;
  533. if (grub_menu_region_check_rect (&cx, &cy, &cw, &ch, 0, 0,
  534. c->width, c->height))
  535. {
  536. if (c->class->draw)
  537. c->class->draw (c, head, cx, cy, cw, ch);
  538. if (grub_menu_region_check_rect (&cx, &cy, &cw, &ch,
  539. c->inner_x, c->inner_y,
  540. c->inner_width,c->inner_height))
  541. draw_child (head, child, cx, cy, cw, ch);
  542. }
  543. }
  544. }
  545. static void
  546. draw_parent (grub_menu_region_update_list_t *head, grub_uitree_t node,
  547. int x, int y, int width, int height)
  548. {
  549. grub_widget_t widget;
  550. widget = node->data;
  551. if (node->flags & GRUB_WIDGET_FLAG_TRANSPARENT)
  552. {
  553. grub_widget_t p;
  554. p = node->parent->data;
  555. draw_parent (head, node->parent, x + widget->x + p->inner_x,
  556. y + widget->y + p->inner_y, width, height);
  557. }
  558. if (widget->class->draw)
  559. widget->class->draw (widget, head, x, y, width, height);
  560. }
  561. void
  562. grub_widget_draw_region (grub_menu_region_update_list_t *head,
  563. grub_uitree_t node, int x, int y,
  564. int width, int height)
  565. {
  566. grub_widget_t w;
  567. grub_uitree_t c;
  568. if (node->flags & GRUB_WIDGET_FLAG_HIDDEN)
  569. return;
  570. w = node->data;
  571. if (! grub_menu_region_check_rect (&x, &y, &width, &height,
  572. 0, 0, w->width, w->height))
  573. return;
  574. c = node;
  575. while (1)
  576. {
  577. grub_widget_t p;
  578. if ((! c->parent) || (! c->parent->data))
  579. break;
  580. p = c->parent->data;
  581. x += ((grub_widget_t) c->data)->x;
  582. y += ((grub_widget_t) c->data)->y;
  583. if (! grub_menu_region_check_rect (&x, &y, &width, &height,
  584. 0, 0,
  585. p->inner_width, p->inner_height))
  586. return;
  587. x += p->inner_x;
  588. y += p->inner_y;
  589. c = c->parent;
  590. }
  591. x += ((grub_widget_t) c->data)->org_x - w->org_x;
  592. y += ((grub_widget_t) c->data)->org_y - w->org_y;
  593. draw_parent (head, node, x, y, width, height);
  594. if (! grub_menu_region_check_rect (&x, &y, &width, &height,
  595. w->inner_x, w->inner_y,
  596. w->inner_width, w->inner_height))
  597. return;
  598. draw_child (head, node, x, y, width, height);
  599. }
  600. void
  601. grub_widget_draw (grub_uitree_t node)
  602. {
  603. grub_widget_t widget;
  604. widget = node->data;
  605. if (widget)
  606. {
  607. grub_menu_region_update_list_t head;
  608. head = 0;
  609. grub_widget_draw_region (&head, node, 0, 0, widget->width,
  610. widget->height);
  611. grub_menu_region_apply_update (head);
  612. }
  613. }
  614. static void
  615. update_position (grub_uitree_t parent, grub_uitree_t node)
  616. {
  617. grub_widget_t p, w;
  618. p = parent->data;
  619. w = node->data;
  620. if ((p) && (w))
  621. {
  622. grub_uitree_t child;
  623. w->org_x = p->org_x + p->inner_x + w->x;
  624. w->org_y = p->org_y + p->inner_y + w->y;
  625. for (child = node->child; child; child = child->next)
  626. update_position (node, child);
  627. }
  628. }
  629. static void
  630. scroll_node (grub_uitree_t node, int dx, int dy)
  631. {
  632. grub_uitree_t child;
  633. for (child = node->child; child; child = child->next)
  634. {
  635. grub_widget_t widget;
  636. widget = child->data;
  637. if ((! widget) || (child->flags & GRUB_WIDGET_FLAG_FIXED_XY))
  638. continue;
  639. widget->x += dx;
  640. widget->y += dy;
  641. update_position (node, child);
  642. }
  643. }
  644. static grub_uitree_t
  645. grub_widget_scroll (grub_uitree_t node)
  646. {
  647. grub_uitree_t save;
  648. grub_widget_t widget;
  649. int x, y, width, height;
  650. save = node;
  651. widget = node->data;
  652. x = 0;
  653. y = 0;
  654. width = widget->width;
  655. height = widget->height;
  656. while (1)
  657. {
  658. grub_widget_t parent;
  659. int dx;
  660. int dy;
  661. if (! node->parent)
  662. break;
  663. parent = node->parent->data;
  664. if (widget->width <= parent->inner_width)
  665. {
  666. x = 0;
  667. width = widget->width;
  668. }
  669. if (widget->height <= parent->inner_height)
  670. {
  671. y = 0;
  672. height = widget->height;
  673. }
  674. x += widget->x;
  675. y += widget->y;
  676. dx = 0;
  677. dy = 0;
  678. if (x + width > parent->inner_width)
  679. {
  680. dx = parent->inner_width - width - x;
  681. x += dx;
  682. }
  683. if (y + height > parent->inner_height)
  684. {
  685. dy = parent->inner_height - height - y;
  686. y += dy;
  687. }
  688. if (x < 0)
  689. {
  690. dx += -x;
  691. x = 0;
  692. }
  693. if (y < 0)
  694. {
  695. dy += -y;
  696. y = 0;
  697. }
  698. if ((dx) || (dy))
  699. {
  700. save = node->parent;
  701. if (node->flags & GRUB_WIDGET_FLAG_FIXED_XY)
  702. {
  703. widget->x += dx;
  704. widget->y += dy;
  705. update_position (save, node);
  706. }
  707. else
  708. scroll_node (save, dx, dy);
  709. }
  710. if (! grub_menu_region_check_rect (&x, &y, &width, &height,
  711. 0, 0,
  712. parent->inner_width,
  713. parent->inner_height))
  714. return node;
  715. x += parent->inner_x;
  716. y += parent->inner_y;
  717. node = node->parent;
  718. widget = node->data;
  719. }
  720. return save;
  721. }
  722. #define DIR_NEXT 1
  723. #define DIR_ANCHOR 2
  724. static char *
  725. get_dir_cmd (grub_uitree_t node, int k)
  726. {
  727. int horizontal, reverse;
  728. int dir;
  729. if (node->parent)
  730. get_direction (node->parent, &horizontal, &reverse);
  731. else
  732. horizontal = reverse = 0;
  733. if (k == GRUB_TERM_LEFT)
  734. dir = DIR_ANCHOR;
  735. else if (k == GRUB_TERM_RIGHT)
  736. dir = DIR_ANCHOR | DIR_NEXT;
  737. else if (k == GRUB_TERM_UP)
  738. dir = 0;
  739. else if (k == GRUB_TERM_DOWN)
  740. dir = DIR_NEXT;
  741. else
  742. return 0;
  743. if (horizontal)
  744. dir ^= DIR_ANCHOR;
  745. if ((reverse) & (! (dir & DIR_ANCHOR)))
  746. dir ^= DIR_NEXT;
  747. if (dir == 0)
  748. return "ui_prev_node";
  749. else if (dir == DIR_NEXT)
  750. return "ui_next_node";
  751. else if (dir == DIR_ANCHOR)
  752. return "ui_prev_anchor";
  753. else
  754. return "ui_next_anchor";
  755. }
  756. static grub_uitree_t
  757. find_selected_node (grub_uitree_t root)
  758. {
  759. grub_uitree_t node;
  760. node = root->child;
  761. while (node)
  762. {
  763. if (node->flags & GRUB_WIDGET_FLAG_SELECTED)
  764. {
  765. if (node->flags & GRUB_WIDGET_FLAG_NODE)
  766. return node;
  767. else
  768. node = node->child;
  769. }
  770. else
  771. node = node->next;
  772. }
  773. return 0;
  774. }
  775. static int
  776. map_key (int key)
  777. {
  778. grub_uitree_t map;
  779. const char *name;
  780. map = grub_uitree_find (&grub_uitree_root, "mapkey");
  781. if (! map)
  782. return key;
  783. name = grub_menu_key2name (key);
  784. if (! name)
  785. return key;
  786. name = grub_uitree_get_prop (map, name);
  787. return (name) ? grub_menu_name2key (name) : key;
  788. }
  789. static char *
  790. onkey (int key)
  791. {
  792. grub_uitree_t map;
  793. const char *name;
  794. map = grub_uitree_find (&grub_uitree_root, "onkey");
  795. if (! map)
  796. return 0;
  797. name = grub_menu_key2name (key);
  798. if (! name)
  799. return 0;
  800. return grub_uitree_get_prop (map, name);
  801. }
  802. void
  803. grub_widget_select_node (grub_uitree_t node, int selected)
  804. {
  805. grub_uitree_t child;
  806. child = node->child;
  807. while (child)
  808. {
  809. if (selected)
  810. child->flags |= GRUB_WIDGET_FLAG_SELECTED;
  811. else
  812. child->flags &= ~GRUB_WIDGET_FLAG_SELECTED;
  813. child = grub_tree_next_node (GRUB_AS_TREE (node), GRUB_AS_TREE (child));
  814. }
  815. while (node)
  816. {
  817. if (selected)
  818. node->flags |= GRUB_WIDGET_FLAG_SELECTED;
  819. else
  820. node->flags &= ~GRUB_WIDGET_FLAG_SELECTED;
  821. node = node->parent;
  822. }
  823. }
  824. static void
  825. change_node (grub_uitree_t prev, grub_uitree_t node)
  826. {
  827. grub_uitree_t child, parent, scroll;
  828. grub_uitree_t dyn_prev, dyn_node;
  829. child = node;
  830. while (child)
  831. {
  832. child->flags |= GRUB_WIDGET_FLAG_MARKED;
  833. child = child->parent;
  834. }
  835. dyn_prev = parent = prev;
  836. prev = 0;
  837. while (! (parent->flags & GRUB_WIDGET_FLAG_MARKED))
  838. {
  839. prev = parent;
  840. if (prev->flags & GRUB_WIDGET_FLAG_DYNAMIC)
  841. dyn_prev = prev;
  842. parent = parent->parent;
  843. }
  844. child = node;
  845. while (child)
  846. {
  847. child->flags &= ~GRUB_WIDGET_FLAG_MARKED;
  848. child = child->parent;
  849. }
  850. dyn_node = node;
  851. scroll = grub_widget_scroll (node);
  852. while (1)
  853. {
  854. grub_uitree_t p;
  855. if (node == scroll)
  856. scroll = 0;
  857. p = node->parent;
  858. if (p == parent)
  859. break;
  860. node = p;
  861. if (node->flags & GRUB_WIDGET_FLAG_DYNAMIC)
  862. dyn_node = node;
  863. }
  864. if (scroll)
  865. grub_widget_draw (scroll);
  866. else if (! prev)
  867. grub_widget_draw (parent);
  868. else
  869. {
  870. grub_widget_draw (dyn_prev);
  871. grub_widget_draw (dyn_node);
  872. }
  873. }
  874. static grub_uitree_t
  875. find_next_node (grub_uitree_t anchor, grub_uitree_t node)
  876. {
  877. grub_uitree_t n;
  878. n = node;
  879. do
  880. {
  881. n = grub_tree_next_node (GRUB_AS_TREE (anchor), GRUB_AS_TREE (n));
  882. if (! n)
  883. n = anchor;
  884. if (n == node)
  885. return 0;
  886. }
  887. while (! (n->flags & GRUB_WIDGET_FLAG_NODE));
  888. return n;
  889. }
  890. static grub_uitree_t
  891. find_prev_node (grub_uitree_t anchor, grub_uitree_t node)
  892. {
  893. grub_uitree_t save, n;
  894. save = 0;
  895. n = anchor;
  896. while (n)
  897. {
  898. if (n == node)
  899. {
  900. if (save)
  901. break;
  902. }
  903. else
  904. {
  905. if (n->flags & GRUB_WIDGET_FLAG_NODE)
  906. save = n;
  907. }
  908. n = grub_tree_next_node (GRUB_AS_TREE (anchor), GRUB_AS_TREE (n));
  909. }
  910. return save;
  911. }
  912. static grub_uitree_t
  913. run_dir_cmd (char *name, grub_uitree_t current_node)
  914. {
  915. grub_uitree_t root, next, node, anchor, save;
  916. root = current_node;
  917. anchor = 0;
  918. while (root)
  919. {
  920. if ((root->flags & GRUB_WIDGET_FLAG_ANCHOR) && (! anchor))
  921. anchor = root;
  922. if (root->flags & GRUB_WIDGET_FLAG_ROOT)
  923. break;
  924. root = root->parent;
  925. }
  926. if ((name[8] == 'a') && (root != anchor))
  927. {
  928. save = anchor->child;
  929. anchor->child = 0;
  930. node = anchor;
  931. anchor = root;
  932. }
  933. else
  934. {
  935. save = 0;
  936. node = current_node;
  937. }
  938. next = (name[3] == 'n') ? find_next_node (anchor, node) :
  939. find_prev_node (anchor, node);
  940. if (save)
  941. node->child = save;
  942. return next;
  943. }
  944. static int
  945. check_timeout (grub_uitree_t root, int *key)
  946. {
  947. char *p;
  948. int total, left, has_key;
  949. grub_uint64_t last_time;
  950. p = grub_env_get ("timeout");
  951. if (! p)
  952. return 0;
  953. total = grub_strtol (p, 0, 0);
  954. if (total < 0)
  955. return 0;
  956. has_key = (grub_checkkey () >= 0);
  957. if ((has_key) || (! total))
  958. {
  959. *key = (has_key) ? GRUB_TERM_ASCII_CHAR (grub_getkey ()) : '\r';
  960. return 0;
  961. }
  962. grub_widget_draw (root);
  963. root = grub_uitree_find_id (root, "__timeout__");
  964. total *= 1000;
  965. left = total;
  966. *key = '\r';
  967. last_time = grub_get_time_ms ();
  968. while (left > 0)
  969. {
  970. grub_uint64_t delta;
  971. if (grub_checkkey () >= 0)
  972. {
  973. *key = GRUB_TERM_ASCII_CHAR (grub_getkey ());
  974. left = 0;
  975. break;
  976. }
  977. if (root)
  978. {
  979. grub_uitree_t child;
  980. grub_menu_region_update_list_t head;
  981. head = 0;
  982. child = root;
  983. while (child)
  984. {
  985. grub_widget_t widget;
  986. widget = child->data;
  987. if ((widget) && (widget->class->set_timeout))
  988. {
  989. widget->class->set_timeout (widget, total, left);
  990. if (left > 0)
  991. widget->class->draw (widget, &head, 0, 0,
  992. widget->width, widget->height);
  993. }
  994. child = grub_tree_next_node (GRUB_AS_TREE (root),
  995. GRUB_AS_TREE (child));
  996. }
  997. grub_menu_region_apply_update (head);
  998. }
  999. delta = grub_get_time_ms () - last_time;
  1000. last_time += delta;
  1001. left -= delta;
  1002. }
  1003. if (root)
  1004. {
  1005. grub_widget_t widget, parent;
  1006. grub_menu_region_update_list_t head;
  1007. widget = root->data;
  1008. parent = root->parent->data;
  1009. head = 0;
  1010. if (p)
  1011. draw_parent (&head, root->parent, widget->x + parent->inner_x,
  1012. widget->y + parent->inner_y,
  1013. widget->width, widget->height);
  1014. grub_menu_region_apply_update (head);
  1015. root->flags |= GRUB_WIDGET_FLAG_HIDDEN;
  1016. }
  1017. return 1;
  1018. }
  1019. int
  1020. grub_widget_input (grub_uitree_t root, int nested)
  1021. {
  1022. int init, c;
  1023. root->flags |= (GRUB_WIDGET_FLAG_ROOT | GRUB_WIDGET_FLAG_ANCHOR);
  1024. grub_widget_current_node = find_selected_node (root);
  1025. if (! grub_widget_current_node)
  1026. {
  1027. grub_widget_current_node = find_next_node (root, root);
  1028. if (! grub_widget_current_node)
  1029. grub_widget_current_node = root;
  1030. else
  1031. grub_widget_select_node (grub_widget_current_node, 1);
  1032. }
  1033. init = c = 0;
  1034. if (! nested)
  1035. init = check_timeout (root, &c);
  1036. if (! init)
  1037. {
  1038. grub_uitree_t node;
  1039. node = grub_uitree_find_id (root, "__timeout__");
  1040. if (node)
  1041. node->flags |= GRUB_WIDGET_FLAG_HIDDEN;
  1042. }
  1043. while (1)
  1044. {
  1045. char *cmd, *users;
  1046. users = 0;
  1047. cmd = onkey (c);
  1048. if (! cmd)
  1049. {
  1050. if (c == '\r')
  1051. {
  1052. cmd = grub_widget_get_prop (grub_widget_current_node, "command");
  1053. users = grub_widget_get_prop (grub_widget_current_node, "users");
  1054. c = 0;
  1055. }
  1056. else if (c == GRUB_TERM_ESC)
  1057. cmd = "ui_escape";
  1058. else if (c == GRUB_TERM_TAB)
  1059. cmd = "ui_next_anchor";
  1060. else
  1061. cmd = get_dir_cmd (grub_widget_current_node, c);
  1062. }
  1063. else if (*cmd == '*')
  1064. {
  1065. cmd++;
  1066. users = "";
  1067. }
  1068. if ((cmd) && (*cmd))
  1069. {
  1070. if ((users) && (! grub_dialog_password (users)))
  1071. {
  1072. grub_errno = 0;
  1073. }
  1074. else if (! grub_strcmp (cmd, "ui_escape"))
  1075. {
  1076. if (nested)
  1077. return GRUB_ERR_MENU_ESCAPE;
  1078. }
  1079. else if (! grub_strcmp (cmd, "ui_quit"))
  1080. {
  1081. return grub_error (GRUB_ERR_MENU_ESCAPE, "quit");
  1082. }
  1083. else if ((! grub_strcmp (cmd, "ui_next_node")) ||
  1084. (! grub_strcmp (cmd, "ui_prev_node")) ||
  1085. (! grub_strcmp (cmd, "ui_next_anchor")) ||
  1086. (! grub_strcmp (cmd, "ui_prev_anchor")))
  1087. {
  1088. grub_uitree_t next;
  1089. next = run_dir_cmd (cmd, grub_widget_current_node);
  1090. if (next)
  1091. {
  1092. grub_widget_select_node (grub_widget_current_node, 0);
  1093. grub_widget_select_node (next, 1);
  1094. if (init)
  1095. change_node (grub_widget_current_node, next);
  1096. grub_widget_current_node = next;
  1097. }
  1098. }
  1099. else if (! grub_memcmp (cmd, "ui_next_class", 13))
  1100. {
  1101. char *class = cmd + 13;
  1102. while (*class == ' ')
  1103. class++;
  1104. if (! *class)
  1105. {
  1106. char *parm;
  1107. parm = grub_uitree_get_prop (grub_widget_current_node,
  1108. "parameters");
  1109. class = grub_dialog_get_parm (grub_widget_current_node,
  1110. parm, "class");
  1111. }
  1112. if (class)
  1113. {
  1114. grub_uitree_t cur, next;
  1115. cur = grub_widget_current_node;
  1116. while (1)
  1117. {
  1118. char *parm, *ncls;
  1119. next = run_dir_cmd ("ui_next_node", cur);
  1120. if ((! next) || (next == grub_widget_current_node))
  1121. break;
  1122. parm = grub_uitree_get_prop (next, "parameters");
  1123. ncls = grub_dialog_get_parm (next, parm, "class");
  1124. if ((ncls) && (! grub_strcmp (class, ncls)))
  1125. {
  1126. grub_widget_select_node (grub_widget_current_node,
  1127. 0);
  1128. grub_widget_select_node (next, 1);
  1129. if (init)
  1130. change_node (grub_widget_current_node, next);
  1131. grub_widget_current_node = next;
  1132. break;
  1133. }
  1134. cur = next;
  1135. }
  1136. }
  1137. }
  1138. else
  1139. {
  1140. int r;
  1141. if ((! c) && (! nested))
  1142. {
  1143. char *index;
  1144. index = grub_uitree_get_prop (grub_widget_current_node,
  1145. "index");
  1146. if (index)
  1147. grub_env_set ("chosen", index);
  1148. }
  1149. r = grub_parser_execute (cmd);
  1150. if (grub_widget_refresh)
  1151. return grub_errno;
  1152. if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
  1153. grub_command_execute ("boot", 0, 0);
  1154. if ((r >= 0) && (r != GRUB_ERR_MENU_ESCAPE) && (nested))
  1155. return r;
  1156. grub_errno = 0;
  1157. }
  1158. }
  1159. if (! init)
  1160. {
  1161. grub_widget_draw (root);
  1162. init++;
  1163. }
  1164. while (1)
  1165. {
  1166. grub_widget_t widget;
  1167. widget = grub_widget_current_node->data;
  1168. if (widget->class->draw_cursor)
  1169. widget->class->draw_cursor (widget);
  1170. c = map_key (GRUB_TERM_ASCII_CHAR (grub_getkey ()));
  1171. if (widget->class->onkey)
  1172. {
  1173. int r;
  1174. r = widget->class->onkey (widget, c);
  1175. if (grub_widget_refresh)
  1176. return r;
  1177. if ((r >= 0) && (nested))
  1178. return r;
  1179. else if (r == GRUB_WIDGET_RESULT_SKIP)
  1180. break;
  1181. }
  1182. else
  1183. break;
  1184. }
  1185. }
  1186. }
  1187. static grub_uitree_t
  1188. find_child (grub_uitree_t node, const char *name)
  1189. {
  1190. grub_uitree_t child;
  1191. if (! *name)
  1192. return 0;
  1193. child = node->child;
  1194. while (child)
  1195. {
  1196. if (! grub_strcmp (child->name, name))
  1197. break;
  1198. child = child->next;
  1199. }
  1200. return child;
  1201. }
  1202. char *
  1203. grub_widget_get_prop (grub_uitree_t node, const char *name)
  1204. {
  1205. grub_uitree_t class_node;
  1206. grub_uitree_t child;
  1207. char *prop, *class;
  1208. prop = grub_uitree_get_prop (node, name);
  1209. if (prop)
  1210. return prop;
  1211. class_node = grub_uitree_find (&grub_uitree_root, "class");
  1212. if (! class_node)
  1213. return 0;
  1214. class = grub_uitree_get_prop (node, "class");
  1215. while (class)
  1216. {
  1217. char *p;
  1218. p = grub_menu_next_field (class, ',');
  1219. child = find_child (class_node, class);
  1220. grub_menu_restore_field (p, ',');
  1221. if (child)
  1222. {
  1223. prop = grub_uitree_get_prop (child, name);
  1224. if (prop)
  1225. return prop;
  1226. }
  1227. class = p;
  1228. }
  1229. prop = 0;
  1230. child = find_child (class_node, node->name);
  1231. if (child)
  1232. prop = grub_uitree_get_prop (child, name);
  1233. return prop;
  1234. }