menu.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  1. /*
  2. Pulldown menu code
  3. Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
  4. 2007, 2009, 2011, 2012
  5. The Free Software Foundation, Inc.
  6. Written by:
  7. Andrew Borodin <aborodin@vmail.ru>, 2012
  8. This file is part of the Midnight Commander.
  9. The Midnight Commander is free software: you can redistribute it
  10. and/or modify it under the terms of the GNU General Public License as
  11. published by the Free Software Foundation, either version 3 of the License,
  12. or (at your option) any later version.
  13. The Midnight Commander is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. GNU General Public License for more details.
  17. You should have received a copy of the GNU General Public License
  18. along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. /** \file menu.c
  21. * \brief Source: pulldown menu code
  22. */
  23. #include <config.h>
  24. #include <ctype.h>
  25. #include <stdarg.h>
  26. #include <string.h>
  27. #include <sys/types.h>
  28. #include "lib/global.h"
  29. #include "lib/tty/tty.h"
  30. #include "lib/skin.h"
  31. #include "lib/tty/mouse.h"
  32. #include "lib/tty/key.h" /* key macros */
  33. #include "lib/strutil.h"
  34. #include "lib/widget.h"
  35. #include "lib/event.h" /* mc_event_raise() */
  36. /*** global variables ****************************************************************************/
  37. /*** file scope macro definitions ****************************************************************/
  38. /*** file scope type declarations ****************************************************************/
  39. /*** file scope variables ************************************************************************/
  40. static cb_ret_t menubar_callback (Widget * w, widget_msg_t msg, int parm);
  41. /*** file scope functions ************************************************************************/
  42. /* --------------------------------------------------------------------------------------------- */
  43. static void
  44. menu_arrange (Menu * menu, dlg_shortcut_str get_shortcut)
  45. {
  46. if (menu != NULL)
  47. {
  48. GList *i;
  49. size_t max_shortcut_len = 0;
  50. menu->max_entry_len = 1;
  51. menu->max_hotkey_len = 1;
  52. for (i = menu->entries; i != NULL; i = g_list_next (i))
  53. {
  54. menu_entry_t *entry = i->data;
  55. if (entry != NULL)
  56. {
  57. size_t len;
  58. len = (size_t) hotkey_width (entry->text);
  59. menu->max_hotkey_len = max (menu->max_hotkey_len, len);
  60. if (get_shortcut != NULL)
  61. entry->shortcut = get_shortcut (entry->command);
  62. if (entry->shortcut != NULL)
  63. {
  64. len = (size_t) str_term_width1 (entry->shortcut);
  65. max_shortcut_len = max (max_shortcut_len, len);
  66. }
  67. }
  68. }
  69. menu->max_entry_len = menu->max_hotkey_len + max_shortcut_len;
  70. }
  71. }
  72. /* --------------------------------------------------------------------------------------------- */
  73. static void
  74. menubar_paint_idx (WMenuBar * menubar, unsigned int idx, int color)
  75. {
  76. const Menu *menu = g_list_nth_data (menubar->menu, menubar->selected);
  77. const menu_entry_t *entry = g_list_nth_data (menu->entries, idx);
  78. const int y = 2 + idx;
  79. int x = menu->start_x;
  80. if (x + menu->max_entry_len + 4 > (gsize) menubar->widget.cols)
  81. x = menubar->widget.cols - menu->max_entry_len - 4;
  82. if (entry == NULL)
  83. {
  84. /* menu separator */
  85. tty_setcolor (MENU_ENTRY_COLOR);
  86. widget_move (&menubar->widget, y, x - 1);
  87. tty_print_alt_char (ACS_LTEE, FALSE);
  88. tty_draw_hline (menubar->widget.y + y, menubar->widget.x + x,
  89. ACS_HLINE, menu->max_entry_len + 3);
  90. widget_move (&menubar->widget, y, x + menu->max_entry_len + 3);
  91. tty_print_alt_char (ACS_RTEE, FALSE);
  92. }
  93. else
  94. {
  95. int yt, xt;
  96. /* menu text */
  97. tty_setcolor (color);
  98. widget_move (&menubar->widget, y, x);
  99. tty_print_char ((unsigned char) entry->first_letter);
  100. tty_getyx (&yt, &xt);
  101. tty_draw_hline (yt, xt, ' ', menu->max_entry_len + 2); /* clear line */
  102. tty_print_string (entry->text.start);
  103. if (entry->text.hotkey != NULL)
  104. {
  105. tty_setcolor (color == MENU_SELECTED_COLOR ? MENU_HOTSEL_COLOR : MENU_HOT_COLOR);
  106. tty_print_string (entry->text.hotkey);
  107. tty_setcolor (color);
  108. }
  109. if (entry->text.end != NULL)
  110. tty_print_string (entry->text.end);
  111. if (entry->shortcut != NULL)
  112. {
  113. widget_move (&menubar->widget, y, x + menu->max_hotkey_len + 3);
  114. tty_print_string (entry->shortcut);
  115. }
  116. /* move cursor to the start of entry text */
  117. widget_move (&menubar->widget, y, x + 1);
  118. }
  119. }
  120. /* --------------------------------------------------------------------------------------------- */
  121. static void
  122. menubar_draw_drop (WMenuBar * menubar)
  123. {
  124. const Menu *menu = g_list_nth_data (menubar->menu, menubar->selected);
  125. const unsigned int count = g_list_length (menu->entries);
  126. int column = menu->start_x - 1;
  127. unsigned int i;
  128. if (column + menu->max_entry_len + 5 > (gsize) menubar->widget.cols)
  129. column = menubar->widget.cols - menu->max_entry_len - 5;
  130. tty_setcolor (MENU_ENTRY_COLOR);
  131. draw_box (menubar->widget.owner,
  132. menubar->widget.y + 1, menubar->widget.x + column,
  133. count + 2, menu->max_entry_len + 5, FALSE);
  134. for (i = 0; i < count; i++)
  135. menubar_paint_idx (menubar, i,
  136. i == menu->selected ? MENU_SELECTED_COLOR : MENU_ENTRY_COLOR);
  137. }
  138. /* --------------------------------------------------------------------------------------------- */
  139. static void
  140. menubar_set_color (WMenuBar * menubar, gboolean current, gboolean hotkey)
  141. {
  142. if (!menubar->is_active)
  143. tty_setcolor (MENU_INACTIVE_COLOR);
  144. else if (current)
  145. tty_setcolor (hotkey ? MENU_HOTSEL_COLOR : MENU_SELECTED_COLOR);
  146. else
  147. tty_setcolor (hotkey ? MENU_HOT_COLOR : MENU_ENTRY_COLOR);
  148. }
  149. /* --------------------------------------------------------------------------------------------- */
  150. static void
  151. menubar_draw (WMenuBar * menubar)
  152. {
  153. GList *i;
  154. /* First draw the complete menubar */
  155. tty_setcolor (menubar->is_active ? MENU_ENTRY_COLOR : MENU_INACTIVE_COLOR);
  156. tty_draw_hline (menubar->widget.y, menubar->widget.x, ' ', menubar->widget.cols);
  157. /* Now each one of the entries */
  158. for (i = menubar->menu; i != NULL; i = g_list_next (i))
  159. {
  160. Menu *menu = i->data;
  161. gboolean is_selected = (menubar->selected == (gsize) g_list_position (menubar->menu, i));
  162. menubar_set_color (menubar, is_selected, FALSE);
  163. widget_move (&menubar->widget, 0, menu->start_x);
  164. tty_print_char (' ');
  165. tty_print_string (menu->text.start);
  166. if (menu->text.hotkey != NULL)
  167. {
  168. menubar_set_color (menubar, is_selected, TRUE);
  169. tty_print_string (menu->text.hotkey);
  170. menubar_set_color (menubar, is_selected, FALSE);
  171. }
  172. if (menu->text.end != NULL)
  173. tty_print_string (menu->text.end);
  174. tty_print_char (' ');
  175. }
  176. if (op_clock_type != NO_CLOCK)
  177. {
  178. mc_log ("show_clock\n");
  179. clock_resume ();
  180. show_clock ();
  181. }
  182. if (menubar->is_dropped)
  183. menubar_draw_drop (menubar);
  184. else
  185. widget_move (&menubar->widget, 0,
  186. ((Menu *) g_list_nth_data (menubar->menu, menubar->selected))->start_x);
  187. }
  188. /* --------------------------------------------------------------------------------------------- */
  189. static void
  190. menubar_remove (WMenuBar * menubar)
  191. {
  192. Dlg_head *h;
  193. if (!menubar->is_dropped)
  194. return;
  195. /* HACK: before refresh the dialog, change the current widget to keep the order
  196. of overlapped widgets. This is useful in multi-window editor.
  197. In general, menubar should be a special object, not an ordinary widget
  198. in the current dialog. */
  199. h = menubar->widget.owner;
  200. h->current = g_list_find (h->widgets, dlg_find_by_id (h, menubar->previous_widget));
  201. menubar->is_dropped = FALSE;
  202. do_refresh ();
  203. menubar->is_dropped = TRUE;
  204. /* restore current widget */
  205. h->current = g_list_find (h->widgets, menubar);
  206. }
  207. /* --------------------------------------------------------------------------------------------- */
  208. static void
  209. menubar_left (WMenuBar * menubar)
  210. {
  211. menubar_remove (menubar);
  212. if (menubar->selected == 0)
  213. menubar->selected = g_list_length (menubar->menu) - 1;
  214. else
  215. menubar->selected--;
  216. menubar_draw (menubar);
  217. }
  218. /* --------------------------------------------------------------------------------------------- */
  219. static void
  220. menubar_right (WMenuBar * menubar)
  221. {
  222. menubar_remove (menubar);
  223. menubar->selected = (menubar->selected + 1) % g_list_length (menubar->menu);
  224. menubar_draw (menubar);
  225. }
  226. /* --------------------------------------------------------------------------------------------- */
  227. static void
  228. menubar_finish (WMenuBar * menubar)
  229. {
  230. menubar->is_dropped = FALSE;
  231. menubar->is_active = FALSE;
  232. menubar->widget.lines = 1;
  233. widget_want_hotkey (menubar->widget, 0);
  234. dlg_select_by_id (menubar->widget.owner, menubar->previous_widget);
  235. do_refresh ();
  236. }
  237. /* --------------------------------------------------------------------------------------------- */
  238. static void
  239. menubar_drop (WMenuBar * menubar, unsigned int selected)
  240. {
  241. menubar->is_dropped = TRUE;
  242. menubar->selected = selected;
  243. menubar_draw (menubar);
  244. }
  245. /* --------------------------------------------------------------------------------------------- */
  246. static void
  247. menubar_execute (WMenuBar * menubar)
  248. {
  249. const Menu *menu = g_list_nth_data (menubar->menu, menubar->selected);
  250. const menu_entry_t *entry = g_list_nth_data (menu->entries, menu->selected);
  251. if ((entry != NULL) && (entry->command != CK_IgnoreKey))
  252. {
  253. mc_global.widget.is_right = (menubar->selected != 0);
  254. menubar_finish (menubar);
  255. menubar->widget.owner->callback (menubar->widget.owner, &menubar->widget,
  256. DLG_ACTION, entry->command, NULL);
  257. do_refresh ();
  258. }
  259. }
  260. /* --------------------------------------------------------------------------------------------- */
  261. static void
  262. menubar_down (WMenuBar * menubar)
  263. {
  264. Menu *menu = g_list_nth_data (menubar->menu, menubar->selected);
  265. const unsigned int len = g_list_length (menu->entries);
  266. menu_entry_t *entry;
  267. menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
  268. do
  269. {
  270. menu->selected = (menu->selected + 1) % len;
  271. entry = (menu_entry_t *) g_list_nth_data (menu->entries, menu->selected);
  272. }
  273. while ((entry == NULL) || (entry->command == CK_IgnoreKey));
  274. menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
  275. }
  276. /* --------------------------------------------------------------------------------------------- */
  277. static void
  278. menubar_up (WMenuBar * menubar)
  279. {
  280. Menu *menu = g_list_nth_data (menubar->menu, menubar->selected);
  281. const unsigned int len = g_list_length (menu->entries);
  282. menu_entry_t *entry;
  283. menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
  284. do
  285. {
  286. if (menu->selected == 0)
  287. menu->selected = len - 1;
  288. else
  289. menu->selected--;
  290. entry = (menu_entry_t *) g_list_nth_data (menu->entries, menu->selected);
  291. }
  292. while ((entry == NULL) || (entry->command == CK_IgnoreKey));
  293. menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
  294. }
  295. /* --------------------------------------------------------------------------------------------- */
  296. static void
  297. menubar_first (WMenuBar * menubar)
  298. {
  299. Menu *menu = g_list_nth_data (menubar->menu, menubar->selected);
  300. menu_entry_t *entry;
  301. if (menu->selected == 0)
  302. return;
  303. menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
  304. menu->selected = 0;
  305. while (TRUE)
  306. {
  307. entry = (menu_entry_t *) g_list_nth_data (menu->entries, menu->selected);
  308. if ((entry == NULL) || (entry->command == CK_IgnoreKey))
  309. menu->selected++;
  310. else
  311. break;
  312. }
  313. menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
  314. }
  315. /* --------------------------------------------------------------------------------------------- */
  316. static void
  317. menubar_last (WMenuBar * menubar)
  318. {
  319. Menu *menu = g_list_nth_data (menubar->menu, menubar->selected);
  320. const unsigned int len = g_list_length (menu->entries);
  321. menu_entry_t *entry;
  322. if (menu->selected == len - 1)
  323. return;
  324. menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
  325. menu->selected = len;
  326. do
  327. {
  328. menu->selected--;
  329. entry = (menu_entry_t *) g_list_nth_data (menu->entries, menu->selected);
  330. }
  331. while ((entry == NULL) || (entry->command == CK_IgnoreKey));
  332. menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
  333. }
  334. /* --------------------------------------------------------------------------------------------- */
  335. static int
  336. menubar_handle_key (WMenuBar * menubar, int key)
  337. {
  338. /* Lowercase */
  339. if (isascii (key))
  340. key = g_ascii_tolower (key);
  341. if (is_abort_char (key))
  342. {
  343. menubar_finish (menubar);
  344. return 1;
  345. }
  346. /* menubar help or menubar navigation */
  347. switch (key)
  348. {
  349. case KEY_F (1):
  350. {
  351. ev_help_t event_data = { NULL, NULL };
  352. if (menubar->is_dropped)
  353. event_data.node =
  354. ((Menu *) g_list_nth_data (menubar->menu, menubar->selected))->help_node;
  355. else
  356. event_data.node = "[Menu Bar]";
  357. mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
  358. menubar_draw (menubar);
  359. return 1;
  360. }
  361. case KEY_LEFT:
  362. case XCTRL ('b'):
  363. menubar_left (menubar);
  364. return 1;
  365. case KEY_RIGHT:
  366. case XCTRL ('f'):
  367. menubar_right (menubar);
  368. return 1;
  369. }
  370. if (!menubar->is_dropped)
  371. {
  372. GList *i;
  373. /* drop menu by hotkey */
  374. for (i = menubar->menu; i != NULL; i = g_list_next (i))
  375. {
  376. Menu *menu = i->data;
  377. if ((menu->text.hotkey != NULL) && (key == g_ascii_tolower (menu->text.hotkey[0])))
  378. {
  379. menubar_drop (menubar, g_list_position (menubar->menu, i));
  380. return 1;
  381. }
  382. }
  383. /* drop menu by Enter or Dowwn key */
  384. if (key == KEY_ENTER || key == XCTRL ('n') || key == KEY_DOWN || key == '\n')
  385. menubar_drop (menubar, menubar->selected);
  386. return 1;
  387. }
  388. {
  389. Menu *menu = g_list_nth_data (menubar->menu, menubar->selected);
  390. GList *i;
  391. /* execute menu command by hotkey */
  392. for (i = menu->entries; i != NULL; i = g_list_next (i))
  393. {
  394. const menu_entry_t *entry = i->data;
  395. if ((entry != NULL) && (entry->command != CK_IgnoreKey)
  396. && (entry->text.hotkey != NULL) && (key == g_ascii_tolower (entry->text.hotkey[0])))
  397. {
  398. menu->selected = g_list_position (menu->entries, i);
  399. menubar_execute (menubar);
  400. return 1;
  401. }
  402. }
  403. /* menu execute by Enter or menu navigation */
  404. switch (key)
  405. {
  406. case KEY_ENTER:
  407. case '\n':
  408. menubar_execute (menubar);
  409. return 1;
  410. case KEY_HOME:
  411. case ALT ('<'):
  412. menubar_first (menubar);
  413. break;
  414. case KEY_END:
  415. case ALT ('>'):
  416. menubar_last (menubar);
  417. break;
  418. case KEY_DOWN:
  419. case XCTRL ('n'):
  420. menubar_down (menubar);
  421. break;
  422. case KEY_UP:
  423. case XCTRL ('p'):
  424. menubar_up (menubar);
  425. break;
  426. }
  427. }
  428. return 0;
  429. }
  430. /* --------------------------------------------------------------------------------------------- */
  431. static cb_ret_t
  432. menubar_callback (Widget * w, widget_msg_t msg, int parm)
  433. {
  434. WMenuBar *menubar = (WMenuBar *) w;
  435. switch (msg)
  436. {
  437. /* We do not want the focus unless we have been activated */
  438. case WIDGET_FOCUS:
  439. if (!menubar->is_active)
  440. return MSG_NOT_HANDLED;
  441. /* Trick to get all the mouse events */
  442. menubar->widget.lines = LINES;
  443. /* Trick to get all of the hotkeys */
  444. widget_want_hotkey (menubar->widget, 1);
  445. menubar_draw (menubar);
  446. return MSG_HANDLED;
  447. /* We don't want the buttonbar to activate while using the menubar */
  448. case WIDGET_HOTKEY:
  449. case WIDGET_KEY:
  450. if (menubar->is_active)
  451. {
  452. menubar_handle_key (menubar, parm);
  453. return MSG_HANDLED;
  454. }
  455. return MSG_NOT_HANDLED;
  456. case WIDGET_CURSOR:
  457. /* Put the cursor in a suitable place */
  458. return MSG_NOT_HANDLED;
  459. case WIDGET_UNFOCUS:
  460. return menubar->is_active ? MSG_NOT_HANDLED : MSG_HANDLED;
  461. case WIDGET_DRAW:
  462. if (menubar->is_visible)
  463. {
  464. menubar_draw (menubar);
  465. return MSG_HANDLED;
  466. }
  467. /* fall through */
  468. case WIDGET_RESIZED:
  469. /* try show menu after screen resize */
  470. send_message (w, WIDGET_FOCUS, 0);
  471. return MSG_HANDLED;
  472. case WIDGET_DESTROY:
  473. menubar_set_menu (menubar, NULL);
  474. return MSG_HANDLED;
  475. default:
  476. return default_proc (msg, parm);
  477. }
  478. }
  479. /* --------------------------------------------------------------------------------------------- */
  480. static int
  481. menubar_event (Gpm_Event * event, void *data)
  482. {
  483. WMenuBar *menubar = (WMenuBar *) data;
  484. Widget *w = (Widget *) data;
  485. gboolean was_active = TRUE;
  486. int left_x, right_x, bottom_y;
  487. Menu *menu;
  488. Gpm_Event local;
  489. if (!mouse_global_in_widget (event, w))
  490. return MOU_UNHANDLED;
  491. /* ignore unsupported events */
  492. if ((event->type & (GPM_UP | GPM_DOWN | GPM_DRAG)) == 0)
  493. return MOU_NORMAL;
  494. /* ignore wheel events if menu is inactive */
  495. if (!menubar->is_active && ((event->buttons & (GPM_B_MIDDLE | GPM_B_UP | GPM_B_DOWN)) != 0))
  496. return MOU_NORMAL;
  497. local = mouse_get_local (event, w);
  498. if (local.y == 1 && (local.type & GPM_UP) != 0)
  499. return MOU_NORMAL;
  500. if (!menubar->is_dropped)
  501. {
  502. menubar->previous_widget = dlg_get_current_widget_id (w->owner);
  503. menubar->is_active = TRUE;
  504. menubar->is_dropped = TRUE;
  505. was_active = FALSE;
  506. }
  507. /* Mouse operations on the menubar */
  508. if (local.y == 1 || !was_active)
  509. {
  510. /* wheel events on menubar */
  511. if ((local.buttons & GPM_B_UP) != 0)
  512. menubar_left (menubar);
  513. else if ((local.buttons & GPM_B_DOWN) != 0)
  514. menubar_right (menubar);
  515. else
  516. {
  517. const unsigned int len = g_list_length (menubar->menu);
  518. unsigned int new_selection = 0;
  519. while ((new_selection < len)
  520. && (local.x > ((Menu *) g_list_nth_data (menubar->menu,
  521. new_selection))->start_x))
  522. new_selection++;
  523. if (new_selection != 0) /* Don't set the invalid value -1 */
  524. new_selection--;
  525. if (!was_active)
  526. {
  527. menubar->selected = new_selection;
  528. dlg_select_widget (menubar);
  529. }
  530. else
  531. {
  532. menubar_remove (menubar);
  533. menubar->selected = new_selection;
  534. }
  535. menubar_draw (menubar);
  536. }
  537. return MOU_NORMAL;
  538. }
  539. if (!menubar->is_dropped || (local.y < 2))
  540. return MOU_NORMAL;
  541. /* middle click -- everywhere */
  542. if (((local.buttons & GPM_B_MIDDLE) != 0) && ((local.type & GPM_DOWN) != 0))
  543. {
  544. menubar_execute (menubar);
  545. return MOU_NORMAL;
  546. }
  547. /* the mouse operation is on the menus or it is not */
  548. menu = (Menu *) g_list_nth_data (menubar->menu, menubar->selected);
  549. left_x = menu->start_x;
  550. right_x = left_x + menu->max_entry_len + 3;
  551. if (right_x > menubar->widget.cols)
  552. {
  553. left_x = menubar->widget.cols - menu->max_entry_len - 3;
  554. right_x = menubar->widget.cols;
  555. }
  556. bottom_y = g_list_length (menu->entries) + 3;
  557. if ((local.x >= left_x) && (local.x <= right_x) && (local.y <= bottom_y))
  558. {
  559. int pos = local.y - 3;
  560. const menu_entry_t *entry = g_list_nth_data (menu->entries, pos);
  561. /* mouse wheel */
  562. if ((local.buttons & GPM_B_UP) != 0 && (local.type & GPM_DOWN) != 0)
  563. {
  564. menubar_up (menubar);
  565. return MOU_NORMAL;
  566. }
  567. if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
  568. {
  569. menubar_down (menubar);
  570. return MOU_NORMAL;
  571. }
  572. /* ignore events above and below dropped down menu */
  573. if ((pos < 0) || (pos >= bottom_y - 3))
  574. return MOU_NORMAL;
  575. if ((entry != NULL) && (entry->command != CK_IgnoreKey))
  576. {
  577. menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
  578. menu->selected = pos;
  579. menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
  580. if ((event->type & GPM_UP) != 0)
  581. menubar_execute (menubar);
  582. }
  583. }
  584. else if (((local.type & GPM_DOWN) != 0) && ((local.buttons & (GPM_B_UP | GPM_B_DOWN)) == 0))
  585. {
  586. /* use click not wheel to close menu */
  587. menubar_finish (menubar);
  588. }
  589. return MOU_NORMAL;
  590. }
  591. /* --------------------------------------------------------------------------------------------- */
  592. /*** public functions ****************************************************************************/
  593. /* --------------------------------------------------------------------------------------------- */
  594. menu_entry_t *
  595. menu_entry_create (const char *name, unsigned long command)
  596. {
  597. menu_entry_t *entry;
  598. entry = g_new (menu_entry_t, 1);
  599. entry->first_letter = ' ';
  600. entry->text = parse_hotkey (name);
  601. entry->command = command;
  602. entry->shortcut = NULL;
  603. return entry;
  604. }
  605. /* --------------------------------------------------------------------------------------------- */
  606. void
  607. menu_entry_free (menu_entry_t * entry)
  608. {
  609. if (entry != NULL)
  610. {
  611. release_hotkey (entry->text);
  612. g_free (entry->shortcut);
  613. g_free (entry);
  614. }
  615. }
  616. /* --------------------------------------------------------------------------------------------- */
  617. Menu *
  618. create_menu (const char *name, GList * entries, const char *help_node)
  619. {
  620. Menu *menu;
  621. menu = g_new (Menu, 1);
  622. menu->start_x = 0;
  623. menu->text = parse_hotkey (name);
  624. menu->entries = entries;
  625. menu->max_entry_len = 1;
  626. menu->max_hotkey_len = 0;
  627. menu->selected = 0;
  628. menu->help_node = g_strdup (help_node);
  629. return menu;
  630. }
  631. /* --------------------------------------------------------------------------------------------- */
  632. void
  633. menu_set_name (Menu * menu, const char *name)
  634. {
  635. release_hotkey (menu->text);
  636. menu->text = parse_hotkey (name);
  637. }
  638. /* --------------------------------------------------------------------------------------------- */
  639. void
  640. destroy_menu (Menu * menu)
  641. {
  642. release_hotkey (menu->text);
  643. g_list_foreach (menu->entries, (GFunc) menu_entry_free, NULL);
  644. g_list_free (menu->entries);
  645. g_free (menu->help_node);
  646. g_free (menu);
  647. }
  648. /* --------------------------------------------------------------------------------------------- */
  649. WMenuBar *
  650. menubar_new (int y, int x, int cols, GList * menu)
  651. {
  652. WMenuBar *menubar;
  653. menubar = g_new0 (WMenuBar, 1);
  654. init_widget (&menubar->widget, y, x, 1, cols, menubar_callback, menubar_event);
  655. widget_want_cursor (menubar->widget, FALSE);
  656. menubar->is_visible = TRUE; /* by default */
  657. menubar_set_menu (menubar, menu);
  658. return menubar;
  659. }
  660. /* --------------------------------------------------------------------------------------------- */
  661. void
  662. menubar_set_menu (WMenuBar * menubar, GList * menu)
  663. {
  664. /* delete previous menu */
  665. if (menubar->menu != NULL)
  666. {
  667. g_list_foreach (menubar->menu, (GFunc) destroy_menu, NULL);
  668. g_list_free (menubar->menu);
  669. }
  670. /* add new menu */
  671. menubar->is_active = FALSE;
  672. menubar->is_dropped = FALSE;
  673. menubar->menu = menu;
  674. menubar->selected = 0;
  675. menubar_arrange (menubar);
  676. }
  677. /* --------------------------------------------------------------------------------------------- */
  678. void
  679. menubar_add_menu (WMenuBar * menubar, Menu * menu)
  680. {
  681. if (menu != NULL)
  682. {
  683. menu_arrange (menu, menubar->widget.owner->get_shortcut);
  684. menubar->menu = g_list_append (menubar->menu, menu);
  685. }
  686. menubar_arrange (menubar);
  687. }
  688. /* --------------------------------------------------------------------------------------------- */
  689. /**
  690. * Properly space menubar items. Should be called when menubar is created
  691. * and also when widget width is changed (i.e. upon xterm resize).
  692. */
  693. void
  694. menubar_arrange (WMenuBar * menubar)
  695. {
  696. int start_x = 1;
  697. GList *i;
  698. int gap;
  699. if (menubar->menu == NULL)
  700. return;
  701. gap = menubar->widget.cols - 2;
  702. /* First, calculate gap between items... */
  703. for (i = menubar->menu; i != NULL; i = g_list_next (i))
  704. {
  705. Menu *menu = (Menu *) i->data;
  706. /* preserve length here, to be used below */
  707. menu->start_x = hotkey_width (menu->text) + 2;
  708. gap -= menu->start_x;
  709. }
  710. if (g_list_next (menubar->menu) == NULL)
  711. gap = 1;
  712. else
  713. gap /= (g_list_length (menubar->menu) - 1);
  714. if (gap <= 0)
  715. {
  716. /* We are out of luck - window is too narrow... */
  717. gap = 1;
  718. }
  719. else if (gap >= 3)
  720. gap = 3;
  721. /* ...and now fix start positions of menubar items */
  722. for (i = menubar->menu; i != NULL; i = g_list_next (i))
  723. {
  724. Menu *menu = (Menu *) i->data;
  725. int len = menu->start_x;
  726. menu->start_x = start_x;
  727. start_x += len + gap;
  728. }
  729. }
  730. /* --------------------------------------------------------------------------------------------- */
  731. /** Find MenuBar widget in the dialog */
  732. WMenuBar *
  733. find_menubar (const Dlg_head * h)
  734. {
  735. return (WMenuBar *) find_widget_type (h, menubar_callback);
  736. }
  737. /* --------------------------------------------------------------------------------------------- */