gui_list.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /* gui_list.c - GUI component to display a selectable list of items. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2008,2009 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/mm.h>
  20. #include <grub/misc.h>
  21. #include <grub/gui.h>
  22. #include <grub/gui_string_util.h>
  23. #include <grub/gfxmenu_view.h>
  24. #include <grub/gfxwidgets.h>
  25. #include <grub/normal_menu.h>
  26. struct grub_gui_list_impl
  27. {
  28. struct grub_gui_list list;
  29. grub_gui_container_t parent;
  30. grub_video_rect_t bounds;
  31. char *id;
  32. int visible;
  33. int icon_width;
  34. int icon_height;
  35. int item_height;
  36. int item_padding;
  37. int item_icon_space;
  38. int item_spacing;
  39. grub_font_t item_font;
  40. grub_font_t selected_item_font;
  41. grub_gui_color_t item_color;
  42. int selected_item_color_set;
  43. grub_gui_color_t selected_item_color;
  44. int draw_scrollbar;
  45. int need_to_recreate_scrollbar;
  46. char *scrollbar_frame_pattern;
  47. char *scrollbar_thumb_pattern;
  48. grub_gfxmenu_box_t scrollbar_frame;
  49. grub_gfxmenu_box_t scrollbar_thumb;
  50. int scrollbar_width;
  51. int first_shown_index;
  52. int need_to_recreate_boxes;
  53. char *theme_dir;
  54. char *menu_box_pattern;
  55. char *selected_item_box_pattern;
  56. grub_gfxmenu_box_t menu_box;
  57. grub_gfxmenu_box_t selected_item_box;
  58. grub_gfxmenu_icon_manager_t icon_manager;
  59. grub_gfxmenu_view_t view;
  60. };
  61. typedef struct grub_gui_list_impl *list_impl_t;
  62. static void
  63. list_destroy (void *vself)
  64. {
  65. list_impl_t self = vself;
  66. grub_free (self->theme_dir);
  67. grub_free (self->menu_box_pattern);
  68. grub_free (self->selected_item_box_pattern);
  69. if (self->menu_box)
  70. self->menu_box->destroy (self->menu_box);
  71. if (self->selected_item_box)
  72. self->selected_item_box->destroy (self->selected_item_box);
  73. if (self->icon_manager)
  74. grub_gfxmenu_icon_manager_destroy (self->icon_manager);
  75. grub_free (self);
  76. }
  77. static int
  78. get_num_shown_items (list_impl_t self)
  79. {
  80. int boxpad = self->item_padding;
  81. int item_vspace = self->item_spacing;
  82. int item_height = self->item_height;
  83. grub_gfxmenu_box_t box = self->menu_box;
  84. int box_top_pad = box->get_top_pad (box);
  85. int box_bottom_pad = box->get_bottom_pad (box);
  86. return (self->bounds.height + item_vspace - 2 * boxpad
  87. - box_top_pad - box_bottom_pad) / (item_height + item_vspace);
  88. }
  89. static int
  90. check_boxes (list_impl_t self)
  91. {
  92. if (self->need_to_recreate_boxes)
  93. {
  94. grub_gui_recreate_box (&self->menu_box,
  95. self->menu_box_pattern,
  96. self->theme_dir);
  97. grub_gui_recreate_box (&self->selected_item_box,
  98. self->selected_item_box_pattern,
  99. self->theme_dir);
  100. self->need_to_recreate_boxes = 0;
  101. }
  102. return (self->menu_box != 0 && self->selected_item_box != 0);
  103. }
  104. static int
  105. check_scrollbar (list_impl_t self)
  106. {
  107. if (self->need_to_recreate_scrollbar)
  108. {
  109. grub_gui_recreate_box (&self->scrollbar_frame,
  110. self->scrollbar_frame_pattern,
  111. self->theme_dir);
  112. grub_gui_recreate_box (&self->scrollbar_thumb,
  113. self->scrollbar_thumb_pattern,
  114. self->theme_dir);
  115. self->need_to_recreate_scrollbar = 0;
  116. }
  117. return (self->scrollbar_frame != 0 && self->scrollbar_thumb != 0);
  118. }
  119. static const char *
  120. list_get_id (void *vself)
  121. {
  122. list_impl_t self = vself;
  123. return self->id;
  124. }
  125. static int
  126. list_is_instance (void *vself __attribute__((unused)), const char *type)
  127. {
  128. return (grub_strcmp (type, "component") == 0
  129. || grub_strcmp (type, "list") == 0);
  130. }
  131. static struct grub_video_bitmap *
  132. get_item_icon (list_impl_t self, int item_index)
  133. {
  134. grub_menu_entry_t entry;
  135. entry = grub_menu_get_entry (self->view->menu, item_index);
  136. if (! entry)
  137. return 0;
  138. return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry);
  139. }
  140. static void
  141. make_selected_item_visible (list_impl_t self)
  142. {
  143. int selected_index = self->view->selected;
  144. if (selected_index < 0)
  145. return; /* No item is selected. */
  146. int num_shown_items = get_num_shown_items (self);
  147. int last_shown_index = self->first_shown_index + (num_shown_items - 1);
  148. if (selected_index < self->first_shown_index)
  149. self->first_shown_index = selected_index;
  150. else if (selected_index > last_shown_index)
  151. self->first_shown_index = selected_index - (num_shown_items - 1);
  152. }
  153. /* Draw a scrollbar on the menu. */
  154. static void
  155. draw_scrollbar (list_impl_t self,
  156. int value, int extent, int min, int max,
  157. int rightx, int topy, int height)
  158. {
  159. grub_gfxmenu_box_t frame = self->scrollbar_frame;
  160. grub_gfxmenu_box_t thumb = self->scrollbar_thumb;
  161. int frame_vertical_pad = (frame->get_top_pad (frame)
  162. + frame->get_bottom_pad (frame));
  163. int frame_horizontal_pad = (frame->get_left_pad (frame)
  164. + frame->get_right_pad (frame));
  165. int tracktop = topy + frame->get_top_pad (frame);
  166. int tracklen = height - frame_vertical_pad;
  167. frame->set_content_size (frame, self->scrollbar_width, tracklen);
  168. int thumby = tracktop + tracklen * (value - min) / (max - min);
  169. int thumbheight = tracklen * extent / (max - min) + 1;
  170. thumb->set_content_size (thumb,
  171. self->scrollbar_width - frame_horizontal_pad,
  172. thumbheight - (thumb->get_top_pad (thumb)
  173. + thumb->get_bottom_pad (thumb)));
  174. frame->draw (frame,
  175. rightx - (self->scrollbar_width + frame_horizontal_pad),
  176. topy);
  177. thumb->draw (thumb,
  178. rightx - (self->scrollbar_width - frame->get_right_pad (frame)),
  179. thumby);
  180. }
  181. /* Draw the list of items. */
  182. static void
  183. draw_menu (list_impl_t self, int width, int drawing_scrollbar,
  184. int num_shown_items)
  185. {
  186. if (! self->menu_box || ! self->selected_item_box)
  187. return;
  188. int boxpad = self->item_padding;
  189. int icon_text_space = self->item_icon_space;
  190. int item_vspace = self->item_spacing;
  191. int ascent = grub_font_get_ascent (self->item_font);
  192. int descent = grub_font_get_descent (self->item_font);
  193. int item_height = self->item_height;
  194. make_selected_item_visible (self);
  195. int scrollbar_h_space = drawing_scrollbar ? self->scrollbar_width : 0;
  196. grub_gfxmenu_box_t selbox = self->selected_item_box;
  197. int sel_leftpad = selbox->get_left_pad (selbox);
  198. int item_top = boxpad;
  199. int item_left = boxpad + sel_leftpad;
  200. int menu_index;
  201. int visible_index;
  202. for (visible_index = 0, menu_index = self->first_shown_index;
  203. visible_index < num_shown_items && menu_index < self->view->menu->size;
  204. visible_index++, menu_index++)
  205. {
  206. int is_selected = (menu_index == self->view->selected);
  207. if (is_selected)
  208. {
  209. int sel_toppad = selbox->get_top_pad (selbox);
  210. selbox->set_content_size (selbox,
  211. (width - 2 * boxpad
  212. - scrollbar_h_space),
  213. item_height);
  214. selbox->draw (selbox,
  215. item_left - sel_leftpad,
  216. item_top - sel_toppad);
  217. }
  218. struct grub_video_bitmap *icon;
  219. if ((icon = get_item_icon (self, menu_index)) != 0)
  220. grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND,
  221. item_left,
  222. item_top + (item_height - self->icon_height) / 2,
  223. 0, 0, self->icon_width, self->icon_height);
  224. const char *item_title =
  225. grub_menu_get_entry (self->view->menu, menu_index)->title;
  226. grub_font_t font =
  227. (is_selected && self->selected_item_font
  228. ? self->selected_item_font
  229. : self->item_font);
  230. grub_gui_color_t text_color =
  231. ((is_selected && self->selected_item_color_set)
  232. ? self->selected_item_color
  233. : self->item_color);
  234. grub_font_draw_string (item_title,
  235. font,
  236. grub_gui_map_color (text_color),
  237. item_left + self->icon_width + icon_text_space,
  238. (item_top + (item_height - (ascent + descent))
  239. / 2 + ascent));
  240. item_top += item_height + item_vspace;
  241. }
  242. }
  243. static void
  244. list_paint (void *vself, const grub_video_rect_t *region)
  245. {
  246. list_impl_t self = vself;
  247. grub_video_rect_t vpsave;
  248. if (! self->visible)
  249. return;
  250. if (!grub_video_have_common_points (region, &self->bounds))
  251. return;
  252. check_boxes (self);
  253. if (! self->menu_box || ! self->selected_item_box)
  254. return;
  255. grub_gui_set_viewport (&self->bounds, &vpsave);
  256. {
  257. grub_gfxmenu_box_t box = self->menu_box;
  258. int box_left_pad = box->get_left_pad (box);
  259. int box_top_pad = box->get_top_pad (box);
  260. int box_right_pad = box->get_right_pad (box);
  261. int box_bottom_pad = box->get_bottom_pad (box);
  262. grub_video_rect_t vpsave2, content_rect;
  263. int num_shown_items = get_num_shown_items (self);
  264. int drawing_scrollbar = (self->draw_scrollbar
  265. && (num_shown_items < self->view->menu->size)
  266. && check_scrollbar (self));
  267. content_rect.x = box_left_pad;
  268. content_rect.y = box_top_pad;
  269. content_rect.width = self->bounds.width - box_left_pad - box_right_pad;
  270. content_rect.height = self->bounds.height - box_top_pad - box_bottom_pad;
  271. box->set_content_size (box, content_rect.width, content_rect.height);
  272. box->draw (box, 0, 0);
  273. grub_gui_set_viewport (&content_rect, &vpsave2);
  274. draw_menu (self, content_rect.width, drawing_scrollbar, num_shown_items);
  275. grub_gui_restore_viewport (&vpsave2);
  276. if (drawing_scrollbar)
  277. draw_scrollbar (self,
  278. self->first_shown_index, num_shown_items,
  279. 0, self->view->menu->size,
  280. self->bounds.width - box_right_pad
  281. + self->scrollbar_width,
  282. box_top_pad + self->item_padding,
  283. self->bounds.height - box_top_pad - box_bottom_pad);
  284. }
  285. grub_gui_restore_viewport (&vpsave);
  286. }
  287. static void
  288. list_set_parent (void *vself, grub_gui_container_t parent)
  289. {
  290. list_impl_t self = vself;
  291. self->parent = parent;
  292. }
  293. static grub_gui_container_t
  294. list_get_parent (void *vself)
  295. {
  296. list_impl_t self = vself;
  297. return self->parent;
  298. }
  299. static void
  300. list_set_bounds (void *vself, const grub_video_rect_t *bounds)
  301. {
  302. list_impl_t self = vself;
  303. self->bounds = *bounds;
  304. }
  305. static void
  306. list_get_bounds (void *vself, grub_video_rect_t *bounds)
  307. {
  308. list_impl_t self = vself;
  309. *bounds = self->bounds;
  310. }
  311. static void
  312. list_get_minimal_size (void *vself, unsigned *width, unsigned *height)
  313. {
  314. list_impl_t self = vself;
  315. if (check_boxes (self))
  316. {
  317. int boxpad = self->item_padding;
  318. int item_vspace = self->item_spacing;
  319. int item_height = self->item_height;
  320. int num_items = 3;
  321. grub_gfxmenu_box_t box = self->menu_box;
  322. int box_left_pad = box->get_left_pad (box);
  323. int box_top_pad = box->get_top_pad (box);
  324. int box_right_pad = box->get_right_pad (box);
  325. int box_bottom_pad = box->get_bottom_pad (box);
  326. unsigned width_s;
  327. *width = grub_font_get_string_width (self->item_font, "Typical OS");
  328. width_s = grub_font_get_string_width (self->selected_item_font,
  329. "Typical OS");
  330. if (*width < width_s)
  331. *width = width_s;
  332. *width += 2 * boxpad + box_left_pad + box_right_pad;
  333. /* Set the menu box height to fit the items. */
  334. *height = (item_height * num_items
  335. + item_vspace * (num_items - 1)
  336. + 2 * boxpad
  337. + box_top_pad + box_bottom_pad);
  338. }
  339. else
  340. {
  341. *width = 0;
  342. *height = 0;
  343. }
  344. }
  345. static grub_err_t
  346. list_set_property (void *vself, const char *name, const char *value)
  347. {
  348. list_impl_t self = vself;
  349. if (grub_strcmp (name, "item_font") == 0)
  350. {
  351. self->item_font = grub_font_get (value);
  352. }
  353. else if (grub_strcmp (name, "selected_item_font") == 0)
  354. {
  355. if (! value || grub_strcmp (value, "inherit") == 0)
  356. self->selected_item_font = 0;
  357. else
  358. self->selected_item_font = grub_font_get (value);
  359. }
  360. else if (grub_strcmp (name, "item_color") == 0)
  361. {
  362. grub_gui_parse_color (value, &self->item_color);
  363. }
  364. else if (grub_strcmp (name, "selected_item_color") == 0)
  365. {
  366. if (! value || grub_strcmp (value, "inherit") == 0)
  367. {
  368. self->selected_item_color_set = 0;
  369. }
  370. else
  371. {
  372. if (grub_gui_parse_color (value, &self->selected_item_color)
  373. == GRUB_ERR_NONE)
  374. self->selected_item_color_set = 1;
  375. }
  376. }
  377. else if (grub_strcmp (name, "icon_width") == 0)
  378. {
  379. self->icon_width = grub_strtol (value, 0, 10);
  380. grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
  381. self->icon_width,
  382. self->icon_height);
  383. }
  384. else if (grub_strcmp (name, "icon_height") == 0)
  385. {
  386. self->icon_height = grub_strtol (value, 0, 10);
  387. grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
  388. self->icon_width,
  389. self->icon_height);
  390. }
  391. else if (grub_strcmp (name, "item_height") == 0)
  392. {
  393. self->item_height = grub_strtol (value, 0, 10);
  394. }
  395. else if (grub_strcmp (name, "item_padding") == 0)
  396. {
  397. self->item_padding = grub_strtol (value, 0, 10);
  398. }
  399. else if (grub_strcmp (name, "item_icon_space") == 0)
  400. {
  401. self->item_icon_space = grub_strtol (value, 0, 10);
  402. }
  403. else if (grub_strcmp (name, "item_spacing") == 0)
  404. {
  405. self->item_spacing = grub_strtol (value, 0, 10);
  406. }
  407. else if (grub_strcmp (name, "visible") == 0)
  408. {
  409. self->visible = grub_strcmp (value, "false") != 0;
  410. }
  411. else if (grub_strcmp (name, "menu_pixmap_style") == 0)
  412. {
  413. self->need_to_recreate_boxes = 1;
  414. grub_free (self->menu_box_pattern);
  415. self->menu_box_pattern = value ? grub_strdup (value) : 0;
  416. }
  417. else if (grub_strcmp (name, "selected_item_pixmap_style") == 0)
  418. {
  419. self->need_to_recreate_boxes = 1;
  420. grub_free (self->selected_item_box_pattern);
  421. self->selected_item_box_pattern = value ? grub_strdup (value) : 0;
  422. }
  423. else if (grub_strcmp (name, "scrollbar_frame") == 0)
  424. {
  425. self->need_to_recreate_scrollbar = 1;
  426. grub_free (self->scrollbar_frame_pattern);
  427. self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0;
  428. }
  429. else if (grub_strcmp (name, "scrollbar_thumb") == 0)
  430. {
  431. self->need_to_recreate_scrollbar = 1;
  432. grub_free (self->scrollbar_thumb_pattern);
  433. self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0;
  434. }
  435. else if (grub_strcmp (name, "scrollbar_width") == 0)
  436. {
  437. self->scrollbar_width = grub_strtol (value, 0, 10);
  438. }
  439. else if (grub_strcmp (name, "scrollbar") == 0)
  440. {
  441. self->draw_scrollbar = grub_strcmp (value, "false") != 0;
  442. }
  443. else if (grub_strcmp (name, "theme_dir") == 0)
  444. {
  445. self->need_to_recreate_boxes = 1;
  446. grub_free (self->theme_dir);
  447. self->theme_dir = value ? grub_strdup (value) : 0;
  448. }
  449. else if (grub_strcmp (name, "id") == 0)
  450. {
  451. grub_free (self->id);
  452. if (value)
  453. self->id = grub_strdup (value);
  454. else
  455. self->id = 0;
  456. }
  457. return grub_errno;
  458. }
  459. /* Set necessary information that the gfxmenu view provides. */
  460. static void
  461. list_set_view_info (void *vself,
  462. grub_gfxmenu_view_t view)
  463. {
  464. list_impl_t self = vself;
  465. grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager,
  466. view->theme_path);
  467. self->view = view;
  468. }
  469. static struct grub_gui_component_ops list_comp_ops =
  470. {
  471. .destroy = list_destroy,
  472. .get_id = list_get_id,
  473. .is_instance = list_is_instance,
  474. .paint = list_paint,
  475. .set_parent = list_set_parent,
  476. .get_parent = list_get_parent,
  477. .set_bounds = list_set_bounds,
  478. .get_bounds = list_get_bounds,
  479. .get_minimal_size = list_get_minimal_size,
  480. .set_property = list_set_property
  481. };
  482. static struct grub_gui_list_ops list_ops =
  483. {
  484. .set_view_info = list_set_view_info
  485. };
  486. grub_gui_component_t
  487. grub_gui_list_new (void)
  488. {
  489. list_impl_t self;
  490. grub_font_t default_font;
  491. grub_gui_color_t default_fg_color;
  492. grub_gui_color_t default_bg_color;
  493. self = grub_zalloc (sizeof (*self));
  494. if (! self)
  495. return 0;
  496. self->list.ops = &list_ops;
  497. self->list.component.ops = &list_comp_ops;
  498. self->visible = 1;
  499. default_font = grub_font_get ("Unknown Regular 16");
  500. default_fg_color = grub_gui_color_rgb (0, 0, 0);
  501. default_bg_color = grub_gui_color_rgb (255, 255, 255);
  502. self->icon_width = 32;
  503. self->icon_height = 32;
  504. self->item_height = 42;
  505. self->item_padding = 14;
  506. self->item_icon_space = 4;
  507. self->item_spacing = 16;
  508. self->item_font = default_font;
  509. self->selected_item_font = 0; /* Default to using the item_font. */
  510. self->item_color = default_fg_color;
  511. self->selected_item_color_set = 0; /* Default to using the item_color. */
  512. self->selected_item_color = default_fg_color;
  513. self->draw_scrollbar = 1;
  514. self->need_to_recreate_scrollbar = 1;
  515. self->scrollbar_frame = 0;
  516. self->scrollbar_thumb = 0;
  517. self->scrollbar_frame_pattern = 0;
  518. self->scrollbar_thumb_pattern = 0;
  519. self->scrollbar_width = 16;
  520. self->first_shown_index = 0;
  521. self->need_to_recreate_boxes = 0;
  522. self->theme_dir = 0;
  523. self->menu_box_pattern = 0;
  524. self->selected_item_box_pattern = 0;
  525. self->menu_box = grub_gfxmenu_create_box (0, 0);
  526. self->selected_item_box = grub_gfxmenu_create_box (0, 0);
  527. self->icon_manager = grub_gfxmenu_icon_manager_new ();
  528. if (! self->icon_manager)
  529. {
  530. self->list.component.ops->destroy (self);
  531. return 0;
  532. }
  533. grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
  534. self->icon_width,
  535. self->icon_height);
  536. return (grub_gui_component_t) self;
  537. }