Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <ctype.h>
  23. #include "physfsx.h"
  24. #include "u_mem.h"
  25. #include "maths.h"
  26. #include "pstypes.h"
  27. #include "gr.h"
  28. #include "strutil.h"
  29. #include "event.h"
  30. #include "window.h"
  31. #include "ui.h"
  32. #include "mouse.h"
  33. #include "key.h"
  34. #include "func.h"
  35. #include "dxxerror.h"
  36.  
  37. #include "compiler-range_for.h"
  38. #include "partial_range.h"
  39. #include <utility>
  40.  
  41. namespace dcx {
  42.  
  43. #define MAXMENUS 30
  44. #define MAXITEMS 32
  45.  
  46. namespace {
  47.  
  48. struct ITEM {
  49.         short                   x, y, w, h;
  50.         RAIIdmem<char[]> Text;
  51.         RAIIdmem<char[]> InactiveText;
  52.         short                   Hotkey;
  53.         int                     (*user_function)(void);
  54. };
  55.  
  56. struct MENU : embed_window_pointer_t {
  57.         short                   x, y, w, h;
  58.         short                           ShowBar;
  59.         short                           CurrentItem;
  60.         uint16_t                        NumItems;
  61.         short                           Displayed;
  62.         short                           Active;
  63.         std::array<ITEM, MAXITEMS> Item;
  64. };
  65.  
  66. }
  67.  
  68. static std::array<MENU, MAXMENUS> Menu;
  69.  
  70. static unsigned num_menus;
  71. static int state;
  72.  
  73. #define CMENU (Menu[0].CurrentItem+1)
  74.  
  75. static window_event_result menubar_handler(window *wind,const d_event &event, MENU *menu);
  76. static window_event_result menu_handler(window *wind,const d_event &event, MENU *menu);
  77.  
  78. //------------------------- Show a menu item -------------------
  79.  
  80. static void item_show( MENU * menu, int n )
  81. {
  82.         ITEM * item = &menu->Item[n];
  83.        
  84.         gr_set_default_canvas();
  85.         auto &canvas = *grd_curcanv;
  86.         // If this is a seperator, then draw it.
  87.         if ( item->Text[0] == '-'  )
  88.         {
  89.                 const auto color = CBLACK;
  90.                 gr_urect(canvas, item->x, item->y+item->h/2, item->x+item->w-1, item->y+item->h/2, color);
  91.                 return;
  92.         }      
  93.  
  94.         if ( menu->CurrentItem==n && menu->ShowBar )
  95.         {
  96.                 if ( menu != &Menu[0] )
  97.                 {
  98.                         const auto color = CBLACK;
  99.                         gr_urect(canvas, item->x+1, item->y+1, item->x+menu->w-2, item->y+item->h-2, color);
  100.                 }
  101.                 gr_set_fontcolor(canvas, CWHITE, CBLACK);
  102.         }else {
  103.                 if ( menu != &Menu[0] )
  104.                 {
  105.                         const auto color = CGREY;
  106.                         gr_urect(canvas, item->x+1, item->y+1, item->x+menu->w-2, item->y+item->h-2, color);
  107.                 }
  108.                 gr_set_fontcolor(canvas, CBLACK, CGREY);
  109.         }
  110.  
  111.         if ( menu != &Menu[0] )
  112.         {
  113.                 if ( menu->Active)
  114.                         gr_ustring(canvas, *canvas.cv_font, item->x + 1, item->y + 1, item->Text.get());
  115.                 else
  116.                         gr_ustring(canvas, *canvas.cv_font, item->x + 1, item->y + 1, item->InactiveText.get());
  117.         } else {
  118.                 if ( menu->Active)
  119.                         gr_ustring(canvas, *canvas.cv_font, item->x, item->y, item->Text.get());
  120.                 else
  121.                         gr_ustring(canvas, *canvas.cv_font, item->x, item->y, item->InactiveText.get());
  122.         }
  123. }
  124.  
  125. static void menu_draw(MENU *menu)
  126. {
  127.         int i;
  128.        
  129.         gr_set_default_canvas();
  130.         auto &canvas = *grd_curcanv;
  131.  
  132.         // Draw the menu background
  133.         gr_urect(canvas, menu->x, menu->y, menu->x + menu->w - 1, menu->y + menu->h - 1, CGREY);
  134.         if ( menu != &Menu[0] )
  135.         {
  136.                 gr_ubox(canvas, menu->x, menu->y, menu->x + menu->w - 1, menu->y + menu->h - 1, CBLACK);
  137.         }
  138.        
  139.         // Draw the items
  140.        
  141.         for (i=0; i< menu->NumItems; i++ )
  142.                 item_show( menu, i );
  143. }
  144.  
  145. //---------------------------- Show a menu ---------------------
  146.  
  147. static void menu_show( MENU * menu )
  148. {
  149.         if (!menu->wind)
  150.         {
  151.                 menu->wind = window_create(grd_curscreen->sc_canvas, menu->x, menu->y, menu->w, menu->h,
  152.                                                                    ((menu == &Menu[0]) ? menubar_handler : menu_handler), menu);
  153.                 if (!menu->wind)
  154.                         return;
  155.                
  156.                 if (menu == &Menu[0])
  157.                         window_set_modal(Menu[0].wind, 0);      // allow windows behind the menubar to accept events (e.g. the keypad dialogs)
  158.         }
  159.  
  160.         window_set_visible(menu->wind, 1);
  161.        
  162.         // Mark as displayed.
  163.         menu->Displayed = 1;
  164. }
  165.  
  166. //-------------------------- Hide a menu -----------------------
  167.  
  168. static void menu_hide( MENU * menu )
  169. {
  170.  
  171.         // Can't hide if it's not already drawn
  172.         if (!menu->Displayed) return;
  173.        
  174.         if ((menu != &Menu[0]) && menu->wind)
  175.                 window_set_visible(menu->wind, 0);      // don't draw or receive events
  176.  
  177.         menu->Active = 0;
  178.         if (Menu[0].wind && menu == &Menu[0])
  179.                 window_set_modal(Menu[0].wind, 0);
  180.                
  181.         // Mark as hidden.
  182.         menu->Displayed = 0;
  183. }
  184.  
  185.  
  186. //------------------------- Move the menu bar ------------------
  187.  
  188. static void menu_move_bar_to( MENU * menu, int number )
  189. {
  190.         int old_item;
  191.  
  192.         old_item = menu->CurrentItem;
  193.         menu->CurrentItem = number;
  194.        
  195.         if (menu->Displayed && (number != old_item))
  196.         {
  197.                 item_show( menu, old_item );
  198.                 item_show( menu, number );
  199.         }
  200. }
  201.  
  202. //------------------------ Match keypress to item ------------------
  203. static int menu_match_keypress( MENU * menu, int keypress )
  204. {
  205.         int i;
  206.         char c;
  207.         if ((keypress & KEY_CTRLED) || (keypress & KEY_SHIFTED))
  208.                 return -1;
  209.        
  210.         keypress &= 0xFF;
  211.  
  212.         c = key_ascii();
  213.                        
  214.         for (i=0; i< menu->NumItems; i++ )
  215.         {
  216.                 auto letter = strrchr(menu->Item[i].Text.get(), CC_UNDERLINE);
  217.                 if (letter)
  218.                 {
  219.                         letter++;
  220.                         if (c==tolower(*letter))
  221.                                 return i;
  222.                 }
  223.         }
  224.         return -1;
  225. }
  226.  
  227.  
  228. static int menu_is_mouse_on( ITEM * item )
  229. {
  230.         int x, y, z;
  231.        
  232.         mouse_get_pos(&x, &y, &z);
  233.  
  234.         if ((x >= item->x) &&
  235.                 (x < item->x + item->w ) &&
  236.                 (y >= item->y) &&
  237.                 (y <= item->y + item->h ) )
  238.                 return 1;
  239.         else
  240.                 return 0;
  241. }
  242.  
  243. static int menu_check_mouse_item( MENU * menu )
  244. {
  245.         int i;
  246.        
  247.         for (i=0; i<menu->NumItems; i++ )
  248.         {
  249.                 if (menu_is_mouse_on( &menu->Item[i] ))
  250.                 {
  251.                         if (menu->Item[i].Text[0] == '-')
  252.                                 return -1;
  253.                         else
  254.                                 return i;
  255.                 }
  256.         }
  257.         return -1;
  258. }
  259.  
  260.  
  261. static void menu_hide_all()
  262. {
  263.         range_for (auto &i, partial_range(Menu, 1u, num_menus))
  264.                 menu_hide(&i);
  265.        
  266.         Menu[0].ShowBar = 0;
  267.         Menu[0].Active = 0;
  268.         if (Menu[0].wind)
  269.                 window_set_modal(Menu[0].wind, 0);
  270.         menu_show( &Menu[0] );
  271.  
  272. }
  273.  
  274.  
  275. static int state2_alt_down;
  276.  
  277. static window_event_result do_state_0(const d_event &event)
  278. {
  279.         int keypress = 0;
  280.        
  281.         if (event.type == EVENT_KEY_COMMAND)
  282.                 keypress = event_key_get(event);
  283.        
  284.         Menu[0].Active = 0;
  285.         if (Menu[0].wind)
  286.                 window_set_modal(Menu[0].wind, 0);
  287.         Menu[0].ShowBar = 0;
  288.  
  289.         if ( keypress & KEY_ALTED )     {
  290.                 const auto i = menu_match_keypress( &Menu[0], keypress );
  291.                 if (i > -1 )
  292.                 {
  293.                         Menu[0].CurrentItem = i;
  294.                         Menu[0].Active = 0;
  295.                         if (Menu[0].wind)
  296.                                 window_set_modal(Menu[0].wind, 0);
  297.  
  298.                         state = 3;     
  299.                         Menu[ CMENU ].ShowBar = 1;
  300.                         Menu[ CMENU ].Active = 1;
  301.                         Menu[0].ShowBar = 1;
  302.        
  303.                         menu_show( &Menu[ CMENU ] );
  304.                         menu_show( &Menu[0] );
  305.                         return window_event_result::handled;
  306.                 }
  307.         }
  308.        
  309.         range_for (auto &i, partial_const_range(Menu, num_menus))
  310.                 range_for (auto &j, partial_const_range(i.Item, i.NumItems))
  311.                 {
  312.                         if ( j.Hotkey == keypress )
  313.                         {
  314.                                 if (j.user_function)
  315.                                         j.user_function();
  316.                                 return window_event_result::handled;
  317.                         }
  318.                 }
  319.                
  320.         if (keypress & KEY_ALTED)
  321.         //if ( (keypress & 0xFF) == KEY_LALT )
  322.         {
  323.                 // Make sure the menubar receives events exclusively
  324.                 state = 1;
  325.                 Menu[0].Active = 1;
  326.                
  327.                 // Put the menubar in front - hope this doesn't mess anything up by leaving it there
  328.                 // If it does, will need to remember the previous front window and restore it.
  329.                 // (Personally, I just use either the mouse or 'hotkeys' for menus)
  330.                 window_select(*Menu[0].wind);
  331.  
  332.                 window_set_modal(Menu[0].wind, 1);
  333.                 menu_show( &Menu[0] );
  334.                 return window_event_result::handled;
  335.         }
  336.  
  337.         const auto i = menu_check_mouse_item( &Menu[0] );
  338.  
  339.         if ( B1_JUST_PRESSED && (i > -1))
  340.         {
  341.                 Menu[0].CurrentItem = i;
  342.                 state = 3;     
  343.                 Menu[ CMENU ].ShowBar = 1;
  344.                 Menu[0].ShowBar = 1;
  345.                 Menu[ CMENU ].Active = 1;
  346.                 Menu[0].Active = 0;
  347.                 window_set_modal(Menu[0].wind, 0);
  348.                 menu_show( &Menu[ CMENU ] );
  349.                 menu_show( &Menu[0] );
  350.                 return window_event_result::handled;
  351.         }
  352.         return window_event_result::ignored;
  353. }
  354.  
  355. static window_event_result do_state_1(const d_event &event)
  356. {
  357.         int i;
  358.         int keypress = 0;
  359.         window_event_result rval = window_event_result::ignored;
  360.        
  361.         if (event.type == EVENT_KEY_COMMAND)
  362.                 keypress = event_key_get(event);
  363.        
  364.         if ((event.type == EVENT_KEY_RELEASE) && !(event_key_get(event) & KEY_ALTED))
  365.         {
  366.                 state = 2;
  367.                 state2_alt_down = 0;
  368.                 Menu[0].ShowBar = 1;
  369.                 Menu[0].Active = 1;
  370.                 menu_show( &Menu[0] );
  371.                 rval = window_event_result::handled;
  372.         }
  373.  
  374.         i = menu_match_keypress( &Menu[0], keypress );
  375.        
  376.         if (i > -1 )
  377.         {
  378.                 Menu[0].CurrentItem = i;
  379.                 Menu[0].Active = 0;
  380.                 window_set_modal(Menu[0].wind, 0);
  381.                 state = 3;     
  382.                 Menu[ CMENU ].ShowBar = 1;
  383.                 Menu[ CMENU ].Active = 1;
  384.                 Menu[0].ShowBar = 1;
  385.  
  386.                 menu_show( &Menu[ CMENU ] );
  387.                 menu_show( &Menu[0] );
  388.                 rval = window_event_result::handled;
  389.         }
  390.  
  391.         i = menu_check_mouse_item( &Menu[0] );
  392.  
  393.         if ( (i == -1) && B1_JUST_RELEASED )
  394.         {
  395.                 state = 0;
  396.                 menu_hide_all();
  397.                 return window_event_result::handled;
  398.         }
  399.  
  400.         if ( B1_JUST_PRESSED && (i > -1))
  401.         {
  402.                 Menu[0].CurrentItem = i;
  403.                 state = 3;     
  404.                 Menu[ CMENU ].ShowBar = 1;
  405.                 Menu[ CMENU ].Active = 1;
  406.                 Menu[0].ShowBar = 1;
  407.                 Menu[0].Active = 0;
  408.                 window_set_modal(Menu[0].wind, 0);
  409.                 menu_show( &Menu[ CMENU ] );
  410.                 menu_show( &Menu[0] );
  411.                 return window_event_result::handled;
  412.         }
  413.        
  414.         return rval;
  415. }
  416.  
  417. static window_event_result do_state_2(const d_event &event)
  418. {
  419.         int i;
  420.         int keypress = 0;
  421.         window_event_result rval = window_event_result::ignored;
  422.         if (event.type == EVENT_KEY_COMMAND)
  423.                 keypress = event_key_get(event);
  424.                
  425.         if (keypress & KEY_ALTED)
  426.         {
  427.                 state2_alt_down = 1;
  428.                 rval = window_event_result::handled;
  429.         }
  430.  
  431.         if ((event.type == EVENT_KEY_RELEASE) && !(event_key_get(event) & KEY_ALTED) && state2_alt_down)
  432.         {
  433.                 state = 0;
  434.                 menu_hide_all();
  435.                 rval = window_event_result::handled;
  436.         }                      
  437.  
  438.         switch( keypress )
  439.         {
  440.         case KEY_ESC:
  441.                 state = 0;
  442.                 menu_hide_all();
  443.                 return window_event_result::handled;
  444.         case KEY_LEFT:
  445.         case KEY_PAD4:
  446.                 i = Menu[0].CurrentItem-1;
  447.                 if (i < 0 ) i = Menu[0].NumItems-1;
  448.                 menu_move_bar_to( &Menu[0], i );
  449.                 return window_event_result::handled;
  450.         case KEY_RIGHT:
  451.         case KEY_PAD6:
  452.                 i = Menu[0].CurrentItem+1;
  453.                 if (i >= Menu[0].NumItems ) i = 0;
  454.                 menu_move_bar_to( &Menu[0], i );
  455.                 return window_event_result::handled;
  456.         case KEY_ENTER:
  457.         case KEY_PADENTER:
  458.         case KEY_DOWN:
  459.         case KEY_PAD2:
  460.                 state = 3;     
  461.                 Menu[ CMENU ].ShowBar = 1;
  462.                 Menu[ CMENU ].Active = 1;
  463.                 Menu[0].Active = 0;
  464.                 window_set_modal(Menu[0].wind, 0);
  465.                 menu_show( &Menu[ 0 ] );
  466.                 menu_show( &Menu[ CMENU ] );
  467.                 return window_event_result::handled;
  468.        
  469.         default:
  470.                 i = menu_match_keypress( &Menu[0], keypress );
  471.        
  472.                 if (i > -1 )
  473.                 {
  474.                         Menu[0].CurrentItem = i;
  475.                         Menu[0].Active = 0;
  476.                         window_set_modal(Menu[0].wind, 0);
  477.                         state = 3;     
  478.                         Menu[ CMENU ].ShowBar = 1;
  479.                         Menu[ CMENU ].Active = 1;
  480.                         Menu[0].ShowBar = 1;
  481.                         menu_show( &Menu[ CMENU ] );
  482.                         menu_show( &Menu[0] );
  483.                         return window_event_result::handled;
  484.                 }
  485.  
  486.                 i = menu_check_mouse_item( &Menu[0] );
  487.  
  488.                 if ( (i == -1) && B1_JUST_RELEASED )
  489.                 {
  490.                         state = 0;
  491.                         menu_hide_all();
  492.                         return window_event_result::handled;
  493.                 }
  494.  
  495.                 if (i > -1)
  496.                 {
  497.                         Menu[0].CurrentItem = i;
  498.                         Menu[0].Active = 0;
  499.                         window_set_modal(Menu[0].wind, 0);
  500.                         state = 3;     
  501.                         Menu[ CMENU ].ShowBar = 1;
  502.                         Menu[ CMENU ].Active = 1;
  503.                         Menu[0].ShowBar = 1;
  504.                         menu_show( &Menu[ CMENU ] );
  505.                         menu_show( &Menu[0] );
  506.                         return window_event_result::handled;
  507.                 }
  508.         }
  509.        
  510.         return rval;
  511. }
  512.  
  513.  
  514.  
  515. static window_event_result menu_handler(window *, const d_event &event, MENU *menu)
  516. {
  517.         int i;
  518.         int keypress = 0;
  519.        
  520.         if (state != 3)
  521.                 return window_event_result::ignored;
  522.        
  523.         if (event.type == EVENT_KEY_COMMAND)
  524.                 keypress = event_key_get(event);
  525.         else if (event.type == EVENT_WINDOW_CLOSE)      // quitting
  526.         {
  527.                 state = 0;
  528.                 menu_hide_all();
  529.                 menu->wind = nullptr;
  530.                 return window_event_result::ignored;
  531.         }
  532.         window_event_result rval = window_event_result::ignored;
  533.         switch( keypress )
  534.         {
  535.                 case 0:
  536.                         break;
  537.                 case KEY_ESC:
  538.                         state = 0;
  539.                         menu_hide_all();
  540.                         rval = window_event_result::handled;
  541.                         break;
  542.                 case KEY_DOWN:
  543.                 case KEY_PAD2:
  544.                         i = Menu[ CMENU ].CurrentItem;
  545.                         do {
  546.                                 i++;           
  547.                                 if ( i >= Menu[ CMENU ].NumItems )
  548.                                         i = 0;
  549.                         } while( Menu[CMENU].Item[i].Text[0] == '-');
  550.                         menu_move_bar_to( &Menu[ CMENU ], i );
  551.                         rval = window_event_result::handled;
  552.                         break;
  553.                 case KEY_UP:
  554.                 case KEY_PAD8:
  555.                         i = Menu[ CMENU ].CurrentItem;
  556.                         do
  557.                         {
  558.                                 i--;
  559.                                 if ( i < 0 )
  560.                                         i = Menu[ CMENU ].NumItems-1;
  561.                         } while( Menu[CMENU].Item[i].Text[0] == '-');
  562.                         menu_move_bar_to( &Menu[ CMENU ], i );
  563.                         rval = window_event_result::handled;
  564.                         break;
  565.                 case KEY_RIGHT:
  566.                 case KEY_PAD6:
  567.                         menu_hide( &Menu[ CMENU ] );
  568.                         i = Menu[0].CurrentItem+1;
  569.                         if (i >= Menu[0].NumItems ) i = 0;
  570.                         menu_move_bar_to( &Menu[0], i );
  571.                         Menu[CMENU].ShowBar = 1;
  572.                         Menu[CMENU].Active = 1;
  573.                         menu_show( &Menu[CMENU] );
  574.                         rval = window_event_result::handled;
  575.                         break;
  576.                 case KEY_LEFT:
  577.                 case KEY_PAD4:
  578.                         menu_hide( &Menu[ CMENU ] );
  579.                         i = Menu[0].CurrentItem-1;
  580.                         if (i < 0 ) i = Menu[0].NumItems-1;
  581.                         menu_move_bar_to( &Menu[0], i );
  582.                         Menu[ CMENU ].ShowBar = 1;
  583.                         Menu[CMENU].Active = 1;
  584.                         menu_show( &Menu[ CMENU ] );
  585.                         rval = window_event_result::handled;
  586.                         break;
  587.                 case KEY_ENTER:
  588.                 case KEY_PADENTER:
  589.                         state = 0;
  590.                         menu_hide_all();
  591.  
  592.                         if (Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function)
  593.                                 Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function();
  594.                        
  595.                         rval = window_event_result::handled;
  596.                         break;
  597.                        
  598.                 default:
  599.                         i = menu_match_keypress( &Menu[ CMENU ], keypress );
  600.  
  601.                         if (i > -1 )
  602.                         {
  603.                                 menu_move_bar_to( &Menu[ CMENU ], i );
  604.                                 state = 0;
  605.                                 menu_hide_all();
  606.                                                                
  607.                                 if (Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function)
  608.                                         Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function();
  609.                                 rval = window_event_result::handled;
  610.                                 break;
  611.                         }
  612.         }
  613.        
  614.         if (event.type == EVENT_MOUSE_MOVED || B1_JUST_RELEASED)
  615.         {
  616.                 i = menu_check_mouse_item( &Menu[CMENU] );
  617.                        
  618.                 if (i > -1 )
  619.                 {
  620.                         if ( B1_JUST_RELEASED )
  621.                         {
  622.                                 menu_move_bar_to( &Menu[ CMENU ], i );
  623.                                 state = 0;
  624.                                 menu_hide_all();
  625.                                                                
  626.                                 if (Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function)
  627.                                         Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function();
  628.                                 rval = window_event_result::handled;
  629.                         }
  630.                         else
  631.                         {
  632.                                 menu_move_bar_to( &Menu[ CMENU ], i );
  633.                                 rval = window_event_result::handled;
  634.                         }
  635.                 } else {
  636.                         i = menu_check_mouse_item( &Menu[0] );
  637.  
  638.                         if (i > -1)
  639.                         {
  640.                                 if ( Menu[0].CurrentItem != i)  {
  641.                                         menu_hide( &Menu[ CMENU ] );
  642.                                         menu_move_bar_to( &Menu[0], i );
  643.                                         Menu[ CMENU ].ShowBar = 1;
  644.                                         Menu[CMENU].Active = 1;
  645.                                         menu_show( &Menu[ CMENU ] );
  646.                                 }
  647.  
  648.                                 rval = window_event_result::handled;
  649.                         }
  650.  
  651.                         if ( B1_JUST_RELEASED )
  652.                         {
  653.                                 state = 0;
  654.                                 menu_hide_all();
  655.                                 rval = window_event_result::handled;
  656.                         }
  657.                 }
  658.         }
  659.        
  660.         if (event.type == EVENT_WINDOW_DRAW)
  661.         {
  662.                 menu_draw(&Menu[CMENU]);
  663.                 return window_event_result::handled;
  664.         }
  665.        
  666.         return rval;
  667. }
  668.  
  669. static window_event_result menubar_handler(window *, const d_event &event, MENU *)
  670. {
  671.         if (event.type == EVENT_WINDOW_DRAW)
  672.         {
  673.                 menu_draw(&Menu[0]);
  674.                 return window_event_result::handled;
  675.         }
  676.         else if (event.type == EVENT_WINDOW_CLOSE)
  677.         {
  678.                 //menu_hide_all();
  679.                 //menu_hide( &Menu[0] );
  680.                
  681.                 range_for (auto &i, partial_range(Menu, 1u, num_menus))
  682.                 {
  683.                         if (i.wind)
  684.                         {
  685.                                 window_close(std::exchange(i.wind, nullptr));
  686.                         }
  687.                 }
  688.                
  689.                 Menu[0].wind = nullptr;
  690.         }
  691.  
  692.         switch (state)
  693.         {
  694.                 case 0:
  695.                         return do_state_0(event);
  696.                 case 1:
  697.                         return do_state_1(event);
  698.                 case 2:
  699.                         return do_state_2(event);
  700.                 case 3:
  701.                         break;
  702.                 default:
  703.                         state = 0;
  704.                         menu_hide_all();
  705.         }
  706.         return window_event_result::ignored;
  707. }
  708.  
  709. static void CommaParse(uint_fast32_t n, char * dest, const PHYSFSX_gets_line_t<200>::line_t &source)
  710. {
  711.         int i = 0, j=0, cn = 0;
  712.  
  713.         // Go to the n'th comma
  714.         while (cn < n )
  715.                 if (source[i++] == ',' )
  716.                         cn++;
  717.         // Read all the whitespace
  718.         while ( source[i]==' ' || source[i]=='\t' || source[i]==13 || source[i]==10 )
  719.                 i++;
  720.  
  721.         // Read up until the next comma
  722.         while ( source[i] != ',' )
  723.         {
  724.                 dest[j] = source[i++];
  725.                 j++;           
  726.         }
  727.  
  728.         // Null-terminate      
  729.         dest[j++] = 0;
  730. }
  731.  
  732. //translate '&' characters to the underline character
  733. static void ul_xlate(char *s)
  734. {
  735.         while ((s=strchr(s,'&'))!=NULL)
  736.                 *s = CC_UNDERLINE;
  737. }
  738.  
  739.  
  740. int menubar_init(grs_canvas &canvas, const char *const file)
  741. {
  742.         int np;
  743.         char buf1[200];
  744.         char buf2[204];
  745.                
  746.         num_menus = state = 0;
  747.  
  748.         // This method should be faster than explicitly setting all the variables (I think)
  749.         Menu = {};
  750.  
  751.         range_for (auto &i, Menu)
  752.                 range_for (auto &j, i.Item)
  753.                         j.Hotkey = -1;
  754.         auto infile = PHYSFSX_openReadBuffered(file);
  755.  
  756.         if (!infile)
  757.         {
  758.                 Warning("Could not find %s\n", file);
  759.                 return 0;
  760.         }
  761.  
  762.         PHYSFSX_gets_line_t<200> buffer;
  763.         while ( PHYSFSX_fgets( buffer, infile) != NULL )
  764.         {
  765.                 if ( buffer[0] == ';' ) continue;
  766.                
  767.                 CommaParse( 0, buf1, buffer );
  768.                 char *p;
  769.                 const auto mi = strtoul(buf1, &p, 10);
  770.                 if (mi >= MAXMENUS)
  771.                 {
  772.                         con_printf(CON_URGENT, "%s:%u: too many menus defined: max=%u, requested=%lu in \"%s\"", __FILE__, __LINE__, MAXMENUS, mi, file);
  773.                         break;
  774.                 }
  775.  
  776.                 CommaParse( 1, buf1, buffer );
  777.                 const auto ii = strtoul(buf1, &p, 10);
  778.                 if (ii >= MAXITEMS)
  779.                 {
  780.                         con_printf(CON_URGENT, "%s:%u: too many items defined: max=%u, requested=%lu in \"%s\"", __FILE__, __LINE__, MAXITEMS, ii, file);
  781.                         break;
  782.                 }
  783.                 auto &menu = Menu[mi];
  784.                 auto &item = menu.Item[ii];
  785.  
  786.                 CommaParse( 2, buf1, buffer );
  787.                 ul_xlate(buf1);
  788.  
  789.                 item.Text.reset(d_strdup(buf1[0] == '-' ? buf1 : (snprintf(buf2, sizeof(buf2), " %.197s ", buf1), buf2)));
  790.                
  791.                 item.InactiveText.reset(d_strdup(item.Text.get()));
  792.                
  793.                 for (int i = 0, j = 0;; i++ )
  794.                 {
  795.                         np = item.Text[i];
  796.                         if (np != CC_UNDERLINE)
  797.                                 item.InactiveText[j++] = np;
  798.                         if (!np)
  799.                                 break;
  800.                 }
  801.  
  802.                 CommaParse( 3, buf1, buffer );
  803.                 if (buf1[0]=='{' && buf1[1] =='}')
  804.                         item.Hotkey = -1;
  805.                 else                    {
  806.                         const auto i = DecodeKeyText(buf1);
  807.                         if (i<1) {
  808.                                 UserError("Unknown key, %s, in %s\n", buf1, file );
  809.                         } else {
  810.                                 item.Hotkey = i;
  811.                         }
  812.                 }
  813.                 CommaParse( 4, buf1, buffer );
  814.  
  815.                 if (buf1[0])
  816.                 {
  817.                         item.user_function = func_get(buf1, &np);
  818.  
  819.                         if (!item.user_function)
  820.                         {
  821.                                 con_printf(CON_URGENT, "%s:%u: unknown function \"%s\" in \"%s\"", __FILE__, __LINE__, buf1, file);
  822.                         }
  823.                 }
  824.                                
  825.                 item.x = menu.x;
  826.                 item.y = menu.y;
  827.  
  828.                 int w, h;
  829.                 if (item.Text[0] == '-')
  830.                 {
  831.                         w = 1; h = 3;
  832.                 } else {
  833.                         gr_get_string_size(*canvas.cv_font, item.Text.get(), &w, &h, nullptr);
  834.                         w += 2;
  835.                         h += 2;
  836.                 }
  837.                                                                
  838.                 if (mi == 0)
  839.                 {
  840.                         menu.h = h;
  841.  
  842.                         item.x = menu.x + menu.w;
  843.  
  844.                         item.y = menu.y;
  845.                        
  846.                         Menu[ii+1].x = menu.x + menu.w;
  847.                         Menu[ii+1].y = menu.h - 2;
  848.  
  849.                         item.w = w;
  850.                         item.h = h;
  851.  
  852.                         menu.w += w;
  853.  
  854.                 }else   {
  855.                         if (w > menu.w)
  856.                         {
  857.                                 menu.w = w;
  858.                                 range_for (auto &i, partial_range(menu.Item, menu.NumItems))
  859.                                         i.w = w;
  860.                         }
  861.                         item.w = menu.w;
  862.                         item.x = menu.x;
  863.                         item.y = menu.y+menu.h;
  864.                         item.h = h;
  865.                         menu.h += h;
  866.                 }
  867.        
  868.                 if (ii >= menu.NumItems)
  869.                 {
  870.                         menu.NumItems = ii + 1;
  871.                 }
  872.  
  873.                 if (mi >= num_menus)
  874.                         num_menus = mi + 1;
  875.  
  876.         }
  877.         Menu[0].w = 700;
  878.  
  879.         return 1;
  880. }
  881.  
  882. void menubar_hide()
  883. {
  884.         state = 0;
  885.         menu_hide_all();
  886.         menu_hide( &Menu[0] );
  887. }
  888.  
  889. void menubar_show()
  890. {
  891.         menu_show( &Menu[0] );
  892. }
  893.  
  894. void menubar_close()
  895. {
  896.         if (!Menu[0].wind)
  897.                 return;
  898.         window_close(std::exchange(Menu[0].wind, nullptr));
  899. }
  900.  
  901. }
  902.