mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-29 05:03:29 +00:00
39da7deefb
svn-id: r29346
474 lines
12 KiB
C++
474 lines
12 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "agi/agi.h"
|
|
#include "agi/sprite.h"
|
|
#include "agi/graphics.h"
|
|
#include "agi/keyboard.h"
|
|
#include "agi/menu.h"
|
|
#include "common/list.h"
|
|
|
|
namespace Agi {
|
|
|
|
// TODO: add constructor/destructor for agi_menu, agi_menu_option
|
|
|
|
struct AgiMenuOption {
|
|
int enabled; /**< option is enabled or disabled */
|
|
int event; /**< menu event */
|
|
int index; /**< number of option in this menu */
|
|
char *text; /**< text of menu option */
|
|
};
|
|
|
|
struct AgiMenu {
|
|
MenuOptionList down; /**< list head for menu options */
|
|
int index; /**< number of menu in menubar */
|
|
int width; /**< width of menu in characters */
|
|
int height; /**< height of menu in characters */
|
|
int col; /**< column of menubar entry */
|
|
int wincol; /**< column of menu window */
|
|
char *text; /**< menu name */
|
|
};
|
|
|
|
AgiMenu *Menu::getMenu(int i) {
|
|
MenuList::iterator iter;
|
|
for (iter = _menubar.begin(); iter != _menubar.end(); ++iter) {
|
|
AgiMenu *m = *iter;
|
|
if (m->index == i)
|
|
return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
AgiMenuOption *Menu::getMenuOption(int i, int j) {
|
|
AgiMenu *m = getMenu(i);
|
|
MenuOptionList::iterator iter;
|
|
for (iter = m->down.begin(); iter != m->down.end(); ++iter) {
|
|
AgiMenuOption* d = *iter;
|
|
if (d->index == j)
|
|
return d;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void Menu::drawMenuBar() {
|
|
_vm->clearLines(0, 0, MENU_BG);
|
|
_vm->flushLines(0, 0);
|
|
|
|
MenuList::iterator iter;
|
|
for (iter = _menubar.begin(); iter != _menubar.end(); ++iter) {
|
|
AgiMenu *m = *iter;
|
|
_vm->printText(m->text, 0, m->col, 0, 40, MENU_FG, MENU_BG);
|
|
}
|
|
|
|
}
|
|
|
|
void Menu::drawMenuHilite(int curMenu) {
|
|
AgiMenu *m = getMenu(curMenu);
|
|
debugC(6, kDebugLevelMenu, "[%s]", m->text);
|
|
_vm->printText(m->text, 0, m->col, 0, 40, MENU_BG, MENU_FG);
|
|
_vm->flushLines(0, 0);
|
|
}
|
|
|
|
/* draw box and pulldowns. */
|
|
void Menu::drawMenuOption(int hMenu) {
|
|
/* find which vertical menu it is */
|
|
AgiMenu *m = getMenu(hMenu);
|
|
|
|
_gfx->drawBox(m->wincol * CHAR_COLS, 1 * CHAR_LINES, (m->wincol + m->width + 2) * CHAR_COLS,
|
|
(1 + m->height + 2) * CHAR_LINES, MENU_BG, MENU_LINE, 0);
|
|
|
|
MenuOptionList::iterator iter;
|
|
for (iter = m->down.begin(); iter != m->down.end(); ++iter) {
|
|
AgiMenuOption* d = *iter;
|
|
_vm->printText(d->text, 0, m->wincol + 1, d->index + 2, m->width + 2,
|
|
MENU_FG, MENU_BG, !d->enabled);
|
|
}
|
|
}
|
|
|
|
void Menu::drawMenuOptionHilite(int hMenu, int vMenu) {
|
|
AgiMenu *m = getMenu(hMenu);
|
|
AgiMenuOption *d = getMenuOption(hMenu, vMenu);
|
|
|
|
// Disabled menu items are "greyed out" with a checkerboard effect,
|
|
// rather than having a different colour. -- dsymonds
|
|
_vm->printText(d->text, 0, m->wincol + 1, vMenu + 2, m->width + 2,
|
|
MENU_BG, MENU_FG, !d->enabled);
|
|
}
|
|
|
|
void Menu::newMenuSelected(int i) {
|
|
_picture->showPic();
|
|
drawMenuBar();
|
|
drawMenuHilite(i);
|
|
drawMenuOption(i);
|
|
}
|
|
|
|
bool Menu::mouseOverText(unsigned int line, unsigned int col, char *s) {
|
|
if (g_mouse.x < col * CHAR_COLS)
|
|
return false;
|
|
|
|
if (g_mouse.x > (col + strlen(s)) * CHAR_COLS)
|
|
return false;
|
|
|
|
if (g_mouse.y < line * CHAR_LINES)
|
|
return false;
|
|
|
|
if (g_mouse.y >= (line + 1) * CHAR_LINES)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
#if 0
|
|
static void add_about_option() {
|
|
const char *text = "About AGI engine";
|
|
|
|
agi_menu_option *d = new agi_menu_option;
|
|
d->text = strdup(text);
|
|
d->enabled = true;
|
|
d->event = 255;
|
|
d->index = (v_max_menu[0] += 1);
|
|
|
|
agi_menu *m = *menubar.begin();
|
|
m->down.push_back(d);
|
|
m->height++;
|
|
if (m->width < (int)strlen(text))
|
|
m->width = strlen(text);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Public functions
|
|
*/
|
|
|
|
Menu::Menu(AgiEngine *vm, GfxMgr *gfx, PictureMgr *picture) {
|
|
_vm = vm;
|
|
_gfx = gfx;
|
|
_picture = picture;
|
|
_hIndex = 0;
|
|
_hCol = 1;
|
|
_hMaxMenu = 0;
|
|
_hCurMenu = 0;
|
|
_vCurMenu = 0;
|
|
}
|
|
|
|
Menu::~Menu() {
|
|
MenuList::iterator iterh;
|
|
for (iterh = _menubar.reverse_begin(); iterh != _menubar.end(); ) {
|
|
AgiMenu *m = *iterh;
|
|
debugC(3, kDebugLevelMenu, "deiniting hmenu %s", m->text);
|
|
MenuOptionList::iterator iterv;
|
|
for (iterv = m->down.reverse_begin(); iterv != m->down.end(); ) {
|
|
AgiMenuOption *d = *iterv;
|
|
debugC(3, kDebugLevelMenu, " deiniting vmenu %s", d->text);
|
|
free(d->text);
|
|
delete d;
|
|
iterv = m->down.reverse_erase(iterv);
|
|
}
|
|
free(m->text);
|
|
delete m;
|
|
iterh = _menubar.reverse_erase(iterh);
|
|
}
|
|
}
|
|
|
|
void Menu::add(const char *s) {
|
|
AgiMenu *m = new AgiMenu;
|
|
m->text = strdup(s);
|
|
while (m->text[strlen(m->text) - 1] == ' ')
|
|
m->text[strlen(m->text) - 1] = 0;
|
|
m->width = 0;
|
|
m->height = 0;
|
|
m->index = _hIndex++;
|
|
m->col = _hCol;
|
|
m->wincol = _hCol - 1;
|
|
_vIndex = 0;
|
|
_vMaxMenu[m->index] = 0;
|
|
_hCol += strlen(m->text) + 1;
|
|
_hMaxMenu = m->index;
|
|
|
|
debugC(3, kDebugLevelMenu, "add menu: '%s' %02x", s, m->text[strlen(m->text)]);
|
|
_menubar.push_back(m);
|
|
}
|
|
|
|
void Menu::addItem(const char *s, int code) {
|
|
int l;
|
|
|
|
AgiMenuOption* d = new AgiMenuOption;
|
|
d->text = strdup(s);
|
|
d->enabled = true;
|
|
d->event = code;
|
|
d->index = _vIndex++;
|
|
|
|
// add to last menu in list
|
|
assert(_menubar.reverse_begin() != _menubar.end());
|
|
AgiMenu *m = *_menubar.reverse_begin();
|
|
m->height++;
|
|
|
|
_vMaxMenu[m->index] = d->index;
|
|
|
|
l = strlen(d->text);
|
|
if (l > 40)
|
|
l = 38;
|
|
if (m->wincol + l > 38)
|
|
m->wincol = 38 - l;
|
|
if (l > m->width)
|
|
m->width = l;
|
|
|
|
debugC(3, kDebugLevelMenu, "Adding menu item: %s (size = %d)", s, m->height);
|
|
m->down.push_back(d);
|
|
}
|
|
|
|
void Menu::submit() {
|
|
debugC(3, kDebugLevelMenu, "Submitting menu");
|
|
|
|
/* add_about_option (); */
|
|
|
|
/* If a menu has no options, delete it */
|
|
MenuList::iterator iter;
|
|
for (iter = _menubar.reverse_begin(); iter != _menubar.end(); ) {
|
|
AgiMenu *m = *iter;
|
|
if (m->down.empty()) {
|
|
free(m->text);
|
|
delete m;
|
|
_hMaxMenu--;
|
|
iter = _menubar.reverse_erase(iter);
|
|
} else {
|
|
--iter;
|
|
}
|
|
}
|
|
|
|
// We need to wait till the main menu is submitted by the game scripts
|
|
// before checking if we can start loading a save game from the command line.
|
|
// Menu initialization and submital takes place when the game starts only
|
|
_vm->checkQuickLoad();
|
|
}
|
|
|
|
bool Menu::keyhandler(int key) {
|
|
static int clockVal;
|
|
static int menuActive = false;
|
|
static int buttonUsed = 0;
|
|
|
|
if (!_vm->getflag(fMenusWork) && !(_vm->getFeatures() & GF_MENUS))
|
|
return false;
|
|
|
|
if (!menuActive) {
|
|
clockVal = _vm->_game.clockEnabled;
|
|
_vm->_game.clockEnabled = false;
|
|
drawMenuBar();
|
|
}
|
|
/*
|
|
* Mouse handling
|
|
*/
|
|
if (g_mouse.button) {
|
|
int hmenu, vmenu;
|
|
|
|
buttonUsed = 1; /* Button has been used at least once */
|
|
if (g_mouse.y <= CHAR_LINES) {
|
|
/* on the menubar */
|
|
hmenu = 0;
|
|
|
|
MenuList::iterator iterh;
|
|
for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) {
|
|
AgiMenu *m = *iterh;
|
|
if (mouseOverText(0, m->col, m->text)) {
|
|
break;
|
|
} else {
|
|
hmenu++;
|
|
}
|
|
}
|
|
|
|
if (hmenu <= _hMaxMenu) {
|
|
if (_hCurMenu != hmenu) {
|
|
_vCurMenu = -1;
|
|
newMenuSelected(hmenu);
|
|
}
|
|
_hCurMenu = hmenu;
|
|
}
|
|
} else {
|
|
/* not in menubar */
|
|
vmenu = 0;
|
|
|
|
AgiMenu *m = getMenu(_hCurMenu);
|
|
MenuOptionList::iterator iterv;
|
|
for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) {
|
|
AgiMenuOption *do1 = *iterv;
|
|
if (mouseOverText(2 + do1->index, m->wincol + 1, do1->text)) {
|
|
break;
|
|
} else {
|
|
vmenu++;
|
|
}
|
|
}
|
|
|
|
if (vmenu <= _vMaxMenu[_hCurMenu]) {
|
|
if (_vCurMenu != vmenu) {
|
|
drawMenuOption(_hCurMenu);
|
|
drawMenuOptionHilite(_hCurMenu, vmenu);
|
|
}
|
|
_vCurMenu = vmenu;
|
|
}
|
|
}
|
|
} else if (buttonUsed) {
|
|
/* Button released */
|
|
buttonUsed = 0;
|
|
|
|
debugC(6, kDebugLevelMenu | kDebugLevelInput, "button released!");
|
|
|
|
if (_vCurMenu < 0)
|
|
_vCurMenu = 0;
|
|
|
|
drawMenuOptionHilite(_hCurMenu, _vCurMenu);
|
|
|
|
if (g_mouse.y <= CHAR_LINES) {
|
|
/* on the menubar */
|
|
} else {
|
|
/* see which option we selected */
|
|
AgiMenu *m = getMenu(_hCurMenu);
|
|
MenuOptionList::iterator iterv;
|
|
for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) {
|
|
AgiMenuOption *d = *iterv;
|
|
if (mouseOverText(2 + d->index, m->wincol + 1, d->text)) {
|
|
/* activate that option */
|
|
if (d->enabled) {
|
|
debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event);
|
|
_vm->_game.evKeyp[d->event].occured = true;
|
|
_vm->_game.evKeyp[d->event].data = d->event;
|
|
// In LSL1, event 0x20 is set when changing the game speed to normal via the menu
|
|
// Do not set the event data to 0x20, as this event is then incorrectly triggered
|
|
// when the spacebar is pressed, which has a keycode equal to 0x20 as well
|
|
// Fixes bug #1751390 - "LSL: after changing game speed, space key turn unfunctional"
|
|
if (d->event == 0x20)
|
|
_vm->_game.evKeyp[d->event].data = d->event + 1;
|
|
goto exit_menu;
|
|
}
|
|
}
|
|
}
|
|
goto exit_menu;
|
|
}
|
|
}
|
|
|
|
if (!menuActive) {
|
|
if (_hCurMenu >= 0) {
|
|
drawMenuHilite(_hCurMenu);
|
|
drawMenuOption(_hCurMenu);
|
|
if (!buttonUsed && _vCurMenu >= 0)
|
|
drawMenuOptionHilite(_hCurMenu, _vCurMenu);
|
|
}
|
|
menuActive = true;
|
|
}
|
|
|
|
switch (key) {
|
|
case KEY_ESCAPE:
|
|
debugC(6, kDebugLevelMenu | kDebugLevelInput, "KEY_ESCAPE");
|
|
goto exit_menu;
|
|
case KEY_ENTER:
|
|
{
|
|
debugC(6, kDebugLevelMenu | kDebugLevelInput, "KEY_ENTER");
|
|
AgiMenuOption* d = getMenuOption(_hCurMenu, _vCurMenu);
|
|
if (d->enabled) {
|
|
debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event);
|
|
_vm->_game.evKeyp[d->event].occured = true;
|
|
goto exit_menu;
|
|
}
|
|
break;
|
|
}
|
|
case KEY_DOWN:
|
|
case KEY_UP:
|
|
_vCurMenu += key == KEY_DOWN ? 1 : -1;
|
|
|
|
if (_vCurMenu < 0)
|
|
_vCurMenu = _vMaxMenu[_hCurMenu];
|
|
if (_vCurMenu > _vMaxMenu[_hCurMenu])
|
|
_vCurMenu = 0;
|
|
|
|
drawMenuOption(_hCurMenu);
|
|
drawMenuOptionHilite(_hCurMenu, _vCurMenu);
|
|
break;
|
|
case KEY_RIGHT:
|
|
case KEY_LEFT:
|
|
_hCurMenu += key == KEY_RIGHT ? 1 : -1;
|
|
|
|
if (_hCurMenu < 0)
|
|
_hCurMenu = _hMaxMenu;
|
|
if (_hCurMenu > _hMaxMenu)
|
|
_hCurMenu = 0;
|
|
|
|
_vCurMenu = 0;
|
|
newMenuSelected(_hCurMenu);
|
|
drawMenuOptionHilite(_hCurMenu, _vCurMenu);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
|
|
exit_menu:
|
|
buttonUsed = 0;
|
|
_picture->showPic();
|
|
_vm->writeStatus();
|
|
|
|
_vm->setvar(vKey, 0);
|
|
_vm->_game.keypress = 0;
|
|
_vm->_game.clockEnabled = clockVal;
|
|
_vm->oldInputMode();
|
|
debugC(3, kDebugLevelMenu, "exit_menu: input mode reset to %d", _vm->_game.inputMode);
|
|
menuActive = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Menu::setItem(int event, int state) {
|
|
/* scan all menus for event number # */
|
|
|
|
debugC(6, kDebugLevelMenu, "event = %d, state = %d", event, state);
|
|
MenuList::iterator iterh;
|
|
for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) {
|
|
AgiMenu *m = *iterh;
|
|
MenuOptionList::iterator iterv;
|
|
for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) {
|
|
AgiMenuOption *d = *iterv;
|
|
if (d->event == event) {
|
|
d->enabled = state;
|
|
// keep going; we need to set the state of every menu item
|
|
// with this event code. -- dsymonds
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Menu::enableAll() {
|
|
MenuList::iterator iterh;
|
|
for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) {
|
|
AgiMenu *m = *iterh;
|
|
MenuOptionList::iterator iterv;
|
|
for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) {
|
|
AgiMenuOption *d = *iterv;
|
|
d->enabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // End of namespace Agi
|