/* 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 "common/algorithm.h" #include "common/textconsole.h" #include "buried/buried.h" #include "buried/graphics.h" #include "buried/message.h" #include "buried/window.h" namespace Buried { const Window *kWindowPosTop = (const Window *)nullptr; const Window *kWindowPosTopMost = (const Window *)-1; Window::Window(BuriedEngine *vm, Window *parent, bool visible) : _vm(vm), _parent(parent), _visible(visible) { _enabled = true; // Add us to the bottom of the parent's window list if (_parent) _parent->_children.push_front(this); } Window::~Window() { // Remove us from any of the parent's window lists if (_parent) { _parent->_children.remove(this); _parent->_topMostChildren.remove(this); } // Remove any of our messages from the queue _vm->removeAllMessages(this); // ...and any timers _vm->removeAllTimers(this); // Make sure we're not the focused window if (_vm->_focusedWindow == this) _vm->_focusedWindow = nullptr; // And also not captured if (_vm->_captureWindow == this) _vm->_captureWindow = nullptr; // Invalidate this window's rect as well _vm->_gfx->invalidateRect(getAbsoluteRect()); } void Window::invalidateRect(const Common::Rect &rect, bool erase) { _vm->_gfx->invalidateRect(makeAbsoluteRect(rect), erase); } Common::Rect Window::getClientRect() const { return Common::Rect(_rect.width(), _rect.height()); } Common::Rect Window::getAbsoluteRect() const { return makeAbsoluteRect(_rect); } void Window::sendMessage(Message *message) { switch (message->getMessageType()) { case kMessageTypeKeyUp: onKeyUp(((KeyUpMessage *)message)->getKeyState(), ((KeyUpMessage *)message)->getFlags()); break; case kMessageTypeKeyDown: onKeyDown(((KeyDownMessage *)message)->getKeyState(), ((KeyDownMessage *)message)->getFlags()); break; case kMessageTypeTimer: onTimer(((TimerMessage *)message)->getTimer()); break; case kMessageTypeMouseMove: onMouseMove(((MouseMoveMessage *)message)->getPoint(), ((MouseMoveMessage *)message)->getFlags()); break; case kMessageTypeLButtonUp: onLButtonUp(((LButtonUpMessage *)message)->getPoint(), ((LButtonUpMessage *)message)->getFlags()); break; case kMessageTypeLButtonDown: onLButtonDown(((LButtonDownMessage *)message)->getPoint(), ((LButtonDownMessage *)message)->getFlags()); break; case kMessageTypeMButtonUp: onMButtonUp(((MButtonUpMessage *)message)->getPoint(), ((MButtonUpMessage *)message)->getFlags()); break; case kMessageTypeRButtonUp: onRButtonUp(((RButtonUpMessage *)message)->getPoint(), ((RButtonUpMessage *)message)->getFlags()); break; case kMessageTypeRButtonDown: onRButtonDown(((RButtonDownMessage *)message)->getPoint(), ((RButtonDownMessage *)message)->getFlags()); break; case kMessageTypeSetCursor: onSetCursor(((SetCursorMessage *)message)->getMessage()); break; case kMessageTypeEnable: onEnable(((EnableMessage *)message)->getEnable()); break; default: error("Unknown message type %d", message->getMessageType()); } delete message; } void Window::postMessage(Message *message) { // Simple wrapper _vm->postMessageToWindow(this, message); } void Window::updateWindow() { // If we're not visible, ignore if (!isWindowVisible()) return; // If we're not in the dirty rect, ignore if (!_vm->_gfx->getDirtyRect().intersects(getAbsoluteRect())) return; // If we need to erase, erase first if (_vm->_gfx->needsErase()) onEraseBackground(); // Always draw this window first onPaint(); // Draw children for (auto &child : _children) child->updateWindow(); // Draw top-most children for (auto &child : _topMostChildren) child->updateWindow(); } void Window::setWindowPos(const Window *insertAfter, int x, int y, int width, int height, uint flags) { if (!(flags & kWindowPosNoZOrder)) { assert(insertAfter != this); // I don't even want to think about this case _parent->_children.remove(this); _parent->_topMostChildren.remove(this); if (insertAfter == kWindowPosTop) { // Reposition the window to the top _parent->_children.push_back(this); } else if (insertAfter == kWindowPosTopMost) { // Reposition the window to the top of the top-most _parent->_topMostChildren.push_back(this); } else { // Reposition the window to after insertAfter WindowList::iterator it = Common::find(_parent->_children.begin(), _parent->_children.end(), insertAfter); if (it == _parent->_children.end()) { it = Common::find(_parent->_topMostChildren.begin(), _parent->_topMostChildren.end(), insertAfter); // It has to be in one of the lists assert(it != _parent->_topMostChildren.end()); _parent->_topMostChildren.insert(it, this); } else { _parent->_children.insert(it, this); } } } if (flags & kWindowPosShowWindow) { assert(!(flags & kWindowPosHideWindow)); showWindow(kWindowShow); } else if (flags & kWindowPosHideWindow) { assert(!(flags & kWindowPosShowWindow)); showWindow(kWindowHide); } if (!(flags & kWindowPosNoActivate)) { // TODO: Activate the window } if (!(flags & kWindowPosNoMove)) _rect.moveTo(x, y); if (!(flags & kWindowPosNoSize)) { _rect.right = _rect.left + width; _rect.bottom = _rect.top + height; } } void Window::showWindow(WindowShowMode showMode) { bool newVisibility = (showMode != kWindowHide); if (_visible != newVisibility) { invalidateWindow(); _visible = newVisibility; } if (showMode == kWindowShowNormal) { // TODO: Activate } } void Window::enableWindow(bool enable) { if (_enabled != enable) { _enabled = enable; postMessage(new EnableMessage(enable)); } } bool Window::isWindowEnabled() const { if (_parent && !_parent->isWindowEnabled()) return false; return _enabled && _visible; } uint Window::setTimer(uint elapse) { return _vm->createTimer(this, elapse); } bool Window::killTimer(uint timer) { return _vm->killTimer(timer); } Common::Rect Window::makeAbsoluteRect(const Common::Rect &rect) const { // No parent; it's already absolute if (!_parent) return rect; Common::Rect parentRect = _parent->getAbsoluteRect(); Common::Rect absoluteRect = rect; absoluteRect.translate(parentRect.left, parentRect.top); absoluteRect.right = MIN(parentRect.right, absoluteRect.right); absoluteRect.bottom = MIN(parentRect.bottom, absoluteRect.bottom); return absoluteRect; } Window *Window::setFocus() { // Don't allow focus to be acquired if the window is disabled if (!isWindowEnabled()) return nullptr; Window *oldWindow = nullptr; // Notify the old window we just took its focus if (_vm->_focusedWindow) { _vm->_focusedWindow->onKillFocus(this); oldWindow = _vm->_focusedWindow; } _vm->_focusedWindow = this; onSetFocus(oldWindow); return oldWindow; } Window *Window::childWindowAtPoint(const Common::Point &point) { for (WindowList::iterator it = _topMostChildren.reverse_begin(); it != _topMostChildren.end(); --it) if ((*it)->getAbsoluteRect().contains(point) && (*it)->isWindowEnabled()) return (*it)->childWindowAtPoint(point); for (WindowList::iterator it = _children.reverse_begin(); it != _children.end(); --it) if ((*it)->getAbsoluteRect().contains(point) && (*it)->isWindowEnabled()) return (*it)->childWindowAtPoint(point); return this; } Window *Window::setCapture() { Window *oldCapturedWindow = _vm->_captureWindow; _vm->_captureWindow = this; return oldCapturedWindow; } Common::Point Window::convertPointToGlobal(const Common::Point &point) { Common::Rect absoluteRect = getAbsoluteRect(); return Common::Point(point.x + absoluteRect.left, point.y + absoluteRect.top); } Common::Point Window::convertPointToLocal(const Common::Point &point) { Common::Rect absoluteRect = getAbsoluteRect(); return Common::Point(point.x - absoluteRect.left, point.y - absoluteRect.top); } Common::Point Window::convertPointToWindow(const Common::Point &point, Window *dest) { return dest->convertPointToLocal(convertPointToGlobal(point)); } bool Window::onSetCursor(uint message) { // The default implementation is to try the parent first if (_parent && _parent->onSetCursor(message)) return true; // Then otherwise default to the arrow _vm->_gfx->setCursor(kCursorArrow); return false; } } // End of namespace Buried