/* 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 "m4/gui/gui_item.h" #include "m4/platform/keys.h" #include "m4/graphics/gr_draw.h" #include "m4/graphics/gr_line.h" #include "m4/graphics/gr_pal.h" #include "m4/mem/mem.h" #include "m4/vars.h" namespace M4 { #define _REDRAW_ALL 2 #define _REDRAW_ACTIVE 4 #define _INIT_LISTBOX 8 #define _LB_BLACK 0x00 #define _LB_LTGREY 0x07 #define _LB_DKGREY 0x08 #define _LB_WHITE 0x0f #define _LB_BROWN 0x07 #define STR_ITEM "gui item" #define STR_LIST "gui list item" #define STR_PROMPT "prompt" // Interface sprites #define scrollUpWidth 11 #define scrollUpHeight 12 static const byte scrollUpData[] = { _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BLACK, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_DKGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY }; static const Buffer scrollUpBuff = { scrollUpWidth, scrollUpHeight, const_cast(&scrollUpData[0]), 0, 0 }; static const byte scrollUpPressedData[] = { _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_LTGREY, _LB_BLACK, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_LTGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_LTGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_BLACK, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_BLACK, _LB_BLACK, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, }; static const Buffer scrollUpPressedBuff = { scrollUpWidth, scrollUpHeight, const_cast(&scrollUpPressedData[0]), 0, 0 }; #define scrollDownWidth 11 #define scrollDownHeight 12 static const byte scrollDownData[] = { _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BLACK, _LB_BLACK, _LB_BLACK, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BLACK, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY }; static const Buffer scrollDownBuff = { scrollDownWidth, scrollDownHeight, const_cast(&scrollDownData[0]), 0, 0 }; static const byte scrollDownPressedData[] = { _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_DKGREY, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BLACK, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BLACK, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_BLACK, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BLACK, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY }; static const Buffer scrollDownPressedBuff = { scrollDownWidth, scrollDownHeight, const_cast(&scrollDownPressedData[0]), 0, 0 }; #define thumbWidth 11 #define thumbHeight 11 static const byte thumbData[] = { _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_BLACK, _LB_BLACK, _LB_BLACK, _LB_BLACK, _LB_BLACK, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_BLACK, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY, _LB_LTGREY }; static const Buffer thumbBuff = { thumbWidth, thumbHeight, const_cast(&thumbData[0]), 0, 0 }; static const byte thumbPressedData[] = { _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_WHITE, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BROWN, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_BLACK, _LB_BLACK, _LB_BLACK, _LB_BLACK, _LB_BLACK, _LB_BLACK, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY, _LB_DKGREY }; static const Buffer thumbPressedBuff = { thumbWidth, thumbHeight, const_cast(&thumbPressedData[0]), 0, 0 }; #define minThumbY (scrollUpHeight + _G(items).buttonHeight) #define maxThumbY (scrollDownHeight + thumbHeight + (_G(items).buttonHeight<<1)) #define thumbYRange (scrollUpHeight + scrollUpHeight + thumbHeight + (_G(items).buttonHeight<<2) + _G(items).buttonHeight - 1) static void Item_Clear_origPrompt(); bool sizeofGUIelement_border(int16 el_type, int32 *w, int32 *h) { if (!w || !h) return false; if ((el_type == MESSAGE) || (el_type == PICTURE)) { *w = 0; *h = 0; } else { *w = 3; *h = 3; } return true; } bool sizeofGUIelement_interior(ButtonDrawRec *bdr, M4Rect *myRect) { if (!myRect || !bdr) return false; if ((bdr->el_type == MESSAGE) || (bdr->el_type == PICTURE)) { myRect->x1 = bdr->x1; myRect->y1 = bdr->y1; myRect->x2 = bdr->x2; myRect->y2 = bdr->y2; } else if ((bdr->el_type == TEXTFIELD) || (bdr->el_type == LISTBOX) || !bdr->pressed) { myRect->x1 = bdr->x1 + 2; myRect->y1 = bdr->y1 + 2; myRect->x2 = bdr->x2 - 1; myRect->y2 = bdr->y2 - 1; } else { myRect->x1 = bdr->x1 + 1; myRect->y1 = bdr->y1 + 1; myRect->x2 = bdr->x2 - 2; myRect->y2 = bdr->y2 - 2; } return true; } bool drawGUIelement(ButtonDrawRec *bdr, M4Rect *myRect) { if (!bdr) return false; const int32 x1 = bdr->x1; const int32 y1 = bdr->y1; const int32 x2 = bdr->x2; const int32 y2 = bdr->y2; const bool pressed = bdr->pressed; if (bdr->el_type == MESSAGE) { sizeofGUIelement_interior(bdr, myRect); return true; } int32 topLeftColor, interiorColor, bottomRightColor, bottomLeftPix, topRightPix; if ((bdr->el_type == TEXTFIELD) || (bdr->el_type == LISTBOX)) { if (!pressed) { topLeftColor = __DKGRAY; bottomRightColor = __WHITE; interiorColor = __LTGRAY; } else { topLeftColor = __BLACK; bottomRightColor = __LTGRAY; interiorColor = __DKGRAY; } bottomLeftPix = topLeftColor; topRightPix = bottomRightColor; } else { if (!pressed) { topLeftColor = __WHITE; interiorColor = __LTGRAY; bottomRightColor = __DKGRAY; bottomLeftPix = topLeftColor; topRightPix = bottomRightColor; } else { topLeftColor = __BLACK; interiorColor = __DKGRAY; bottomRightColor = __WHITE; bottomLeftPix = bottomRightColor; topRightPix = topLeftColor; } } if (bdr->fillMe == FILL_INTERIOR) { gr_color_set(interiorColor); gr_buffer_rect_fill(bdr->scrBuf, x1 + 1, y1 + 1, x2 - x1 - 1, y2 - y1 - 1); } gr_color_set(topLeftColor); gr_hline(bdr->scrBuf, x1, x2 - 1, y1); // Top inner line gr_vline(bdr->scrBuf, x1, y1 + 1, y2 - 1); // Left inner edge gr_color_set(bottomRightColor); gr_hline(bdr->scrBuf, x1 + 1, x2, y2); // Bottom outer edge gr_vline(bdr->scrBuf, x2, y1 + 1, y2 - 1); // Right outer edge buffer_put_pixel(bdr->scrBuf, x1, y2, (byte)bottomLeftPix); // Bottom left pixel buffer_put_pixel(bdr->scrBuf, x2, y1, (byte)topRightPix); // Top right pixel sizeofGUIelement_interior(bdr, myRect); return true; } bool InitItems() { _G(items).origPrompt = nullptr; _G(items).undoPrompt = nullptr; _G(items).currTextField = nullptr; _G(items).clipBoard[0] = '\0'; _G(items).clipBoard[99] = '\0'; if (!sizeofGUIelement_border(BUTTON, &_G(items).buttonWidth, &_G(items).buttonHeight)) return false; return true; } Item *Item_create(Item *parent, enum ItemType type, int32 tag, M4CALLBACK cb) { Item *temp = (Item *)mem_alloc(sizeof(Item), STR_ITEM); if (temp == nullptr) return nullptr; temp->callback = cb; temp->type = type; temp->prompt = nullptr; temp->tag = tag; temp->x = temp->y = temp->w = temp->h = 0; temp->myList = nullptr; // add to end of list if (parent) { while (parent->next) parent = parent->next; parent->next = temp; temp->prev = parent; } else temp->prev = nullptr; temp->next = nullptr; return temp; } void Item_destroy(Item *myItem) { ListItem *myList = myItem->myList; ListItem *tempListItem = myList; while (tempListItem) { myList = myList->next; mem_free((void *)tempListItem); tempListItem = myList; } if (myItem->prompt) mem_free(myItem->prompt); mem_free((void *)myItem); } void Item_empty_list(Item *myItem) { ListItem *myList = myItem->myList; ListItem *tempListItem = myList; while (tempListItem) { myList = myList->next; mem_free((void *)tempListItem); tempListItem = myList; } myItem->myList = nullptr; myItem->currItem = nullptr; myItem->viewTop = nullptr; myItem->viewBottom = nullptr; myItem->myListCount = 0; myItem->viewIndex = 0; myItem->thumbY = minThumbY; } static int32 item_string_width(char *myStr, int32 spacing) { char *tempPtr, highlightChar[2]; highlightChar[0] = '~'; highlightChar[1] = '\0'; if (!myStr) return 0; int32 column = 0; char *tempPtr2 = strrchr(myStr, '^'); if (tempPtr2 != nullptr) { *tempPtr2 = '\0'; tempPtr = strrchr(myStr, '^'); if (tempPtr == nullptr) return 0; tempPtr++; column = (int32)atoi(tempPtr); *tempPtr2 = '^'; tempPtr2++; } else tempPtr2 = myStr; tempPtr = strchr(tempPtr2, '~'); int32 highlightNum = 0; while (tempPtr) { highlightNum++; char *tempPtr3 = tempPtr + 1; tempPtr = strchr(tempPtr3, '~'); } const int32 strWidth = column + gr_font_string_width(tempPtr2, spacing) - (gr_font_string_width(highlightChar, spacing) * highlightNum); return strWidth; } static int32 item_string_write(Buffer *target, char *myStr, int32 x, int32 y, int32 w, int32 spacing, int32 color, int32 highlight) { char tempStr[2]; if (!target) return false; char *tempPtr = strchr(myStr, '^'); if (tempPtr == nullptr) { tempPtr = strchr(myStr, '~'); if (tempPtr == nullptr) { gr_font_set_color((char)color); gr_font_write(target, myStr, x, y, w, spacing); return true; } } tempStr[1] = '\0'; int32 column = 0; char *strPtr = myStr; while (strPtr) { column = 0; if (*strPtr == '^') { if ((tempPtr = strchr((char *)(strPtr + 1), '^')) == nullptr) return false; *tempPtr = '\0'; strPtr++; column = (int32)atoi(strPtr); *tempPtr = '^'; strPtr = tempPtr + 1; } int32 currX = x + column; char *nextStrPtr = strchr(strPtr, '^'); if (nextStrPtr) *nextStrPtr = '\0'; char *highlightPtr = strchr(strPtr, '~'); while (highlightPtr) { if (highlightPtr != strPtr) { *highlightPtr = '\0'; gr_font_set_color((char)color); gr_font_write(target, strPtr, currX, y, 0, spacing); currX += gr_font_string_width(strPtr, spacing); *highlightPtr = '~'; } if (*(highlightPtr + 1) == '\0') return true; tempStr[0] = *(highlightPtr + 1); gr_font_set_color((char)highlight); gr_font_write(target, tempStr, currX, y, 0, spacing); currX += gr_font_string_width(tempStr, spacing); strPtr = highlightPtr + 2; highlightPtr = strchr(strPtr, '~'); } if (*strPtr != '\0') { gr_font_set_color((char)color); gr_font_write(target, strPtr, currX, y, 0, spacing); } if (nextStrPtr) *nextStrPtr = '^'; strPtr = nextStrPtr; } return true; } static void CorrectItemWidthHeight(Item *item, int32 fontHeight) { if (!item) return; int32 tempWidth, tempHeight, minWidth, minHeight; switch (item->type) { case LISTBOX: minHeight = scrollUpHeight + thumbHeight + scrollDownHeight + _G(items).buttonHeight * 3; //scrollup + thumb + scrolldown heights + (newHeight for each) if (!sizeofGUIelement_border(LISTBOX, &tempWidth, &tempHeight)) return; minWidth = scrollUpWidth + thumbWidth + scrollDownWidth + _G(items).buttonWidth * 3 + (tempWidth + 1); //same as above + width of listbox if (item->h < minHeight) item->h = minHeight; if (item->w < minWidth) item->w = minWidth; break; case MESSAGE: if (!sizeofGUIelement_border(MESSAGE, &tempWidth, &tempHeight)) return; item->w = item_string_width(item->prompt, 1) + tempWidth; item->h = fontHeight + tempHeight; break; case TEXTFIELD: if (!sizeofGUIelement_border(TEXTFIELD, &tempWidth, &tempHeight)) return; item->h = fontHeight + tempHeight + 1; break; case BUTTON: case REPEAT_BUTTON: if (!sizeofGUIelement_border(BUTTON, &tempWidth, &tempHeight)) return; item->w = item_string_width(item->prompt, 1) + tempWidth; item->h = fontHeight + tempHeight + 1; break; case PICTURE: default: break; } } Item *ItemAdd(Item *itemList, int32 x, int32 y, int32 w, int32 h, const char *prompt, int32 tag, enum ItemType type, M4CALLBACK cb, int32 promptMax) { int32 listboxWidth, listboxHeight; Item *item = Item_create(itemList, type, tag, cb); if (item == nullptr) return nullptr; Font *myFont = gr_font_get(); const int32 fontHeight = gr_font_get_height(); item->myFont = myFont; item->x = x; item->y = y; item->w = w; item->h = h; item->status = ITEM_NORMAL; item->callback = cb; switch (type) { case LISTBOX: item->prompt = nullptr; item->myListCount = 0; item->viewIndex = 0; item->thumbY = minThumbY; item->myList = nullptr; item->currItem = nullptr; item->viewTop = nullptr; item->viewBottom = nullptr; break; case PICTURE: item->myFont = nullptr; item->prompt = nullptr; // FIXME: Refactor out const_cast item->aux = const_cast(prompt); break; case TEXTFIELD: if ((int)strlen(prompt) > (promptMax + 1)) item->promptMax = strlen(prompt) + 1; else item->promptMax = promptMax + 1; item->prompt = (char *)mem_alloc(item->promptMax, STR_PROMPT); Common::strcpy_s(item->prompt, 256, prompt); item->aux = &(item->prompt[strlen(item->prompt)]); item->aux2 = item->aux; break; case MESSAGE: case BUTTON: case REPEAT_BUTTON: item->prompt = mem_strdup(prompt); break; default: break; } CorrectItemWidthHeight(item, fontHeight); if (type == LISTBOX) { if (!sizeofGUIelement_border(LISTBOX, &listboxWidth, &listboxHeight)) { Item_destroy(item); return nullptr; } item->listView = (item->h - listboxHeight - 2) / fontHeight; } return item; } Item *ItemFind(Item *itemList, int32 tag) { if (!itemList) return nullptr; if (tag > 0) { while (itemList && (itemList->tag != tag)) itemList = itemList->next; } else { itemList = nullptr; } return itemList; } bool Item_SetViewBottom(Item *i) { int32 count; bool found = false; if (!i || !i->viewTop) return false; ListItem *myListItem = i->viewTop; for (count = 1; (count < i->listView) && myListItem->next; count++) { if (myListItem == i->currItem) found = true; myListItem = myListItem->next; } if (myListItem == i->currItem) found = true; if (count == i->listView) i->viewBottom = myListItem; else i->viewBottom = nullptr; return found; } static void CalculateViewIndex(Item *myItem) { if (!myItem || !myItem->viewTop) return; int32 i = 0; ListItem *myListItem = myItem->myList; while (myListItem != myItem->viewTop) { i++; myListItem = myListItem->next; } myItem->viewIndex = i; if (myItem->myListCount > myItem->listView) { if (myItem->viewBottom && myItem->viewBottom->next) { myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } else myItem->thumbY = myItem->h - maxThumbY; } else myItem->thumbY = minThumbY; } static void SetViewIndex(Item *myItem) { if (!myItem || !myItem->myList) return; if (myItem->thumbY == myItem->h - maxThumbY) { myItem->viewIndex = myItem->myListCount - myItem->listView; } else { myItem->viewIndex = ((myItem->thumbY - minThumbY) * (myItem->myListCount - myItem->listView)) / (myItem->h - thumbYRange); } ListItem *myListItem = myItem->myList; for (int32 i = 0; i < myItem->viewIndex; i++) { myListItem = myListItem->next; } myItem->viewTop = myListItem; Item_SetViewBottom(myItem); } bool ListItemExists(Item *myItem, char *prompt, int32 listTag) { if (!myItem) return false; ListItem *myListItems = myItem->myList; if (prompt) { while (myListItems && strcmp(myListItems->prompt, prompt)) { myListItems = myListItems->next; } } else { while (myListItems && (myListItems->tag != listTag)) { myListItems = myListItems->next; } } if (myListItems) return true; return false; } bool ListItemAdd(Item *myItem, char *prompt, int32 listTag, int32 addMode, ListItem *changedItem) { ListItem *newListItem; if (!myItem) return false; if (changedItem) newListItem = changedItem; else { newListItem = (ListItem *)mem_alloc(sizeof(ListItem), STR_LIST); if (newListItem == nullptr) return false; Common::strlcpy(newListItem->prompt, prompt, 80); newListItem->tag = listTag; } //Add it into the list in the correct place... ListItem *myList = myItem->myList; if (!myList) { newListItem->prev = nullptr; newListItem->next = nullptr; myItem->myList = newListItem; myItem->currItem = newListItem; myItem->viewTop = newListItem; myItem->viewIndex = 0; myItem->thumbY = minThumbY; } else { switch (addMode) { case LIST_SEQUN: while (myList->next) myList = myList->next; myList->next = newListItem; newListItem->prev = myList; newListItem->next = nullptr; break; case LIST_ALPH: if (strcmp(newListItem->prompt, myList->prompt) <= 0) { //add to front newListItem->prev = nullptr; newListItem->next = myList; myList->prev = newListItem; myItem->myList = newListItem; myItem->currItem = newListItem; myItem->viewTop = newListItem; } else { while (myList->next && strcmp(newListItem->prompt, myList->next->prompt) > 0) { myList = myList->next; } if (myList->next) { newListItem->next = myList->next; newListItem->prev = myList; myList->next->prev = newListItem; myList->next = newListItem; } else { newListItem->next = nullptr; newListItem->prev = myList; myList->next = newListItem; } } break; case LIST_BY_TAG: default: if (newListItem->tag <= myList->tag) { //add to front newListItem->prev = nullptr; newListItem->next = myList; myList->prev = newListItem; myItem->myList = newListItem; myItem->currItem = newListItem; myItem->viewTop = newListItem; } else { while (myList->next && (newListItem->tag > myList->next->tag)) { myList = myList->next; } if (myList->next) { newListItem->next = myList->next; newListItem->prev = myList; myList->next->prev = newListItem; myList->next = newListItem; } else { newListItem->next = nullptr; newListItem->prev = myList; myList->next = newListItem; } } break; } if (changedItem) { myItem->currItem = myList; myItem->viewTop = myList; } } myItem->myListCount++; if (!Item_SetViewBottom(myItem)) ViewCurrListItem(myItem); else CalculateViewIndex(myItem); return true; } bool ListItemDelete(Item *myItem, ListItem *myListItem, int32 listTag) { ListItem *myList; if (!myItem) return false; if (!myListItem) { myList = myItem->myList; while (myList && (myList->tag != listTag)) myList = myList->next; } else myList = myListItem; if (!myList) return false; if (myList == myItem->myList) { //first in the list... myItem->myList = myItem->myList->next; if (myItem->myList) myItem->myList->prev = nullptr; } else { myList->prev->next = myList->next; if (myList->next) myList->next->prev = myList->prev; } if (myList == myItem->currItem) { if (myList->next) myItem->currItem = myList->next; else myItem->currItem = myList->prev; } if (myList == myItem->viewTop) { if (myItem->viewTop->prev) myItem->viewTop = myItem->viewTop->prev; else myItem->viewTop = myItem->viewTop->next; } myItem->myListCount--; Item_SetViewBottom(myItem); if (!myItem->viewBottom) ViewCurrListItem(myItem); else CalculateViewIndex(myItem); mem_free((void *)myList); return true; } bool ListItemChange(Item *myItem, ListItem *myListItem, int32 listTag, char *newPrompt, int32 newTag, int32 changeMode) { ListItem *myList; if (!myItem) return false; if (!myListItem) { myList = myItem->myList; while (myList && (myList->tag != listTag)) myList = myList->next; } else myList = myListItem; if (!myList) return false; if (!strcmp(myList->prompt, newPrompt) && myList->tag == newTag) return false; Common::strcpy_s(myList->prompt, newPrompt); const int32 oldTag = myList->tag; myList->tag = newTag; if (((changeMode == LIST_BY_TAG) && (oldTag != newTag)) || (changeMode == LIST_ALPH)) { if (myList == myItem->myList) { //first in the list... myItem->myList = myItem->myList->next; if (myItem->myList) myItem->myList->prev = nullptr; } else { myList->prev->next = myList->next; if (myList->next) myList->next->prev = myList->prev; } ListItemAdd(myItem, nullptr, 0, changeMode, myList); } return true; } bool GetNextListItem(Item *myItem) { if (myItem->currItem) { ListItem *nextItem = myItem->currItem->next; if (!nextItem) return false; if (myItem->currItem == myItem->viewBottom) { myItem->viewBottom = nextItem; myItem->viewTop = myItem->viewTop->next; myItem->viewIndex++; if (myItem->viewBottom && myItem->viewBottom->next) { myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } else myItem->thumbY = myItem->h - maxThumbY; } myItem->currItem = nextItem; return true; } return false; } bool GetNextPageList(Item *myItem) { bool changed = false; if (myItem->currItem && myItem->viewBottom) { for (int32 i = 0; i < (myItem->listView - 1); i++) { if (myItem->viewBottom->next) { if (myItem->currItem == myItem->viewTop) myItem->currItem = myItem->currItem->next; myItem->viewTop = myItem->viewTop->next; myItem->viewBottom = myItem->viewBottom->next; myItem->viewIndex++; changed = true; } else break; } if (myItem->viewBottom && myItem->viewBottom->next) { myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } else myItem->thumbY = myItem->h - maxThumbY; } return changed; } bool GetPrevListItem(Item *myItem) { if (myItem->currItem) { ListItem *prevItem = myItem->currItem->prev; if (!prevItem) return false; if (myItem->currItem == myItem->viewTop) { myItem->viewTop = prevItem; myItem->viewBottom = myItem->viewBottom->prev; myItem->viewIndex--; myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } myItem->currItem = prevItem; return true; } return false; } bool GetPrevPageList(Item *myItem) { bool changed = false; if (myItem->currItem && myItem->viewBottom) { for (int32 i = 0; i < (myItem->listView - 1); i++) { if (myItem->viewTop->prev) { if (myItem->currItem == myItem->viewBottom) myItem->currItem = myItem->currItem->prev; myItem->viewTop = myItem->viewTop->prev; myItem->viewBottom = myItem->viewBottom->prev; myItem->viewIndex--; changed = true; } else break; } myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } return changed; } void ViewCurrListItem(Item *myItem) { bool breakFlag = false; if (!myItem->currItem) { myItem->viewTop = nullptr; myItem->viewBottom = nullptr; return; } myItem->viewTop = myItem->currItem; ListItem *tempItem = myItem->currItem; int32 i = myItem->listView - 2; while (tempItem->next && (i > 0)) { tempItem = tempItem->next; i--; } if (i > 0) { int32 j = 0; while ((j <= i) && !breakFlag) { if (myItem->viewTop->prev) { myItem->viewTop = myItem->viewTop->prev; j++; } else breakFlag = true; } if (j > i) myItem->viewBottom = tempItem; else myItem->viewBottom = nullptr; } else if (!tempItem->next) { if (myItem->viewTop->prev) { myItem->viewTop = myItem->viewTop->prev; myItem->viewBottom = tempItem; } else myItem->viewBottom = nullptr; } else myItem->viewBottom = tempItem->next; CalculateViewIndex(myItem); } ListItem *ListItemFind(Item *myItem, int32 searchMode, char *searchStr, int32 parm1) { if (!myItem) return nullptr; ListItem *myList = myItem->myList; if (searchMode == LIST_BY_TAG) { while (myList && (myList->tag != parm1)) myList = myList->next; } else if (searchMode == LIST_ALPH) { while (myList && scumm_strnicmp(myList->prompt, searchStr, strlen(searchStr))) myList = myList->next; } else if (searchMode == LIST_SEQUN) { int32 i = 0; while (myList && (i < parm1)) { myList = myList->next; i++; } } else return nullptr; return myList; } bool ListItemSearch(Item *myItem, int32 searchMode, char *searchStr, int32 parm1) { ListItem *myList = ListItemFind(myItem, searchMode, searchStr, parm1); if (!myList) { myItem->currItem = myItem->myList; myItem->viewTop = myItem->myList; Item_SetViewBottom(myItem); CalculateViewIndex(myItem); return false; } myItem->currItem = myList; if (!Item_SetViewBottom(myItem)) ViewCurrListItem(myItem); else CalculateViewIndex(myItem); return true; } bool DoubleClickOnListBox(Item *myItem, int32 xOffset, int32 yOffset) { int32 listboxContentX2; M4Rect interiorRect; ButtonDrawRec bdr; if (!myItem) return false; Font *currFont = gr_font_get(); if (currFont != myItem->myFont) gr_font_set(myItem->myFont); const int32 fontHeight = gr_font_get_height(); if (currFont != myItem->myFont) gr_font_set(currFont); if (myItem->myListCount > myItem->listView) { listboxContentX2 = myItem->w - scrollUpWidth - _G(items).buttonWidth - 1; } else listboxContentX2 = myItem->w - 1; bdr.el_type = LISTBOX; bdr.pressed = true; //since this procedure will only be called myItem is the default item bdr.x1 = 0; bdr.y1 = 0; bdr.x2 = listboxContentX2; bdr.y2 = myItem->h - 1; if (!sizeofGUIelement_interior(&bdr, &interiorRect)) return false; xOffset -= interiorRect.x1; yOffset -= interiorRect.y1; listboxContentX2 = interiorRect.x2 - interiorRect.x1; if ((xOffset < 0) || (xOffset > listboxContentX2)) return false; if ((yOffset < 0) || (yOffset > fontHeight *myItem->listView)) return false; int32 itemOffset = 0; ListItem *myListItem = myItem->viewTop; while (myListItem && (itemOffset + fontHeight <= yOffset)) { myListItem = myListItem->next; itemOffset += fontHeight; } if (!myListItem) return false; myItem->currItem = myListItem; return true; } bool ClickOnListBox(Item *myItem, int32 xOffset, int32 yOffset, int32 scrollType) { int32 boxWidth; ListItem *myListItem; bool scrollable = false; bool changed = false; int32 i; static int32 thumbOffset; M4Rect interiorRect; ButtonDrawRec bdr; if (!myItem) return false; if (myItem->myListCount > myItem->listView) { scrollable = true; boxWidth = myItem->w - (scrollUpHeight + _G(items).buttonHeight + 1); if (myItem->status & THUMB_PRESSED) { int32 newThumbY = yOffset - thumbOffset; if (newThumbY < minThumbY) newThumbY = minThumbY; else if (newThumbY > (myItem->h - maxThumbY)) newThumbY = myItem->h - maxThumbY; myItem->thumbY = newThumbY; SetViewIndex(myItem); return true; } if (scrollType && (!(myItem->status & BOX_PRESSED)) && (xOffset >= (myItem->w - scrollUpWidth - _G(items).buttonWidth)) && (xOffset < myItem->w)) { if ((yOffset >= 0) && (yOffset <= (scrollUpHeight + _G(items).buttonHeight - 1))) { if (((myItem->status & AREA_PRESSED) == 0) || (myItem->status & SU_PRESSED)) { myItem->status = SU_PRESSED; if (myItem->viewTop->prev) { myItem->viewTop = myItem->viewTop->prev; myItem->viewBottom = myItem->viewBottom->prev; myItem->viewIndex--; myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); changed = true; } else changed = false; } else changed = false; } else if ((yOffset >= (scrollUpHeight + _G(items).buttonHeight)) && (yOffset < myItem->thumbY)) { if ((scrollType & PAGEABLE) && ( (myItem->status & AREA_PRESSED) == 0 || (myItem->status & PU_PRESSED))) { myItem->status = PU_PRESSED; for (i = 0; i < myItem->listView - 1; i++) { if (myItem->viewTop->prev) { myItem->viewTop = myItem->viewTop->prev; myItem->viewBottom = myItem->viewBottom->prev; myItem->viewIndex--; changed = true; } else break; } myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } else changed = false; } else if ((yOffset >= (myItem->h - (scrollDownHeight + _G(items).buttonHeight))) && (yOffset < myItem->h)) { if (((myItem->status & AREA_PRESSED) == 0) || (myItem->status & SD_PRESSED)) { myItem->status = SD_PRESSED; if (myItem->viewBottom->next) { myItem->viewTop = myItem->viewTop->next; myItem->viewBottom = myItem->viewBottom->next; myItem->viewIndex++; if (myItem->viewBottom && myItem->viewBottom->next) { myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } else myItem->thumbY = myItem->h - maxThumbY; changed = true; } else changed = false; } else changed = false; } else if ((scrollType & PAGEABLE) && (yOffset > (myItem->thumbY + thumbHeight + _G(items).buttonHeight)) && (yOffset < (myItem->h - scrollDownHeight - _G(items).buttonHeight))) { if (((myItem->status & AREA_PRESSED) == 0) || (myItem->status & PD_PRESSED)) { myItem->status = PD_PRESSED; for (i = 0; i < myItem->listView - 1; i++) { if (myItem->viewBottom->next) { myItem->viewTop = myItem->viewTop->next; myItem->viewBottom = myItem->viewBottom->next; myItem->viewIndex++; changed = true; } else break; } if (myItem->viewBottom && myItem->viewBottom->next) { myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } else myItem->thumbY = myItem->h - maxThumbY; } else changed = false; } else if (((myItem->status & AREA_PRESSED) == 0) && (yOffset >= (myItem->thumbY)) && (yOffset < (myItem->thumbY + thumbHeight + _G(items).buttonHeight))) { myItem->status = THUMB_PRESSED; thumbOffset = yOffset - myItem->thumbY; } else changed = false; return changed; } } else boxWidth = myItem->w - 1; if ((xOffset < 0) || (xOffset > boxWidth)) return false; if (((myItem->status & AREA_PRESSED) == 0) || (myItem->status & BOX_PRESSED)) { myItem->status = (BOX_PRESSED | ITEM_PRESSED); Font *currFont = gr_font_get(); if (myItem->myFont != currFont) gr_font_set(myItem->myFont); const int32 fontHeight = gr_font_get_height(); if (myItem->myFont != currFont) gr_font_set(currFont); bdr.el_type = LISTBOX; bdr.pressed = true; //since this procedure will only be called myItem is the default item bdr.x1 = 0; bdr.y1 = 0; bdr.x2 = myItem->w - 1; bdr.y2 = myItem->h - 1; if (!sizeofGUIelement_interior(&bdr, &interiorRect)) return false; yOffset -= interiorRect.y1; if (yOffset < 0) { if (scrollType && scrollable && myItem->viewTop->prev) { myItem->viewTop = myItem->viewTop->prev; myItem->viewBottom = myItem->viewBottom->prev; myItem->viewIndex--; myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } myListItem = myItem->viewTop; } else if (yOffset >= fontHeight * myItem->listView) { if (scrollType && scrollable && myItem->viewBottom->next) { myItem->viewTop = myItem->viewTop->next; myItem->viewBottom = myItem->viewBottom->next; myItem->viewIndex++; if (myItem->viewBottom && myItem->viewBottom->next) { myItem->thumbY = minThumbY + (((myItem->h - thumbYRange) * myItem->viewIndex) / (myItem->myListCount - myItem->listView)); } else myItem->thumbY = myItem->h - maxThumbY; } myListItem = myItem->viewBottom; } else { int32 itemOffset = 0; myListItem = myItem->viewTop; while (myListItem && (itemOffset + fontHeight <= yOffset)) { myListItem = myListItem->next; itemOffset += fontHeight; } } if ((!myListItem) || (myListItem == myItem->currItem)) return false; myItem->currItem = myListItem; return true; } return false; } bool ResetDefaultListBox(Item *myItem) { bool changed; if (!myItem) return false; if ((myItem->status & AREA_PRESSED) == 0) changed = false; else if (myItem->status & BOX_PRESSED) changed = false; else changed = true; myItem->status = ITEM_NORMAL; return changed; } bool Item_change_prompt(Item *myItem, const char *newPrompt) { if (!strcmp(myItem->prompt, newPrompt)) return false; if (myItem->type == TEXTFIELD) { if ((int)strlen(newPrompt) >= myItem->promptMax) myItem->promptMax = strlen(newPrompt) + 1; Common::strcpy_s(myItem->prompt, 256, newPrompt); myItem->aux = &(myItem->prompt[strlen(myItem->prompt)]); myItem->aux2 = myItem->aux; Item_Clear_origPrompt(); } else if (myItem->type == LISTBOX) { return false; } else if (strlen(myItem->prompt) < strlen(newPrompt)) { mem_free(myItem->prompt); myItem->prompt = mem_strdup(newPrompt); } else Common::strcpy_s(myItem->prompt, 256, newPrompt); Font *currFont = gr_font_get(); if (myItem->myFont != currFont) gr_font_set(myItem->myFont); const int32 fontHeight = gr_font_get_height(); CorrectItemWidthHeight(myItem, fontHeight); if (myItem->myFont != currFont) gr_font_set(currFont); return true; } static void Item_Clear_origPrompt() { if (_G(items).origPrompt) { mem_free(_G(items).origPrompt); _G(items).origPrompt = nullptr; } if (_G(items).undoPrompt) { mem_free(_G(items).undoPrompt); _G(items).undoPrompt = nullptr; } } Item *Item_RestoreTextField(void) { if (!_G(items).origPrompt) return nullptr; Common::strcpy_s(_G(items).currTextField->prompt, 256, _G(items).origPrompt); _G(items).currTextField->aux = &(_G(items).currTextField->prompt[strlen(_G(items).currTextField->prompt)]); _G(items).currTextField->aux2 = _G(items).currTextField->aux; mem_free(_G(items).origPrompt); _G(items).origPrompt = nullptr; if (_G(items).undoPrompt) { mem_free(_G(items).undoPrompt); _G(items).undoPrompt = nullptr; } return _G(items).currTextField; } Item *Item_CheckTextField(void) { Item *myItem = nullptr; if (_G(items).origPrompt) { if (strcmp(_G(items).currTextField->prompt, _G(items).origPrompt)) myItem = _G(items).currTextField; Item_Clear_origPrompt(); } return myItem; } void Item_SaveTextField(Item *myItem) { _G(items).origPrompt = mem_strdup(myItem->prompt); _G(items).currTextField = myItem; } static void Item_SaveTextFieldChange(Item *myItem, bool majorChange) { if (_G(items).undoPrompt && (!majorChange)) return; if (_G(items).undoPrompt) mem_free(_G(items).undoPrompt); _G(items).undoPrompt = mem_strdup(myItem->prompt); _G(items).undoAux = _G(items).undoPrompt + (myItem->aux - myItem->prompt); _G(items).undoAux2 = _G(items).undoPrompt + (myItem->aux2 - myItem->prompt); } static bool Item_UndoTextFieldChange(void) { if (_G(items).undoPrompt) { char *tempBuf = mem_strdup(_G(items).currTextField->prompt); char *tempAux = tempBuf + (_G(items).currTextField->aux - _G(items).currTextField->prompt); char *tempAux2 = tempBuf + (_G(items).currTextField->aux2 - _G(items).currTextField->prompt); Common::strcpy_s(_G(items).currTextField->prompt, 256, _G(items).undoPrompt); _G(items).currTextField->aux = _G(items).currTextField->prompt + (_G(items).undoAux - _G(items).undoPrompt); _G(items).currTextField->aux2 = _G(items).currTextField->prompt + (_G(items).undoAux2 - _G(items).undoPrompt); mem_free(_G(items).undoPrompt); _G(items).undoPrompt = tempBuf; _G(items).undoAux = tempAux; _G(items).undoAux2 = tempAux2; return true; } return false; } void SetTextBlockBegin(Item *myItem, int32 relXPos) { bool finished = false; M4Rect interiorRect; ButtonDrawRec bdr; bdr.el_type = TEXTFIELD; bdr.pressed = true; //since this procedure will only be called myItem is the default item bdr.x1 = 0; bdr.y1 = 0; bdr.x2 = myItem->w - 1; bdr.y2 = myItem->h - 1; if (!sizeofGUIelement_interior(&bdr, &interiorRect)) return; relXPos -= interiorRect.x1; if (relXPos <= 0) myItem->aux = myItem->prompt; else if (relXPos >= item_string_width(myItem->prompt, 1)) { myItem->aux = &(myItem->prompt[strlen(myItem->prompt)]); } else { char *scan = &(myItem->prompt[1]); finished = false; while (!finished) { const char myChar = *scan; *scan = '\0'; if (item_string_width(myItem->prompt, 1) > relXPos) { myItem->aux = scan - 1; finished = true; } *scan++ = myChar; } } myItem->aux2 = myItem->aux; } void SetTextBlockEnd(Item *myItem, int32 relXPos) { bool finished = false; M4Rect interiorRect; ButtonDrawRec bdr; bdr.el_type = TEXTFIELD; bdr.pressed = true; //since this procedure will only be called myItem is the default item bdr.x1 = 0; bdr.y1 = 0; bdr.x2 = myItem->w - 1; bdr.y2 = myItem->h - 1; if (!sizeofGUIelement_interior(&bdr, &interiorRect)) return; relXPos -= interiorRect.x1; if (relXPos <= 0) myItem->aux2 = myItem->prompt; else if (relXPos >= item_string_width(myItem->prompt, 1)) { myItem->aux2 = &(myItem->prompt[strlen(myItem->prompt)]); } else { char *scan = &(myItem->prompt[1]); finished = false; while (!finished) { char myChar = *scan; *scan = '\0'; if (item_string_width(myItem->prompt, 1) > relXPos) { myItem->aux2 = scan - 1; finished = true; } *scan++ = myChar; } } } static int32 CopyTextBlock(Item *myItem) { int32 numOfCopiedChars = 0; const char *beginBlock, *endBlock; if (myItem->aux != myItem->aux2) { if (myItem->aux < myItem->aux2) { beginBlock = myItem->aux; endBlock = myItem->aux2; } else { beginBlock = myItem->aux2; endBlock = myItem->aux; } numOfCopiedChars = endBlock - beginBlock; Common::strlcpy(_G(items).clipBoard, beginBlock, 100); if (endBlock - beginBlock <= 99) { _G(items).clipBoard[endBlock - beginBlock] = '\0'; } else numOfCopiedChars = 99; } return numOfCopiedChars; } static int32 DeleteTextBlock(Item *myItem) { int32 numOfDeletedChars = 0; char tempBuf[80]; char *beginBlock, *endBlock; if (myItem->aux != myItem->aux2) { Item_SaveTextFieldChange(myItem, true); if (myItem->aux < myItem->aux2) { beginBlock = myItem->aux; endBlock = myItem->aux2; } else { beginBlock = myItem->aux2; endBlock = myItem->aux; } numOfDeletedChars = endBlock - beginBlock; Common::strcpy_s(tempBuf, endBlock); Common::strcpy_s(beginBlock, 80, tempBuf); myItem->aux = beginBlock; myItem->aux2 = beginBlock; } return numOfDeletedChars; } bool Item_TextEdit(Item *myItem, int32 parm1) { char tempBuf[80]; bool absorbed = true; switch (parm1) { case KEY_LEFT: if (myItem->aux < myItem->aux2) myItem->aux2 = myItem->aux; else if (myItem->aux > myItem->aux2) myItem->aux = myItem->aux2; else if (myItem->aux != myItem->prompt) { myItem->aux--; myItem->aux2--; } break; case KEY_RIGHT: if (myItem->aux < myItem->aux2) myItem->aux = myItem->aux2; else if (myItem->aux > myItem->aux2) myItem->aux2 = myItem->aux; else if (*(myItem->aux) != '\0') { myItem->aux++; myItem->aux2++; } break; case KEY_ALT_LEFT: if (myItem->aux2 != myItem->prompt) { myItem->aux2--; } break; case KEY_ALT_RIGHT: if (*(myItem->aux2) != '\0') { myItem->aux2++; } break; case KEY_ALT_UP: if (myItem->aux2 != myItem->prompt) { myItem->aux2 = myItem->prompt; } break; case KEY_ALT_DOWN: if (*(myItem->aux2) != '\0') { myItem->aux2 = &(myItem->prompt[strlen(myItem->prompt)]); } break; case KEY_HOME: myItem->aux = myItem->prompt; myItem->aux2 = myItem->aux; break; case KEY_END: myItem->aux = &(myItem->prompt[strlen(myItem->prompt)]); myItem->aux2 = myItem->aux; break; case KEY_DELETE: if (!_G(items).origPrompt) Item_SaveTextField(myItem); if ((DeleteTextBlock(myItem) <= 0) && (*(myItem->aux) != '\0')) { Item_SaveTextFieldChange(myItem, false); if (*(myItem->aux + 1) == '\0') { *(myItem->aux) = '\0'; } else { Common::strcpy_s(tempBuf, myItem->aux + 1); Common::strcpy_s(myItem->aux, 80, tempBuf); } myItem->aux2 = myItem->aux; } break; case KEY_BACKSP: if (!_G(items).origPrompt) Item_SaveTextField(myItem); if ((DeleteTextBlock(myItem) <= 0) && (myItem->aux != myItem->prompt)) { Item_SaveTextFieldChange(myItem, false); if (*(myItem->aux) == '\0') { myItem->aux--; *(myItem->aux) = '\0'; } else { Common::strcpy_s(tempBuf, myItem->aux); myItem->aux--; Common::strcpy_s(myItem->aux, 80, tempBuf); } myItem->aux2 = myItem->aux; } break; case KEY_ALT_X: if (!_G(items).origPrompt) Item_SaveTextField(myItem); if (CopyTextBlock(myItem) > 0) { DeleteTextBlock(myItem); } break; case KEY_ALT_C: CopyTextBlock(myItem); break; case KEY_ALT_V: if (!_G(items).origPrompt) Item_SaveTextField(myItem); if ((int)strlen(_G(items).clipBoard) && ((int)strlen(myItem->prompt) - abs(myItem->aux2 - myItem->aux) + (int)strlen(_G(items).clipBoard) < myItem->promptMax)) { if (DeleteTextBlock(myItem) <= 0) Item_SaveTextFieldChange(myItem, true); Common::strcpy_s(tempBuf, myItem->aux); Common::strcpy_s(myItem->aux, 100, _G(items).clipBoard); Common::strcat_s(myItem->prompt, 80, tempBuf); myItem->aux = myItem->aux + strlen(_G(items).clipBoard); myItem->aux2 = myItem->aux; } break; case KEY_ALT_Z: Item_UndoTextFieldChange(); break; default: if (parm1 >= 32 && parm1 <= 127) { // Visible key character... if (!_G(items).origPrompt) Item_SaveTextField(myItem); // back up original... DeleteTextBlock(myItem); // if a block was highlighted... if ((int)strlen(myItem->prompt) < (myItem->promptMax - 1)) { // room in the string... Item_SaveTextFieldChange(myItem, false); // save a minor change... if (*(myItem->aux) == '\0') { // add to the end of the string... *(myItem->aux++) = (char)parm1; *(myItem->aux) = '\0'; } else { Common::strcpy_s(tempBuf, myItem->aux); *(myItem->aux++) = (char)parm1; Common::strcpy_s(myItem->aux, 80, tempBuf); } myItem->aux2 = myItem->aux; } } else absorbed = false; break; } return absorbed; } bool Item_show(Item *i, void *bdrDialog, Buffer *scrBuf, int32 itemType) { int32 listboxContentX2, viewCount, temp; char myChar, *beginBlock, *endBlock; ListItem *myList; Buffer pictBuff; const Buffer *tempBuff; ButtonDrawRec bdr; M4Rect interiorRect; if (!i) return false; int32 x1 = i->x; int32 y1 = i->y; int32 x2 = x1 + i->w - 1; int32 y2 = y1 + i->h - 1; if ((x1 < 0) || (y1 < 0) || (x2 >= scrBuf->w) || (y2 >= scrBuf->h)) return false; Font *currFont = gr_font_get(); if (i->myFont != currFont) gr_font_set(i->myFont); const int32 fontHeight = gr_font_get_height(); gr_color_set(__LTGRAY); gr_buffer_rect_fill(scrBuf, x1, y1, i->w, i->h); bdr.el_type = i->type; bdr.dialog = bdrDialog; bdr.scrBuf = scrBuf; bdr.fillMe = FILL_INTERIOR; bdr.x1 = x1; bdr.y1 = y1; bdr.x2 = x2; bdr.y2 = y2; if (((i->type == TEXTFIELD) && (itemType == ITEM_DEFAULT)) || (i->status & ITEM_PRESSED)) { bdr.pressed = true; } else { bdr.pressed = false; } if ((i->type != LISTBOX) && (i->type != PICTURE)) { if (!drawGUIelement(&bdr, &interiorRect)) return false; x1 = interiorRect.x1; y1 = interiorRect.y1; x2 = interiorRect.x2; y2 = interiorRect.y2; } switch (i->type) { case MESSAGE: item_string_write(scrBuf, i->prompt, x1, y1, 0, 1, __BLACK, __BLUE); break; case PICTURE: pictBuff.w = i->w; pictBuff.h = i->h; pictBuff.stride = i->w; pictBuff.data = (uint8 *)(i->aux); gr_buffer_rect_copy_2(&pictBuff, scrBuf, 0, 0, x1, y1, pictBuff.w, pictBuff.h); break; case TEXTFIELD: if (itemType == ITEM_DEFAULT) { gr_font_set_color(__GREEN); gr_font_write(scrBuf, i->prompt, x1, y1, 0, 1); if (i->aux == i->aux2) { myChar = *(i->aux); *(i->aux) = '\0'; temp = x1 + gr_font_string_width(i->prompt, 1); *(i->aux) = myChar; gr_color_set(__RED); gr_vline(scrBuf, temp, y1, y2); } else { if (i->aux < i->aux2) { beginBlock = i->aux; endBlock = i->aux2; } else { beginBlock = i->aux2; endBlock = i->aux; } myChar = *beginBlock; *beginBlock = '\0'; temp = x1 + gr_font_string_width(i->prompt, 1); *beginBlock = myChar; myChar = *endBlock; *endBlock = '\0'; gr_color_set(__RED); gr_buffer_rect_fill(scrBuf, temp, y1, gr_font_string_width(beginBlock, 1) + 1, fontHeight + 1); gr_font_set_color(__WHITE); gr_font_write(scrBuf, beginBlock, temp, y1, 0, 1); *endBlock = myChar; } } else { gr_font_set_color(__BLACK); gr_font_write(scrBuf, i->prompt, x1, y1, 0, 1); } break; case BUTTON: case REPEAT_BUTTON: gr_color_set(__DKGRAY); if (i->status & ITEM_PRESSED) { item_string_write(scrBuf, i->prompt, x1, y1, 0, 1, __RED, __BLUE); } else { item_string_write(scrBuf, i->prompt, x1, y1, 0, 1, __BLACK, __BLUE); } if (itemType == ITEM_RETURN) { bdr.fillMe = false; bdr.el_type = BUTTON; bdr.pressed = false; bdr.x1 = i->x - _G(items).buttonWidth; bdr.y1 = i->y - _G(items).buttonHeight; bdr.x2 = i->x + i->w + _G(items).buttonWidth - 1; bdr.y2 = i->y + i->h + _G(items).buttonHeight - 1; drawGUIelement(&bdr, nullptr); } break; case LISTBOX: if (i->myListCount > i->listView) { listboxContentX2 = x2 - (_G(items).buttonWidth + scrollUpWidth); //DRAW IN THE PIPES BEHIND THE THUMB temp = y1 + i->thumbY; bdr.pressed = false; bdr.fillMe = true; bdr.el_type = TEXTFIELD; bdr.x1 = listboxContentX2 + 4; bdr.y1 = y1 + (scrollUpHeight >> 1); bdr.x2 = x2 - 4; bdr.y2 = temp + (thumbHeight >> 1); drawGUIelement(&bdr, nullptr); bdr.x1 = listboxContentX2 + 4; bdr.y1 = temp + (thumbHeight >> 1); bdr.x2 = x2 - 4; bdr.y2 = y2 - (scrollDownHeight >> 1); drawGUIelement(&bdr, nullptr); bdr.el_type = BUTTON; if (i->status & THUMB_PRESSED) { bdr.pressed = true; tempBuff = &thumbPressedBuff; } else { bdr.pressed = false; tempBuff = &thumbBuff; } bdr.x1 = listboxContentX2 + 1; bdr.y1 = temp; bdr.x2 = x2; bdr.y2 = temp + thumbHeight + _G(items).buttonHeight - 1; drawGUIelement(&bdr, &interiorRect); gr_buffer_rect_copy_2(tempBuff, scrBuf, 0, 0, (short)interiorRect.x1, interiorRect.y1, thumbWidth, thumbHeight); if (i->status & SU_PRESSED) { bdr.pressed = true; tempBuff = &scrollUpPressedBuff; } else { bdr.pressed = false; tempBuff = &scrollUpBuff; } bdr.x1 = listboxContentX2 + 1; bdr.y1 = y1; bdr.x2 = x2; bdr.y2 = y1 + scrollUpHeight + _G(items).buttonHeight - 1; drawGUIelement(&bdr, &interiorRect); gr_buffer_rect_copy_2(tempBuff, scrBuf, 0, 0, (short)interiorRect.x1, interiorRect.y1, scrollUpWidth, scrollUpHeight); if (i->status & SD_PRESSED) { bdr.pressed = true; tempBuff = &scrollDownPressedBuff; } else { bdr.pressed = false; tempBuff = &scrollDownBuff; } bdr.x1 = listboxContentX2 + 1; bdr.y1 = y2 - (scrollDownHeight + _G(items).buttonHeight) + 1; bdr.x2 = x2; bdr.y2 = y2; drawGUIelement(&bdr, &interiorRect); gr_buffer_rect_copy_2(tempBuff, scrBuf, 0, 0, (short)interiorRect.x1, interiorRect.y1, scrollDownWidth, scrollDownHeight); } else { listboxContentX2 = x2 + 1; } if (itemType == ITEM_DEFAULT) bdr.pressed = true; else bdr.pressed = false; bdr.el_type = LISTBOX; bdr.x1 = x1; bdr.y1 = y1; bdr.x2 = listboxContentX2 - 1; bdr.y2 = y2; drawGUIelement(&bdr, &interiorRect); x1 = interiorRect.x1; y1 = interiorRect.y1; myList = i->viewTop; viewCount = 0; while (myList && (viewCount < i->listView)) { if (myList == i->currItem) { item_string_write(scrBuf, myList->prompt, x1, y1 + (fontHeight * (viewCount)), 0, 1, __RED, __BLUE); } else if (itemType == ITEM_DEFAULT) { item_string_write(scrBuf, myList->prompt, x1, y1 + (fontHeight * (viewCount)), 0, 1, __WHITE, __BLUE); } else { item_string_write(scrBuf, myList->prompt, x1, y1 + (fontHeight * (viewCount)), 0, 1, __BLACK, __BLUE); } viewCount++; myList = myList->next; } break; default: break; } if (i->myFont != currFont) gr_font_set(currFont); return true; } Item *Item_set_pressed(Item *itemList, Item *myItem, int32 tag) { if (!myItem) myItem = ItemFind(itemList, tag); if (myItem) myItem->status = (myItem->status & AREA_PRESSED) + ITEM_PRESSED; else return nullptr; return myItem; } Item *Item_set_unpressed(Item *itemList, Item *myItem, int32 tag) { if (!myItem) myItem = ItemFind(itemList, tag); if (myItem) myItem->status = ITEM_NORMAL; else return nullptr; return myItem; } Item *Item_set_default(Item *itemList, Item *currDefault, int32 tag) { Item *myItem = nullptr; if (tag > 0) { myItem = itemList; while (myItem && (myItem->tag != tag)) myItem = myItem->next; } if (myItem != currDefault) { if (currDefault) { currDefault->status = ITEM_NORMAL; } if (myItem && (myItem->type == TEXTFIELD)) { myItem->aux = myItem->prompt; myItem->aux2 = &(myItem->prompt[strlen(myItem->prompt)]); } else if (myItem && (myItem->type != LISTBOX)) myItem = nullptr; } if (myItem) myItem->status = (myItem->status & AREA_PRESSED) + ITEM_PRESSED; return myItem; } Item *Item_set_next_default(Item *currDefault, Item *itemList) { Item *nextDefault; if (currDefault) { nextDefault = currDefault->next; while (nextDefault && (nextDefault->type != LISTBOX) && (nextDefault->type != TEXTFIELD)) { nextDefault = nextDefault->next; } if (!nextDefault) nextDefault = itemList; while (nextDefault && (nextDefault->type != LISTBOX) && (nextDefault->type != TEXTFIELD)) { nextDefault = nextDefault->next; } } else { nextDefault = itemList; while (nextDefault && (nextDefault->type != LISTBOX) && (nextDefault->type != TEXTFIELD)) { nextDefault = nextDefault->next; } } if (nextDefault != currDefault) { if (currDefault) { currDefault->status = ITEM_NORMAL; } if (nextDefault) { if (nextDefault->type == TEXTFIELD) { nextDefault->aux = nextDefault->prompt; nextDefault->aux2 = &(nextDefault->prompt[strlen(nextDefault->prompt)]); } nextDefault->status = ITEM_PRESSED; } } return nextDefault; } Item *Item_set_prev_default(Item *currDefault, Item *listBottom) { Item *prevDefault; if (currDefault) { prevDefault = currDefault->prev; while (prevDefault && (prevDefault->type != LISTBOX) && (prevDefault->type != TEXTFIELD)) { prevDefault = prevDefault->prev; } if (!prevDefault) prevDefault = listBottom; while (prevDefault && (prevDefault->type != LISTBOX) && (prevDefault->type != TEXTFIELD)) { prevDefault = prevDefault->prev; } } else { prevDefault = listBottom; while (prevDefault && (prevDefault->type != LISTBOX) && (prevDefault->type != TEXTFIELD)) { prevDefault = prevDefault->prev; } } if (prevDefault != currDefault) { if (currDefault) { currDefault->status = ITEM_NORMAL; } if (prevDefault) { if (prevDefault->type == TEXTFIELD) { prevDefault->aux = prevDefault->prompt; prevDefault->aux2 = &(prevDefault->prompt[strlen(prevDefault->prompt)]); } prevDefault->status = ITEM_PRESSED; } } return prevDefault; } } // End of namespace M4