scummvm/engines/m4/viewmgr.cpp

448 lines
12 KiB
C++

/* 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.
*
* $URL$
* $Id$
*
*/
// TODO: Views have a _coords rect, so I'm not sure if x/y is needed in the onRefresh
#include "m4/m4.h"
#include "m4/viewmgr.h"
#include "m4/mads_anim.h"
namespace M4 {
void returnToMainMenuFn(MadsM4Engine *vm) {
vm->_palette->resetColorCounts();
vm->_palette->setMadsSystemPalette();
vm->loadMenu(MAIN_MENU);
}
RectList::RectList() {
}
RectList::~RectList() {
}
void RectList::addRect(int x1, int y1, int x2, int y2) {
addRect(Common::Rect(x1, y1, x2, y2));
}
void RectList::addRect(const Common::Rect &rect) {
/* TODO:
Implement the following:
- Don't add the Rect if it's contained in any Rect in the list
- Split up the Rect if it intersects any Rect in the list
and add the resulting partial Rects instead
*/
push_back(rect);
}
int RectList::find(const Common::Point &pt) {
for (uint idx = 0; idx < size(); ++idx) {
if (this->operator [](idx).contains(pt.x, pt.y))
return idx;
}
return -1;
}
//--------------------------------------------------------------------------
HotkeyList::HotkeyList(View *owner) : _view(owner) {
}
HotkeyList::~HotkeyList() {
for (uint32 i = 0; i < _hotkeys.size(); i++)
delete _hotkeys[i];
}
void HotkeyList::add(uint32 key, Hotkey::Callback callback) {
_hotkeys.push_back(new Hotkey(key, callback));
}
void HotkeyList::remove(uint32 key) {
for (uint32 i = 0; i < _hotkeys.size(); i++) {
if (_hotkeys[i]->key == key) {
delete _hotkeys[i];
_hotkeys.remove_at(i);
break;
}
}
}
bool HotkeyList::call(uint32 key) {
for (uint32 i = 0; i < _hotkeys.size(); i++) {
if (_hotkeys[i]->key == key) {
if (_hotkeys[i]->callback)
(_hotkeys[i]->callback)(_vm, _view, key);
return true;
}
}
return false;
}
//--------------------------------------------------------------------------
// View constructor
View::View(MadsM4Engine *vm, const Common::Rect &viewBounds, bool transparent)
: M4Surface(viewBounds.width(), viewBounds.height()), _hotkeys(this), _vm(vm) {
SCREEN_FLAGS_DEFAULT;
_coords = viewBounds;
_transparent = transparent;
}
View::View(MadsM4Engine *vm, int x, int y, bool transparent)
: M4Surface(), _hotkeys(this), _vm(vm) {
SCREEN_FLAGS_DEFAULT;
_coords.left = x;
_coords.top = y;
_coords.right = _vm->_screen->width();
_coords.bottom = _vm->_screen->height();
_transparent = transparent;
}
void View::getCoordinates(Common::Rect &rect) {
rect = _coords;
}
void View::extract(int *status) {
}
void View::show() {
_screenFlags.visible = true;
_vm->_viewManager->moveToFront(this);
_vm->_viewManager->restore(_coords);
}
void View::hide() {
_screenFlags.visible = false;
_vm->_viewManager->restore(_coords);
}
void View::moveToBack() {
_vm->_viewManager->moveToBack(this);
}
void View::moveAbsolute(int x, int y) {
// TODO: Handle clipping and offscreen
Common::Rect oldCoords = _coords;
_coords.moveTo(x, y);
_vm->_viewManager->restore(oldCoords);
_vm->_viewManager->restore(_coords);
}
void View::moveRelative(int x, int y) {
// TODO: Handle clipping and offscreen
Common::Rect oldCoords = _coords;
_coords.translate(x, y);
_vm->_viewManager->restore(oldCoords);
_vm->_viewManager->restore(_coords);
}
void View::resize(int newWidth, int newHeight) {
Common::Rect oldCoords = _coords;
if (newWidth >= 0)
_coords.setWidth(newWidth);
if (newHeight >= 0)
_coords.setHeight(newHeight);
_vm->_viewManager->restore(oldCoords);
_vm->_viewManager->restore(_coords);
}
void View::restore(int x1, int y1, int x2, int y2) {
_vm->_viewManager->restore(_coords.left + x1, _coords.top + y1, _coords.left + x2, _coords.top + y2);
}
void View::onRefresh(RectList *rects, M4Surface *destSurface) {
assert(destSurface);
if (rects == NULL)
// No rect list specified, so copy entire surface
copyTo(destSurface, _coords.left, _coords.top, _transparent ? 0 : -1);
else {
// Loop through the set of specified rectangles
RectList::iterator i;
for (i = rects->begin(); i != rects->end(); ++i) {
Common::Rect &destRect = *i;
Common::Rect srcBounds(destRect.left - _coords.left, destRect.top - _coords.top,
destRect.right - _coords.left, destRect.bottom - _coords.top);
copyTo(destSurface, srcBounds, destRect.left, destRect.top, _transparent ? 0 : -1);
}
}
}
//--------------------------------------------------------------------------
ViewManager::ViewManager(MadsM4Engine *vm): _systemHotkeys(HotkeyList(NULL)), _vm(vm) {
_captureScreen = NULL;
_captureEvents = false;
}
ViewManager::~ViewManager() {
// Delete any remaining active views
ListIterator i;
for (i = _views.begin(); i != _views.end(); ++i)
delete (*i);
}
void ViewManager::addView(View *view) {
_views.push_back(view);
moveToFront(view);
}
// Warning: After calling this method, the passed view object will no longer be valid
void ViewManager::deleteView(View *view) {
_views.remove(view);
delete view;
}
void ViewManager::handleEvents(const Common::Event &event) {
}
void ViewManager::handleKeyboardEvents(uint32 keycode) {
Common::Point mousePos = _vm->_mouse->currentPos();
View *view;
bool blockedFlag;
bool foundFlag;
bool handledFlag;
// Scan view list for one which accepts or blocks keyboard events. If one is found,
// then the event is passed to it
view = NULL;
handledFlag = false;
foundFlag = false;
blockedFlag = false;
// Loop from the front to back view
ListIterator i;
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag; --i) {
view = *i;
if (!view->isVisible()) continue;
if (view->screenFlags().blocks & SCREVENT_KEY)
blockedFlag = true;
if (view->screenFlags().get & SCREVENT_KEY) {
foundFlag = true;
handledFlag = (view->onEvent)(KEVENT_KEY, keycode, mousePos.x, mousePos.y, _captureEvents);
if (_captureEvents)
_captureScreen = view;
}
}
// Scan view list for one with a hotkey list, aborting if a view is found that either
// blocks keyboard events, or has a hotkey list that includes the keycode
blockedFlag = false;
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag && !blockedFlag; --i) {
view = *i;
if (!view->isVisible()) continue;
if (view->screenFlags().blocks & SCREVENT_KEY)
blockedFlag = true;
if (view->screenFlags().get & SCREVENT_KEY) {
if (view->hotkeys().call(keycode)) {
handledFlag = true;
_captureEvents = false;
//_vm->_dialogs->keyMouseCollision(); // TODO
}
}
}
// Final check: if no view handled or blocked the key, check against the system hotkey list
if (!handledFlag && !blockedFlag) {
handledFlag = _systemHotkeys.call(keycode);
if (handledFlag) {
_captureEvents = false;
//_vm->_dialogs->keyMouseCollision(); // TODO
}
}
}
void ViewManager::handleMouseEvents(M4EventType event) {
Common::Point mousePos = _vm->_mouse->currentPos();
ListIterator i;
View *view;
bool blockedFlag;
bool foundFlag;
// If a window sets the _captureEvents flag to true, it will receive all events until
// it sets it to false, even if it's not the top window
if (_captureEvents) {
assert(_captureScreen);
if (_captureScreen->screenFlags().get & SCREVENT_MOUSE)
(_captureScreen->onEvent)(event, 0, mousePos.x, mousePos.y, _captureEvents);
} else {
blockedFlag = false;
foundFlag = false;
view = NULL;
// Loop from the front to back view
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag && !blockedFlag; --i) {
view = *i;
if (!view->isVisible()) continue;
if (view->screenFlags().blocks & SCREVENT_MOUSE)
blockedFlag = true;
if ((view->screenFlags().get & SCREVENT_MOUSE) && view->isInside(mousePos.x, mousePos.y))
foundFlag = true;
}
if (foundFlag)
view->onEvent(event, 0, mousePos.x, mousePos.y, _captureEvents);
else
_captureEvents = false;
if (_captureEvents)
_captureScreen = view;
}
}
void ViewManager::restore(int x1, int y1, int x2, int y2) {
RectList *rl = new RectList();
Common::Rect redrawBounds(x1, y1, x2, y2);
rl->addRect(x1, y1, x2, y2);
for (ListIterator i = _views.begin(); i != _views.end(); ++i) {
View *v = *i;
if (v->isVisible() && v->bounds().intersects(redrawBounds))
v->onRefresh(rl, _vm->_screen);
}
_vm->_screen->update();
}
void ViewManager::restore(const Common::Rect &rect) {
restore(rect.left, rect.top, rect.right, rect.bottom);
}
void ViewManager::moveToFront(View *view) {
if (_views.size() < 2)
return;
_views.remove(view);
ListIterator i = _views.begin();
while ((i != _views.end()) && ((*i)->layer() <= view->layer()))
++i;
_views.insert(i, view);
}
void ViewManager::moveToBack(View *view) {
if (_views.size() < 2)
return;
_views.remove(view);
ListIterator i = _views.begin();
while ((i != _views.end()) && ((*i)->layer() < view->layer()))
++i;
_views.insert(i, view);
}
View *ViewManager::getView(int screenType) {
ListIterator i = _views.begin();
while (i != _views.end()) {
if ((*i)->screenType() == screenType)
return *i;
++i;
}
return NULL;
}
void ViewManager::updateState() {
Common::List<View *> viewList = _views;
for (ListIterator i = viewList.begin(); i != viewList.end(); ++i) {
if (_vm->_events->quitFlag)
return;
View *v = *i;
v->updateState();
}
}
void ViewManager::refreshAll() {
_vm->_screen->clear();
for (ListIterator i = _views.begin(); i != _views.end(); ++i) {
View *v = *i;
if (v->isVisible())
v->onRefresh(NULL, _vm->_screen);
}
_vm->_screen->update();
}
void ViewManager::showTextView(const char *textViewName, bool returnToMainMenu) {
// Deactivate the scene if it's currently active
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
if (view != NULL)
_vm->_viewManager->deleteView(view);
// Deactivate the main menu if it's currently active
view = _vm->_viewManager->getView(VIEWID_MAINMENU);
if (view != NULL)
_vm->_viewManager->deleteView(view);
// Activate the textview view
_vm->_font->setFont(FONT_CONVERSATION_MADS);
TextviewView *textView = new TextviewView(_vm);
_vm->_viewManager->addView(textView);
if (returnToMainMenu)
textView->setScript(textViewName, returnToMainMenuFn);
else
textView->setScript(textViewName, NULL);
}
void ViewManager::showAnimView(const char *animViewName, bool returnToMainMenu) {
// Deactivate the scene if it's currently active
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
if (view != NULL)
_vm->_viewManager->deleteView(view);
// Deactivate the main menu if it's currently active
view = _vm->_viewManager->getView(VIEWID_MAINMENU);
if (view != NULL)
_vm->_viewManager->deleteView(view);
// Activate the animview view
AnimviewView *animView = new AnimviewView(_vm);
_vm->_viewManager->addView(animView);
if (returnToMainMenu)
animView->setScript(animViewName, returnToMainMenuFn);
else
animView->setScript(animViewName, NULL);
}
} // End of namespace M4