/* 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. * */ #include "agi/agi.h" #include "agi/graphics.h" #include "agi/keyboard.h" #include "agi/menu.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 color. -- 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(int line, int col, char *s) { if (_vm->_mouse.x < col * CHAR_COLS) return false; if (_vm->_mouse.x > (int)(col + strlen(s)) * CHAR_COLS) return false; if (_vm->_mouse.y < line * CHAR_LINES) return false; if (_vm->_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; } } } bool Menu::keyhandler(int key) { static int clockVal; static int menuActive = false; static int buttonUsed = 0; bool exitMenu = false; if (!_vm->getflag(fMenusWork) && !(_vm->getFeatures() & GF_MENUS)) return false; if (!menuActive) { clockVal = _vm->_game.clockEnabled; _vm->_game.clockEnabled = false; drawMenuBar(); } // Mouse handling if (_vm->_mouse.button) { int hmenu, vmenu; buttonUsed = 1; // Button has been used at least once if (_vm->_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 (_vm->_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.controllerOccured[d->event] = true; _vm->_menuSelected = true; break; } } } exitMenu = true; } } if (!exitMenu) { 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"); exitMenu = true; break; 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.controllerOccured[d->event] = true; _vm->_menuSelected = true; exitMenu = true; } 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; } } if (exitMenu) { 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; } } } AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, bool positive) const { if (_amigaStyle) { if (positive) { if (pressed) { // Positive pressed Amiga-style button if (_olderAgi) { return AgiTextColor(amigaBlack, amigaOrange); } else { return AgiTextColor(amigaBlack, amigaPurple); } } else { // Positive unpressed Amiga-style button return AgiTextColor(amigaWhite, amigaGreen); } } else { // _amigaStyle && !positive if (pressed) { // Negative pressed Amiga-style button return AgiTextColor(amigaBlack, amigaCyan); } else { // Negative unpressed Amiga-style button return AgiTextColor(amigaWhite, amigaRed); } } } else { // PC-style button if (hasFocus || pressed) { // A pressed or in focus PC-style button return AgiTextColor(pcWhite, pcBlack); } else { // An unpressed PC-style button without focus return AgiTextColor(pcBlack, pcWhite); } } } AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, int baseFgColor, int baseBgColor) const { return getColor(hasFocus, pressed, AgiTextColor(baseFgColor, baseBgColor)); } AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, const AgiTextColor &baseColor) const { if (hasFocus || pressed) return baseColor.swap(); else return baseColor; } int AgiButtonStyle::getTextOffset(bool hasFocus, bool pressed) const { return (pressed && !_amigaStyle) ? 1 : 0; } bool AgiButtonStyle::getBorder(bool hasFocus, bool pressed) const { return _amigaStyle && !_authenticAmiga && (hasFocus || pressed); } void AgiButtonStyle::setAmigaStyle(bool amigaStyle, bool olderAgi, bool authenticAmiga) { _amigaStyle = amigaStyle; _olderAgi = olderAgi; _authenticAmiga = authenticAmiga; } void AgiButtonStyle::setPcStyle(bool pcStyle) { setAmigaStyle(!pcStyle); } AgiButtonStyle::AgiButtonStyle(Common::RenderMode renderMode) { setAmigaStyle(renderMode == Common::kRenderAmiga); } } // End of namespace Agi