/* 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 3 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, see . * */ #include "graphics/thumbnail.h" #include "m4/gui/gui_menu_items.h" #include "m4/adv_r/other.h" #include "m4/adv_r/adv_background.h" #include "m4/adv_r/adv_control.h" #include "m4/adv_r/adv_player.h" #include "m4/core/errors.h" #include "m4/core/imath.h" #include "m4/gui/game_menu.h" #include "m4/gui/gui_event.h" #include "m4/gui/hotkeys.h" #include "m4/graphics/gr_line.h" #include "m4/graphics/gr_sprite.h" #include "m4/graphics/krn_pal.h" #include "m4/gui/gui_sys.h" #include "m4/gui/gui_vmng.h" #include "m4/mem/mem.h" #include "m4/platform/keys.h" #include "m4/vars.h" #include "m4/m4.h" #include "m4/platform/timer.h" namespace M4 { namespace GUI { void gui_DrawSprite(Sprite *mySprite, Buffer *myBuff, int32 x, int32 y) { DrawRequest spriteDrawReq; Buffer drawSpriteBuff; if ((!mySprite) || (!myBuff)) { return; } if (mySprite->sourceHandle) { HLock(mySprite->sourceHandle); mySprite->data = (uint8 *)((intptr) * (mySprite->sourceHandle) + mySprite->sourceOffset); drawSpriteBuff.w = mySprite->w; drawSpriteBuff.stride = mySprite->w; drawSpriteBuff.h = mySprite->h; drawSpriteBuff.encoding = (mySprite->encoding) & (uint8)0x7f; drawSpriteBuff.data = mySprite->data; spriteDrawReq.Src = &drawSpriteBuff; spriteDrawReq.Dest = myBuff; spriteDrawReq.x = x; spriteDrawReq.y = y; spriteDrawReq.scaleX = 100; spriteDrawReq.scaleY = 100; spriteDrawReq.srcDepth = 0; spriteDrawReq.depthCode = nullptr; spriteDrawReq.Pal = nullptr; spriteDrawReq.ICT = nullptr; gr_sprite_draw(&spriteDrawReq); // Unlock the handle HUnLock(mySprite->sourceHandle); } } //----------------------------- MENU DIALOG FUNCTIONS ---------------------------------// bool guiMenu::initialize(RGB8 *myPalette) { int32 i; // This procedure is called before *any* menu is created - the following global var is used // By the guiMenu::eventHandler() to trap events - it must be cleared. _GM(menuCurrItem) = nullptr; if (_G(menuSystemInitialized)) { return true; } // Set this now to prevent re-entry into the menu system _G(menuSystemInitialized) = true; // Pause the game game_pause(true); // Hide the Message Log window // TODO hide_message_log_dialog(); // Hide the interface if (INTERFACE_VISIBLE) { _GM(interfaceWasVisible) = true; interface_hide(); } else { _GM(interfaceWasVisible) = false; } _GM(menuPalette) = myPalette; krn_fade_to_grey(_GM(menuPalette), 5, 1); _GM(dumpedCodes) = false; _GM(dumpedBackground) = false; // Load in the font _GM(menuFont) = gr_font_load("FONTMENU.FNT"); // Alloc space for the save/load tables if ((_GM(slotTitles) = (char **)mem_alloc(sizeof(char *) * MAX_SLOTS, "slot desc array")) == nullptr) { return false; } for (i = 0; i < MAX_SLOTS; i++) { if ((_GM(slotTitles)[i] = (char *)mem_alloc(80, "slot title")) == nullptr) { return false; } } _GM(slotInUse) = (bool *)mem_alloc(sizeof(bool) * MAX_SLOTS, "slotUnUse array"); if (_GM(slotInUse) == nullptr) { return false; } // Allocate space for the thumbnail sprites _GM(thumbNails) = (Sprite **)mem_alloc(sizeof(Sprite *) * MAX_SLOTS, "thumbNail array"); if (_GM(thumbNails) == nullptr) { return false; } for (i = 0; i < MAX_SLOTS; i++) { _GM(thumbNails)[i] = (Sprite *)mem_alloc(sizeof(Sprite), "thumbNail"); if (_GM(thumbNails)[i] == nullptr) { return false; } _GM(thumbNails)[i]->sourceHandle = nullptr; } return true; } void guiMenu::shutdown(bool fadeToColor) { int32 i; // Verify that we need to shutdown if (!_G(menuSystemInitialized)) { return; } _GM(menuCurrItem) = nullptr; // Turf the font gr_font_dealloc(_GM(menuFont)); _GM(menuFont) = nullptr; // Turf the slot arrays for (i = 0; i < MAX_SLOTS; i++) { if (_GM(slotTitles)[i]) { mem_free((void *)_GM(slotTitles)[i]); } } mem_free((void *)_GM(slotTitles)); mem_free((void *)_GM(slotInUse)); // Turf the thumbnail sprites for (i = 0; i < MAX_SLOTS; i++) { if (_GM(thumbNails)[i]) { mem_free((void *)_GM(thumbNails)[i]); } } mem_free((void *)_GM(thumbNails)); // Restore the background and codes if necessary if (_GM(dumpedBackground)) { if (!adv_restoreBackground()) { error_show(FL, 0, "unable to restore background"); } } if (_GM(dumpedCodes)) { if (!adv_restoreCodes()) { error_show(FL, 0, "unable to restore screen codes"); } } // Fade the screen - up to color if the game resumes, down to black if a new game was loaded if (fadeToColor) { krn_fade_from_grey(_GM(menuPalette), 5, 1, TO_COLOR); } else { krn_fade_from_grey(_GM(menuPalette), 5, 1, TO_BLACK); } // See if the interface needs to be restored if (_GM(interfaceWasVisible)) { interface_show(); } // Allow the mouse to change from a clock UnlockMouseSprite(); // Unpause the game game_pause(false); // Menu is now uninitialized _G(menuSystemInitialized) = false; } GrBuff *guiMenu::copyBackground(guiMenu *myMenu, int32 x, int32 y, int32 w, int32 h) { // Verify params if (!myMenu || !myMenu->menuBuffer) { return nullptr; } // Create a new grbuff struct GrBuff *copyOfBackground = new GrBuff(w, h); // Get the source and destination buffers Buffer *srcBuff = myMenu->menuBuffer->get_buffer(); Buffer *destBuff = copyOfBackground->get_buffer(); if (!srcBuff || !destBuff) { delete copyOfBackground; return nullptr; } // Copy the rect gr_buffer_rect_copy_2(srcBuff, destBuff, x, y, 0, 0, w, h); // Now release the buffers myMenu->menuBuffer->release(); copyOfBackground->release(); return copyOfBackground; } void guiMenu::show(void *s, void *r, void *b, int32 destX, int32 destY) { ScreenContext *myScreen = (ScreenContext *)s; RectList *myRectList = (RectList *)r; Buffer *destBuffer = (Buffer *)b; RectList *myRect; // Parameter verification if (!myScreen) { return; } guiMenu *myMenu = (guiMenu *)(myScreen->scrnContent); if (!myMenu) { return; } GrBuff *myMenuBuffer = myMenu->menuBuffer; if (!myMenuBuffer) { return; } Buffer *myBuffer = myMenuBuffer->get_buffer(); if (!myBuffer) { return; } // If no destBuffer, then draw directly to video if (!destBuffer) { myRect = myRectList; while (myRect) { vmng_refresh_video(myRect->x1, myRect->y1, myRect->x1 - myScreen->x1, myRect->y1 - myScreen->y1, myRect->x2 - myScreen->x1, myRect->y2 - myScreen->y1, myBuffer); myRect = myRect->next; } } // Else draw to the dest buffer else { myRect = myRectList; while (myRect) { gr_buffer_rect_copy_2(myBuffer, destBuffer, myRect->x1 - myScreen->x1, myRect->y1 - myScreen->y1, destX, destY, myRect->x2 - myRect->x1 + 1, myRect->y2 - myRect->y1 + 1); myRect = myRect->next; } } // Release myBuffer myMenuBuffer->release(); } guiMenu *guiMenu::create(Sprite *backgroundSprite, int32 x1, int32 y1, int32 scrnFlags) { Buffer drawSpriteBuff; DrawRequest spriteDrawReq; // Verify params if (!backgroundSprite) { return nullptr; } guiMenu *newMenu = new guiMenu(); newMenu->menuBuffer = new GrBuff(backgroundSprite->w, backgroundSprite->h); newMenu->itemList = nullptr; newMenu->cb_return = nullptr; newMenu->cb_esc = nullptr; newMenu->menuEventHandler = (EventHandler)guiMenu::eventHandler; // Draw the background in to the menuBuffer Buffer *tempBuff = newMenu->menuBuffer->get_buffer(); // Copy background into menu-buffer because it is not rectangular (matte ink effect) Buffer *matte = _G(gameDrawBuff)->get_buffer(); // get a pointer to the game background buffer if (tempBuff->h > (_G(gameDrawBuff)->h - y1)) { // if temp buffer is going to hang off the bottom of the game buffer gr_buffer_rect_copy_2(matte, tempBuff, x1, y1, 0, 0, tempBuff->w, _G(gameDrawBuff)->h - y1); // copy the differnce } else { gr_buffer_rect_copy_2(matte, tempBuff, x1, y1, 0, 0, tempBuff->w, tempBuff->h); // copy all of it } _G(gameDrawBuff)->release(); // Release the buffer so it can be moved if nesessary // draw the sprite if (backgroundSprite->sourceHandle) { HLock(backgroundSprite->sourceHandle); backgroundSprite->data = (uint8 *)((intptr) * (backgroundSprite->sourceHandle) + backgroundSprite->sourceOffset); drawSpriteBuff.w = backgroundSprite->w; drawSpriteBuff.stride = backgroundSprite->w; drawSpriteBuff.h = backgroundSprite->h; drawSpriteBuff.encoding = (backgroundSprite->encoding) & (uint8)0x7f; drawSpriteBuff.data = backgroundSprite->data; spriteDrawReq.Src = &drawSpriteBuff; spriteDrawReq.Dest = tempBuff; spriteDrawReq.x = 0; spriteDrawReq.y = 0; spriteDrawReq.scaleX = 100; spriteDrawReq.scaleY = 100; spriteDrawReq.srcDepth = 0; spriteDrawReq.depthCode = nullptr; spriteDrawReq.Pal = nullptr; spriteDrawReq.ICT = nullptr; gr_sprite_draw(&spriteDrawReq); // Unlock the handle HUnLock(backgroundSprite->sourceHandle); } // Release the tempBuffer newMenu->menuBuffer->release(); if (!vmng_screen_create(x1, y1, x1 + backgroundSprite->w - 1, y1 + backgroundSprite->h - 1, 69, scrnFlags, (void *)newMenu, (RefreshFunc)guiMenu::show, (EventHandler)guiMenu::eventHandler)) { return nullptr; } return newMenu; } void guiMenu::destroy(guiMenu *myMenu) { // Verify params if (!myMenu) { return; } // Destroy the items menuItem *myItem = myMenu->itemList; while (myItem) { myMenu->itemList = myItem->next; (myItem->destroy)(myItem); myItem = myMenu->itemList; } // Destroy the buffer delete myMenu->menuBuffer; // Destroy the menu delete myMenu; } void guiMenu::configure(guiMenu *myMenu, CALLBACK cb_return, CALLBACK cb_esc) { if (!myMenu) { return; } myMenu->cb_return = cb_return; myMenu->cb_esc = cb_esc; } bool guiMenu::eventHandler(guiMenu *theMenu, int32 eventType, int32 parm1, int32 parm2, int32 parm3, bool *currScreen) { guiMenu *myMenu = (guiMenu *)theMenu; menuItem *myItem; int32 status; static int32 movingX; static int32 movingY; static bool movingScreen = false; // Initialize the vars bool handled = false; if (currScreen) *currScreen = false; // Make sure the screen exists and is active ScreenContext *myScreen = vmng_screen_find(theMenu, &status); if ((!myScreen) || (status != SCRN_ACTIVE)) { return false; } // If the escape key was pressed, it takes priority over items handling the event if ((eventType == EVENT_KEY) && (parm1 == KEY_ESCAPE)) { if (myMenu->cb_esc) { _GM(menuCurrItem) = nullptr; (myMenu->cb_esc)(nullptr, theMenu); return true; } } // If the return key was pressed, it takes priority over items handling the event if ((eventType == EVENT_KEY) && (parm1 == KEY_RETURN)) { if (myMenu->cb_return) { _GM(menuCurrItem) = nullptr; (myMenu->cb_return)(nullptr, theMenu); return true; } } // Convert the global coordinates to coords relative to the menu const int32 menuX = parm2 - myScreen->x1; const int32 menuY = parm3 - myScreen->y1; // If we are currently handling the events for an item, continue until that item releases control if (_GM(menuCurrItem)) { handled = (_GM(menuCurrItem)->itemEventHandler)(_GM(menuCurrItem), eventType, parm1, menuX, menuY, (void **)&_GM(menuCurrItem)); if (_GM(menuCurrItem) && currScreen) { *currScreen = true; } if (handled) { return true; } } // See what kind of event we have if (eventType == EVENT_MOUSE) { // Scroll through the list of items until we find one that the cursor is on top of myItem = myMenu->itemList; while (myItem && (!((menuX >= myItem->x1) && (menuX <= myItem->x2) && (menuY >= myItem->y1) && (menuY <= myItem->y2)))) { myItem = myItem->next; } // If an item is found, then if it has an event handler, handle the event and return true if (myItem) { if (myItem->itemEventHandler) { (myItem->itemEventHandler)(myItem, eventType, parm1, menuX, menuY, (void **)&_GM(menuCurrItem)); if (_GM(menuCurrItem) && currScreen) { *currScreen = true; } return true; } } } else if (eventType == EVENT_KEY) { // Else the event is a key event - loop through - see if anyone grabs it myItem = myMenu->itemList; while (myItem && (!handled)) { if (myItem->itemEventHandler) { handled = (myItem->itemEventHandler)(myItem, eventType, parm1, -1, -1, nullptr); } myItem = myItem->next; } return handled; } // Otherwise the event is not handled by any of the menu items. Let the menu screen itself handle the event switch (parm1) { case _ME_L_click: case _ME_doubleclick: if (!(myScreen->scrnFlags & SF_IMMOVABLE)) { if(currScreen) *currScreen = true; movingScreen = true; movingX = parm2; movingY = parm3; } break; case _ME_L_drag: case _ME_doubleclick_drag: if (movingScreen) { MoveScreenDelta(myScreen, parm2 - movingX, parm3 - movingY); movingX = parm2; movingY = parm3; } break; case _ME_L_release: case _ME_doubleclick_release: if (currScreen) *currScreen = false; movingScreen = false; break; case _ME_move: case _ME_L_hold: case _ME_doubleclick_hold: default: break; } return true; } menuItem *guiMenu::getItem(int32 tag, guiMenu *myMenu) { // Verify params if (!myMenu) { return nullptr; } menuItem *myItem = myMenu->itemList; while (myItem && (myItem->tag != tag)) { myItem = myItem->next; } return myItem; } void guiMenu::itemDelete(menuItem *myItem, int32 tag, guiMenu *myMenu) { // Verify params if (!myMenu) { return; } if (!myItem) myItem = guiMenu::getItem(tag, myMenu); if (!myItem) return; // Remove myItem from the item list if (myItem->next) { myItem->next->prev = myItem->prev; } if (myItem->prev) { myItem->prev->next = myItem->next; } else { myMenu->itemList = myItem->next; } // If the item is marked transparent, we can remove it from the menu if (myItem->transparent) { if (!myItem->background) { return; } Buffer *backgroundBuff = myItem->background->get_buffer(); if (!backgroundBuff) { return; } // Get the menu buffer and draw the sprite to it Buffer *myBuff = myMenu->menuBuffer->get_buffer(); if (!myBuff) { return; } // Copy the clean piece of the background to the menu buffer gr_buffer_rect_copy_2(backgroundBuff, myBuff, 0, 0, myItem->x1, myItem->y1, backgroundBuff->w, backgroundBuff->h); // Release both buffers myMenu->menuBuffer->release(); myItem->background->release(); } // Destroy the item; if (myItem->destroy) { myItem->destroy(myItem); } } void guiMenu::itemRefresh(menuItem *myItem, int32 tag, guiMenu *myMenu) { int32 status; // Verify params if (!myMenu) { return; } if (!myItem) { myItem = guiMenu::getItem(tag, myMenu); } if (!myItem) { return; } // Draw myItem (myItem->redraw)(myItem, myItem->myMenu, myItem->x1, myItem->y1, 0, 0); // Update the video ScreenContext *myScreen = vmng_screen_find((void *)myItem->myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + myItem->x1, myScreen->y1 + myItem->y1, myScreen->x1 + myItem->x2, myScreen->y1 + myItem->y2); } } bool guiMenu::loadSprites(const char *series, int32 numSprites) { // Load in the game menu series if (LoadSpriteSeries(series, &_GM(menuSeriesHandle), &_GM(menuSeriesOffset), &_GM(menuSeriesPalOffset), _GM(menuPalette)) <= 0) { return false; } _GM(menuSeriesResource) = mem_strdup(series); // Update the palette for the menu gr_pal_set_range(_GM(menuPalette), 59, 197); _GM(spriteCount) = numSprites; // Create the _GM(menuSprites) array if ((_GM(menuSprites) = (Sprite **)mem_alloc(sizeof(Sprite *) * _GM(spriteCount), "sprites array")) == nullptr) { return false; } // Create the menu sprites for (int32 i = 0; i < _GM(spriteCount); i++) { if ((_GM(menuSprites)[i] = CreateSprite(_GM(menuSeriesHandle), _GM(menuSeriesOffset), i, nullptr, nullptr)) == nullptr) { return false; } } return true; } void guiMenu::unloadSprites() { if (!_GM(menuSeriesResource)) { return; } // Unload the sprites from memory rtoss(_GM(menuSeriesResource)); mem_free(_GM(menuSeriesResource)); _GM(menuSeriesResource) = nullptr; _GM(menuSeriesHandle) = nullptr; _GM(menuSeriesOffset) = -1; _GM(menuSeriesPalOffset) = -1; // Turf the sprites for (int32 i = 0; i < _GM(spriteCount); i++) { mem_free((void *)_GM(menuSprites)[i]); } // Turf the sprites array mem_free((void *)_GM(menuSprites)); _GM(menuSprites) = nullptr; _GM(spriteCount) = 0; } //----------------------------- GENERAL ITEM FUNCTIONS ---------------------------------// void menuItem::destroyItem(menuItem *theItem) { // Verify params if (!theItem) { return; } delete theItem->background; delete theItem; } bool menuItem::cursorInsideItem(menuItem *myItem, int32 cursorX, int32 cursorY) { if ((cursorX >= myItem->x1) && (cursorX <= myItem->x2) && (cursorY >= myItem->y1) && (cursorY <= myItem->y2)) { return true; } return false; } //----------------------------- BUTTON FUNCTIONS ---------------------------------// void menuItemButton::drawButton(menuItemButton *myItem, guiMenu *myMenu, int32 x, int32 y, int32, int32) { Buffer *backgroundBuff = nullptr; Sprite *mySprite = nullptr; char tempStr[32]; // Verify params if (!myItem || !myMenu) { return; } // If the item is marked transparent, get the background buffer if (myItem->transparent) { if (!myItem->background) { return; } backgroundBuff = myItem->background->get_buffer(); if (!backgroundBuff) { return; } } // Select the sprite switch (myItem->buttonType) { case BTN_TYPE_GM_GENERIC: switch (myItem->itemFlags) { case BTN_STATE_NORM: mySprite = _GM(menuSprites)[GM_BUTTON_NORM]; break; case BTN_STATE_OVER: mySprite = _GM(menuSprites)[GM_BUTTON_OVER]; break; case BTN_STATE_PRESS: mySprite = _GM(menuSprites)[GM_BUTTON_PRESS]; break; default: case BTN_STATE_GREY: mySprite = _GM(menuSprites)[GM_BUTTON_GREY]; break; } break; case BTN_TYPE_SL_TEXT: switch (myItem->itemFlags) { case BTN_STATE_OVER: if (IS_RIDDLE) gr_font_set_color(96); else font_set_colors(TEXT_COLOR_OVER_SHADOW, TEXT_COLOR_OVER_FOREGROUND, TEXT_COLOR_OVER_HILITE); mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LINE_OVER]; break; case BTN_STATE_PRESS: if (IS_RIDDLE) gr_font_set_color(96); else font_set_colors(TEXT_COLOR_PRESS_SHADOW, TEXT_COLOR_PRESS_FOREGROUND, TEXT_COLOR_PRESS_HILITE); mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LINE_PRESS]; break; case BTN_STATE_GREY: if (IS_RIDDLE) gr_font_set_color(202); else font_set_colors(TEXT_COLOR_GREY_SHADOW, TEXT_COLOR_GREY_FOREGROUND, TEXT_COLOR_GREY_HILITE); mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LINE_NORM]; break; default: case BTN_STATE_NORM: if (IS_RIDDLE) gr_font_set_color(96); else font_set_colors(TEXT_COLOR_NORM_SHADOW, TEXT_COLOR_NORM_FOREGROUND, TEXT_COLOR_NORM_HILITE); mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LINE_NORM]; break; } break; /** ORION BURGER BUTTON TYPES **/ case BTN_TYPE_SL_SAVE: switch (myItem->itemFlags) { case BTN_STATE_NORM: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SAVE_BTN_NORM]; break; case BTN_STATE_OVER: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SAVE_BTN_OVER]; break; case BTN_STATE_PRESS: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SAVE_BTN_PRESS]; break; default: case BTN_STATE_GREY: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SAVE_BTN_GREY]; break; } break; case BTN_TYPE_SL_LOAD: switch (myItem->itemFlags) { case BTN_STATE_NORM: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LOAD_BTN_NORM]; break; case BTN_STATE_OVER: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LOAD_BTN_OVER]; break; case BTN_STATE_PRESS: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LOAD_BTN_PRESS]; break; default: case BTN_STATE_GREY: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LOAD_BTN_GREY]; break; } break; case BTN_TYPE_SL_CANCEL: switch (myItem->itemFlags) { case BTN_STATE_NORM: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_CANCEL_BTN_NORM]; break; case BTN_STATE_OVER: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_CANCEL_BTN_OVER]; break; case BTN_STATE_PRESS: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_CANCEL_BTN_PRESS]; break; default: case BTN_STATE_GREY: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_CANCEL_BTN_NORM]; break; } break; case BTN_TYPE_OM_DONE: switch (myItem->itemFlags) { case BTN_STATE_NORM: mySprite = _GM(menuSprites)[(int)Burger::GUI::OM_DONE_BTN_NORM]; break; case BTN_STATE_OVER: mySprite = _GM(menuSprites)[(int)Burger::GUI::OM_DONE_BTN_OVER]; break; case BTN_STATE_PRESS: mySprite = _GM(menuSprites)[(int)Burger::GUI::OM_DONE_BTN_PRESS]; break; default: case BTN_STATE_GREY: mySprite = _GM(menuSprites)[(int)Burger::GUI::OM_DONE_BTN_GREY]; break; } break; case BTN_TYPE_OM_CANCEL: switch (myItem->itemFlags) { case BTN_STATE_NORM: mySprite = _GM(menuSprites)[(int)Burger::GUI::OM_CANCEL_BTN_NORM]; break; case BTN_STATE_OVER: mySprite = _GM(menuSprites)[(int)Burger::GUI::OM_CANCEL_BTN_OVER]; break; case BTN_STATE_PRESS: mySprite = _GM(menuSprites)[(int)Burger::GUI::OM_CANCEL_BTN_PRESS]; break; default: case BTN_STATE_GREY: mySprite = _GM(menuSprites)[(int)Burger::GUI::OM_CANCEL_BTN_NORM]; break; } break; /** RIDDLE BUTTON TYPES **/ case BTN_TYPE_OM_SCROLLING_ON: switch (myItem->itemFlags) { case BTN_STATE_OVER: mySprite = _GM(menuSprites)[(int)Riddle::GUI::OM_SCROLLING_ON_BTN_OVER]; break; case BTN_STATE_PRESS: mySprite = _GM(menuSprites)[(int)Riddle::GUI::OM_SCROLLING_ON_BTN_PRESS]; break; case BTN_STATE_NORM: default: mySprite = _GM(menuSprites)[(int)Riddle::GUI::OM_SCROLLING_ON_BTN_NORM]; break; } break; case BTN_TYPE_OM_SCROLLING_OFF: switch (myItem->itemFlags) { case BTN_STATE_OVER: mySprite = _GM(menuSprites)[(int)Riddle::GUI::OM_SCROLLING_OFF_BTN_OVER]; break; case BTN_STATE_PRESS: mySprite = _GM(menuSprites)[(int)Riddle::GUI::OM_SCROLLING_OFF_BTN_PRESS]; break; case BTN_STATE_NORM: default: mySprite = _GM(menuSprites)[(int)Riddle::GUI::OM_SCROLLING_OFF_BTN_NORM]; break; } break; default: break; } // Get the menu buffer Buffer *myBuff = myMenu->menuBuffer->get_buffer(); if (!myBuff) { return; } // If the item is tagged as transparent, we need to fill in it's background behind it if (backgroundBuff) { gr_buffer_rect_copy_2(backgroundBuff, myBuff, 0, 0, x, y, backgroundBuff->w, backgroundBuff->h); myItem->background->release(); } // Draw the button sprite in gui_DrawSprite(mySprite, myBuff, x, y); // If the button is a textbutton, write in the text if ((myItem->buttonType == BTN_TYPE_SL_TEXT) && myItem->prompt) { // Write in the special tag Common::sprintf_s(tempStr, 32, "%02d", myItem->tag - 1000 + _GM(firstSlotIndex)); gr_font_set(_GM(menuFont)); gr_font_write(myBuff, tempStr, x + 4, y + 1, 0, -1); gr_font_write(myBuff, myItem->prompt, x + 26, y + 1, 0, -1); } // Release the menu buffer myMenu->menuBuffer->release(); } bool menuItemButton::handler(menuItemButton *myItem, int32 eventType, int32 event, int32 x, int32 y, void **currItem) { ScreenContext *myScreen; int32 status; // Verify params if (!myItem) { return false; } if (!(eventType == EVENT_MOUSE)) { return false; } if (myItem->itemFlags == BTN_STATE_GREY) { return false; } bool redrawItem = false; bool execCallback = false; bool handled = true; switch (event) { case _ME_L_click: case _ME_doubleclick: if (menuItem::cursorInsideItem(myItem, x, y)) { myItem->itemFlags = BTN_STATE_PRESS; *currItem = myItem; redrawItem = true; } else { *currItem = nullptr; if (myItem->itemFlags != BTN_STATE_NORM) { myItem->itemFlags = BTN_STATE_NORM; redrawItem = true; } } break; case _ME_L_drag: case _ME_doubleclick_drag: if (!*currItem) { return true; } if (menuItem::cursorInsideItem(myItem, x, y)) { if (myItem->itemFlags != BTN_STATE_PRESS) { myItem->itemFlags = BTN_STATE_PRESS; redrawItem = true; } } else { if (myItem->itemFlags != BTN_STATE_OVER) { myItem->itemFlags = BTN_STATE_OVER; redrawItem = true; } } break; case _ME_L_release: case _ME_doubleclick_release: if (menuItem::cursorInsideItem(myItem, x, y)) { if (*currItem) { execCallback = true; if (myItem->buttonType == BTN_TYPE_OM_SCROLLING_ON) myItem->buttonType = BTN_TYPE_OM_SCROLLING_OFF; else if (myItem->buttonType == BTN_TYPE_OM_SCROLLING_OFF) myItem->buttonType = BTN_TYPE_OM_SCROLLING_ON; } else { *currItem = myItem; } myItem->itemFlags = BTN_STATE_OVER; redrawItem = true; } else { *currItem = nullptr; myItem->itemFlags = BTN_STATE_NORM; redrawItem = true; handled = false; } break; case _ME_move: if (menuItem::cursorInsideItem(myItem, x, y)) { *currItem = myItem; if (myItem->itemFlags != BTN_STATE_OVER) { myItem->itemFlags = BTN_STATE_OVER; redrawItem = true; } } else { *currItem = nullptr; if (myItem->itemFlags != BTN_STATE_NORM) { myItem->itemFlags = BTN_STATE_NORM; redrawItem = true; handled = false; } } break; case _ME_L_hold: case _ME_doubleclick_hold: default: break; } // See if we need to redraw the button if (redrawItem) { (myItem->redraw)(myItem, myItem->myMenu, myItem->x1, myItem->y1, 0, 0); myScreen = vmng_screen_find(myItem->myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + myItem->x1, myScreen->y1 + myItem->y1, myScreen->x1 + myItem->x2, myScreen->y1 + myItem->y2); } } // See if we need to call the callback function if (execCallback && myItem->callback) { if (IS_RIDDLE) digi_play("950_s51", 2, 255, -1, 950); guiMenu *currMenu = myItem->myMenu; int32 currTag = myItem->tag; _GM(buttonClosesDialog) = false; (myItem->callback)(myItem, myItem->myMenu); status = 0; myScreen = _GM(buttonClosesDialog) ? nullptr : vmng_screen_find(myItem->myMenu, &status); if ((!myScreen) || (status != SCRN_ACTIVE)) { *currItem = nullptr; } else { menuItem *tempItem = guiMenu::getItem(currTag, currMenu); if (!tempItem) { *currItem = nullptr; } } } return handled; } menuItemButton *menuItemButton::add(guiMenu *myMenu, int32 tag, int32 x, int32 y, int32 w, int32 h, CALLBACK callback, int32 buttonType, bool ghosted, bool transparent, const char *prompt, ItemHandlerFunction i_handler) { int32 status; // Verify params if (!myMenu) { return nullptr; } // Allocate a new one menuItemButton *newItem = new menuItemButton(); // Initialize the struct newItem->next = myMenu->itemList; newItem->prev = nullptr; if (myMenu->itemList) { myMenu->itemList->prev = newItem; } myMenu->itemList = newItem; newItem->myMenu = myMenu; newItem->tag = tag; newItem->x1 = x; newItem->y1 = y; newItem->x2 = x + w - 1; newItem->y2 = y + h - 1; newItem->callback = callback; if (!transparent) { newItem->transparent = false; newItem->background = nullptr; } else { newItem->transparent = true; newItem->background = guiMenu::copyBackground(myMenu, x, y, w, h); } if (ghosted) { newItem->itemFlags = BTN_STATE_GREY; } else { newItem->itemFlags = BTN_STATE_NORM; } newItem->buttonType = buttonType; // Note: prompt is not duplicated, therefore, make sure the name is stored in non-volatile memory newItem->prompt = prompt; newItem->specialTag = tag - 1000; newItem->redraw = (DrawFunction)menuItemButton::drawButton; newItem->destroy = (DestroyFunction)menuItem::destroyItem; newItem->itemEventHandler = i_handler; // Draw the button in now (newItem->redraw)(newItem, myMenu, x, y, 0, 0); // See if the screen is currently visible ScreenContext *myScreen = vmng_screen_find(myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + newItem->x1, myScreen->y1 + newItem->y1, myScreen->x1 + newItem->x2, myScreen->y1 + newItem->y2); } return newItem; } void menuItemButton::disableButton(menuItemButton *myItem, int32 tag, guiMenu *myMenu) { // Verify params if (!myMenu) return; if (!myItem) myItem = (menuItemButton *)guiMenu::getItem(tag, myMenu); if (!myItem) return; myItem->itemFlags = BTN_STATE_GREY; } void menuItemButton::enableButton(menuItemButton *myItem, int32 tag, guiMenu *myMenu) { // Verify params if (!myMenu) return; if (!myItem) myItem = (menuItemButton *)guiMenu::getItem(tag, myMenu); if (!myItem) return; myItem->itemFlags = BTN_STATE_NORM; } //----------------------------- MSG FUNCTIONS ---------------------------------// menuItemMsg *menuItemMsg::msgAdd(guiMenu *myMenu, int32 tag, int32 x, int32 y, int32 w, int32 h, bool transparent) { int32 status; // Verify params if (!myMenu) { return nullptr; } // Allocate a new one menuItemMsg *newItem = new menuItemMsg(); // Initialize the struct newItem->next = myMenu->itemList; newItem->prev = nullptr; if (myMenu->itemList) { myMenu->itemList->prev = newItem; } myMenu->itemList = newItem; newItem->myMenu = myMenu; newItem->tag = tag; newItem->x1 = x; newItem->y1 = y; newItem->x2 = x + w - 1; newItem->y2 = y + h - 1; newItem->callback = nullptr; if (!transparent) { newItem->transparent = false; newItem->background = nullptr; } else { newItem->transparent = true; newItem->background = guiMenu::copyBackground(myMenu, x, y, w, h); } newItem->redraw = (DrawFunction)menuItemMsg::drawMsg; newItem->destroy = (DestroyFunction)menuItem::destroyItem; newItem->itemEventHandler = nullptr; // Draw the message in now (newItem->redraw)(newItem, myMenu, x, y, 0, 0); // See if the screen is currently visible ScreenContext *myScreen = vmng_screen_find(myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + newItem->x1, myScreen->y1 + newItem->y1, myScreen->x1 + newItem->x2, myScreen->y1 + newItem->y2); } return newItem; } void menuItemMsg::drawMsg(menuItemMsg *myItem, guiMenu *myMenu, int32 x, int32 y, int32, int32) { Buffer *backgroundBuff = nullptr; Sprite *mySprite = nullptr; // Verify params if (!myItem || !myMenu) { return; } // If the item is marked transparent, get the background buffer if (myItem->transparent) { if (!myItem->background) { return; } backgroundBuff = myItem->background->get_buffer(); if (!backgroundBuff) { return; } } // Select the sprite switch (myItem->tag) { case SL_TAG_SAVE_TITLE_LABEL: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SAVE_TITLE]; break; case SL_TAG_LOAD_TITLE_LABEL: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LOAD_TITLE]; break; case SL_TAG_SAVE_LABEL: mySprite = _GM(menuSprites)[myItem->itemFlags ? SaveLoadMenuBase::SL_SAVE_LABEL_GREY : SaveLoadMenuBase::SL_SAVE_LABEL_7]; break; case SL_TAG_LOAD_LABEL: mySprite = _GM(menuSprites)[myItem->itemFlags ? SaveLoadMenuBase::SL_LOAD_LABEL_GREY : SaveLoadMenuBase::SL_LOAD_LABEL_9]; break; case SL_TAG_THUMBNAIL: mySprite = _GM(saveLoadThumbNail); break; default: break; } // Get the menu buffer and draw the sprite to it Buffer *myBuff = myMenu->menuBuffer->get_buffer(); if (!myBuff) { return; } // If the item is tagged as transparent, we need to fill in it's background behind it if (backgroundBuff) { gr_buffer_rect_copy_2(backgroundBuff, myBuff, 0, 0, x, y, backgroundBuff->w, backgroundBuff->h); myItem->background->release(); } else if (myItem->tag == SL_TAG_THUMBNAIL && mySprite->w == 160) { // Hack for handling smaller ScummVM thumbnails for (int yp = y; yp < (y + SaveLoadMenuBase::SL_THUMBNAIL_H); ++yp) { byte *line = myBuff->data + myBuff->stride * yp + x; Common::fill(line, line + SaveLoadMenuBase::SL_THUMBNAIL_W, 0); } x += 25; y += 25; } // Draw the sprite in gui_DrawSprite(mySprite, myBuff, x, y); // Release the menu buffer myMenu->menuBuffer->release(); } //------------------------------- HSLIDER MENU ITEM ---------------------------------// void menuItemHSlider::drawHSlider(menuItemHSlider *myItem, guiMenu *myMenu, int32 x, int32 y, int32, int32) { Buffer *backgroundBuff = nullptr; Sprite *mySprite; // Verify params if (!myItem || !myMenu) return; // If the item is marked transparent, get the background buffer if (myItem->transparent) { if (!myItem->background) { return; } backgroundBuff = myItem->background->get_buffer(); if (!backgroundBuff) { return; } } // Get the menu buffer and draw the sprite to it Buffer *myBuff = myMenu->menuBuffer->get_buffer(); if (!myBuff) { return; } // If the item is tagged as transparent, we need to fill in it's background behind it if (backgroundBuff) { gr_buffer_rect_copy_2(backgroundBuff, myBuff, 0, 0, x, y, backgroundBuff->w, backgroundBuff->h); myItem->background->release(); } // Get the slider info and select the thumb sprite switch (myItem->itemFlags) { case H_THUMB_OVER: mySprite = _GM(menuSprites)[IS_RIDDLE ? (int)Riddle::GUI::OM_SLIDER_BTN_OVER : (int)Burger::GUI::OM_SLIDER_BTN_OVER]; break; case H_THUMB_PRESS: mySprite = _GM(menuSprites)[IS_RIDDLE ? (int)Riddle::GUI::OM_SLIDER_BTN_PRESS : (int)Burger::GUI::OM_SLIDER_BTN_PRESS]; break; default: case H_THUMB_NORM: mySprite = _GM(menuSprites)[IS_RIDDLE ? (int)Riddle::GUI::OM_SLIDER_BTN_NORM : (int)Burger::GUI::OM_SLIDER_BTN_NORM]; break; } // Fill in everything left of the thumb with a hilite color if (myItem->thumbX > 2) { if (IS_RIDDLE) { gr_color_set(120); gr_buffer_rect_fill(myBuff, myItem->x1 + 2, myItem->y1 + 3, myItem->thumbX - 2, myItem->thumbH - 6); } else { gr_color_set(129); gr_buffer_rect_fill(myBuff, myItem->x1 + 3, myItem->y1 + 9, myItem->thumbX, myItem->thumbH - 18); } } // Draw in the thumb gui_DrawSprite(mySprite, myBuff, myItem->x1 + myItem->thumbX, myItem->y1); // Release the menu buffer myMenu->menuBuffer->release(); } bool menuItemHSlider::handler(menuItemHSlider *myItem, int32 eventType, int32 event, int32 x, int32 y, void **currItem) { ScreenContext *myScreen; int32 status; int32 deltaSlide; static bool movingFlag; static int32 movingX; // Verify params if (!myItem) return false; if (!(eventType == EVENT_MOUSE)) { return false; } bool redrawItem = false; bool handled = true; bool execCallback = false; switch (event) { case _ME_L_click: case _ME_doubleclick: if (menuItem::cursorInsideItem(myItem, x, y) && (x - myItem->x1 >= myItem->thumbX) && (x - myItem->x1 <= myItem->thumbX + myItem->thumbW - 1)) { myItem->itemFlags = H_THUMB_PRESS; movingFlag = true; movingX = x; *currItem = myItem; redrawItem = true; } else { *currItem = nullptr; myItem->itemFlags = 0; redrawItem = true; } break; case _ME_L_drag: case _ME_doubleclick_drag: if (!*currItem) { return true; } if (movingFlag) { if (x < movingX) { deltaSlide = imath_min(myItem->thumbX, movingX - x); if (deltaSlide > 0) { myItem->thumbX -= deltaSlide; redrawItem = true; myItem->percent = myItem->thumbX * 100 / myItem->maxThumbX; execCallback = true; } } else if (x > movingX) { deltaSlide = imath_min(myItem->maxThumbX - myItem->thumbX, x - movingX); if (deltaSlide > 0) { myItem->thumbX += deltaSlide; redrawItem = true; myItem->percent = myItem->thumbX * 100 / myItem->maxThumbX; execCallback = true; } } movingX = x; if (movingX < (myItem->thumbX + myItem->x1)) { movingX = myItem->thumbX + myItem->x1; } else if (movingX > (myItem->thumbX + myItem->thumbW - 1 + myItem->x1)) { movingX = myItem->thumbX + myItem->thumbW - 1 + myItem->x1; } } else { *currItem = nullptr; } break; case _ME_L_release: case _ME_doubleclick_release: if (!*currItem) { return true; } movingFlag = false; if (menuItem::cursorInsideItem(myItem, x, y) && (x - myItem->x1 >= myItem->thumbX) && (x - myItem->x1 <= myItem->thumbX + myItem->thumbW - 1)) { myItem->itemFlags = H_THUMB_OVER; *currItem = myItem; } else { myItem->itemFlags = H_THUMB_NORM; *currItem = nullptr; } redrawItem = true; execCallback = true; break; case _ME_move: if (menuItem::cursorInsideItem(myItem, x, y) && (x - myItem->x1 >= myItem->thumbX) && (x - myItem->x1 <= myItem->thumbX + myItem->thumbW - 1)) { if (myItem->itemFlags != H_THUMB_OVER) { myItem->itemFlags = H_THUMB_OVER; *currItem = myItem; redrawItem = true; } } else { if (myItem->itemFlags != H_THUMB_NORM) { myItem->itemFlags = H_THUMB_NORM; *currItem = nullptr; redrawItem = true; handled = false; } } break; case _ME_L_hold: case _ME_doubleclick_hold: default: break; } // See if we need to redraw the hslider if (redrawItem) { (myItem->redraw)(myItem, myItem->myMenu, myItem->x1, myItem->y1, 0, 0); myScreen = vmng_screen_find(myItem->myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + myItem->x1, myScreen->y1 + myItem->y1, myScreen->x1 + myItem->x2, myScreen->y1 + myItem->y2); } } // See if we need to call the callback function if (execCallback && myItem->callback) { (myItem->callback)((void *)myItem, myItem->myMenu); myScreen = vmng_screen_find(myItem->myMenu, &status); if ((!myScreen) || (status != SCRN_ACTIVE)) { *currItem = nullptr; } } return handled; } menuItemHSlider *menuItemHSlider::add(guiMenu *myMenu, int32 tag, int32 x, int32 y, int32 w, int32 h, int32 initPercent, CALLBACK callback, bool transparent) { int32 status; // Verify params if (!myMenu) { return nullptr; } // Allocate a new one menuItemHSlider *newItem = new menuItemHSlider(); // Initialize the struct newItem->next = myMenu->itemList; newItem->prev = nullptr; if (myMenu->itemList) { myMenu->itemList->prev = newItem; } myMenu->itemList = newItem; newItem->myMenu = myMenu; newItem->tag = tag; newItem->x1 = x; newItem->y1 = y; newItem->x2 = x + w - 1; newItem->y2 = y + h - 1; newItem->callback = callback; if (!transparent) { newItem->transparent = false; newItem->background = nullptr; } else { newItem->transparent = true; newItem->background = guiMenu::copyBackground(myMenu, x, y, w, h); } // Initialize the new slider newItem->itemFlags = H_THUMB_NORM; auto *thumb = _GM(menuSprites)[IS_RIDDLE ? (int)Riddle::GUI::OM_SLIDER_BTN_NORM : (int)Burger::GUI::OM_SLIDER_BTN_NORM]; newItem->thumbW = thumb->w; newItem->thumbH = thumb->h; newItem->maxThumbX = w - thumb->w; if (initPercent < 0) { initPercent = 0; } else if (initPercent > 100) { initPercent = 100; } // Calculate the initial thumbX newItem->percent = initPercent; newItem->thumbX = initPercent * newItem->maxThumbX / 100; newItem->redraw = (DrawFunction)menuItemHSlider::drawHSlider; newItem->destroy = (DestroyFunction)menuItem::destroyItem; newItem->itemEventHandler = (ItemHandlerFunction)menuItemHSlider::handler; // Draw the slider in now (newItem->redraw)(newItem, myMenu, x, y, 0, 0); // See if the screen is currently visible ScreenContext *myScreen = vmng_screen_find(myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + newItem->x1, myScreen->y1 + newItem->y1, myScreen->x1 + newItem->x2, myScreen->y1 + newItem->y2); } return newItem; } //------------------------------- VSLIDER MENU ITEM ---------------------------------// menuItemVSlider *menuItemVSlider::add(guiMenu *myMenu, int32 tag, int32 x, int32 y, int32 w, int32 h, int32 initPercent, CALLBACK callback, bool transparent) { int32 status; // Verify params if (!myMenu) return nullptr; // Allocate a new one menuItemVSlider *newItem = new menuItemVSlider(); // Initialize the struct newItem->next = myMenu->itemList; newItem->prev = nullptr; if (myMenu->itemList) { myMenu->itemList->prev = newItem; } myMenu->itemList = newItem; newItem->myMenu = myMenu; newItem->tag = tag; newItem->x1 = x; newItem->y1 = y; newItem->x2 = x + w - 1; newItem->y2 = y + h - 1; newItem->callback = callback; if (!transparent) { newItem->transparent = false; newItem->background = nullptr; } else { newItem->transparent = true; newItem->background = guiMenu::copyBackground(myMenu, x, y, w, h); } newItem->itemFlags = menuItemVSlider::VS_NORM; if (IS_RIDDLE) { newItem->thumbW = _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_NORM_21]->w; newItem->thumbH = _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_NORM_21]->h; newItem->minThumbY = _GM(menuSprites)[SaveLoadMenuBase::SL_UP_BTN_NORM_13]->h; newItem->maxThumbY = _GM(menuSprites)[SaveLoadMenuBase::SL_UP_BTN_NORM_13]->h + _GM(menuSprites)[SaveLoadMenuBase::SL_SCROLL_BAR_24]->h - _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_NORM_21]->h - 1; } else { newItem->thumbW = _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_NORM_21]->w; newItem->thumbH = _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_NORM_21]->h; newItem->minThumbY = _GM(menuSprites)[SaveLoadMenuBase::SL_UP_BTN_NORM_13]->h + 1; newItem->maxThumbY = _GM(menuSprites)[SaveLoadMenuBase::SL_UP_BTN_NORM_13]->h + _GM(menuSprites)[SaveLoadMenuBase::SL_SCROLL_BAR_24]->h - _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_NORM_21]->h - 1; } // Calculate the initial thumbY newItem->percent = imath_max(imath_min(initPercent, 100), 0); newItem->thumbY = newItem->minThumbY + ((newItem->percent * (newItem->maxThumbY - newItem->minThumbY)) / 100); newItem->redraw = (DrawFunction)menuItemVSlider::drawVSlider; newItem->destroy = (DestroyFunction)menuItem::destroyItem; newItem->itemEventHandler = (ItemHandlerFunction)menuItemVSlider::handler; // Draw the vslider in now (newItem->redraw)(newItem, myMenu, x, y, 0, 0); // See if the screen is currently visible ScreenContext *myScreen = vmng_screen_find(myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + newItem->x1, myScreen->y1 + newItem->y1, myScreen->x1 + newItem->x2, myScreen->y1 + newItem->y2); } return newItem; } void menuItemVSlider::drawVSlider(menuItemVSlider *myItem, guiMenu *myMenu, int32 x, int32 y, int32, int32) { Buffer *backgroundBuff = nullptr; // Verify params if (!myItem || !myMenu) return; // If the item is marked transparent, get the background buffer if (myItem->transparent) { if (!myItem->background) { return; } backgroundBuff = myItem->background->get_buffer(); if (!backgroundBuff) { return; } } // Get the menu buffer Buffer *myBuff = myMenu->menuBuffer->get_buffer(); if (!myBuff) { return; } // If the item is tagged as transparent, we need to fill in it's background behind it if (backgroundBuff) { gr_buffer_rect_copy_2(backgroundBuff, myBuff, 0, 0, x, y, backgroundBuff->w, backgroundBuff->h); myItem->background->release(); } // Set the different sprite components Sprite *vbarSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SCROLL_BAR_24]; Sprite *upSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_UP_BTN_NORM_13]; Sprite *thumbSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_NORM_21]; Sprite *downSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_DOWN_BTN_NORM_14]; if ((myItem->itemFlags & VS_STATUS) == VS_GREY) { upSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_UP_BTN_GREY_19]; thumbSprite = nullptr; downSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_DOWN_BTN_GREY_20]; } else if ((myItem->itemFlags & VS_STATUS) == VS_OVER) { if ((myItem->itemFlags & VS_COMPONENT) == VS_UP) { upSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_UP_BTN_OVER_15]; } else if ((myItem->itemFlags & VS_COMPONENT) == VS_THUMB) { thumbSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_OVER_22]; } else if ((myItem->itemFlags & VS_COMPONENT) == VS_DOWN) { downSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_DOWN_BTN_OVER_16]; } } else if ((myItem->itemFlags & VS_STATUS) == VS_PRESS) { if ((myItem->itemFlags & VS_COMPONENT) == VS_UP) { upSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_UP_BTN_PRESS_17]; } else if ((myItem->itemFlags & VS_COMPONENT) == VS_THUMB) { thumbSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_SLIDER_BTN_PRESS_23]; } else if ((myItem->itemFlags & VS_COMPONENT) == VS_DOWN) { downSprite = _GM(menuSprites)[SaveLoadMenuBase::SL_DOWN_BTN_PRESS_18]; } } // Draw the sprite components gui_DrawSprite(vbarSprite, myBuff, x, y + upSprite->h); gui_DrawSprite(upSprite, myBuff, x, y); gui_DrawSprite(thumbSprite, myBuff, x, y + myItem->thumbY); gui_DrawSprite(downSprite, myBuff, x, y + upSprite->h + vbarSprite->h); // Release the menu buffer myMenu->menuBuffer->release(); } int32 menuItemVSlider::whereIsCursor(menuItemVSlider *myVSlider, int32 y) { if (y < myVSlider->minThumbY) { return VS_UP; } else if (y < myVSlider->thumbY) { return VS_PAGE_UP; } else if (y < myVSlider->thumbY + myVSlider->thumbH) { return VS_THUMB; } else if (y < myVSlider->maxThumbY + myVSlider->thumbH) { return VS_PAGE_DOWN; } else { return VS_DOWN; } } bool menuItemVSlider::handler(menuItemVSlider *myItem, int32 eventType, int32 event, int32 x, int32 y, void **currItem) { int32 tempFlags; ScreenContext *myScreen; int32 status; int32 deltaSlide; static bool movingFlag; static int32 movingY; static int32 callbackTime; // Verify params if (!myItem) return false; if (!(eventType == EVENT_MOUSE)) return false; if ((myItem->itemFlags & VS_STATUS) == VS_GREY) { *currItem = nullptr; return false; } const int32 currTime = timer_read_60(); bool redrawItem = false; bool handled = true; bool execCallback = false; switch (event) { case _ME_L_click: case _ME_doubleclick: if (menuItem::cursorInsideItem(myItem, x, y)) { // digi_play(inv_click_snd, 2, 255, -1, inv_click_snd_room_lock); *currItem = myItem; tempFlags = menuItemVSlider::whereIsCursor(myItem, y - myItem->y1); if (tempFlags == VS_THUMB) { movingFlag = true; movingY = y; } if ((tempFlags == VS_PAGE_UP) || (tempFlags == VS_PAGE_DOWN)) { myItem->itemFlags = tempFlags + VS_NORM; } else { myItem->itemFlags = tempFlags + VS_PRESS; redrawItem = true; } execCallback = true; } else { *currItem = nullptr; myItem->itemFlags = VS_NORM; redrawItem = true; } break; case _ME_L_drag: case _ME_doubleclick_drag: if (!*currItem) { return true; } if (movingFlag) { if (y < movingY) { deltaSlide = imath_min(myItem->thumbY - myItem->minThumbY, movingY - y); if (deltaSlide > 0) { myItem->thumbY -= deltaSlide; myItem->percent = ((myItem->thumbY - myItem->minThumbY) * 100) / (myItem->maxThumbY - myItem->minThumbY); redrawItem = true; execCallback = true; } } else if (y > movingY) { deltaSlide = imath_min(myItem->maxThumbY - myItem->thumbY, y - movingY); if (deltaSlide > 0) { myItem->thumbY += deltaSlide; myItem->percent = ((myItem->thumbY - myItem->minThumbY) * 100) / (myItem->maxThumbY - myItem->minThumbY); redrawItem = true; execCallback = true; } } movingY = y; if (movingY < (myItem->thumbY + myItem->y1)) { movingY = myItem->thumbY + myItem->y1; } else if (movingY > (myItem->thumbY + myItem->thumbH - 1 + myItem->y1)) { movingY = myItem->thumbY + myItem->thumbH - 1 + myItem->y1; } } else { if (menuItem::cursorInsideItem(myItem, x, y)) { tempFlags = menuItemVSlider::whereIsCursor(myItem, y - myItem->y1); if ((myItem->itemFlags & VS_COMPONENT) == tempFlags) { if ((tempFlags != VS_PAGE_UP) && (tempFlags != VS_PAGE_DOWN) && ((myItem->itemFlags & VS_STATUS) != VS_PRESS)) { myItem->itemFlags = tempFlags + VS_PRESS; redrawItem = true; } if (currTime - callbackTime > 6) { execCallback = true; } } else { if ((myItem->itemFlags & VS_STATUS) != VS_OVER) { myItem->itemFlags = (myItem->itemFlags & VS_COMPONENT) + VS_OVER; redrawItem = true; } } execCallback = true; } else { if ((myItem->itemFlags & VS_STATUS) != VS_OVER) { myItem->itemFlags = (myItem->itemFlags & VS_COMPONENT) + VS_OVER; redrawItem = true; } } } break; case _ME_L_release: case _ME_doubleclick_release: movingFlag = false; if (menuItem::cursorInsideItem(myItem, x, y)) { tempFlags = menuItemVSlider::whereIsCursor(myItem, y - myItem->y1); if ((tempFlags == VS_PAGE_UP) || (tempFlags == VS_PAGE_DOWN)) { myItem->itemFlags = VS_NORM; } else { myItem->itemFlags = tempFlags + VS_OVER; *currItem = myItem; } } else { myItem->itemFlags = VS_NORM; *currItem = nullptr; } redrawItem = true; if (!_GM(currMenuIsSave)) { SaveLoadMenuBase::updateThumbnails(_GM(firstSlotIndex), (guiMenu *)myItem->myMenu); } break; case _ME_move: if (menuItem::cursorInsideItem(myItem, x, y)) { *currItem = myItem; tempFlags = menuItemVSlider::whereIsCursor(myItem, y - myItem->y1); if ((myItem->itemFlags & VS_COMPONENT) != tempFlags) { if ((tempFlags == VS_PAGE_UP) || (tempFlags == VS_PAGE_DOWN)) { myItem->itemFlags = VS_NORM; } else { myItem->itemFlags = tempFlags + VS_OVER; } redrawItem = true; } } else { *currItem = nullptr; if (myItem->itemFlags != VS_NORM) { myItem->itemFlags = VS_NORM; redrawItem = true; handled = false; } } break; case _ME_L_hold: case _ME_doubleclick_hold: if (!*currItem) { return true; } if (menuItem::cursorInsideItem(myItem, x, y)) { tempFlags = menuItemVSlider::whereIsCursor(myItem, y - myItem->y1); if ((myItem->itemFlags & VS_COMPONENT) == tempFlags) { if (currTime - callbackTime > 6) { execCallback = true; } } } break; default: break; } // See if we need to redraw the vslider if (redrawItem) { (myItem->redraw)(myItem, myItem->myMenu, myItem->x1, myItem->y1, 0, 0); myScreen = vmng_screen_find(myItem->myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + myItem->x1, myScreen->y1 + myItem->y1, myScreen->x1 + myItem->x2, myScreen->y1 + myItem->y2); } } // See if we need to call the callback function if (execCallback && myItem->callback) { callbackTime = currTime; (myItem->callback)((void *)myItem, myItem->myMenu); myScreen = vmng_screen_find(myItem->myMenu, &status); if ((!myScreen) || (status != SCRN_ACTIVE)) { *currItem = nullptr; } } return handled; } void menuItemVSlider::disableVSlider(menuItemVSlider *myItem, int32 tag, guiMenu *myMenu) { // Verify params if (!myMenu) return; if (!myItem) myItem = (menuItemVSlider *)guiMenu::getItem(tag, myMenu); if (!myItem) return; myItem->itemFlags = menuItemVSlider::VS_GREY; } void menuItemVSlider::enableVSlider(menuItemVSlider *myItem, int32 tag, guiMenu *myMenu) { // Verify params if (!myMenu) return; if (!myItem) myItem = (menuItemVSlider *)guiMenu::getItem(tag, myMenu); if (!myItem) return; myItem->itemFlags = menuItemVSlider::VS_NORM; } //----------------------------- TEXTFIELD MENU ITEM ---------------------------------// void menuItemTextField::drawTextField(menuItemTextField *myItem, guiMenu *myMenu, int32 x, int32 y, int32, int32) { Buffer *backgroundBuff = nullptr; Sprite *mySprite; char tempStr[64]; // Verify params if (!myItem || !myMenu) return; // If the item is marked transparent, get the background buffer if (myItem->transparent) { if (!myItem->background) { return; } backgroundBuff = myItem->background->get_buffer(); if (!backgroundBuff) { return; } } // Select the sprite switch (myItem->itemFlags) { case TF_GREY: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LINE_NORM]; break; case TF_OVER: case TF_NORM: default: mySprite = _GM(menuSprites)[SaveLoadMenuBase::SL_LINE_OVER]; break; } // Get the menu buffer and draw the sprite to it Buffer *myBuff = myMenu->menuBuffer->get_buffer(); if (!myBuff) { return; } // If the item is tagged as transparent, we need to fill in it's background behind it if (backgroundBuff) { gr_buffer_rect_copy_2(backgroundBuff, myBuff, 0, 0, x, y, backgroundBuff->w, backgroundBuff->h); myItem->background->release(); } // Draw the item sprite in gui_DrawSprite(mySprite, myBuff, x, y); //write in the special tag gr_font_set_color(menuItem::TEXT_COLOR_NORM_FOREGROUND); Common::sprintf_s(tempStr, 64, "%02d", myItem->specialTag); gr_font_set(_GM(menuFont)); gr_font_write(myBuff, tempStr, x + 4, y + 1, 0, -1); //write in the text gr_font_write(myBuff, &myItem->prompt[0], x + 26, y + 1, 0, -1); if (myItem->itemFlags == TF_OVER) { // Draw in the cursor if (myItem->cursor) { const char tempChar = *myItem->cursor; *myItem->cursor = '\0'; const int32 cursorX = gr_font_string_width(&myItem->prompt[0], -1); *myItem->cursor = tempChar; gr_color_set(menuItem::TEXT_COLOR_OVER_FOREGROUND); gr_vline(myBuff, x + cursorX + 26, y + 1, y + 12); } } // Release the menu buffer myMenu->menuBuffer->release(); } bool menuItemTextField::handler(menuItemTextField *myItem, int32 eventType, int32 event, int32 x, int32 y, void **currItem) { ScreenContext *myScreen; int32 status, temp; char tempStr[80]; // Verify params if (!myItem) return false; if (myItem->itemFlags == TF_GREY) { return false; } bool redrawItem = false; bool execCallback = false; const bool handled = true; if (eventType == EVENT_MOUSE) { switch (event) { case _ME_L_click: case _ME_doubleclick: _GM(deleteSaveDesc) = false; if (menuItem::cursorInsideItem(myItem, x, y)) { *currItem = myItem; } break; case _ME_L_drag: case _ME_doubleclick_drag: break; case _ME_L_release: case _ME_doubleclick_release: if (!*currItem) { return true; } *currItem = nullptr; if (menuItem::cursorInsideItem(myItem, x, y)) { if (myItem->itemFlags == TF_OVER) { temp = strlen(myItem->prompt); if (temp > 0) { Common::strcpy_s(tempStr, myItem->prompt); char *tempPtr = &tempStr[temp]; gr_font_set(_GM(menuFont)); temp = gr_font_string_width(tempStr, -1); while ((tempPtr != &tempStr[0]) && (temp > x - myItem->x1 - 26)) { *--tempPtr = '\0'; temp = gr_font_string_width(tempStr, -1); } myItem->cursor = &myItem->prompt[tempPtr - &tempStr[0]]; redrawItem = true; } } else if (event == _ME_doubleclick_release) { execCallback = true; } } break; case _ME_move: case _ME_L_hold: case _ME_doubleclick_hold: default: break; } } else if ((eventType == EVENT_KEY) && (myItem->itemFlags == TF_OVER)) { switch (event) { case KEY_RETURN: _GM(deleteSaveDesc) = false; execCallback = true; break; case KEY_HOME: _GM(deleteSaveDesc) = false; myItem->cursor = &myItem->prompt[0]; redrawItem = true; break; case KEY_END: _GM(deleteSaveDesc) = false; myItem->cursor = myItem->promptEnd; redrawItem = true; break; case KEY_LEFT: _GM(deleteSaveDesc) = false; if (myItem->cursor > &myItem->prompt[0]) { myItem->cursor--; redrawItem = true; } break; case KEY_RIGHT: _GM(deleteSaveDesc) = false; if (myItem->cursor < myItem->promptEnd) { myItem->cursor++; redrawItem = true; } break; case KEY_DELETE: if (_GM(deleteSaveDesc)) { myItem->prompt[0] = '\0'; myItem->promptEnd = &myItem->prompt[0]; myItem->cursor = myItem->promptEnd; redrawItem = true; } else if (myItem->cursor < myItem->promptEnd) { Common::strcpy_s(tempStr, (char *)(myItem->cursor + 1)); Common::strcpy_s(myItem->cursor, 80, tempStr); myItem->promptEnd--; redrawItem = true; } break; case KEY_BACKSP: _GM(deleteSaveDesc) = false; if (myItem->cursor > &myItem->prompt[0]) { Common::strcpy_s(tempStr, myItem->cursor); myItem->promptEnd--; myItem->cursor--; Common::strcpy_s(myItem->cursor, 80, tempStr); redrawItem = true; } break; default: _GM(deleteSaveDesc) = false; gr_font_set(_GM(menuFont)); temp = gr_font_string_width(&myItem->prompt[0], -1); if ((strlen(&myItem->prompt[0]) < 79) && (temp < myItem->pixWidth - 12) && (event >= 32) && (event <= 127)) { if (myItem->cursor < myItem->promptEnd) { Common::strcpy_s(tempStr, (char *)myItem->cursor); Common::sprintf_s(myItem->cursor, 80, "%c%s", (char)event, tempStr); } else { *myItem->cursor = (char)event; *(myItem->cursor + 1) = '\0'; } myItem->cursor++; myItem->promptEnd++; redrawItem = true; } break; } } else if ((eventType == EVENT_KEY) && (event == KEY_RETURN)) { // The only events a NORM textfield can respond to are doubleclick_release and keypress execCallback = true; } else { // Otherwise the event will not be handled return false; } // See if we need to redraw the button if (redrawItem) { (myItem->redraw)(myItem, myItem->myMenu, myItem->x1, myItem->y1, 0, 0); myScreen = vmng_screen_find(myItem->myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + myItem->x1, myScreen->y1 + myItem->y1, myScreen->x1 + myItem->x2, myScreen->y1 + myItem->y2); } } // See if we need to call the callback function if (execCallback && myItem->callback) { (myItem->callback)((void *)myItem, myItem->myMenu); myScreen = vmng_screen_find(myItem->myMenu, &status); if ((!myScreen) || (status != SCRN_ACTIVE)) { *currItem = nullptr; } } return handled; } menuItemTextField *menuItemTextField::add(guiMenu *myMenu, int32 tag, int32 x, int32 y, int32 w, int32 h, int32 initFlags, const char *prompt, int32 specialTag, CALLBACK callback, bool transparent) { int32 status; // Verify params if (!myMenu) return nullptr; // Allocate a new one menuItemTextField *newItem = new menuItemTextField(); // Initialize the struct newItem->next = myMenu->itemList; newItem->prev = nullptr; if (myMenu->itemList) { myMenu->itemList->prev = newItem; } myMenu->itemList = newItem; newItem->myMenu = myMenu; newItem->tag = tag; newItem->x1 = x; newItem->y1 = y; newItem->x2 = x + w - 1; newItem->y2 = y + h - 1; newItem->callback = callback; if (!transparent) { newItem->transparent = false; newItem->background = nullptr; } else { newItem->transparent = true; newItem->background = guiMenu::copyBackground(myMenu, x, y, w, h); } menuItemTextField *textInfo = (menuItemTextField *)mem_alloc(sizeof(menuItemTextField), "menu item textfield"); if (textInfo == nullptr) { return nullptr; } textInfo->itemFlags = initFlags; textInfo->specialTag = specialTag; textInfo->pixWidth = w - 27; if (prompt) { Common::strcpy_s(&textInfo->prompt[0], 80, prompt); textInfo->promptEnd = &textInfo->prompt[strlen(prompt)]; } else { textInfo->prompt[0] = '\0'; textInfo->promptEnd = &textInfo->prompt[0]; } textInfo->cursor = textInfo->promptEnd; newItem->redraw = (DrawFunction)menuItemTextField::drawTextField; newItem->destroy = (DestroyFunction)menuItem::destroyItem; newItem->itemEventHandler = (ItemHandlerFunction)menuItemTextField::handler; // Draw the vslider in now (newItem->redraw)(newItem, myMenu, x, y, 0, 0); // See if the screen is currently visible ScreenContext *myScreen = vmng_screen_find(myMenu, &status); if (myScreen && (status == SCRN_ACTIVE)) { RestoreScreens(myScreen->x1 + newItem->x1, myScreen->y1 + newItem->y1, myScreen->x1 + newItem->x2, myScreen->y1 + newItem->y2); } return newItem; } } // namespace GUI } // namespace M4