1c2e1c6946
The allocated block is part of the dmem subsystem, so return a type that reflects that.
960 lines
20 KiB
C++
960 lines
20 KiB
C++
/*
|
|
* Portions of this file are copyright Rebirth contributors and licensed as
|
|
* described in COPYING.txt.
|
|
* Portions of this file are copyright Parallax Software and licensed
|
|
* according to the Parallax license below.
|
|
* See COPYING.txt for license details.
|
|
|
|
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
|
|
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
|
|
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
|
|
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
|
|
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
|
|
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
|
|
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
|
|
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
|
|
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
|
|
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include "physfsx.h"
|
|
#include "u_mem.h"
|
|
#include "maths.h"
|
|
#include "pstypes.h"
|
|
#include "gr.h"
|
|
#include "strutil.h"
|
|
#include "event.h"
|
|
#include "window.h"
|
|
#include "ui.h"
|
|
#include "mouse.h"
|
|
#include "key.h"
|
|
#include "func.h"
|
|
#include "dxxerror.h"
|
|
|
|
#include "compiler-range_for.h"
|
|
#include "d_enumerate.h"
|
|
#include "d_range.h"
|
|
#include "partial_range.h"
|
|
#include <utility>
|
|
|
|
namespace dcx {
|
|
|
|
namespace {
|
|
|
|
struct ITEM {
|
|
short x = 0, y = 0, w = 0, h = 0;
|
|
RAIIdmem<char[]> Text;
|
|
RAIIdmem<char[]> InactiveText;
|
|
short Hotkey = -1;
|
|
int (*user_function)(void) = nullptr;
|
|
};
|
|
|
|
struct MENU {
|
|
window *wind = nullptr;
|
|
short x = 0, y = 0, w = 0, h = 0;
|
|
uint8_t ShowBar = 0;
|
|
uint8_t CurrentItem = 0;
|
|
uint16_t NumItems = 0;
|
|
uint8_t Displayed = 0;
|
|
uint8_t Active = 0;
|
|
std::array<ITEM, 32> Item;
|
|
};
|
|
|
|
struct menubar_window : window
|
|
{
|
|
MENU &menubar;
|
|
explicit menubar_window(grs_canvas &src, const int x, const int y, const int w, const int h, MENU &menu) :
|
|
window(src, x, y, w, h),
|
|
menubar(menu)
|
|
{
|
|
}
|
|
virtual window_event_result event_handler(const d_event &) override;
|
|
};
|
|
|
|
struct menu_window : window
|
|
{
|
|
MENU &menu;
|
|
explicit menu_window(grs_canvas &src, const int x, const int y, const int w, const int h, MENU &menu) :
|
|
window(src, x, y, w, h),
|
|
menu(menu)
|
|
{
|
|
}
|
|
virtual window_event_result event_handler(const d_event &) override;
|
|
};
|
|
|
|
static std::array<MENU *, 30> Menu;
|
|
|
|
static unsigned num_menus;
|
|
static int state;
|
|
|
|
#define CMENU (Menu[0]->CurrentItem+1)
|
|
|
|
//------------------------- Show a menu item -------------------
|
|
|
|
static void item_show(const MENU &menu, const int n)
|
|
{
|
|
const auto &item = menu.Item[n];
|
|
if (!item.Text)
|
|
return;
|
|
|
|
gr_set_default_canvas();
|
|
auto &canvas = *grd_curcanv;
|
|
// If this is a seperator, then draw it.
|
|
if (item.Text[0] == '-')
|
|
{
|
|
const auto color = CBLACK;
|
|
gr_urect(canvas, item.x, item.y + item.h / 2, item.x + item.w - 1, item.y + item.h / 2, color);
|
|
return;
|
|
}
|
|
|
|
if (menu.CurrentItem == n && menu.ShowBar)
|
|
{
|
|
if (&menu != Menu[0])
|
|
{
|
|
const auto color = CBLACK;
|
|
gr_urect(canvas, item.x + 1, item.y + 1, item.x + menu.w - 2, item.y + item.h - 2, color);
|
|
}
|
|
gr_set_fontcolor(canvas, CWHITE, CBLACK);
|
|
}else {
|
|
if (&menu != Menu[0])
|
|
{
|
|
const auto color = CGREY;
|
|
gr_urect(canvas, item.x + 1, item.y + 1, item.x + menu.w - 2, item.y + item.h - 2, color);
|
|
}
|
|
gr_set_fontcolor(canvas, CBLACK, CGREY);
|
|
}
|
|
|
|
if (&menu != Menu[0])
|
|
{
|
|
if (menu.Active)
|
|
gr_ustring(canvas, *canvas.cv_font, item.x + 1, item.y + 1, item.Text.get());
|
|
else
|
|
gr_ustring(canvas, *canvas.cv_font, item.x + 1, item.y + 1, item.InactiveText.get());
|
|
} else {
|
|
if (menu.Active)
|
|
gr_ustring(canvas, *canvas.cv_font, item.x, item.y, item.Text.get());
|
|
else
|
|
gr_ustring(canvas, *canvas.cv_font, item.x, item.y, item.InactiveText.get());
|
|
}
|
|
}
|
|
|
|
static void menu_draw(const MENU &menu)
|
|
{
|
|
gr_set_default_canvas();
|
|
auto &canvas = *grd_curcanv;
|
|
|
|
// Draw the menu background
|
|
gr_urect(canvas, menu.x, menu.y, menu.x + menu.w - 1, menu.y + menu.h - 1, CGREY);
|
|
if (&menu != Menu[0])
|
|
{
|
|
gr_ubox(canvas, menu.x, menu.y, menu.x + menu.w - 1, menu.y + menu.h - 1, CBLACK);
|
|
}
|
|
|
|
// Draw the items
|
|
|
|
for (const auto i : xrange(menu.NumItems))
|
|
item_show( menu, i );
|
|
}
|
|
|
|
//---------------------------- Show a menu ---------------------
|
|
|
|
static void menu_show(MENU &menu)
|
|
{
|
|
menu.wind->set_visible(1);
|
|
// Mark as displayed.
|
|
menu.Displayed = 1;
|
|
}
|
|
|
|
static void menu_other_show()
|
|
{
|
|
auto &menu = *Menu[CMENU];
|
|
if (!menu.wind)
|
|
{
|
|
menu.wind = window_create<menu_window>(grd_curscreen->sc_canvas, menu.x, menu.y, menu.w, menu.h, menu);
|
|
}
|
|
menu_show(menu);
|
|
}
|
|
|
|
//-------------------------- Hide a menu -----------------------
|
|
|
|
static void menu_hide(MENU &menu)
|
|
{
|
|
// Can't hide if it's not already drawn
|
|
if (!menu.Displayed) return;
|
|
|
|
if (&menu != Menu[0] && menu.wind)
|
|
menu.wind->set_visible(0); // don't draw or receive events
|
|
|
|
menu.Active = 0;
|
|
if (&menu == Menu[0] && menu.wind)
|
|
menu.wind->set_modal(0);
|
|
// Mark as hidden.
|
|
menu.Displayed = 0;
|
|
}
|
|
|
|
|
|
//------------------------- Move the menu bar ------------------
|
|
|
|
static void menu_move_bar_to(MENU &menu, const int number)
|
|
{
|
|
const auto old_item = menu.CurrentItem;
|
|
menu.CurrentItem = number;
|
|
|
|
if (menu.Displayed && number != old_item)
|
|
{
|
|
item_show( menu, old_item );
|
|
item_show( menu, number );
|
|
}
|
|
}
|
|
|
|
//------------------------ Match keypress to item ------------------
|
|
static int menu_match_keypress(MENU &menu, int keypress)
|
|
{
|
|
char c;
|
|
if ((keypress & KEY_CTRLED) || (keypress & KEY_SHIFTED))
|
|
return -1;
|
|
|
|
keypress &= 0xFF;
|
|
|
|
c = key_ascii();
|
|
|
|
for (auto &&[i, item] : enumerate(partial_range(menu.Item, menu.NumItems)))
|
|
{
|
|
auto letter = strrchr(item.Text.get(), CC_UNDERLINE);
|
|
if (letter)
|
|
{
|
|
letter++;
|
|
if (c==tolower(*letter))
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int menu_is_mouse_on(const ITEM &item)
|
|
{
|
|
int x, y, z;
|
|
|
|
mouse_get_pos(&x, &y, &z);
|
|
|
|
if (x >= item.x &&
|
|
x < item.x + item.w &&
|
|
y >= item.y &&
|
|
y <= item.y + item.h)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int menu_check_mouse_item(const MENU &menu)
|
|
{
|
|
for (auto &&[i, item] : enumerate(partial_range(menu.Item, menu.NumItems)))
|
|
{
|
|
if (menu_is_mouse_on(item))
|
|
{
|
|
if (item.Text[0] == '-')
|
|
return -1;
|
|
else
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
static void menu_hide_all()
|
|
{
|
|
range_for (auto &i, partial_range(Menu, 1u, num_menus))
|
|
menu_hide(*i);
|
|
auto &menubar = *Menu[0];
|
|
menubar.ShowBar = 0;
|
|
menubar.Active = 0;
|
|
if (menubar.wind)
|
|
menubar.wind->set_modal(0);
|
|
menubar_show();
|
|
}
|
|
|
|
|
|
static int state2_alt_down;
|
|
|
|
static window_event_result do_state_0(const d_event &event)
|
|
{
|
|
int keypress = 0;
|
|
|
|
if (event.type == EVENT_KEY_COMMAND)
|
|
keypress = event_key_get(event);
|
|
|
|
auto &menubar = *Menu[0];
|
|
menubar.Active = 0;
|
|
if (menubar.wind)
|
|
menubar.wind->set_modal(0);
|
|
menubar.ShowBar = 0;
|
|
|
|
if ( keypress & KEY_ALTED ) {
|
|
const auto i = menu_match_keypress(menubar, keypress);
|
|
if (i > -1 )
|
|
{
|
|
auto &menubar = *Menu[0];
|
|
menubar.CurrentItem = i;
|
|
menubar.Active = 0;
|
|
if (menubar.wind)
|
|
menubar.wind->set_modal(0);
|
|
|
|
state = 3;
|
|
auto &other_menu = *Menu[CMENU];
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menubar.ShowBar = 1;
|
|
|
|
menu_other_show();
|
|
menubar_show();
|
|
return window_event_result::handled;
|
|
}
|
|
}
|
|
|
|
range_for (auto &i, partial_const_range(Menu, num_menus))
|
|
for (auto &j : partial_const_range(i->Item, i->NumItems))
|
|
{
|
|
if ( j.Hotkey == keypress )
|
|
{
|
|
if (j.user_function)
|
|
j.user_function();
|
|
return window_event_result::handled;
|
|
}
|
|
}
|
|
|
|
if (keypress & KEY_ALTED)
|
|
//if ( (keypress & 0xFF) == KEY_LALT )
|
|
{
|
|
// Make sure the menubar receives events exclusively
|
|
state = 1;
|
|
menubar.Active = 1;
|
|
|
|
// Put the menubar in front - hope this doesn't mess anything up by leaving it there
|
|
// If it does, will need to remember the previous front window and restore it.
|
|
// (Personally, I just use either the mouse or 'hotkeys' for menus)
|
|
window_select(*menubar.wind);
|
|
|
|
menubar.wind->set_modal(1);
|
|
menubar_show();
|
|
return window_event_result::handled;
|
|
}
|
|
|
|
const auto i = menu_check_mouse_item(menubar);
|
|
|
|
if ( B1_JUST_PRESSED && (i > -1))
|
|
{
|
|
menubar.CurrentItem = i;
|
|
state = 3;
|
|
auto &other_menu = *Menu[CMENU];
|
|
other_menu.ShowBar = 1;
|
|
menubar.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menubar.Active = 0;
|
|
menubar.wind->set_modal(0);
|
|
menu_other_show();
|
|
menubar_show();
|
|
return window_event_result::handled;
|
|
}
|
|
return window_event_result::ignored;
|
|
}
|
|
|
|
static window_event_result do_state_1(const d_event &event)
|
|
{
|
|
int keypress = 0;
|
|
window_event_result rval = window_event_result::ignored;
|
|
|
|
if (event.type == EVENT_KEY_COMMAND)
|
|
keypress = event_key_get(event);
|
|
auto &menubar = *Menu[0];
|
|
if ((event.type == EVENT_KEY_RELEASE) && !(event_key_get(event) & KEY_ALTED))
|
|
{
|
|
state = 2;
|
|
state2_alt_down = 0;
|
|
menubar.ShowBar = 1;
|
|
menubar.Active = 1;
|
|
menubar_show();
|
|
rval = window_event_result::handled;
|
|
}
|
|
|
|
if (const auto i = menu_match_keypress(menubar, keypress); i > -1)
|
|
{
|
|
menubar.CurrentItem = i;
|
|
menubar.Active = 0;
|
|
menubar.wind->set_modal(0);
|
|
state = 3;
|
|
auto &other_menu = *Menu[CMENU];
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menubar.ShowBar = 1;
|
|
|
|
menu_other_show();
|
|
menubar_show();
|
|
rval = window_event_result::handled;
|
|
}
|
|
|
|
const auto i = menu_check_mouse_item(menubar);
|
|
|
|
if ( (i == -1) && B1_JUST_RELEASED )
|
|
{
|
|
state = 0;
|
|
menu_hide_all();
|
|
return window_event_result::handled;
|
|
}
|
|
|
|
if ( B1_JUST_PRESSED && (i > -1))
|
|
{
|
|
menubar.CurrentItem = i;
|
|
state = 3;
|
|
auto &other_menu = *Menu[CMENU];
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menubar.ShowBar = 1;
|
|
menubar.Active = 0;
|
|
menubar.wind->set_modal(0);
|
|
menu_other_show();
|
|
menubar_show();
|
|
return window_event_result::handled;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static window_event_result do_state_2(const d_event &event)
|
|
{
|
|
int i;
|
|
int keypress = 0;
|
|
window_event_result rval = window_event_result::ignored;
|
|
if (event.type == EVENT_KEY_COMMAND)
|
|
keypress = event_key_get(event);
|
|
|
|
if (keypress & KEY_ALTED)
|
|
{
|
|
state2_alt_down = 1;
|
|
rval = window_event_result::handled;
|
|
}
|
|
|
|
if ((event.type == EVENT_KEY_RELEASE) && !(event_key_get(event) & KEY_ALTED) && state2_alt_down)
|
|
{
|
|
state = 0;
|
|
menu_hide_all();
|
|
rval = window_event_result::handled;
|
|
}
|
|
|
|
switch( keypress )
|
|
{
|
|
case KEY_ESC:
|
|
state = 0;
|
|
menu_hide_all();
|
|
return window_event_result::handled;
|
|
case KEY_LEFT:
|
|
case KEY_PAD4:
|
|
{
|
|
auto &menubar = *Menu[0];
|
|
i = menubar.CurrentItem;
|
|
if (i <= 0)
|
|
i = menubar.NumItems;
|
|
menu_move_bar_to(menubar, i - 1);
|
|
return window_event_result::handled;
|
|
}
|
|
case KEY_RIGHT:
|
|
case KEY_PAD6:
|
|
{
|
|
auto &menubar = *Menu[0];
|
|
i = menubar.CurrentItem + 1;
|
|
if (i >= menubar.NumItems)
|
|
i = 0;
|
|
menu_move_bar_to(menubar, i);
|
|
return window_event_result::handled;
|
|
}
|
|
case KEY_ENTER:
|
|
case KEY_PADENTER:
|
|
case KEY_DOWN:
|
|
case KEY_PAD2:
|
|
state = 3;
|
|
{
|
|
auto &other_menu = *Menu[CMENU];
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
}
|
|
{
|
|
auto &menubar = *Menu[0];
|
|
menubar.Active = 0;
|
|
menubar.wind->set_modal(0);
|
|
}
|
|
menubar_show();
|
|
menu_other_show();
|
|
return window_event_result::handled;
|
|
|
|
default:
|
|
{
|
|
auto &menubar = *Menu[0];
|
|
i = menu_match_keypress(menubar, keypress);
|
|
|
|
if (i > -1 )
|
|
{
|
|
menubar.CurrentItem = i;
|
|
menubar.Active = 0;
|
|
menubar.wind->set_modal(0);
|
|
state = 3;
|
|
auto &other_menu = *Menu[CMENU];
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menubar.ShowBar = 1;
|
|
menu_other_show();
|
|
menubar_show();
|
|
return window_event_result::handled;
|
|
}
|
|
|
|
i = menu_check_mouse_item(menubar);
|
|
|
|
if ( (i == -1) && B1_JUST_RELEASED )
|
|
{
|
|
state = 0;
|
|
menu_hide_all();
|
|
return window_event_result::handled;
|
|
}
|
|
|
|
if (i > -1)
|
|
{
|
|
menubar.CurrentItem = i;
|
|
menubar.Active = 0;
|
|
menubar.wind->set_modal(0);
|
|
state = 3;
|
|
auto &other_menu = *Menu[CMENU];
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menubar.ShowBar = 1;
|
|
menu_other_show();
|
|
menubar_show();
|
|
return window_event_result::handled;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
window_event_result menu_window::event_handler(const d_event &event)
|
|
{
|
|
int keypress = 0;
|
|
|
|
if (state != 3)
|
|
return window_event_result::ignored;
|
|
|
|
if (event.type == EVENT_KEY_COMMAND)
|
|
keypress = event_key_get(event);
|
|
else if (event.type == EVENT_WINDOW_CLOSE) // quitting
|
|
{
|
|
state = 0;
|
|
menu_hide_all();
|
|
menu.wind = nullptr;
|
|
return window_event_result::ignored;
|
|
}
|
|
window_event_result rval = window_event_result::ignored;
|
|
switch( keypress )
|
|
{
|
|
case 0:
|
|
break;
|
|
case KEY_ESC:
|
|
state = 0;
|
|
menu_hide_all();
|
|
rval = window_event_result::handled;
|
|
break;
|
|
case KEY_DOWN:
|
|
case KEY_PAD2:
|
|
{
|
|
auto &other_menu = *Menu[CMENU];
|
|
auto i = other_menu.CurrentItem;
|
|
do {
|
|
i++;
|
|
if (i >= other_menu.NumItems)
|
|
i = 0;
|
|
} while(other_menu.Item[i].Text[0] == '-');
|
|
menu_move_bar_to(other_menu, i);
|
|
rval = window_event_result::handled;
|
|
break;
|
|
}
|
|
case KEY_UP:
|
|
case KEY_PAD8:
|
|
{
|
|
auto &other_menu = *Menu[CMENU];
|
|
auto i = other_menu.CurrentItem;
|
|
do
|
|
{
|
|
if (i <= 0)
|
|
i = other_menu.NumItems;
|
|
--i;
|
|
} while(other_menu.Item[i].Text[0] == '-');
|
|
menu_move_bar_to(other_menu, i);
|
|
rval = window_event_result::handled;
|
|
break;
|
|
}
|
|
case KEY_RIGHT:
|
|
case KEY_PAD6:
|
|
{
|
|
auto &other_menu = *Menu[CMENU];
|
|
menu_hide(other_menu);
|
|
auto &menubar = *Menu[0];
|
|
auto i = menubar.CurrentItem + 1;
|
|
if (i >= menubar.NumItems)
|
|
i = 0;
|
|
menu_move_bar_to(menubar, i);
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menu_other_show();
|
|
rval = window_event_result::handled;
|
|
break;
|
|
}
|
|
case KEY_LEFT:
|
|
case KEY_PAD4:
|
|
{
|
|
auto &other_menu = *Menu[CMENU];
|
|
menu_hide(other_menu);
|
|
auto &menubar = *Menu[0];
|
|
auto i = menubar.CurrentItem;
|
|
if (i <= 0)
|
|
i = menubar.NumItems;
|
|
--i;
|
|
menu_move_bar_to(menubar, i);
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menu_other_show();
|
|
rval = window_event_result::handled;
|
|
break;
|
|
}
|
|
case KEY_ENTER:
|
|
case KEY_PADENTER:
|
|
{
|
|
state = 0;
|
|
menu_hide_all();
|
|
auto &other_menu = *Menu[CMENU];
|
|
|
|
if (const auto f = other_menu.Item[other_menu.CurrentItem].user_function)
|
|
f();
|
|
|
|
rval = window_event_result::handled;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
auto &other_menu = *Menu[CMENU];
|
|
const auto i = menu_match_keypress(other_menu, keypress);
|
|
if (i > -1 )
|
|
{
|
|
menu_move_bar_to(other_menu, i);
|
|
state = 0;
|
|
menu_hide_all();
|
|
|
|
if (const auto f = other_menu.Item[other_menu.CurrentItem].user_function)
|
|
f();
|
|
rval = window_event_result::handled;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (event.type == EVENT_MOUSE_MOVED || B1_JUST_RELEASED)
|
|
{
|
|
auto &other_menu = *Menu[CMENU];
|
|
const auto i = menu_check_mouse_item(other_menu);
|
|
|
|
if (i > -1 )
|
|
{
|
|
if ( B1_JUST_RELEASED )
|
|
{
|
|
menu_move_bar_to(other_menu, i);
|
|
state = 0;
|
|
menu_hide_all();
|
|
|
|
if (const auto f = other_menu.Item[other_menu.CurrentItem].user_function)
|
|
f();
|
|
rval = window_event_result::handled;
|
|
}
|
|
else
|
|
{
|
|
menu_move_bar_to(other_menu, i);
|
|
rval = window_event_result::handled;
|
|
}
|
|
} else {
|
|
auto &menubar = *Menu[0];
|
|
const auto i = menu_check_mouse_item(menubar);
|
|
|
|
if (i > -1)
|
|
{
|
|
if (menubar.CurrentItem != i)
|
|
{
|
|
menu_hide(other_menu);
|
|
menu_move_bar_to(menubar, i);
|
|
other_menu.ShowBar = 1;
|
|
other_menu.Active = 1;
|
|
menu_other_show();
|
|
}
|
|
rval = window_event_result::handled;
|
|
}
|
|
|
|
if ( B1_JUST_RELEASED )
|
|
{
|
|
state = 0;
|
|
menu_hide_all();
|
|
rval = window_event_result::handled;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (event.type == EVENT_WINDOW_DRAW)
|
|
{
|
|
auto &other_menu = *Menu[CMENU];
|
|
menu_draw(other_menu);
|
|
return window_event_result::handled;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
window_event_result menubar_window::event_handler(const d_event &event)
|
|
{
|
|
if (event.type == EVENT_WINDOW_DRAW)
|
|
{
|
|
menu_draw(menubar);
|
|
return window_event_result::handled;
|
|
}
|
|
else if (event.type == EVENT_WINDOW_CLOSE)
|
|
{
|
|
range_for (const auto pi, partial_range(Menu, 1u, num_menus))
|
|
{
|
|
auto &i = *pi;
|
|
if (i.wind)
|
|
{
|
|
window_close(std::exchange(i.wind, nullptr));
|
|
}
|
|
}
|
|
menubar.wind = nullptr;
|
|
}
|
|
|
|
switch (state)
|
|
{
|
|
case 0:
|
|
return do_state_0(event);
|
|
case 1:
|
|
return do_state_1(event);
|
|
case 2:
|
|
return do_state_2(event);
|
|
case 3:
|
|
break;
|
|
default:
|
|
state = 0;
|
|
menu_hide_all();
|
|
}
|
|
return window_event_result::ignored;
|
|
}
|
|
|
|
static void CommaParse(uint_fast32_t n, char * dest, const PHYSFSX_gets_line_t<200>::line_t &source)
|
|
{
|
|
int i = 0, j=0, cn = 0;
|
|
|
|
// Go to the n'th comma
|
|
while (cn < n )
|
|
if (source[i++] == ',' )
|
|
cn++;
|
|
// Read all the whitespace
|
|
while ( source[i]==' ' || source[i]=='\t' || source[i]==13 || source[i]==10 )
|
|
i++;
|
|
|
|
// Read up until the next comma
|
|
while ( source[i] != ',' )
|
|
{
|
|
dest[j] = source[i++];
|
|
j++;
|
|
}
|
|
|
|
// Null-terminate
|
|
dest[j++] = 0;
|
|
}
|
|
|
|
//translate '&' characters to the underline character
|
|
static void ul_xlate(char *s)
|
|
{
|
|
while ((s=strchr(s,'&'))!=NULL)
|
|
*s = CC_UNDERLINE;
|
|
}
|
|
|
|
static MENU &menu_allocate(MENU *& p)
|
|
{
|
|
if (p)
|
|
return *p;
|
|
p = new MENU;
|
|
return *p;
|
|
}
|
|
|
|
}
|
|
|
|
int menubar_init(grs_canvas &canvas, const char *const file)
|
|
{
|
|
int np;
|
|
char buf1[200];
|
|
char buf2[204];
|
|
|
|
num_menus = state = 0;
|
|
|
|
// This method should be faster than explicitly setting all the variables (I think)
|
|
Menu = {};
|
|
|
|
auto &&[infile, physfserr] = PHYSFSX_openReadBuffered(file);
|
|
if (!infile)
|
|
{
|
|
Warning("Menubar: failed to open \"%s\": %s\n", file, PHYSFS_getErrorByCode(physfserr));
|
|
return 0;
|
|
}
|
|
|
|
PHYSFSX_gets_line_t<200> buffer;
|
|
while ( PHYSFSX_fgets( buffer, infile) != NULL )
|
|
{
|
|
if ( buffer[0] == ';' ) continue;
|
|
|
|
CommaParse( 0, buf1, buffer );
|
|
char *p;
|
|
const auto mi = strtoul(buf1, &p, 10);
|
|
if (mi >= Menu.size())
|
|
{
|
|
con_printf(CON_URGENT, "%s:%u: too many menus defined: max=%" DXX_PRI_size_type ", requested=%lu in \"%s\"", __FILE__, __LINE__, Menu.size(), mi, file);
|
|
break;
|
|
}
|
|
|
|
CommaParse( 1, buf1, buffer );
|
|
const auto ii = strtoul(buf1, &p, 10);
|
|
auto &menu = menu_allocate(Menu[mi]);
|
|
if (ii >= menu.Item.size())
|
|
{
|
|
con_printf(CON_URGENT, "%s:%u: too many items defined: max=%" DXX_PRI_size_type ", requested=%lu in \"%s\"", __FILE__, __LINE__, menu.Item.size(), ii, file);
|
|
break;
|
|
}
|
|
auto &item = menu.Item[ii];
|
|
|
|
CommaParse( 2, buf1, buffer );
|
|
ul_xlate(buf1);
|
|
|
|
item.Text = d_strdup(buf1[0] == '-' ? buf1 : (snprintf(buf2, sizeof(buf2), " %.197s ", buf1), buf2));
|
|
item.InactiveText = d_strdup(item.Text.get());
|
|
for (int i = 0, j = 0;; i++ )
|
|
{
|
|
np = item.Text[i];
|
|
if (np != CC_UNDERLINE)
|
|
item.InactiveText[j++] = np;
|
|
if (!np)
|
|
break;
|
|
}
|
|
|
|
CommaParse( 3, buf1, buffer );
|
|
if (buf1[0]=='{' && buf1[1] =='}')
|
|
item.Hotkey = -1;
|
|
else {
|
|
const auto i = DecodeKeyText(buf1);
|
|
if (i<1) {
|
|
UserError("Unknown key, %s, in %s\n", buf1, file );
|
|
} else {
|
|
item.Hotkey = i;
|
|
}
|
|
}
|
|
CommaParse( 4, buf1, buffer );
|
|
|
|
if (buf1[0])
|
|
{
|
|
item.user_function = func_get(buf1, &np);
|
|
|
|
if (!item.user_function)
|
|
{
|
|
con_printf(CON_URGENT, "%s:%u: unknown function \"%s\" in \"%s\"", __FILE__, __LINE__, buf1, file);
|
|
}
|
|
}
|
|
|
|
item.x = menu.x;
|
|
item.y = menu.y;
|
|
|
|
int w, h;
|
|
if (item.Text[0] == '-')
|
|
{
|
|
w = 1; h = 3;
|
|
} else {
|
|
const auto r = gr_get_string_size(*canvas.cv_font, item.Text.get());
|
|
w = r.width + 2;
|
|
h = r.height + 2;
|
|
}
|
|
|
|
if (mi == 0)
|
|
{
|
|
menu.h = h;
|
|
|
|
item.x = menu.x + menu.w;
|
|
|
|
item.y = menu.y;
|
|
|
|
auto &next_menu = menu_allocate(Menu[ii + 1]);
|
|
next_menu.x = menu.x + menu.w;
|
|
next_menu.y = menu.h - 2;
|
|
|
|
item.w = w;
|
|
item.h = h;
|
|
|
|
menu.w += w;
|
|
|
|
}else {
|
|
if (w > menu.w)
|
|
{
|
|
menu.w = w;
|
|
range_for (auto &i, partial_range(menu.Item, menu.NumItems))
|
|
i.w = w;
|
|
}
|
|
item.w = menu.w;
|
|
item.x = menu.x;
|
|
item.y = menu.y+menu.h;
|
|
item.h = h;
|
|
menu.h += h;
|
|
}
|
|
|
|
if (ii >= menu.NumItems)
|
|
{
|
|
menu.NumItems = ii + 1;
|
|
}
|
|
|
|
if (mi >= num_menus)
|
|
num_menus = mi + 1;
|
|
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void menubar_hide()
|
|
{
|
|
state = 0;
|
|
menu_hide_all();
|
|
menu_hide(*Menu[0]);
|
|
}
|
|
|
|
void menubar_show()
|
|
{
|
|
auto &menu = *Menu[0];
|
|
if (!menu.wind)
|
|
{
|
|
menu.wind = window_create<menubar_window>(grd_curscreen->sc_canvas, menu.x, menu.y, menu.w, menu.h, menu);
|
|
menu.wind->set_modal(0); // allow windows behind the menubar to accept events (e.g. the keypad dialogs)
|
|
}
|
|
menu_show(menu);
|
|
}
|
|
|
|
void menubar_close()
|
|
{
|
|
auto &menubar = *Menu[0];
|
|
if (!menubar.wind)
|
|
return;
|
|
window_close(std::exchange(menubar.wind, nullptr));
|
|
}
|
|
|
|
}
|