diff options
Diffstat (limited to 'engines/macventure/gui.cpp')
-rw-r--r-- | engines/macventure/gui.cpp | 1469 |
1 files changed, 1469 insertions, 0 deletions
diff --git a/engines/macventure/gui.cpp b/engines/macventure/gui.cpp new file mode 100644 index 0000000000..3b7c3a244b --- /dev/null +++ b/engines/macventure/gui.cpp @@ -0,0 +1,1469 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * Based on + * WebVenture (c) 2010, Sean Kasun + * https://github.com/mrkite/webventure, http://seancode.com/webventure/ + * + * Used with explicit permission from the author + */ + +#include "common/file.h" +#include "common/system.h" +#include "common/debug-channels.h" +#include "common/debug.h" +#include "image/bmp.h" +#include "graphics/macgui/macfontmanager.h" + +#include "macventure/gui.h" +#include "macventure/dialog.h" + +namespace MacVenture { + +enum { + kCursorWidth = 2, + kCursorHeight = 2 +}; + +enum { + kExitButtonWidth = 10, + kExitButtonHeight = 10 +}; + +enum { + kMenuHighLevel = -1, + kMenuAbout = 0, + kMenuFile = 1, + kMenuEdit = 2, + kMenuSpecial = 3 +}; + +enum { + kCommandNum = 8 +}; + +enum { + kDragThreshold = 5 +}; + +const bool kLoadStaticMenus = true; + +static const Graphics::MenuData menuSubItems[] = { + { kMenuHighLevel, "File", 0, 0, false }, + { kMenuHighLevel, "Edit", 0, 0, false }, + { kMenuHighLevel, "Special", 0, 0, false }, + { kMenuHighLevel, "Font", 0, 0, false }, + { kMenuHighLevel, "FontSize", 0, 0, false }, + + //{ kMenuAbout, "About", kMenuActionAbout, 0, true}, + + { kMenuFile, "New", kMenuActionNew, 0, true }, + { kMenuFile, NULL, 0, 0, false }, + { kMenuFile, "Open...", kMenuActionOpen, 0, true }, + { kMenuFile, "Save", kMenuActionSave, 0, true }, + { kMenuFile, "Save as...", kMenuActionSaveAs, 0, true }, + { kMenuFile, NULL, 0, 0, false }, + { kMenuFile, "Quit", kMenuActionQuit, 0, true }, + + { kMenuEdit, "Undo", kMenuActionUndo, 'Z', false }, + { kMenuEdit, NULL, 0, 0, false }, + { kMenuEdit, "Cut", kMenuActionCut, 'K', false }, + { kMenuEdit, "Copy", kMenuActionCopy, 'C', false }, + { kMenuEdit, "Paste", kMenuActionPaste, 'V', false }, + { kMenuEdit, "Clear", kMenuActionClear, 'B', false }, + + { kMenuSpecial, "Clean Up", kMenuActionCleanUp, 0, false }, + { kMenuSpecial, "Mess Up", kMenuActionMessUp, 0, false }, + + { 0, NULL, 0, 0, false } +}; + + +bool commandsWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui); +bool mainGameWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui); +bool outConsoleWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui); +bool selfWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui); +bool exitsWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui); +bool diplomaWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui); +bool inventoryWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui); + +void menuCommandsCallback(int action, Common::String &text, void *data); + +Gui::Gui(MacVentureEngine *engine, Common::MacResManager *resman) { + _engine = engine; + _resourceManager = resman; + _windowData = NULL; + _controlData = NULL; + _draggedObj.id = 0; + _draggedObj.pos = Common::Point(0, 0); + _dialog = NULL; + + _cursor = new Cursor(this); + + _consoleText = new ConsoleText(this); + _graphics = NULL; + + initGUI(); +} + +Gui::~Gui() { + + if (_windowData) + delete _windowData; + + if (_controlData) + delete _controlData; + + if (_exitsData) + delete _exitsData; + + if (_cursor) + delete _cursor; + + if (_consoleText) + delete _consoleText; + + if (_dialog) + delete _dialog; + + clearAssets(); + + if (_graphics) + delete _graphics; +} + +void Gui::initGUI() { + _screen.create(kScreenWidth, kScreenHeight, Graphics::PixelFormat::createFormatCLUT8()); + _wm.setScreen(&_screen); + + // Menu + _menu = _wm.addMenu(); + if (!loadMenus()) + error("GUI: Could not load menus"); + _menu->setCommandsCallback(menuCommandsCallback, this); + _menu->calcDimensions(); + + loadGraphics(); + + if (!loadWindows()) + error("GUI: Could not load windows"); + + initWindows(); + + assignObjReferences(); + + if (!loadControls()) + error("GUI: Could not load controls"); + + draw(); + +} + +void Gui::reloadInternals() { + clearAssets(); + loadGraphics(); +} + +void Gui::draw() { + // Will be performance-improved after the milestone + _wm.setFullRefresh(true); + + drawWindows(); + + _wm.draw(); + + drawDraggedObject(); + drawDialog(); + // TODO: When window titles with custom borders are in MacGui, this should be used. + //drawWindowTitle(kMainGameWindow, _mainGameWindow->getSurface()); +} + +void Gui::drawMenu() { + _menu->draw(&_screen); +} + +void Gui::drawTitle() { + warning("drawTitle hasn't been tested yet"); +} + +void Gui::clearControls() { + if (!_controlData) + return; + + Common::Array<CommandButton>::iterator it = _controlData->begin(); + for (; it != _controlData->end(); ++it) { + it->unselect(); + } +} + +void Gui::initWindows() { + // Game Controls Window + _controlsWindow = _wm.addWindow(false, false, false); + _controlsWindow->setDimensions(getWindowData(kCommandsWindow).bounds); + _controlsWindow->setActive(false); + _controlsWindow->setCallback(commandsWindowCallback, this); + loadBorders(_controlsWindow, findWindowData(kCommandsWindow).type); + + // Main Game Window + _mainGameWindow = _wm.addWindow(false, false, false); + _mainGameWindow->setDimensions(getWindowData(kMainGameWindow).bounds); + _mainGameWindow->setActive(false); + _mainGameWindow->setCallback(mainGameWindowCallback, this); + loadBorders(_mainGameWindow, findWindowData(kMainGameWindow).type); + + // In-game Output Console + _outConsoleWindow = _wm.addWindow(true, true, false); + // HACK We have to hand-create the dimensions, otherwise they don't fit + const WindowData &wd = getWindowData(kOutConsoleWindow); + Common::Rect dimensions = wd.bounds; + dimensions.setWidth(dimensions.width() - borderBounds(wd.type).rightOffset); + _outConsoleWindow->setDimensions(dimensions); + _outConsoleWindow->setActive(false); + _outConsoleWindow->setCallback(outConsoleWindowCallback, this); + loadBorders(_outConsoleWindow, findWindowData(kOutConsoleWindow).type); + + // Self Window + _selfWindow = _wm.addWindow(false, true, false); + _selfWindow->setDimensions(getWindowData(kSelfWindow).bounds); + _selfWindow->setActive(false); + _selfWindow->setCallback(selfWindowCallback, this); + loadBorders(_selfWindow, findWindowData(kSelfWindow).type); + + // Exits Window + _exitsWindow = _wm.addWindow(false, false, false); + _exitsWindow->setDimensions(getWindowData(kExitsWindow).bounds); + _exitsWindow->setActive(false); + _exitsWindow->setCallback(exitsWindowCallback, this); + + // TODO: In the original, the background is actually a clickable + // object that can be used to refer to the room itself. In that case, + // the background should be kPatternDarkGray. + _exitsWindow->setBackgroundPattern(kPatternLightGray); + loadBorders(_exitsWindow, findWindowData(kExitsWindow).type); +} + +const WindowData &Gui::getWindowData(WindowReference reference) { + return findWindowData(reference); +} + +const Graphics::Font &Gui::getCurrentFont() { + return *_wm._fontMan->getFont(Graphics::MacFont(Graphics::kMacFontChicago, 12)); +} + +void Gui::bringToFront(WindowReference winID) { + findWindow(winID)->setActive(true); +} + +void Gui::setWindowTitle(WindowReference winID, Common::String string) { + findWindowData(winID).title = string; + findWindowData(winID).titleLength = string.size(); +} + +void Gui::updateWindowInfo(WindowReference ref, ObjID objID, const Common::Array<ObjID> &children) { + if (ref == kNoWindow) { + return; + } + WindowData &data = findWindowData(ref); + data.children.clear(); + data.objRef = objID; + uint32 originx = 0x7fff; + uint32 originy = 0x7fff; + for (uint i = 0; i < children.size(); i++) { + if (children[i] != 1) { + ObjID child = children[i]; + if (ref != kMainGameWindow) { + Common::Point childPos = _engine->getObjPosition(child); + originx = originx > (uint)childPos.x ? (uint)childPos.x : originx; + originy = originy > (uint)childPos.y ? (uint)childPos.y : originy; + } + data.children.push_back(DrawableObject(child, kBlitBIC)); + } + } + if (originx != 0x7fff) { + data.bounds.left = originx; + } + if (originy != 0x7fff) { + data.bounds.top = originy; + } + if (ref != kMainGameWindow) { + data.updateScroll = true; + } +} + +void Gui::addChild(WindowReference target, ObjID child) { + findWindowData(target).children.push_back(DrawableObject(child, kBlitBIC)); +} + +void Gui::removeChild(WindowReference target, ObjID child) { + WindowData &data = findWindowData(target); + uint index = 0; + for (;index < data.children.size(); index++) { + if (data.children[index].obj == child) { + break; + } + } + + if (index < data.children.size()) + data.children.remove_at(index); +} + +void Gui::assignObjReferences() { + findWindowData(kSelfWindow).objRef = 0; +} + +WindowReference Gui::createInventoryWindow(ObjID objRef) { + Graphics::MacWindow *newWindow = _wm.addWindow(true, true, true); + WindowData newData; + GlobalSettings settings = _engine->getGlobalSettings(); + newData.refcon = (WindowReference)ABS(_inventoryWindows.size() + kInventoryStart); // This is a HACK + + if (_windowData->back().refcon < 0x80) { // There is already another inventory window + newData.bounds = _windowData->back().bounds; // Inventory windows are always last + newData.bounds.translate(newData.bounds.left + settings._invOffsetX, newData.bounds.top + settings._invOffsetY); + } else { + BorderBounds bbs = borderBounds(kInvWindow); + newData.bounds = Common::Rect( + settings._invLeft - bbs.leftOffset, + settings._invTop - bbs.topOffset, + settings._invLeft + settings._invWidth, + settings._invTop + settings._invHeight); + } + newData.type = kInvWindow; + newData.hasCloseBox = true; + newData.visible = true; + newData.objRef = objRef; + _windowData->push_back(newData); + + newWindow->setDimensions(newData.bounds); + newWindow->setCallback(inventoryWindowCallback, this); + newWindow->setCloseable(true); + loadBorders(newWindow, newData.type); + _inventoryWindows.push_back(newWindow); + + debugC(1, kMVDebugGUI, "Create new inventory window. Reference: %d", newData.refcon); + return newData.refcon; +} + +void Gui::loadBorders(Graphics::MacWindow *target, MVWindowType type) { + loadBorder(target, type, false); + loadBorder(target, type, true); +} + +void Gui::loadBorder(Graphics::MacWindow *target, MVWindowType type, bool active) { + + Common::SeekableReadStream *stream = _engine->getBorderFile(type, active); + + if (stream) { + BorderBounds bbs = borderBounds(type); + target->loadBorder(*stream, active, bbs.leftOffset, bbs.rightOffset, bbs.topOffset, bbs.bottomOffset); + + delete stream; + } +} + +void Gui::loadGraphics() { + if (_graphics) + delete _graphics; + _graphics = new Container(_engine->getFilePath(kGraphicPathID)); +} + +void Gui::clearAssets() { + Common::HashMap<ObjID, ImageAsset*>::const_iterator it = _assets.begin(); + for (; it != _assets.end(); it++) { + delete it->_value; + } + _assets.clear(); +} + +bool Gui::loadMenus() { + + if (kLoadStaticMenus) { + // We assume that, if there are static menus, we don't need dynamic ones + _menu->addStaticMenus(menuSubItems); + return true; + } + + Common::MacResIDArray resArray; + Common::SeekableReadStream *res; + Common::MacResIDArray::const_iterator iter; + + if ((resArray = _resourceManager->getResIDArray(MKTAG('M', 'E', 'N', 'U'))).size() == 0) + return false; + + _menu->addMenuSubItem(0, "Abb", kMenuActionAbout, 0, 'A', true); + + int i = 1; + for (iter = resArray.begin(); iter != resArray.end(); ++iter) { + res = _resourceManager->getResource(MKTAG('M', 'E', 'N', 'U'), *iter); + uint16 key; + uint16 style; + uint8 titleLength; + char *title; + + /* Skip menuID, width, height, resourceID, placeholder */ + for (int skip = 0; skip < 5; skip++) { + res->readUint16BE(); + } + titleLength = res->readByte(); + title = new char[titleLength + 1]; + res->read(title, titleLength); + title[titleLength] = '\0'; + + if (titleLength > 1) { + _menu->addMenuItem(title); + + // Read submenu items + while ((titleLength = res->readByte())) { + title = new char[titleLength + 1]; + res->read(title, titleLength); + title[titleLength] = '\0'; + // Skip icon + res->readUint16BE(); + // Read key + key = res->readUint16BE(); + // Skip mark + res->readUint16BE(); + // Read style + style = res->readUint16BE(); + _menu->addMenuSubItem(i, title, 0, style, key, false); + } + } + + i++; + delete res; + } + + return true; +} + +bool Gui::loadWindows() { + Common::MacResIDArray resArray; + Common::SeekableReadStream *res; + Common::MacResIDArray::const_iterator iter; + + _windowData = new Common::List<WindowData>(); + + if ((resArray = _resourceManager->getResIDArray(MKTAG('W', 'I', 'N', 'D'))).size() == 0) + return false; + + uint32 id = kCommandsWindow; + for (iter = resArray.begin(); iter != resArray.end(); ++iter) { + res = _resourceManager->getResource(MKTAG('W', 'I', 'N', 'D'), *iter); + WindowData data; + uint16 top, left, bottom, right; + top = res->readUint16BE(); + left = res->readUint16BE(); + bottom = res->readUint16BE(); + right = res->readUint16BE(); + data.type = (MVWindowType)res->readUint16BE(); + BorderBounds bbs = borderBounds(data.type); + data.bounds = Common::Rect( + left - bbs.leftOffset, + top - bbs.topOffset, + right + bbs.rightOffset, + bottom + bbs.bottomOffset); + + data.visible = res->readUint16BE(); + data.hasCloseBox = res->readUint16BE(); + data.refcon = (WindowReference)id; id++; + res->readUint32BE(); // Skip the true id. For some reason it's reading 0 + data.titleLength = res->readByte(); + if (data.titleLength) { + char *newTitle = new char[data.titleLength + 1]; + res->read(newTitle, data.titleLength); + newTitle[data.titleLength] = '\0'; + data.title = Common::String(newTitle); + delete[] newTitle; + } + data.scrollPos = Common::Point(0, 0); + + debugC(1, kMVDebugGUI, "Window loaded: %s", data.title.c_str()); + + _windowData->push_back(data); + + delete res; + } + + return true; +} + +bool Gui::loadControls() { + Common::MacResIDArray resArray; + Common::SeekableReadStream *res; + Common::MacResIDArray::const_iterator iter; + + _controlData = new Common::Array<CommandButton>(); + _exitsData = new Common::Array<CommandButton>(); + + if ((resArray = _resourceManager->getResIDArray(MKTAG('C', 'N', 'T', 'L'))).size() == 0) + return false; + + uint32 id = kControlExitBox; + for (iter = resArray.begin(); iter != resArray.end(); ++iter) { + res = _resourceManager->getResource(MKTAG('C', 'N', 'T', 'L'), *iter); + ControlData data; + uint16 top, left, bottom, right; + top = res->readUint16BE(); + left = res->readUint16BE(); + bottom = res->readUint16BE(); + right = res->readUint16BE(); + data.scrollValue = res->readUint16BE(); + data.visible = res->readByte(); + res->readByte(); // Unused + data.scrollMax = res->readUint16BE(); + data.scrollMin = res->readUint16BE(); + data.cdef = res->readUint16BE(); + data.refcon = (ControlAction)res->readUint32BE(); + data.type = (ControlType)id; id++; + data.titleLength = res->readByte(); + if (data.titleLength) { + char *title = new char[data.titleLength + 1]; + res->read(title, data.titleLength); + title[data.titleLength] = '\0'; + data.title = Common::String(title); + delete[] title; + } + if (data.type != kControlExitBox) { + BorderBounds bbs = borderBounds(getWindowData(kCommandsWindow).type); + // We just want to move the button, not change it's size + data.bounds = Common::Rect(left + bbs.leftOffset, top + bbs.topOffset, right + bbs.leftOffset, bottom + bbs.topOffset); + } else { + data.bounds = Common::Rect(left, top, right, bottom); + } + + + _controlData->push_back(CommandButton(data, this)); + + delete res; + } + + return true; +} + +void Gui::drawWindows() { + + drawCommandsWindow(); + drawMainGameWindow(); + drawSelfWindow(); + drawInventories(); + drawExitsWindow(); + drawConsoleWindow(); + +} + +void Gui::drawCommandsWindow() { + if (_engine->needsClickToContinue()) { + Graphics::ManagedSurface *srf = _controlsWindow->getSurface(); + WindowData data = getWindowData(kCommandsWindow); + srf->fillRect(Common::Rect(0, 0, srf->w, srf->h), kColorWhite); + getCurrentFont().drawString( + srf, + _engine->getCommandsPausedString(), + 0, + (srf->h / 2) - getCurrentFont().getFontHeight(), + data.bounds.right - data.bounds.left, + kColorBlack, + Graphics::kTextAlignCenter); + } else { + Common::Array<CommandButton>::const_iterator it = _controlData->begin(); + for (; it != _controlData->end(); ++it) { + CommandButton button = *it; + if (button.getData().type != kControlExitBox) + button.draw(*_controlsWindow->getSurface()); + } + } +} + +void Gui::drawMainGameWindow() { + const WindowData &data = getWindowData(kMainGameWindow); + BorderBounds border = borderBounds(data.type); + ObjID objRef = data.objRef; + + _mainGameWindow->setDirty(true); + + if (data.objRef > 0 && data.objRef < 2000) { + ensureAssetLoaded(objRef); + + _assets[objRef]->blitInto( + _mainGameWindow->getSurface(), + border.leftOffset, + border.topOffset, + kBlitDirect); + } + + drawObjectsInWindow(data, _mainGameWindow->getSurface()); + + if (DebugMan.isDebugChannelEnabled(kMVDebugGUI)) { + Graphics::MacWindow *win = findWindow(data.refcon); + Common::Rect innerDims = win->getInnerDimensions(); + int x = win->getDimensions().left; + int y = win->getDimensions().top; + innerDims.translate(-x, -y); + win->getSurface()->frameRect(innerDims, kColorGreen); + } + + findWindow(kMainGameWindow)->setDirty(true); +} + +void Gui::drawSelfWindow() { + drawObjectsInWindow(getWindowData(kSelfWindow), _selfWindow->getSurface()); + if (_engine->isObjSelected(1)) { + invertWindowColors(kSelfWindow); + } + findWindow(kSelfWindow)->setDirty(true); +} + +void Gui::drawInventories() { + + Graphics::ManagedSurface *srf; + for (uint i = 0; i < _inventoryWindows.size(); i++) { + const WindowData &data = getWindowData((WindowReference)(kInventoryStart + i)); + Graphics::MacWindow *win = findWindow(data.refcon); + srf = win->getSurface(); + srf->clear(kColorGreen); + srf->fillRect(srf->getBounds(), kColorWhite); + drawObjectsInWindow(data, srf); + + if (DebugMan.isDebugChannelEnabled(kMVDebugGUI)) { + Common::Rect innerDims = win->getInnerDimensions(); + int x = win->getDimensions().left; + int y = win->getDimensions().top; + innerDims.translate(-x, -y); + srf->frameRect(innerDims, kColorGreen); + } + + findWindow(data.refcon)->setDirty(true); + } + +} + +void Gui::drawExitsWindow() { + _exitsWindow->setBackgroundPattern(kPatternLightGray); + + Graphics::ManagedSurface *srf = _exitsWindow->getSurface(); + + Common::Array<CommandButton>::const_iterator it = _exitsData->begin(); + for (; it != _exitsData->end(); ++it) { + CommandButton button = *it; + button.draw(*srf); + } + + findWindow(kExitsWindow)->setDirty(true); +} + +void Gui::drawConsoleWindow() { + + Graphics::ManagedSurface *srf = _outConsoleWindow->getSurface(); + BorderBounds bounds = borderBounds(getWindowData(kOutConsoleWindow).type); + _consoleText->renderInto(srf, bounds, kConsoleLeftOffset); +} + +void Gui::drawObjectsInWindow(const WindowData &targetData, Graphics::ManagedSurface *surface) { + BorderBounds border = borderBounds(targetData.type); + Common::Point pos; + ObjID child; + BlitMode mode; + + if (targetData.children.size() == 0) { + return; + } + + Graphics::ManagedSurface composeSurface; + createInnerSurface(&composeSurface, surface, border); + assert(composeSurface.w <= surface->w && + composeSurface.h <= surface->h); + composeSurface.clear(kColorGreen); + + for (uint i = 0; i < targetData.children.size(); i++) { + child = targetData.children[i].obj; + mode = (BlitMode)targetData.children[i].mode; + pos = _engine->getObjPosition(child); + pos -= targetData.scrollPos; + ensureAssetLoaded(child); + + _assets[child]->blitInto( + &composeSurface, + pos.x, + pos.y, + mode); + + if (_engine->isObjVisible(child)) { + if (_engine->isObjSelected(child) || + child == _draggedObj.id) { + + _assets[child]->blitInto( + &composeSurface, pos.x, pos.y, kBlitOR); + } + } + + if (DebugMan.isDebugChannelEnabled(kMVDebugGUI)) { + Common::Rect testBounds = _engine->getObjBounds(child); + testBounds.translate(-targetData.scrollPos.x, -targetData.scrollPos.y); + surface->frameRect(testBounds, kColorGreen); + } + } + Common::Point composePosition = Common::Point(border.leftOffset, border.topOffset); + surface->transBlitFrom(composeSurface, composePosition, kColorGreen); +} + +void Gui::drawWindowTitle(WindowReference target, Graphics::ManagedSurface *surface) { + // TODO: Implement when MacGui supports titles in windows with custom borders. +} + +void Gui::drawDraggedObject() { + if (_draggedObj.id != 0 && + _engine->isObjVisible(_draggedObj.id)) { + ensureAssetLoaded(_draggedObj.id); + ImageAsset *asset = _assets[_draggedObj.id]; + + // In case of overflow from the right/top + uint w = asset->getWidth() + MIN((int16)0, _draggedObj.pos.x); + uint h = asset->getHeight() + MIN((int16)0, _draggedObj.pos.y); + + // In case of overflow from the bottom/left + if (_draggedObj.pos.x > 0 && _draggedObj.pos.x + w > kScreenWidth) { + w = kScreenWidth - _draggedObj.pos.x; + } + if (_draggedObj.pos.y > 0 && _draggedObj.pos.y + h > kScreenHeight) { + h = kScreenHeight - _draggedObj.pos.y; + } + + Common::Point target = _draggedObj.pos; + if (target.x < 0) { + target.x = 0; + } + if (target.y < 0) { + target.y = 0; + } + + _draggedSurface.create(w, h, _screen.format); + _draggedSurface.blitFrom( + _screen, + Common::Rect( + target.x, + target.y, + target.x + _draggedSurface.w, + target.y + _draggedSurface.h), + Common::Point(0, 0)); + asset->blitInto(&_draggedSurface, MIN((int16)0, _draggedObj.pos.x), MIN((int16)0, _draggedObj.pos.y), kBlitBIC); + + g_system->copyRectToScreen( + _draggedSurface.getBasePtr(0, 0), + _draggedSurface.pitch, + target.x, + target.y, + _draggedSurface.w, + _draggedSurface.h + ); + } +} + +void Gui::drawDialog() { + if (_dialog) { + _dialog->draw(); + } +} + +void Gui::updateWindow(WindowReference winID, bool containerOpen) { + if (winID == kNoWindow) { + return; + } + if (winID == kSelfWindow || containerOpen) { + WindowData &data = findWindowData(winID); + if (winID == kCommandsWindow) { + Common::Array<CommandButton>::iterator it = _controlData->begin(); + for (; it != _controlData->end(); ++it) { + it->unselect(); + } + } + Common::Array<DrawableObject> &children = data.children; + for (uint i = 0; i < children.size(); i++) { + uint flag = 0; + ObjID child = children[i].obj; + BlitMode mode = kBlitDirect; + bool off = !_engine->isObjVisible(child); + if (flag || !off || !_engine->isObjClickable(child)) { + mode = kBlitBIC; + if (off || flag) { + mode = kBlitXOR; + } else if (!off && _engine->isObjSelected(child)) { + mode = kBlitOR; + } + children[i] = DrawableObject(child, mode); + } else { + children[i] = DrawableObject(child, kBlitXOR); + } + } + if (winID == kMainGameWindow) { + drawMainGameWindow(); + } else { + Graphics::MacWindow *winRef = findWindow(winID); + winRef->getSurface()->fillRect(data.bounds, kColorGray); + } + if (data.type == kZoomDoc && data.updateScroll) { + warning("Unimplemented: update scroll"); + } + } +} + +void Gui::clearExits() { + _exitsData->clear(); +} + +void Gui::unselectExits() { + Common::Array<CommandButton>::const_iterator it = _exitsData->begin(); + for (; it != _exitsData->end(); ++it) { + CommandButton button = *it; + button.unselect(); + } +} + +void Gui::updateExit(ObjID obj) { + if (!_engine->isObjExit(obj)) { + return; + } + BorderBounds border = borderBounds(getWindowData(kExitsWindow).type); + + int ctl = -1; + int i = 0; + Common::Array<CommandButton>::const_iterator it = _exitsData->begin(); + for (;it != _exitsData->end(); it++) { + if (it->getData().refcon == obj) + ctl = i; + else + i++; + } + + if (ctl != -1) + _exitsData->remove_at(ctl); + + if (!_engine->isHiddenExit(obj) && + _engine->getParent(obj) == _engine->getParent(1)) { + ControlData data; + data.titleLength = 0; + data.refcon = (ControlAction)obj; // Objects can be exits (actions) + Common::Point pos = _engine->getObjExitPosition(obj); + pos.x += border.leftOffset; + pos.y += border.topOffset; + data.bounds = Common::Rect(pos.x, pos.y, pos.x + kExitButtonWidth, pos.y + kExitButtonHeight); + data.visible = true; + + _exitsData->push_back(CommandButton(data, this)); + } +} + +void Gui::printText(const Common::String &text) { + debugC(1, kMVDebugGUI, "Print Text: %s", text.c_str()); + _consoleText->printLine(text, _outConsoleWindow->getDimensions().width()); +} + +void Gui::showPrebuiltDialog(PrebuiltDialogs type) { + closeDialog(); + _dialog = new Dialog(this, type); +} + +bool Gui::isDialogOpen() { + return _dialog != NULL; +} + +void Gui::setTextInput(Common::String str) { + _engine->setTextInput(str); +} + + +void Gui::closeDialog() { + delete _dialog; + _dialog = NULL; +} + +void Gui::getTextFromUser() { + if (_dialog) { + delete _dialog; + } + showPrebuiltDialog(kSpeakDialog); +} + +void Gui::loadGame() { + _engine->scummVMSaveLoadDialog(false); +} + +void Gui::saveGame() { + _engine->scummVMSaveLoadDialog(true); +} + +void Gui::newGame() { + _engine->newGame(); +} + +void Gui::quitGame() { + _engine->requestQuit(); +} + +void Gui::createInnerSurface(Graphics::ManagedSurface *innerSurface, Graphics::ManagedSurface *outerSurface, const BorderBounds &borders) { + innerSurface->create( + outerSurface->w - borders.leftOffset - borders.rightOffset, + outerSurface->h - borders.topOffset - borders.bottomOffset, + outerSurface->format); +} + +void Gui::moveDraggedObject(Common::Point target) { + ensureAssetLoaded(_draggedObj.id); + _draggedObj.pos = target + _draggedObj.mouseOffset; + + // TODO FInd more elegant way of making pow2 + _draggedObj.hasMoved = (_draggedObj.startPos.sqrDist(_draggedObj.pos) >= (kDragThreshold * kDragThreshold)); + + debugC(4, kMVDebugGUI, "Dragged obj position: (%d, %d), mouse offset: (%d, %d), hasMoved: %d, dist: %d, threshold: %d", + _draggedObj.pos.x, _draggedObj.pos.y, + _draggedObj.mouseOffset.x, _draggedObj.mouseOffset.y, + _draggedObj.hasMoved, + _draggedObj.startPos.sqrDist(_draggedObj.pos), + kDragThreshold * kDragThreshold + ); + +} + +WindowReference Gui::findWindowAtPoint(Common::Point point) { + Common::List<WindowData>::iterator it; + Graphics::MacWindow *win; + for (it = _windowData->begin(); it != _windowData->end(); it++) { + win = findWindow(it->refcon); + if (win && it->refcon != kDiplomaWindow) { //HACK, diploma should be cosnidered + if (win->getDimensions().contains(point)) { + return it->refcon; + } + } + } + return kNoWindow; +} + +Common::Point Gui::getGlobalScrolledSurfacePosition(WindowReference reference) { + const WindowData &data = getWindowData(reference); + BorderBounds border = borderBounds(data.type); + Graphics::MacWindow *win = findWindow(reference); + if (!win) { + return Common::Point(0, 0); + } + return Common::Point( + win->getDimensions().left + border.leftOffset - data.scrollPos.x, + win->getDimensions().top + border.topOffset - data.scrollPos.y); +} + +WindowData &Gui::findWindowData(WindowReference reference) { + assert(_windowData); + + Common::List<WindowData>::iterator iter = _windowData->begin(); + while (iter->refcon != reference && iter != _windowData->end()) { + iter++; + } + + if (iter->refcon == reference) + return *iter; + + error("GUI: Could not locate the desired window data"); +} + +Graphics::MacWindow *Gui::findWindow(WindowReference reference) { + if (reference < 0x80 && reference >= kInventoryStart) { // It's an inventory window + return _inventoryWindows[reference - kInventoryStart]; + } + switch (reference) { + case MacVenture::kNoWindow: + return NULL; + case MacVenture::kCommandsWindow: + return _controlsWindow; + case MacVenture::kMainGameWindow: + return _mainGameWindow; + case MacVenture::kOutConsoleWindow: + return _outConsoleWindow; + case MacVenture::kSelfWindow: + return _selfWindow; + case MacVenture::kExitsWindow: + return _exitsWindow; + case MacVenture::kDiplomaWindow: + return _diplomaWindow; + default: + return NULL; + } + return NULL; +} + +void Gui::ensureInventoryOpen(WindowReference reference, ObjID id) { + assert(reference < 0x80 && reference >= kInventoryStart); + if (reference - kInventoryStart == (int)_inventoryWindows.size()) { + createInventoryWindow(id); + } +} + +WindowReference Gui::getObjWindow(ObjID objID) { + switch (objID) { + case 0xfffc: return kExitsWindow; + case 0xfffd: return kSelfWindow; + case 0xfffe: return kOutConsoleWindow; + case 0xffff: return kCommandsWindow; + } + + return findObjWindow(objID); +} + +WindowReference Gui::findObjWindow(ObjID objID) { + // This is a bit of a HACK, we take advantage of the consecutive nature of references + for (uint i = kCommandsWindow; i <= kDiplomaWindow; i++) { + const WindowData &data = getWindowData((WindowReference)i); + if (data.objRef == objID) { + return data.refcon; + } + } + + for (uint i = kInventoryStart; i < _inventoryWindows.size() + kInventoryStart; i++) { + const WindowData &data = getWindowData((WindowReference)i); + if (data.objRef == objID) { + return data.refcon; + } + } + + return kNoWindow; +} + +void Gui::checkSelect(const WindowData &data, Common::Point pos, const Common::Rect &clickRect, WindowReference ref) { + ObjID child = 0; + for (Common::Array<DrawableObject>::const_iterator it = data.children.begin(); it != data.children.end(); it++) { + if (canBeSelected((*it).obj, clickRect, ref)) { + child = (*it).obj; + } + } + if (child != 0) { + selectDraggable(child, ref, pos); + bringToFront(ref); + } +} + +bool Gui::canBeSelected(ObjID obj, const Common::Rect &clickRect, WindowReference ref) { + return (_engine->isObjClickable(obj) && + isRectInsideObject(clickRect, obj)); +} + +bool Gui::isRectInsideObject(Common::Rect target, ObjID obj) { + ensureAssetLoaded(obj); + Common::Rect bounds = _engine->getObjBounds(obj); + Common::Rect intersection = bounds.findIntersectingRect(target); + // We translate it to the image's coord system + intersection = Common::Rect( + intersection.left - bounds.left, + intersection.top - bounds.top, + intersection.left - bounds.left + intersection.width(), + intersection.top - bounds.top + intersection.height()); + + return _assets[obj]->isRectInside(intersection); +} + +void Gui::selectDraggable(ObjID child, WindowReference origin, Common::Point click) { + if (_engine->isObjClickable(child) && _draggedObj.id == 0) { + _draggedObj.hasMoved = false; + _draggedObj.id = child; + _draggedObj.startWin = origin; + Common::Point localizedClick = click - getGlobalScrolledSurfacePosition(origin); + _draggedObj.mouseOffset = _engine->getObjPosition(child) - localizedClick; + _draggedObj.pos = click + _draggedObj.mouseOffset; + _draggedObj.startPos = _draggedObj.pos; + } +} + +void Gui::handleDragRelease(bool shiftPressed, bool isDoubleClick) { + if (_draggedObj.id != 0) { + WindowReference destinationWindow = findWindowAtPoint(_draggedObj.pos); + if (destinationWindow == kNoWindow) { + return; + } + if (_draggedObj.hasMoved) { + const WindowData &destinationWindowData = getWindowData(destinationWindow); + ObjID destObject = destinationWindowData.objRef; + Common::Point dropPosition = _draggedObj.pos - _draggedObj.startPos; + dropPosition = localizeTravelledDistance(dropPosition, _draggedObj.startWin, destinationWindow); + debugC(3, kMVDebugGUI, "Drop the object %d at obj %d, pos (%d, %d)", _draggedObj.id, destObject, dropPosition.x, dropPosition.y); + + _engine->handleObjectDrop(_draggedObj.id, dropPosition, destObject); + } + _engine->handleObjectSelect(_draggedObj.id, destinationWindow, shiftPressed, isDoubleClick); + _draggedObj.id = 0; + _draggedObj.hasMoved = false; + } +} + +Common::Rect Gui::calculateClickRect(Common::Point clickPos, Common::Rect windowBounds) { + int left = clickPos.x - windowBounds.left; + int top = clickPos.y - windowBounds.top; + return Common::Rect(left - kCursorWidth, top - kCursorHeight, left + kCursorWidth, top + kCursorHeight); +} + +Common::Point Gui::localizeTravelledDistance(Common::Point point, WindowReference origin, WindowReference target) { + if (origin != target) { + // ori.local to global + point += getGlobalScrolledSurfacePosition(origin); + if (findWindow(target)) { + // dest.globalToLocal + point -= getGlobalScrolledSurfacePosition(target); + } + } + return point; +} + +void Gui::removeInventoryWindow(WindowReference ref) { + _inventoryWindows.remove_at(ref - kInventoryStart); + Common::List<WindowData>::iterator it; + for (it = _windowData->begin(); it != _windowData->end(); it++) { + if (it->refcon == ref) { + _windowData->erase(it); + break; + } + } +} + + +/* HANDLERS */ +void Gui::handleMenuAction(MenuAction action) { + switch (action) { + case MacVenture::kMenuActionAbout: + warning("Unimplemented MacVenture Menu Action: About"); + break; + case MacVenture::kMenuActionNew: + _engine->newGame(); + break; + case MacVenture::kMenuActionOpen: + loadGame(); + break; + case MacVenture::kMenuActionSave: + saveGame(); + break; + case MacVenture::kMenuActionSaveAs: + saveGame(); + break; + case MacVenture::kMenuActionQuit: + _engine->requestQuit(); + break; + case MacVenture::kMenuActionUndo: + warning("Unimplemented MacVenture Menu Action: Undo"); + break; + case MacVenture::kMenuActionCut: + warning("Unimplemented MacVenture Menu Action: Cut"); + break; + case MacVenture::kMenuActionCopy: + warning("Unimplemented MacVenture Menu Action: Copy"); + break; + case MacVenture::kMenuActionPaste: + warning("Unimplemented MacVenture Menu Action: Paste"); + break; + case MacVenture::kMenuActionClear: + warning("Unimplemented MacVenture Menu Action: Clear"); + break; + case MacVenture::kMenuActionCleanUp: + warning("Unimplemented MacVenture Menu Action: Clean Up"); + break; + case MacVenture::kMenuActionMessUp: + warning("Unimplemented MacVenture Menu Action: Mess Up"); + break; + case MacVenture::kMenuActionCommand: + warning("Unimplemented MacVenture Menu Action: GENERIC"); + break; + default: + break; + } +} + +/* CALLBACKS */ + +bool commandsWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) { + Gui *g = (Gui*)gui; + return g->processCommandEvents(click, event); +} + +bool mainGameWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) { + Gui *g = (Gui*)gui; + return g->processMainGameEvents(click, event); +} + +bool outConsoleWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) { + Gui *g = (Gui*)gui; + return g->processOutConsoleEvents(click, event); +} + +bool selfWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) { + Gui *g = (Gui*)gui; + + return g->processSelfEvents(click, event); +} + +bool exitsWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) { + Gui *g = (Gui*)gui; + + return g->processExitsEvents(click, event); +} + +bool diplomaWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) { + Gui *g = (Gui*)gui; + + return g->processDiplomaEvents(click, event); +} + +bool inventoryWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) { + Gui *g = (Gui*)gui; + + return g->processInventoryEvents(click, event); +} + +void menuCommandsCallback(int action, Common::String &text, void *data) { + Gui *g = (Gui *)data; + + g->handleMenuAction((MenuAction)action); +} + + +void Gui::invertWindowColors(WindowReference winID) { + Graphics::ManagedSurface *srf = findWindow(winID)->getSurface(); + for (uint y = 0; y < srf->h; y++) { + for (uint x = 0; x < srf->w; x++) { + byte p = *(byte *)srf->getBasePtr(x, y); + *(byte *)srf->getBasePtr(x, y) = + (p == kColorWhite) ? kColorBlack : kColorGray; + } + } +} + +bool Gui::tryCloseWindow(WindowReference winID) { + WindowData data = findWindowData(winID); + Graphics::MacWindow *win = findWindow(winID); + _wm.removeWindow(win); + if (winID < 0x80) { + removeInventoryWindow(winID); + } + return true; +} + +Common::Point Gui::getObjMeasures(ObjID obj) { + ensureAssetLoaded(obj); + int w = _assets[obj]->getWidth(); + int h = _assets[obj]->getHeight(); + return Common::Point(w, h); +} + +bool Gui::processEvent(Common::Event &event) { + bool processed = false; + + processed |= _cursor->processEvent(event); + + if (_dialog && _dialog->processEvent(event)) { + return true; + } + + if (event.type == Common::EVENT_MOUSEMOVE) { + if (_draggedObj.id != 0) { + moveDraggedObject(event.mouse); + } + processed = true; + } + + processed |= _wm.processEvent(event); + return (processed); +} + +bool Gui::processCommandEvents(WindowClick click, Common::Event &event) { + if (event.type == Common::EVENT_LBUTTONUP) { + if (_engine->needsClickToContinue()) { + _engine->selectControl(kClickToContinue); + return true; + } + + Common::Point position( + event.mouse.x - _controlsWindow->getDimensions().left, + event.mouse.y - _controlsWindow->getDimensions().top); + + CommandButton data; + if (!_controlData) + return false; + + Common::Array<CommandButton>::iterator it = _controlData->begin(); + for (; it != _controlData->end(); ++it) { + if (it->isInsideBounds(position)) { + it->select(); + data = *it; + } else { + it->unselect(); + } + } + + _engine->selectControl(data.getData().refcon); + _engine->refreshReady(); + _engine->preparedToRun(); + } + return false; +} + +bool MacVenture::Gui::processMainGameEvents(WindowClick click, Common::Event &event) { + if (_engine->needsClickToContinue()) + return true; + + return false; +} + +bool MacVenture::Gui::processOutConsoleEvents(WindowClick click, Common::Event &event) { + if (_engine->needsClickToContinue()) + return true; + + if (click == kBorderScrollUp && event.type == Common::EVENT_LBUTTONDOWN) { + _consoleText->scrollUp(); + return true; + } + if (click == kBorderScrollDown && event.type == Common::EVENT_LBUTTONDOWN) { + _consoleText->scrollDown(); + return true; + } + + return getWindowData(kOutConsoleWindow).visible; +} + +bool MacVenture::Gui::processSelfEvents(WindowClick click, Common::Event &event) { + if (_engine->needsClickToContinue()) + return true; + + if (event.type == Common::EVENT_LBUTTONUP) { + _engine->handleObjectSelect(1, kSelfWindow, false, false); + } + return true; +} + +bool MacVenture::Gui::processExitsEvents(WindowClick click, Common::Event &event) { + if (event.type == Common::EVENT_LBUTTONUP) { + if (_engine->needsClickToContinue()) { + return true; + } + + Common::Point position( + event.mouse.x - _exitsWindow->getDimensions().left, + event.mouse.y - _exitsWindow->getDimensions().top); + + CommandButton button; + if (!_exitsData) + return false; + + Common::Array<CommandButton>::iterator it = _exitsData->begin(); + for (; it != _exitsData->end(); ++it) { + if (it->isInsideBounds(position)) { + it->select(); + button = *it; + _engine->handleObjectSelect(button.getData().refcon, kExitsWindow, false, false); + return true; + } else { + it->unselect(); + } + } + + } + return getWindowData(kExitsWindow).visible; +} + +bool MacVenture::Gui::processDiplomaEvents(WindowClick click, Common::Event &event) { + if (_engine->needsClickToContinue()) + return true; + + return getWindowData(kDiplomaWindow).visible; +} + +bool Gui::processInventoryEvents(WindowClick click, Common::Event &event) { + if (event.type == Common::EVENT_LBUTTONDOWN && click == kBorderCloseButton) { + WindowReference ref = findWindowAtPoint(event.mouse); + if (ref == kNoWindow) { + return false; + } + + if (click == kBorderCloseButton) { + removeInventoryWindow(ref); + return true; + } + } + + if (_engine->needsClickToContinue()) + return true; + + if (event.type == Common::EVENT_LBUTTONDOWN) { + // Find the appropriate window + WindowReference ref = findWindowAtPoint(event.mouse); + if (ref == kNoWindow) { + return false; + } + + WindowData &data = findWindowData((WindowReference) ref); + + if (click == kBorderScrollUp) { + data.scrollPos.y = MAX(0, data.scrollPos.y - kScrollAmount); + } + if (click == kBorderScrollDown) { + data.scrollPos.y += kScrollAmount; + } + if (click == kBorderScrollLeft) { + data.scrollPos.x = MAX(0, data.scrollPos.x - kScrollAmount); + } + if (click == kBorderScrollRight) { + data.scrollPos.x += kScrollAmount; + } + } + return true; +} + +void Gui::selectForDrag(Common::Point cursorPosition) { + WindowReference ref = findWindowAtPoint(cursorPosition); + if (ref == kNoWindow) { + return; + } + + Graphics::MacWindow *win = findWindow(ref); + WindowData &data = findWindowData((WindowReference) ref); + + Common::Rect clickRect = calculateClickRect(cursorPosition + data.scrollPos, win->getDimensions()); + checkSelect(data, cursorPosition, clickRect, (WindowReference)ref); +} + +void Gui::handleSingleClick() { + debugC(2, kMVDebugGUI, "Registered Single Click"); + // HACK THERE HAS TO BE A MORE ELEGANT WAY + if (_dialog) { + return; + } + handleDragRelease(false, false); +} + +void Gui::handleDoubleClick() { + debugC(2, kMVDebugGUI, "Registered Double Click"); + if (_dialog) { + return; + } + handleDragRelease(false, true); +} + +void Gui::ensureAssetLoaded(ObjID obj) { + if (!_assets.contains(obj)) { + _assets[obj] = new ImageAsset(obj, _graphics); + } +} + + +} // End of namespace MacVenture |