menu.c 26 KB

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