diff options
Diffstat (limited to 'engines/agi/menu.cpp')
-rw-r--r-- | engines/agi/menu.cpp | 947 |
1 files changed, 539 insertions, 408 deletions
diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp index 008c208c82..49c2d0eeab 100644 --- a/engines/agi/menu.cpp +++ b/engines/agi/menu.cpp @@ -22,534 +22,665 @@ #include "agi/agi.h" #include "agi/graphics.h" +#include "agi/text.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; -} +GfxMenu::GfxMenu(AgiEngine *vm, GfxMgr *gfx, PictureMgr *picture, TextMgr *text) { + _vm = vm; + _gfx = gfx; + _picture = picture; + _text = text; -AgiMenuOption *Menu::getMenuOption(int i, int j) { - AgiMenu *m = getMenu(i); - MenuOptionList::iterator iter; + _allowed = true; + _submitted = false; + _delayedExecuteViaKeyboard = false; + _delayedExecuteViaMouse = false; - for (iter = m->down.begin(); iter != m->down.end(); ++iter) { - AgiMenuOption* d = *iter; - if (d->index == j) - return d; - } + _setupMenuColumn = 1; + _setupMenuItemColumn = 1; + + _lastSelectedMenuNr = 0; + + _mouseModeItemNr = -1; - return NULL; + _drawnMenuNr = -1; + _drawnMenuHeight = 0; + _drawnMenuWidth = 0; + _drawnMenuY = 0; + _drawnMenuX = 0; } -void Menu::drawMenuBar() { - _vm->clearLines(0, 0, MENU_BG); - _vm->flushLines(0, 0); +GfxMenu::~GfxMenu() { + for (GuiMenuArray::iterator itemIter = _array.begin(); itemIter != _array.end(); ++itemIter) + delete *itemIter; + _array.clear(); - MenuList::iterator iter; - for (iter = _menubar.begin(); iter != _menubar.end(); ++iter) { - AgiMenu *m = *iter; + for (GuiMenuItemArray::iterator menuIter = _itemArray.begin(); menuIter != _itemArray.end(); ++menuIter) + delete *menuIter; + _itemArray.clear(); +} + +void GfxMenu::addMenu(const char *menuText) { + int16 curColumnEnd = _setupMenuColumn; + + // already submitted? in that case no further changes possible + if (_submitted) + return; + + GuiMenuEntry *menuEntry = new GuiMenuEntry(); - _vm->printText(m->text, 0, m->col, 0, 40, MENU_FG, MENU_BG); + menuEntry->text = menuText; + menuEntry->textLen = menuEntry->text.size(); + + // Cut menu name in case menu bar is full + // Happens in at least the fan game Get Outta Space Quest + // Original interpreter had graphical issues in this case + // TODO: this whole code needs to get reworked anyway to support different types of menu bars depending on platform + curColumnEnd += menuEntry->textLen; + while ((menuEntry->textLen) && (curColumnEnd > 40)) { + menuEntry->text.deleteLastChar(); + menuEntry->textLen--; + curColumnEnd--; } + menuEntry->row = 0; + menuEntry->column = _setupMenuColumn; + menuEntry->itemCount = 0; + menuEntry->firstItemNr = _itemArray.size(); + menuEntry->selectedItemNr = menuEntry->firstItemNr; + menuEntry->maxItemTextLen = 0; + _array.push_back(menuEntry); + + _setupMenuColumn += menuEntry->textLen + 1; } -void Menu::drawMenuHilite(int curMenu) { - AgiMenu *m = getMenu(curMenu); +void GfxMenu::addMenuItem(const char *menuItemText, uint16 controllerSlot) { + int16 arrayCount = _array.size(); - debugC(6, kDebugLevelMenu, "[%s]", m->text); + // already submitted? in that case no further changes possible + if (_submitted) + return; - _vm->printText(m->text, 0, m->col, 0, 40, MENU_BG, MENU_FG); - _vm->flushLines(0, 0); -} + if (arrayCount == 0) + error("tried to add a menu item before adding an actual menu"); -// draw box and pulldowns. -void Menu::drawMenuOption(int hMenu) { - // find which vertical menu it is - AgiMenu *m = getMenu(hMenu); + // go to latest menu entry + GuiMenuEntry *curMenuEntry = _array.back(); - _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); + GuiMenuItemEntry *menuItemEntry = new GuiMenuItemEntry(); - MenuOptionList::iterator iter; + menuItemEntry->enabled = true; + menuItemEntry->text = menuItemText; + menuItemEntry->textLen = menuItemEntry->text.size(); + menuItemEntry->controllerSlot = controllerSlot; - for (iter = m->down.begin(); iter != m->down.end(); ++iter) { - AgiMenuOption* d = *iter; + // Original interpreter on PC used the length of the first item for drawing + // At least in KQ2 on Apple IIgs follow-up items are longer, which would result in graphic glitches. + // That's why we remember the longest item and draw according to that + if (curMenuEntry->maxItemTextLen < menuItemEntry->textLen) { + curMenuEntry->maxItemTextLen = menuItemEntry->textLen; + } - _vm->printText(d->text, 0, m->wincol + 1, d->index + 2, m->width + 2, - MENU_FG, MENU_BG, !d->enabled); + if (curMenuEntry->itemCount == 0) { + // for first menu item of menu calculated column + if (menuItemEntry->textLen + curMenuEntry->column < (FONT_COLUMN_CHARACTERS - 1)) { + _setupMenuItemColumn = curMenuEntry->column; + } else { + _setupMenuItemColumn = (FONT_COLUMN_CHARACTERS - 1) - menuItemEntry->textLen; + } } -} -void Menu::drawMenuOptionHilite(int hMenu, int vMenu) { - AgiMenu *m = getMenu(hMenu); - AgiMenuOption *d = getMenuOption(hMenu, vMenu); + menuItemEntry->row = 2 + curMenuEntry->itemCount; + menuItemEntry->column = _setupMenuItemColumn; - // 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); -} + _itemArray.push_back(menuItemEntry); -void Menu::newMenuSelected(int i) { - _picture->showPic(); - drawMenuBar(); - drawMenuHilite(i); - drawMenuOption(i); + curMenuEntry->itemCount++; } -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; +void GfxMenu::submit() { + GuiMenuEntry *menuEntry = nullptr; + GuiMenuItemEntry *menuItemEntry = nullptr; + int16 menuCount = _array.size(); + int16 menuNr = 0; + int16 menuItemNr = 0; + int16 menuItemLastNr = 0; + + if ((_array.size() == 0) || (_itemArray.size() == 0)) + return; + + _submitted = true; + + // WORKAROUND: For Apple II gs we try to fix the menu text + // On this platform it seems a system font was used and the menu was drawn differently (probably system menu?) + // Still the text was misaligned anyway, but it looks worse in our (the original PC) implementation + // Atari ST SQ1 had one bad menu entry as well, we fix that too. + switch (_vm->getPlatform()) { + case Common::kPlatformApple2GS: + case Common::kPlatformAtariST: + // Go through all menus + for (menuNr = 0; menuNr < menuCount; menuNr++) { + menuEntry = _array[menuNr]; + menuItemLastNr = menuEntry->firstItemNr + menuEntry->itemCount; + + // Go through all items of current menu + for (menuItemNr = menuEntry->firstItemNr; menuItemNr < menuItemLastNr; menuItemNr++) { + menuItemEntry = _itemArray[menuItemNr]; + + if (menuItemEntry->textLen < menuEntry->maxItemTextLen) { + // current item text is shorter than the maximum? + int16 missingCharCount = menuEntry->maxItemTextLen - menuItemEntry->textLen; + + if (menuItemEntry->text.contains('>')) { + // text contains '>', we now try to find a '<' + // and then add spaces in case this item is shorter than the first item + int16 textPos = menuItemEntry->textLen - 1; + + while (textPos > 0) { + if (menuItemEntry->text[textPos] == '<') + break; + textPos--; + } + + if (textPos > 0) { + while (missingCharCount) { + menuItemEntry->text.insertChar(' ', textPos); + missingCharCount--; + } + } + } else { + // Also check if text consists only of '-', which is the separator + // These were sometimes also too small + int16 separatorCount = 0; + int16 charPos = 0; + + while (charPos < menuItemEntry->textLen) { + if (menuItemEntry->text[charPos] != '-') + break; + separatorCount++; + charPos++; + } + + if (separatorCount == menuItemEntry->textLen) { + // Separator detected + while (missingCharCount) { + menuItemEntry->text.insertChar('-', 0); + missingCharCount--; + } + } else { + // Append spaces to the end to fill it up + int16 textPos = menuItemEntry->textLen; + while (missingCharCount) { + menuItemEntry->text.insertChar(' ', textPos); + textPos++; + missingCharCount--; + } + } + } - if (_vm->_mouse.y >= (line + 1) * CHAR_LINES) - return false; + menuItemEntry->textLen = menuItemEntry->text.size(); + } + } + } + break; + default: + break; + } +} - return true; +void GfxMenu::itemEnable(uint16 controllerSlot) { + itemEnableDisable(controllerSlot, 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); +void GfxMenu::itemDisable(uint16 controllerSlot) { + itemEnableDisable(controllerSlot, false); } -#endif -/* - * Public functions - */ +void GfxMenu::itemEnableDisable(uint16 controllerSlot, bool enabled) { + GuiMenuItemArray::iterator listIterator; + GuiMenuItemArray::iterator listEnd = _itemArray.end(); + GuiMenuItemEntry *menuItemEntry; -Menu::Menu(AgiEngine *vm, GfxMgr *gfx, PictureMgr *picture) { - _vm = vm; - _gfx = gfx; - _picture = picture; - _hIndex = 0; - _hCol = 1; - _hMaxMenu = 0; - _hCurMenu = 0; - _vCurMenu = 0; -} + listIterator = _itemArray.begin(); + while (listIterator != listEnd) { + menuItemEntry = *listIterator; + if (menuItemEntry->controllerSlot == controllerSlot) { + menuItemEntry->enabled = enabled; + } -Menu::~Menu() { - MenuList::iterator iterh; - for (iterh = _menubar.reverse_begin(); iterh != _menubar.end(); ) { - AgiMenu *m = *iterh; + listIterator++; + } +} - debugC(3, kDebugLevelMenu, "deiniting hmenu %s", m->text); +void GfxMenu::itemEnableAll() { + GuiMenuItemArray::iterator listIterator; + GuiMenuItemArray::iterator listEnd = _itemArray.end(); + GuiMenuItemEntry *menuItemEntry; - MenuOptionList::iterator iterv; + listIterator = _itemArray.begin(); + while (listIterator != listEnd) { + menuItemEntry = *listIterator; + menuItemEntry->enabled = true; - for (iterv = m->down.reverse_begin(); iterv != m->down.end(); ) { - AgiMenuOption *d = *iterv; + listIterator++; + } +} - debugC(3, kDebugLevelMenu, " deiniting vmenu %s", d->text); +// return true, in case a menu was actually created and submitted by the scripts +bool GfxMenu::isAvailable() { + return _submitted; +} - free(d->text); - delete d; +void GfxMenu::accessAllow() { + _allowed = true; +} - iterv = m->down.reverse_erase(iterv); - } - free(m->text); - delete m; +void GfxMenu::accessDeny() { + _allowed = false; +} - iterh = _menubar.reverse_erase(iterh); - } +void GfxMenu::delayedExecuteViaKeyboard() { + _delayedExecuteViaKeyboard = true; + _delayedExecuteViaMouse = false; +} +void GfxMenu::delayedExecuteViaMouse() { + _delayedExecuteViaKeyboard = false; + _delayedExecuteViaMouse = true; } -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); +bool GfxMenu::delayedExecuteActive() { + return _delayedExecuteViaKeyboard | _delayedExecuteViaMouse; } -void Menu::addItem(const char *s, int code) { - int l; +void GfxMenu::execute() { + bool viaKeyboard = _delayedExecuteViaKeyboard; + bool viaMouse = _delayedExecuteViaMouse; + _delayedExecuteViaKeyboard = false; + _delayedExecuteViaMouse = false; - AgiMenuOption* d = new AgiMenuOption; + // got submitted? -> safety check + if (!_submitted) + return; - d->text = strdup(s); - d->enabled = true; - d->event = code; - d->index = _vIndex++; + // access allowed at the moment? + if (!_allowed) + return; - // add to last menu in list - assert(_menubar.reverse_begin() != _menubar.end()); - AgiMenu *m = *_menubar.reverse_begin(); - m->height++; + _text->charPos_Push(); + _text->charAttrib_Push(); + _text->clearLine(0, _text->calculateTextBackground(15)); - _vMaxMenu[m->index] = d->index; + // Draw all menus + for (uint16 menuNr = 0; menuNr < _array.size(); menuNr++) { + drawMenuName(menuNr, false); + } - 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; + // Draw last selected menu + _drawnMenuNr = _lastSelectedMenuNr; - debugC(3, kDebugLevelMenu, "Adding menu item: %s (size = %d)", s, m->height); + // Unless we are in "via mouse" mode. In that case check current mouse position + if (viaMouse) { + int16 mouseRow = _vm->_mouse.pos.y; + int16 mouseColumn = _vm->_mouse.pos.x; + _gfx->translateDisplayPosToFontScreen(mouseColumn, mouseRow); - m->down.push_back(d); -} + mouseFindMenuSelection(mouseRow, mouseColumn, _drawnMenuNr, _mouseModeItemNr); + } -void Menu::submit() { - debugC(3, kDebugLevelMenu, "Submitting menu"); + if (_drawnMenuNr >= 0) { + if (viaKeyboard) { + drawMenu(_drawnMenuNr, _array[_drawnMenuNr]->selectedItemNr); + } + if (viaMouse) { + drawMenu(_drawnMenuNr, _mouseModeItemNr); + } + } - // add_about_option (); + if (viaKeyboard) { + _vm->cycleInnerLoopActive(CYCLE_INNERLOOP_MENU_VIA_KEYBOARD); + } else if (viaMouse) { + _vm->cycleInnerLoopActive(CYCLE_INNERLOOP_MENU_VIA_MOUSE); + } - // If a menu has no options, delete it - MenuList::iterator iter; - for (iter = _menubar.reverse_begin(); iter != _menubar.end(); ) { - AgiMenu *m = *iter; + do { + _vm->processAGIEvents(); + } while (_vm->cycleInnerLoopIsActive() && !(_vm->shouldQuit() || _vm->_restartGame)); - if (m->down.empty()) { - free(m->text); - delete m; + if (_drawnMenuNr >= 0) { + removeActiveMenu(_drawnMenuNr); + } - _hMaxMenu--; + if (viaKeyboard) { + // In "via Keyboard" mode, remember last selection + _lastSelectedMenuNr = _drawnMenuNr; + } - iter = _menubar.reverse_erase(iter); + _text->charAttrib_Pop(); + _text->charPos_Pop(); + + // Restore status line + if (_text->statusEnabled()) { + _text->statusDraw(); + } else { + if (_text->getWindowRowMin() == 0) { + // WORKAROUND: Playarea starts right at the stop, so instead of clearing that part, render it from playarea + // Required for at least Donald Duck + // This was not done by original AGI, which means the upper pixel line were cleared in this case. + _gfx->render_Block(0, 0, SCRIPT_WIDTH, FONT_VISUAL_HEIGHT); } else { - --iter; + _text->clearLine(0, 0); } } } -bool Menu::keyhandler(int key) { - static int clockVal; - static int menuActive = false; - static int buttonUsed = 0; - bool exitMenu = false; +void GfxMenu::drawMenuName(int16 menuNr, bool inverted) { + GuiMenuEntry *menuEntry = _array[menuNr]; + bool disabledLook = false; - if (!_vm->getflag(fMenusWork) && !(_vm->getFeatures() & GF_MENUS)) - return false; + // Don't draw in case there is no text + if (!menuEntry->text.size()) + return; - if (!menuActive) { - clockVal = _vm->_game.clockEnabled; - _vm->_game.clockEnabled = false; - drawMenuBar(); + if (!inverted) { + _text->charAttrib_Set(0, _text->calculateTextBackground(15)); + } else { + _text->charAttrib_Set(15, _text->calculateTextBackground(0)); } - // Mouse handling - if (_vm->_mouse.button) { - int hmenu, vmenu; + _text->charPos_Set(menuEntry->row, menuEntry->column); - buttonUsed = 1; // Button has been used at least once + if (menuEntry->itemCount == 0) + disabledLook = true; - if (_vm->_mouse.y <= CHAR_LINES) { - // on the menubar - hmenu = 0; + _text->displayText(menuEntry->text.c_str(), disabledLook); +} - MenuList::iterator iterh; +void GfxMenu::drawItemName(int16 itemNr, bool inverted) { + GuiMenuItemEntry *itemEntry = _itemArray[itemNr]; + bool disabledLook = false; - for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) { - AgiMenu *m = *iterh; + if (!inverted) { + _text->charAttrib_Set(0, _text->calculateTextBackground(15)); + } else { + _text->charAttrib_Set(15, _text->calculateTextBackground(0)); + } - if (mouseOverText(0, m->col, m->text)) { - break; - } else { - hmenu++; - } - } + _text->charPos_Set(itemEntry->row, itemEntry->column); - if (hmenu <= _hMaxMenu) { - if (_hCurMenu != hmenu) { - _vCurMenu = -1; - newMenuSelected(hmenu); - } - _hCurMenu = hmenu; - } - } else { - // not in menubar - vmenu = 0; + if (itemEntry->enabled == false) + disabledLook = true; - AgiMenu *m = getMenu(_hCurMenu); + _text->displayText(itemEntry->text.c_str(), disabledLook); +} - MenuOptionList::iterator iterv; +void GfxMenu::drawMenu(int16 selectedMenuNr, int16 selectedMenuItemNr) { + GuiMenuEntry *menuEntry = _array[selectedMenuNr]; + GuiMenuItemEntry *itemEntry = _itemArray[menuEntry->firstItemNr]; + int16 itemNr = menuEntry->firstItemNr; + int16 itemCount = menuEntry->itemCount; - for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) { - AgiMenuOption *do1 = *iterv; + // draw menu name as inverted + drawMenuName(selectedMenuNr, true); - if (mouseOverText(2 + do1->index, m->wincol + 1, do1->text)) { - break; - } else { - vmenu++; - } - } + // calculate active menu dimensions + _drawnMenuHeight = (menuEntry->itemCount + 2) * FONT_VISUAL_HEIGHT; + _drawnMenuWidth = (menuEntry->maxItemTextLen * FONT_VISUAL_WIDTH) + 8; + _drawnMenuY = (1 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT; + //(menuEntry->itemCount + 3 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT - 1; + _drawnMenuX = (itemEntry->column - 1) * FONT_VISUAL_WIDTH; - if (vmenu <= _vMaxMenu[_hCurMenu]) { - if (_vCurMenu != vmenu) { - drawMenuOption(_hCurMenu); - drawMenuOptionHilite(_hCurMenu, vmenu); - } - _vCurMenu = vmenu; - } - } - } else if (buttonUsed) { - // Button released - buttonUsed = 0; + _gfx->drawBox(_drawnMenuX, _drawnMenuY, _drawnMenuWidth, _drawnMenuHeight, 15, 0); - debugC(6, kDebugLevelMenu | kDebugLevelInput, "button released!"); + while (itemCount) { + if (itemNr == selectedMenuItemNr) { + drawItemName(itemNr, true); + } else { + drawItemName(itemNr, false); + } + itemNr++; + itemCount--; + } +} - if (_vCurMenu < 0) - _vCurMenu = 0; +void GfxMenu::removeActiveMenu(int16 selectedMenuNr) { + // draw menu name normally again + drawMenuName(selectedMenuNr, false); - drawMenuOptionHilite(_hCurMenu, _vCurMenu); + // overwrite actual menu items by rendering play screen + _gfx->render_Block(_drawnMenuX, _drawnMenuY, _drawnMenuWidth, _drawnMenuHeight); +} - 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; - } +void GfxMenu::keyPress(uint16 newKey) { + GuiMenuEntry *menuEntry = _array[_drawnMenuNr]; + GuiMenuItemEntry *itemEntry = _itemArray[menuEntry->selectedItemNr]; + int16 newMenuNr = _drawnMenuNr; + int16 newItemNr = menuEntry->selectedItemNr; + + switch (newKey) { + case AGI_KEY_ENTER: + // check, if current item is actually enabled + if (!itemEntry->enabled) + return; + + // Trigger controller + _vm->_game.controllerOccured[itemEntry->controllerSlot] = true; + + _vm->cycleInnerLoopInactive(); // exit execute-loop + break; + case AGI_KEY_ESCAPE: + _vm->cycleInnerLoopInactive(); // exit execute-loop + break; + + // these here change menu item + case AGI_KEY_UP: + newItemNr--; + break; + case AGI_KEY_DOWN: + newItemNr++; + break; + case AGI_KEY_PAGE_UP: + // select first item of current menu + newItemNr = menuEntry->firstItemNr; + break; + case AGI_KEY_PAGE_DOWN: + // select last item of current menu + newItemNr = menuEntry->firstItemNr + menuEntry->itemCount - 1; + break; + + case AGI_KEY_LEFT: + newMenuNr--; + break; + case AGI_KEY_RIGHT: + newMenuNr++; + break; + case AGI_KEY_HOME: + // select first menu + newMenuNr = 0; + break; + case AGI_KEY_END: + // select last menu + newMenuNr = _array.size() - 1; + break; + + default: + break; } - if (!exitMenu) { - if (!menuActive) { - if (_hCurMenu >= 0) { - drawMenuHilite(_hCurMenu); - drawMenuOption(_hCurMenu); - if (!buttonUsed && _vCurMenu >= 0) - drawMenuOptionHilite(_hCurMenu, _vCurMenu); - } - menuActive = true; - } + if (newMenuNr != _drawnMenuNr) { + // selected menu was changed + int16 lastMenuNr = _array.size() - 1; - 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; + if (newMenuNr < 0) { + newMenuNr = lastMenuNr; + } else if (newMenuNr > lastMenuNr) { + newMenuNr = 0; } - 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 (newMenuNr != _drawnMenuNr) { + removeActiveMenu(_drawnMenuNr); + _drawnMenuNr = newMenuNr; + drawMenu(_drawnMenuNr, _array[_drawnMenuNr]->selectedItemNr); } } - if (exitMenu) { - buttonUsed = 0; - _picture->showPic(); - _vm->writeStatus(); + if (newItemNr != menuEntry->selectedItemNr) { + // selected item was changed + int16 lastItemNr = menuEntry->firstItemNr + menuEntry->itemCount - 1; - _vm->setvar(vKey, 0); - _vm->_game.keypress = 0; - _vm->_game.clockEnabled = clockVal; - _vm->oldInputMode(); + if (newItemNr < menuEntry->firstItemNr) { + newItemNr = lastItemNr; + } else if (newItemNr > lastItemNr) { + newItemNr = menuEntry->firstItemNr; + } - debugC(3, kDebugLevelMenu, "exit_menu: input mode reset to %d", _vm->_game.inputMode); - menuActive = false; + if (newItemNr != menuEntry->selectedItemNr) { + // still changed after clip -> draw changes + drawItemName(menuEntry->selectedItemNr, false); + drawItemName(newItemNr, true); + menuEntry->selectedItemNr = newItemNr; + } } - - return true; } -void Menu::setItem(int event, int state) { - // scan all menus for event number # +// This gets called: +// During "via keyboard" mode in case user actively clicks on something +// During "via mouse" mode all the time, so that current mouse cursor position modifies active selection +// In "via mouse" mode, we check if user let go of the left mouse button and then select the item that way +void GfxMenu::mouseEvent(uint16 newKey) { + // Find out, where current mouse cursor actually is + int16 mouseRow = _vm->_mouse.pos.y; + int16 mouseColumn = _vm->_mouse.pos.x; + + _gfx->translateDisplayPosToFontScreen(mouseColumn, mouseRow); + + int16 activeMenuNr, activeItemNr; + mouseFindMenuSelection(mouseRow, mouseColumn, activeMenuNr, activeItemNr); - debugC(6, kDebugLevelMenu, "event = %d, state = %d", event, state); - MenuList::iterator iterh; + switch (newKey) { + case AGI_MOUSE_BUTTON_LEFT: + // User clicked somewhere, in this case check if user clicked on status bar or on one of the currently shown menu items + // Happens in "via keyboard" mode only + // We do not close menu in case user clicked on something invalid - for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) { - AgiMenu *m = *iterh; - MenuOptionList::iterator iterv; + if (activeItemNr >= 0) { + GuiMenuItemEntry *itemEntry = _itemArray[activeItemNr]; + if (!itemEntry->enabled) + return; - for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) { - AgiMenuOption *d = *iterv; + // Trigger controller + _vm->_game.controllerOccured[itemEntry->controllerSlot] = true; - if (d->event == event) { - d->enabled = state; - // keep going; we need to set the state of every menu item - // with this event code. -- dsymonds + _vm->cycleInnerLoopInactive(); // exit execute-loop + return; + } + if (activeMenuNr >= 0) { + // User clicked on a menu, check if that menu is already active + if (activeMenuNr != _drawnMenuNr) { + removeActiveMenu(_drawnMenuNr); + _drawnMenuNr = activeMenuNr; + drawMenu(_drawnMenuNr, _array[_drawnMenuNr]->selectedItemNr); } } + return; // exit all the time, we do not want to change the user selection while in "via keyboard" mode + break; + default: + break; } -} -void Menu::enableAll() { - MenuList::iterator iterh; - for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) { - AgiMenu *m = *iterh; - MenuOptionList::iterator iterv; + // If mouse is not selecting any menu, just use the last menu instead + if (activeMenuNr < 0) { + activeMenuNr = _drawnMenuNr; + } - for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) { - AgiMenuOption *d = *iterv; + if (activeMenuNr != _drawnMenuNr) { + if (_drawnMenuNr >= 0) { + removeActiveMenu(_drawnMenuNr); + } - d->enabled = true; + _drawnMenuNr = activeMenuNr; + + if (_drawnMenuNr >= 0) { + drawMenu(_drawnMenuNr, activeItemNr); } + _mouseModeItemNr = activeItemNr; } -} - -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); - } + if (activeItemNr != _mouseModeItemNr) { + if (_mouseModeItemNr >= 0) { + drawItemName(_mouseModeItemNr, false); } - } 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); + if (activeItemNr >= 0) { + drawItemName(activeItemNr, true); } + _mouseModeItemNr = activeItemNr; } -} -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; -} + if (_vm->_mouse.button == kAgiMouseButtonUp) { + // User has stopped pressing the mouse button, if any item number is selected -> execute it + if (activeItemNr >= 0) { + GuiMenuItemEntry *itemEntry = _itemArray[activeItemNr]; + if (itemEntry->enabled) { + // Trigger controller + _vm->_game.controllerOccured[itemEntry->controllerSlot] = true; + } + } -int AgiButtonStyle::getTextOffset(bool hasFocus, bool pressed) const { - return (pressed && !_amigaStyle) ? 1 : 0; + _vm->cycleInnerLoopInactive(); // exit execute-loop + return; + } } -bool AgiButtonStyle::getBorder(bool hasFocus, bool pressed) const { - return _amigaStyle && !_authenticAmiga && (hasFocus || pressed); -} +void GfxMenu::mouseFindMenuSelection(int16 mouseRow, int16 mouseColumn, int16 &activeMenuNr, int16 &activeMenuItemNr) { + GuiMenuEntry *menuEntry = nullptr; + int16 menuCount = _array.size(); -void AgiButtonStyle::setAmigaStyle(bool amigaStyle, bool olderAgi, bool authenticAmiga) { - _amigaStyle = amigaStyle; - _olderAgi = olderAgi; - _authenticAmiga = authenticAmiga; -} + for (int16 menuNr = 0; menuNr < menuCount; menuNr++) { + menuEntry = _array[menuNr]; -void AgiButtonStyle::setPcStyle(bool pcStyle) { - setAmigaStyle(!pcStyle); -} + if (mouseRow == menuEntry->row) { + // line match + if ((mouseColumn >= menuEntry->column) && (mouseColumn < (menuEntry->column + menuEntry->textLen))) { + // full match + activeMenuNr = menuNr; + activeMenuItemNr = -1; // no item selected + return; + } + } + } -AgiButtonStyle::AgiButtonStyle(Common::RenderMode renderMode) { - setAmigaStyle(renderMode == Common::kRenderAmiga); + // Now also check current menu + if (_drawnMenuNr >= 0) { + // A menu is currently shown + menuEntry = _array[_drawnMenuNr]; + + int16 itemNr = menuEntry->firstItemNr; + int16 itemCount = menuEntry->itemCount; + + while (itemCount) { + GuiMenuItemEntry *itemEntry = _itemArray[itemNr]; + + if (mouseRow == itemEntry->row) { + // line match + if ((mouseColumn >= itemEntry->column) && (mouseColumn < (itemEntry->column + itemEntry->textLen))) { + // full match + if (itemEntry->enabled) { + // Only see it, when it's currently enabled + activeMenuNr = _drawnMenuNr; + activeMenuItemNr = itemNr; + return; + } + } + } + itemNr++; + itemCount--; + } + } + activeMenuNr = -1; + activeMenuItemNr = -1; + return; } } // End of namespace Agi |