mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-21 09:21:08 +00:00
1216 lines
31 KiB
C++
1216 lines
31 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.
|
|
*
|
|
*/
|
|
|
|
#include "common/events.h"
|
|
#include "common/keyboard.h"
|
|
#include "common/textconsole.h"
|
|
|
|
#include "m4/globals.h"
|
|
#include "m4/events.h"
|
|
#include "m4/font.h"
|
|
#include "m4/graphics.h"
|
|
#include "m4/viewmgr.h"
|
|
#include "m4/gui.h"
|
|
#include "m4/midi.h"
|
|
#include "m4/scene.h"
|
|
#include "m4/m4.h"
|
|
|
|
namespace M4 {
|
|
|
|
//--------------------------------------------------------------------------
|
|
// DialogView class
|
|
//
|
|
// Defines a generic base class for dialogs, that some of the classes
|
|
// in the object hierharchy require as a parent
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DialogView::close() {
|
|
// Default to simply destroying the given dialog
|
|
_vm->_viewManager->deleteView(this);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// GUIObject class
|
|
//
|
|
// Defines a generic object that appears in a view
|
|
//--------------------------------------------------------------------------
|
|
|
|
GUIObject::GUIObject(View *owner, const Common::Rect &bounds) {
|
|
_parent = owner;
|
|
_bounds = bounds;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MenuObject class
|
|
//
|
|
// Defines a specialised GUI object that appears in a dialog
|
|
//--------------------------------------------------------------------------
|
|
|
|
MenuObject::MenuObject(DialogView *owner, int objectId, int xs, int ys, int width, int height,
|
|
bool greyed, bool transparent):
|
|
GUIObject(owner, Common::Rect(xs, ys, xs + width, ys + height)) {
|
|
|
|
_objectId = objectId;
|
|
_bounds.top = ys;
|
|
_bounds.bottom = ys + height - 1;
|
|
_bounds.left = xs;
|
|
_bounds.right = xs + width - 1;
|
|
_transparent = transparent;
|
|
_objectState = greyed ? OS_GREYED : OS_NORMAL;
|
|
_callback = NULL;
|
|
|
|
if (transparent) {
|
|
_background = new M4Surface(width, height);
|
|
Common::Rect srcBounds(xs, ys, xs + width - 1, ys + height - 1);
|
|
_background->copyFrom(owner, srcBounds, 0, 0);
|
|
} else {
|
|
_background = NULL;
|
|
}
|
|
}
|
|
|
|
MenuObject::~MenuObject() {
|
|
delete _background;
|
|
}
|
|
|
|
void MenuObject::onExecute() {
|
|
// If a callback function has been specified, then execute it
|
|
if (_callback)
|
|
_callback(parent(), this);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MenuButton class
|
|
//
|
|
// Defines a button object
|
|
//--------------------------------------------------------------------------
|
|
|
|
MenuButton::MenuButton(DialogView *owner, int buttonId, int xs, int ys, int width, int height,
|
|
MenuButton::Callback callbackFn, bool greyed, bool transparent,
|
|
ObjectType buttonType):
|
|
MenuObject(owner, buttonId, xs, ys, width, height, greyed, transparent) {
|
|
|
|
_objectType = buttonType;
|
|
_callback = callbackFn;
|
|
}
|
|
|
|
bool MenuButton::onEvent(M4EventType event, int32 param, int x, int y, MenuObject *¤tItem) {
|
|
bool redrawFlag = false;
|
|
bool callbackFlag = false;
|
|
bool handledFlag = true;
|
|
|
|
if (_objectState == OS_GREYED)
|
|
return false;
|
|
|
|
switch (event) {
|
|
case MEVENT_LEFT_CLICK:
|
|
case MEVENT_DOUBLECLICK:
|
|
if (isInside(x, y)) {
|
|
_objectState = OS_PRESSED;
|
|
if (currentItem != NULL)
|
|
currentItem = this;
|
|
redrawFlag = true;
|
|
} else {
|
|
currentItem = NULL;
|
|
if (_objectState != OS_NORMAL) {
|
|
_objectState = OS_PRESSED;
|
|
redrawFlag = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MEVENT_LEFT_DRAG:
|
|
case MEVENT_DOUBLECLICK_DRAG:
|
|
if (!currentItem) {
|
|
return true;
|
|
}
|
|
if (isInside(x, y)) {
|
|
if (_objectState != OS_PRESSED) {
|
|
_objectState = OS_PRESSED;
|
|
redrawFlag = true;
|
|
}
|
|
}
|
|
else {
|
|
if (_objectState != OS_MOUSEOVER) {
|
|
_objectState = OS_MOUSEOVER;
|
|
redrawFlag = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MEVENT_LEFT_RELEASE:
|
|
case MEVENT_DOUBLECLICK_RELEASE:
|
|
if (isInside(x, y)) {
|
|
if (currentItem) {
|
|
callbackFlag = true;
|
|
if (_objectType == OBJTYPE_OM_SWITCH_ON)
|
|
_objectType = OBJTYPE_OM_SWITCH_OFF;
|
|
else if (_objectType == OBJTYPE_OM_SWITCH_OFF)
|
|
_objectType = OBJTYPE_OM_SWITCH_ON;
|
|
}
|
|
else {
|
|
currentItem = this;
|
|
}
|
|
|
|
_objectState = OS_MOUSEOVER;
|
|
redrawFlag = true;
|
|
|
|
} else {
|
|
currentItem = NULL;
|
|
_objectState = OS_MOUSEOVER;
|
|
redrawFlag = true;
|
|
handledFlag = false;
|
|
}
|
|
break;
|
|
|
|
case MEVENT_MOVE:
|
|
if (isInside(x, y)) {
|
|
currentItem = this;
|
|
if (_objectState != OS_MOUSEOVER) {
|
|
_objectState = OS_MOUSEOVER;
|
|
redrawFlag = true;
|
|
}
|
|
}
|
|
else {
|
|
currentItem = NULL;
|
|
if (_objectState != OS_NORMAL) {
|
|
_objectState = OS_NORMAL;
|
|
redrawFlag = true;
|
|
handledFlag = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MEVENT_LEFT_HOLD:
|
|
case MEVENT_DOUBLECLICK_HOLD:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//see if we need to redraw the button
|
|
if (redrawFlag) {
|
|
onRefresh();
|
|
|
|
// TODO: There may be a more efficient mechanism than refreshing the entire screen
|
|
// when a menu object refreshes itself
|
|
if (parent()->screenFlags().visible)
|
|
_vm->_viewManager->refreshAll();
|
|
}
|
|
|
|
// If a callback is flagged, then handle it
|
|
|
|
if (callbackFlag)
|
|
onExecute();
|
|
|
|
return handledFlag;
|
|
}
|
|
|
|
void MenuButton::onRefresh() {
|
|
M4Sprite *sprite = NULL;
|
|
SpriteAsset &sprites = *parent()->sprites();
|
|
|
|
// Switch handling for the various button types
|
|
switch (_objectType) {
|
|
case OBJTYPE_BUTTON:
|
|
sprite = sprites[GM_BUTTON_GREYED + _objectState];
|
|
break;
|
|
|
|
case OBJTYPE_OM_SWITCH_ON:
|
|
switch (_objectState) {
|
|
case OS_MOUSEOVER:
|
|
sprite = sprites[MENU_SS_SWITCH_ON_MOUSEOVER];
|
|
break;
|
|
case OS_PRESSED:
|
|
sprite = sprites[MENU_SS_SWITCH_ON_PRESSED];
|
|
break;
|
|
default:
|
|
sprite = sprites[MENU_SS_SWITCH_ON_NORMAL];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OBJTYPE_OM_SWITCH_OFF:
|
|
switch (_objectState) {
|
|
case OS_MOUSEOVER:
|
|
sprite = sprites[MENU_SS_SWITCH_OFF_MOUSEOVER];
|
|
break;
|
|
case OS_PRESSED:
|
|
sprite = sprites[MENU_SS_SWITCH_OFF_PRESSED];
|
|
break;
|
|
default:
|
|
sprite = sprites[MENU_SS_SWITCH_OFF_NORMAL];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OBJTYPE_OM_DONE:
|
|
sprite = sprites[OM_DONE_BTN_GREYED + _objectState];
|
|
break;
|
|
|
|
case OBJTYPE_OM_CANCEL:
|
|
sprite = (_objectState == OS_GREYED) ? sprites[OM_CANCEL_BTN_NORMAL] :
|
|
sprites[OM_CANCEL_BTN_NORMAL + _objectState - 1];
|
|
break;
|
|
|
|
case OBJTYPE_SL_SAVE:
|
|
sprite = sprites[SL_SAVE_BTN_GREYED + _objectState];
|
|
break;
|
|
|
|
case OBJTYPE_SL_LOAD:
|
|
sprite = sprites[SL_LOAD_BTN_GREYED + _objectState];
|
|
break;
|
|
|
|
case OBJTYPE_SL_CANCEL:
|
|
sprite = (_objectState == OS_GREYED) ? sprites[SL_CANCEL_BTN_NORMAL] :
|
|
sprites[SL_CANCEL_BTN_NORMAL + _objectState - 1];
|
|
break;
|
|
|
|
case OBJTYPE_SL_TEXT:
|
|
switch (_objectState) {
|
|
case OS_MOUSEOVER:
|
|
_vm->_font->current()->setColors(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND,
|
|
TEXT_COLOR_MOUSEOVER_HILIGHT);
|
|
sprite = sprites[SL_LINE_MOUSEOVER];
|
|
break;
|
|
|
|
case OS_PRESSED:
|
|
_vm->_font->current()->setColors(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND,
|
|
TEXT_COLOR_PRESSED_HILIGHT);
|
|
sprite = sprites[SL_LINE_PRESSED];
|
|
break;
|
|
|
|
case OS_GREYED:
|
|
_vm->_font->current()->setColors(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND,
|
|
TEXT_COLOR_GREYED_HILIGHT);
|
|
sprite = sprites[SL_LINE_NORMAL];
|
|
break;
|
|
|
|
default:
|
|
case OS_NORMAL:
|
|
_vm->_font->current()->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND,
|
|
TEXT_COLOR_NORMAL_HILIGHT);
|
|
sprite = sprites[SL_LINE_NORMAL];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error("Unknown object type");
|
|
break;
|
|
}
|
|
|
|
// If no sprite object was set, then exit without doing anything
|
|
if (!sprite)
|
|
return;
|
|
|
|
// Draw the button
|
|
if (_transparent) {
|
|
// Transparent button, so restore original background
|
|
if (!_background)
|
|
return;
|
|
else
|
|
_background->copyTo(parent(), _bounds.left, _bounds.top);
|
|
}
|
|
|
|
sprite->copyTo(parent(), _bounds.left, _bounds.top, 0);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MenuHorizSlider class
|
|
//
|
|
// Defines a horizontal slider that allows selection of a percentage
|
|
//--------------------------------------------------------------------------
|
|
|
|
MenuHorizSlider::MenuHorizSlider(DialogView *owner, int objectId, int xs, int ys,
|
|
int width, int height, int initialPercentage, Callback callbackFn, bool transparent):
|
|
MenuObject(owner, objectId, xs, ys, width, height, false, transparent) {
|
|
|
|
_objectType = OBJTYPE_SLIDER;
|
|
_callback = callbackFn;
|
|
|
|
SpriteAsset &sprites = *owner->sprites();
|
|
_sliderState = HSLIDER_THUMB_NORMAL;
|
|
_thumbSize.x = sprites[OM_SLIDER_BTN_NORMAL]->width();
|
|
_thumbSize.y = sprites[OM_SLIDER_BTN_NORMAL]->height();
|
|
_maxThumbX = width - _thumbSize.x;
|
|
_percent = MAX(MIN(initialPercentage, 100), 0);
|
|
_thumbX = initialPercentage * _maxThumbX / 100;
|
|
}
|
|
|
|
void MenuHorizSlider::onRefresh() {
|
|
// If the slider is transparent, first copy in the background
|
|
if (_transparent) {
|
|
// Transparent button
|
|
if (!_background)
|
|
return;
|
|
|
|
_background->copyTo(parent(), _bounds.left, _bounds.top, 0);
|
|
}
|
|
|
|
// Get the thumb sprite for the slider
|
|
SpriteAsset &sprites = *parent()->sprites();
|
|
M4Sprite *sprite = sprites[OM_SLIDER_BTN_NORMAL + _sliderState];
|
|
assert(sprite);
|
|
|
|
// Fill in the area to the left of the thumbnail
|
|
if (_thumbX > 2) {
|
|
Common::Rect leftBounds(_bounds.left + 3, _bounds.top + 9, _bounds.left + _thumbX,
|
|
_bounds.top + _thumbSize.y - 9);
|
|
parent()->fillRect(leftBounds, SLIDER_BAR_COLOR);
|
|
}
|
|
|
|
// Draw the thumbnail
|
|
sprite->copyTo(parent(), _bounds.left + _thumbX, _bounds.top, 0);
|
|
}
|
|
|
|
bool MenuHorizSlider::onEvent(M4EventType event, int32 param, int x, int y, MenuObject *¤tItem) {
|
|
static bool movingFlag = false;
|
|
static int movingX = 0;
|
|
bool redrawFlag = false, handledFlag = false, callbackFlag = false;
|
|
|
|
if (event == KEVENT_KEY)
|
|
return false;
|
|
|
|
switch (event) {
|
|
case MEVENT_LEFT_CLICK:
|
|
case MEVENT_DOUBLECLICK:
|
|
if (isInside(x, y) && (x - _bounds.left >= _thumbX) &&
|
|
(x - _bounds.left <= _thumbX + _thumbSize.x - 1)) {
|
|
// The thumbnail has been clicked
|
|
_sliderState = HSLIDER_THUMB_PRESSED;
|
|
movingFlag = true;
|
|
movingX = x;
|
|
currentItem = this;
|
|
redrawFlag = true;
|
|
} else {
|
|
currentItem = NULL;
|
|
_sliderState = HSLIDER_THUMB_NORMAL;
|
|
redrawFlag = true;
|
|
}
|
|
redrawFlag = true;
|
|
break;
|
|
|
|
case MEVENT_LEFT_DRAG:
|
|
case MEVENT_DOUBLECLICK_DRAG:
|
|
if (!currentItem)
|
|
return true;
|
|
|
|
if (movingFlag) {
|
|
if (x != movingX) {
|
|
if (x < movingX)
|
|
_thumbX -= MIN(_thumbX, movingX - x);
|
|
else
|
|
_thumbX += MIN(_maxThumbX - _thumbX, x - movingX);
|
|
_percent = _thumbX * 100 / _maxThumbX;
|
|
redrawFlag = callbackFlag = true;
|
|
}
|
|
movingX = CLIP(x, _bounds.left + _thumbX,
|
|
_bounds.left + _thumbX + _thumbSize.x - 1);
|
|
} else {
|
|
currentItem = NULL;
|
|
}
|
|
break;
|
|
|
|
case MEVENT_LEFT_RELEASE:
|
|
case MEVENT_DOUBLECLICK_RELEASE:
|
|
if (!currentItem)
|
|
return true;
|
|
|
|
movingFlag = false;
|
|
if (isInside(x, y) && (x - _bounds.left >= _thumbX) &&
|
|
(x - _bounds.left <= _thumbX + _thumbSize.x - 1)) {
|
|
_sliderState = HSLIDER_THUMB_MOUSEOVER;
|
|
currentItem = this;
|
|
} else {
|
|
_sliderState = HSLIDER_THUMB_NORMAL;
|
|
currentItem = NULL;
|
|
}
|
|
|
|
redrawFlag = true;
|
|
callbackFlag = true;
|
|
break;
|
|
|
|
case MEVENT_MOVE:
|
|
if (isInside(x, y) && (x - _bounds.left >= _thumbX) &&
|
|
(x - _bounds.left <= _thumbX + _thumbSize.x - 1)) {
|
|
if (_sliderState != HSLIDER_THUMB_MOUSEOVER) {
|
|
_sliderState = HSLIDER_THUMB_MOUSEOVER;
|
|
currentItem = this;
|
|
}
|
|
} else {
|
|
if (_sliderState != HSLIDER_THUMB_NORMAL) {
|
|
_sliderState = HSLIDER_THUMB_NORMAL;
|
|
currentItem = NULL;
|
|
handledFlag = false;
|
|
}
|
|
}
|
|
redrawFlag = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (redrawFlag)
|
|
onRefresh();
|
|
|
|
if (callbackFlag)
|
|
onExecute();
|
|
|
|
return handledFlag;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MenuVertSlider class
|
|
//
|
|
// Defines a vertical slider that's used in the save/load dialog
|
|
//--------------------------------------------------------------------------
|
|
|
|
MenuVertSlider::MenuVertSlider(DialogView *owner, int objectId, int xs, int ys,
|
|
int width, int height, int initialPercentage, Callback callbackFn, bool transparent):
|
|
MenuObject(owner, objectId, xs, ys, width, height, false, transparent) {
|
|
|
|
_objectType = OBJTYPE_SLIDER;
|
|
_callback = callbackFn;
|
|
|
|
SpriteAsset &sprites = *owner->sprites();
|
|
_sliderState = VSLIDER_NONE;
|
|
_thumbSize.x = sprites[SL_SLIDER_BTN_NORMAL]->width();
|
|
_thumbSize.y = sprites[SL_SLIDER_BTN_NORMAL]->height();
|
|
_minThumbY = sprites[SL_UP_BTN_NORMAL]->height() + 1;
|
|
_maxThumbY = sprites[SL_UP_BTN_NORMAL]->height() + sprites[SL_SCROLLBAR]->height() -
|
|
sprites[SL_SLIDER_BTN_NORMAL]->height() - 1;
|
|
|
|
_percent = MAX(MIN(initialPercentage, 100), 0);
|
|
_thumbY = _minThumbY + ((_percent * (_maxThumbY - _minThumbY)) / 100);
|
|
}
|
|
|
|
MenuVertSliderState MenuVertSlider::getSliderArea(int y) {
|
|
if (y < _minThumbY)
|
|
return VSLIDER_UP;
|
|
else if (y < _thumbY)
|
|
return VSLIDER_PAGE_UP;
|
|
else if (y < _thumbY + _thumbSize.y)
|
|
return VSLIDER_THUMBNAIL;
|
|
else if (y < _maxThumbY + _thumbSize.y)
|
|
return VSLIDER_PAGE_DOWN;
|
|
else
|
|
return VSLIDER_DOWN;
|
|
}
|
|
|
|
void MenuVertSlider::onRefresh() {
|
|
// If the slider is transparent, first copy in the background
|
|
if (_transparent) {
|
|
// Transparent button
|
|
if (!_background)
|
|
return;
|
|
|
|
_background->copyTo(parent(), _bounds.left, _bounds.top, 0);
|
|
}
|
|
|
|
// Get the various needed sprites
|
|
SpriteAsset &sprites = *parent()->sprites();
|
|
M4Sprite *barSprite = sprites[SL_SCROLLBAR];
|
|
M4Sprite *thumbSprite = sprites[SL_SLIDER_BTN_NORMAL];
|
|
M4Sprite *upSprite = sprites[SL_UP_BTN_NORMAL];
|
|
M4Sprite *downSprite = sprites[SL_DOWN_BTN_NORMAL];
|
|
|
|
if (_objectState == OS_GREYED) {
|
|
upSprite = sprites[SL_UP_BTN_GREYED];
|
|
downSprite = sprites[SL_DOWN_BTN_GREYED];
|
|
thumbSprite = NULL;
|
|
|
|
} else if (_objectState == OS_MOUSEOVER) {
|
|
if (_sliderState == VSLIDER_UP)
|
|
upSprite = sprites[SL_UP_BTN_MOUSEOVER];
|
|
else if (_sliderState == VSLIDER_THUMBNAIL)
|
|
thumbSprite = sprites[SL_SLIDER_BTN_MOUSEOVER];
|
|
else if (_sliderState == VSLIDER_DOWN)
|
|
downSprite = sprites[SL_DOWN_BTN_MOUSEOVER];
|
|
}
|
|
else if (_objectState == OS_PRESSED) {
|
|
if (_sliderState == VSLIDER_UP)
|
|
upSprite = sprites[SL_UP_BTN_PRESSED];
|
|
else if (_sliderState == VSLIDER_THUMBNAIL)
|
|
thumbSprite = sprites[SL_SLIDER_BTN_PRESSED];
|
|
else if (_sliderState == VSLIDER_DOWN)
|
|
downSprite = sprites[SL_DOWN_BTN_PRESSED];
|
|
}
|
|
|
|
// Draw the sprites
|
|
upSprite->copyTo(parent(), _bounds.left, _bounds.top, 0);
|
|
barSprite->copyTo(parent(), _bounds.left, _bounds.top + upSprite->height(), 0);
|
|
downSprite->copyTo(parent(), _bounds.left, _bounds.top + upSprite->height() + barSprite->height(), 0);
|
|
if (thumbSprite)
|
|
thumbSprite->copyTo(parent(), _bounds.left, _bounds.top + _thumbY, 0);
|
|
}
|
|
|
|
bool MenuVertSlider::onEvent(M4EventType event, int32 param, int x, int y, MenuObject *¤tItem) {
|
|
static bool movingFlag = false;
|
|
static int movingY = 0;
|
|
static uint32 callbackTime;
|
|
MenuVertSliderState tempState;
|
|
int delta;
|
|
uint32 currentTime = g_system->getMillis();
|
|
bool redrawFlag = false;
|
|
bool handledFlag = true;
|
|
bool callbackFlag = false;
|
|
|
|
if (event == KEVENT_KEY)
|
|
return false;
|
|
|
|
if (_objectState == OS_GREYED) {
|
|
currentItem = NULL;
|
|
return false;
|
|
}
|
|
|
|
switch (event) {
|
|
case MEVENT_LEFT_CLICK:
|
|
case MEVENT_DOUBLECLICK:
|
|
if (isInside(x, y)) {
|
|
currentItem = this;
|
|
tempState = getSliderArea(y - _bounds.top);
|
|
if (tempState == VSLIDER_THUMBNAIL) {
|
|
movingFlag = true;
|
|
movingY = y;
|
|
}
|
|
if ((tempState == VSLIDER_PAGE_UP) || (tempState == VSLIDER_PAGE_DOWN)) {
|
|
_sliderState = tempState;
|
|
setState(OS_NORMAL);
|
|
} else {
|
|
_sliderState = tempState;
|
|
setState(OS_PRESSED);
|
|
redrawFlag = true;
|
|
}
|
|
callbackFlag = true;
|
|
} else {
|
|
currentItem = NULL;
|
|
setState(OS_NORMAL);
|
|
redrawFlag = true;
|
|
}
|
|
break;
|
|
|
|
case MEVENT_LEFT_DRAG:
|
|
case MEVENT_DOUBLECLICK_DRAG:
|
|
if (!currentItem)
|
|
return true;
|
|
|
|
if (movingFlag) {
|
|
if (y < movingY) {
|
|
delta = MIN(_thumbY - _minThumbY, movingY - y);
|
|
if (delta > 0) {
|
|
_thumbY -= delta;
|
|
_percent = ((_thumbY - _minThumbY) * 100) / (_maxThumbY - _minThumbY);
|
|
redrawFlag = true;
|
|
callbackFlag = true;
|
|
}
|
|
}
|
|
else if (y > movingY) {
|
|
delta = MIN(_maxThumbY - _thumbY, y - movingY);
|
|
if (delta > 0) {
|
|
_thumbY += delta;
|
|
_percent = ((_thumbY - _minThumbY) * 100) / (_maxThumbY - _minThumbY);
|
|
redrawFlag = true;
|
|
callbackFlag = true;
|
|
}
|
|
}
|
|
movingY = y;
|
|
|
|
if (movingY < (_thumbY + _bounds.top))
|
|
movingY = _thumbY + _bounds.top;
|
|
else if (movingY > (_bounds.top + _thumbY + _thumbSize.y - 1))
|
|
movingY = _bounds.top + _thumbY + _thumbSize.y - 1;
|
|
|
|
} else {
|
|
if (isInside(x, y)) {
|
|
tempState = getSliderArea(y - _bounds.top);
|
|
if (_sliderState == tempState) {
|
|
if ((tempState != VSLIDER_PAGE_UP) && (tempState != VSLIDER_PAGE_DOWN) &&
|
|
(_objectState != OS_PRESSED)) {
|
|
_sliderState = tempState;
|
|
setState(OS_PRESSED);
|
|
redrawFlag = true;
|
|
}
|
|
if (currentTime - callbackTime > 100)
|
|
callbackFlag = true;
|
|
|
|
} else {
|
|
if (_objectState != OS_MOUSEOVER)
|
|
setState(OS_MOUSEOVER);
|
|
redrawFlag = true;
|
|
}
|
|
callbackFlag = true;
|
|
|
|
} else {
|
|
if (_objectState != OS_MOUSEOVER) {
|
|
setState(OS_MOUSEOVER);
|
|
redrawFlag = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MEVENT_LEFT_RELEASE:
|
|
case MEVENT_DOUBLECLICK_RELEASE:
|
|
movingFlag = false;
|
|
if (isInside(x, y)) {
|
|
tempState = getSliderArea(y - _bounds.top);
|
|
if ((tempState == VSLIDER_PAGE_UP) || (tempState == VSLIDER_PAGE_DOWN))
|
|
setState(OS_NORMAL);
|
|
else {
|
|
setState(OS_MOUSEOVER);
|
|
currentItem = this;
|
|
}
|
|
} else {
|
|
setState(OS_NORMAL);
|
|
currentItem = NULL;
|
|
}
|
|
|
|
redrawFlag = true;
|
|
if (parent()->getMenuType() == LOAD_MENU)
|
|
updateThumbnails();
|
|
break;
|
|
|
|
case MEVENT_MOVE:
|
|
if (isInside(x, y)) {
|
|
currentItem = this;
|
|
tempState = getSliderArea(y - _bounds.top);
|
|
if (_sliderState != tempState) {
|
|
if ((tempState == VSLIDER_PAGE_UP) || (tempState == VSLIDER_PAGE_DOWN))
|
|
_objectState = OS_NORMAL;
|
|
else {
|
|
_sliderState = tempState;
|
|
_objectState = OS_MOUSEOVER;
|
|
}
|
|
redrawFlag = true;
|
|
}
|
|
} else {
|
|
currentItem = NULL;
|
|
|
|
if (_objectState != OS_NORMAL) {
|
|
_objectState = OS_NORMAL;
|
|
redrawFlag = true;
|
|
handledFlag = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MEVENT_LEFT_HOLD:
|
|
case MEVENT_DOUBLECLICK_HOLD:
|
|
if (!currentItem)
|
|
return true;
|
|
|
|
if (isInside(x, y)) {
|
|
tempState = getSliderArea(y - _bounds.top);
|
|
|
|
if (_sliderState == tempState) {
|
|
if (currentTime - callbackTime > 100)
|
|
callbackFlag = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (redrawFlag)
|
|
onRefresh();
|
|
|
|
if (callbackFlag) {
|
|
callbackTime = currentTime;
|
|
onExecute();
|
|
}
|
|
|
|
return handledFlag;
|
|
}
|
|
|
|
void MenuVertSlider::setPercentage(int value) {
|
|
_percent = value;
|
|
_thumbY = _minThumbY + ((_percent * (_maxThumbY - _minThumbY)) / 100);
|
|
onRefresh();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MenuMessage class
|
|
//
|
|
// Defines a message menu object
|
|
//--------------------------------------------------------------------------
|
|
|
|
MenuMessage::MenuMessage(DialogView *owner, int objectId, int xs, int ys, int width, int height,
|
|
bool transparent):
|
|
MenuObject(owner, objectId, xs, ys, width, height, false, transparent) {
|
|
}
|
|
|
|
void MenuMessage::onRefresh() {
|
|
SpriteAsset &sprites = *parent()->sprites();
|
|
M4Surface *sprite = NULL;
|
|
|
|
// Get the correct sprite to use
|
|
switch (_objectId) {
|
|
case SLTAG_SAVELOAD_LABEL:
|
|
sprite = (parent()->getMenuType() == SAVE_MENU) ? sprites[SL_SAVE_LABEL] :
|
|
sprites[SL_LOAD_LABEL];
|
|
break;
|
|
}
|
|
assert(sprite);
|
|
|
|
// Draw the sprite
|
|
if (_transparent) {
|
|
// Transparent button
|
|
if (!_background)
|
|
return;
|
|
|
|
// Restore original background and then do a transparent copy of the sprite
|
|
_background->copyTo(parent(), _bounds.left, _bounds.top);
|
|
}
|
|
|
|
sprite->copyTo(parent(), _bounds.left, _bounds.top, 0);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MenuImage class
|
|
//
|
|
// Defines a menu item that displays a given surface
|
|
//--------------------------------------------------------------------------
|
|
|
|
MenuImage::MenuImage(DialogView *owner, int objectId, int xs, int ys, int width, int height,
|
|
M4Surface *image, bool transparent):
|
|
MenuObject(owner, objectId, xs, ys, width, height, false, transparent) {
|
|
|
|
_sprite = image;
|
|
}
|
|
|
|
void MenuImage::onRefresh() {
|
|
if (!_sprite)
|
|
return;
|
|
|
|
// Draw the sprite
|
|
if (_transparent) {
|
|
// Transparent button
|
|
if (!_background)
|
|
return;
|
|
|
|
// Restore original background and then do a transparent copy of the sprite
|
|
_background->copyTo(parent(), _bounds.left, _bounds.top);
|
|
}
|
|
|
|
_sprite->copyTo(parent(), _bounds.left + (_bounds.width() - _sprite->width()) / 2,
|
|
_bounds.top + (_bounds.height() - _sprite->height()) / 2, 0);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MenuSaveLoadText class
|
|
//
|
|
// Defines a save/load dialog text entry
|
|
//--------------------------------------------------------------------------
|
|
|
|
MenuSaveLoadText::MenuSaveLoadText(DialogView *owner, int textId, int xs, int ys,
|
|
int width, int height, Callback callbackFn, bool greyed, bool transparent,
|
|
bool loadFlag, const char *displayText, int displayValue):
|
|
MenuButton(owner, textId, xs, ys, width, height, callbackFn, greyed, transparent, OBJTYPE_SL_TEXT) {
|
|
|
|
_loadFlag = loadFlag;
|
|
_displayText = displayText;
|
|
_displayValue = displayValue;
|
|
_index = textId - SLTAG_SLOTS_START;
|
|
_visible = true;
|
|
}
|
|
|
|
void MenuSaveLoadText::onRefresh() {
|
|
if (!_visible) return;
|
|
_vm->_font->setFont(FONT_MENU);
|
|
MenuButton::onRefresh();
|
|
|
|
if ((_objectType == OBJTYPE_SL_TEXT) && (_displayText != NULL)) {
|
|
int xp = _bounds.left + 4;
|
|
if (_displayValue != 0) {
|
|
char tempBuffer[5];
|
|
sprintf(tempBuffer, "%02d", _displayValue);
|
|
_vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1);
|
|
xp = _bounds.left + 26;
|
|
}
|
|
|
|
_vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1);
|
|
}
|
|
}
|
|
|
|
bool MenuSaveLoadText::onEvent(M4::M4EventType event, int32 param, int x, int y, M4::MenuObject *¤tItem) {
|
|
if (!_visible) return false;
|
|
bool handledFlag = MenuButton::onEvent(event, param, x, y, currentItem);
|
|
|
|
// Further handling will only be done when in load mode and a slot hasn't been selected
|
|
if (!_loadFlag || (parent()->_selectedSlot != -1))
|
|
return handledFlag;
|
|
|
|
// Only handling for certain event types
|
|
if ((event != MEVENT_MOVE) && (event != MEVENT_LEFT_DRAG) &&
|
|
(event != MEVENT_LEFT_RELEASE) && (event != MEVENT_DOUBLECLICK_DRAG) &&
|
|
(event != MEVENT_DOUBLECLICK_RELEASE))
|
|
return handledFlag;
|
|
|
|
MenuImage *thumbnail = (MenuImage *) parent()->getItem(SLTAG_THUMBNAIL);
|
|
|
|
// Check whether the cursor is over the button
|
|
if ((_objectState == OS_MOUSEOVER) || (_objectState == OS_PRESSED)) {
|
|
if (_index != parent()->_highlightedSlot) {
|
|
// Selected slot has changed
|
|
if (parent()->_savegameThumbnail != NULL)
|
|
delete parent()->_savegameThumbnail;
|
|
|
|
parent()->_highlightedSlot = _index;
|
|
parent()->_savegameThumbnail = _vm->_saveLoad->getThumbnail(_index + 1);
|
|
thumbnail->setSprite(parent()->_savegameThumbnail);
|
|
}
|
|
|
|
} else {
|
|
// If the mouse has moved outside the area of all the save slots, then the
|
|
// thumbnail needs to be removed
|
|
|
|
Common::Rect slotArea(50, 256, 288, 377);
|
|
if (isInside(x, y) || !slotArea.contains(x, y)) {
|
|
if (parent()->_savegameThumbnail) {
|
|
delete parent()->_savegameThumbnail;
|
|
parent()->_savegameThumbnail = NULL;
|
|
}
|
|
|
|
thumbnail->setSprite(parent()->sprites()->getFrame(SL_EMPTY_THUMBNAIL));
|
|
parent()->_highlightedSlot = -1;
|
|
}
|
|
}
|
|
|
|
return handledFlag;
|
|
}
|
|
|
|
void MenuSaveLoadText::setVisible(bool value) {
|
|
_visible = value;
|
|
parent()->refresh(_bounds);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MenuTextField class
|
|
//
|
|
// Defines a text entry field
|
|
//--------------------------------------------------------------------------
|
|
|
|
MenuTextField::MenuTextField(DialogView *owner, int fieldId, int xs, int ys, int width,
|
|
int height, bool greyed, Callback callbackFn,
|
|
const char *displayText, int displayValue, bool transparent):
|
|
MenuObject(owner, fieldId, xs, ys, width, height, greyed, transparent) {
|
|
|
|
_displayValue = displayValue;
|
|
_callback = callbackFn;
|
|
_pixelWidth = width - 27;
|
|
if (displayText) {
|
|
strcpy(_displayText, displayText);
|
|
_promptEnd = &_displayText[strlen(_displayText)];
|
|
} else {
|
|
_displayText[0] = '\0';
|
|
_promptEnd = &_displayText[0];
|
|
}
|
|
_cursor = _promptEnd;
|
|
}
|
|
|
|
void MenuTextField::onRefresh() {
|
|
bool focused = _objectState != OS_GREYED;
|
|
|
|
// Get the thumb sprite for the slider
|
|
SpriteAsset &sprites = *parent()->sprites();
|
|
M4Sprite *sprite = sprites[SL_LINE_NORMAL + OS_MOUSEOVER - 1];
|
|
|
|
// If the slider is transparent, first copy in the background
|
|
if (_transparent) {
|
|
// Transparent button
|
|
if (!_background)
|
|
return;
|
|
|
|
_background->copyTo(parent(), _bounds.left, _bounds.top, 0);
|
|
}
|
|
|
|
// Draw the sprite
|
|
sprite->copyTo(parent(), _bounds.left, _bounds.top, 0);
|
|
|
|
// Draw the text
|
|
|
|
_vm->_font->setFont(FONT_MENU);
|
|
_vm->_font->current()->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND,
|
|
TEXT_COLOR_NORMAL_HILIGHT);
|
|
int xp = _bounds.left + 4;
|
|
|
|
if (_displayValue != 0) {
|
|
char tempBuffer[5];
|
|
sprintf(tempBuffer, "%02d", _displayValue);
|
|
_vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1);
|
|
xp = _bounds.left + 26;
|
|
}
|
|
|
|
_vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1);
|
|
|
|
if (focused) {
|
|
// Draw in the cursor
|
|
|
|
if (_cursor) {
|
|
// Get the width of the string up to the cursor position
|
|
char tempCh = *_cursor;
|
|
*_cursor = '\0';
|
|
int stringWidth = _vm->_font->current()->getWidth(_displayText);
|
|
*_cursor = tempCh;
|
|
|
|
parent()->setColor(TEXT_COLOR_MOUSEOVER_FOREGROUND);
|
|
parent()->vLine(xp + stringWidth + 1, _bounds.top + 1, _bounds.top + 12);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuObject *¤tItem) {
|
|
char tempStr[MAX_SAVEGAME_NAME];
|
|
int tempLen;
|
|
char *tempP;
|
|
bool handledFlag = false, redrawFlag = false, callbackFlag = false;
|
|
|
|
if (_objectState == OS_GREYED)
|
|
return false;
|
|
|
|
switch (event) {
|
|
case MEVENT_LEFT_CLICK:
|
|
case MEVENT_DOUBLECLICK:
|
|
parent()->_deleteSaveDesc = false;
|
|
if (isInside(x, y))
|
|
currentItem = this;
|
|
break;
|
|
|
|
case MEVENT_LEFT_RELEASE:
|
|
case MEVENT_DOUBLECLICK_RELEASE:
|
|
if (!currentItem)
|
|
return true;
|
|
currentItem = NULL;
|
|
|
|
if (isInside(x, y)) {
|
|
if (_objectState == OS_MOUSEOVER) {
|
|
tempLen = strlen(_displayText);
|
|
if (tempLen > 0) {
|
|
strcpy(tempStr, _displayText);
|
|
tempP = &tempStr[tempLen];
|
|
_vm->_font->setFont(FONT_MENU);
|
|
|
|
tempLen = _vm->_font->current()->getWidth(tempStr);
|
|
while ((tempP != &tempStr[0]) && (tempLen > x - _bounds.left - 26)) {
|
|
*--tempP = '\0';
|
|
tempLen = _vm->_font->current()->getWidth(tempStr);
|
|
}
|
|
|
|
_cursor = &_displayText[tempP - &tempStr[0]];
|
|
redrawFlag = true;
|
|
}
|
|
} else if (event == MEVENT_DOUBLECLICK_RELEASE) {
|
|
callbackFlag = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KEVENT_KEY:
|
|
switch (param) {
|
|
case Common::KEYCODE_RETURN:
|
|
case Common::KEYCODE_KP_ENTER:
|
|
parent()->_deleteSaveDesc = false;
|
|
callbackFlag = true;
|
|
break;
|
|
break;
|
|
|
|
case Common::KEYCODE_HOME:
|
|
parent()->_deleteSaveDesc = false;
|
|
_cursor = &_displayText[0];
|
|
redrawFlag = true;
|
|
break;
|
|
|
|
case Common::KEYCODE_END:
|
|
parent()->_deleteSaveDesc = false;
|
|
_cursor = _promptEnd;
|
|
redrawFlag = true;
|
|
break;
|
|
|
|
case Common::KEYCODE_LEFT:
|
|
case Common::KEYCODE_KP4:
|
|
parent()->_deleteSaveDesc = false;
|
|
if (_cursor > &_displayText[0]) {
|
|
--_cursor;
|
|
redrawFlag = true;
|
|
}
|
|
break;
|
|
|
|
case Common::KEYCODE_RIGHT:
|
|
case Common::KEYCODE_KP6:
|
|
parent()->_deleteSaveDesc = false;
|
|
if (_cursor < _promptEnd) {
|
|
++_cursor;
|
|
redrawFlag = true;
|
|
}
|
|
break;
|
|
|
|
case Common::KEYCODE_DELETE:
|
|
if (parent()->_deleteSaveDesc) {
|
|
_displayText[0] = '\0';
|
|
_promptEnd = &_displayText[0];
|
|
_cursor = _promptEnd;
|
|
redrawFlag = true;
|
|
} else if (_cursor < _promptEnd) {
|
|
strcpy(tempStr, _cursor + 1);
|
|
strcpy(_cursor, tempStr);
|
|
--_promptEnd;
|
|
redrawFlag = true;
|
|
}
|
|
break;
|
|
|
|
case Common::KEYCODE_BACKSPACE:
|
|
parent()->_deleteSaveDesc = false;
|
|
if (_cursor > &_displayText[0]) {
|
|
strcpy(tempStr, _cursor);
|
|
--_promptEnd;
|
|
--_cursor;
|
|
strcpy(_cursor, tempStr);
|
|
redrawFlag = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
parent()->_deleteSaveDesc = false;
|
|
_vm->_font->setFont(FONT_MENU);
|
|
|
|
tempLen = _vm->_font->current()->getWidth(_displayText);
|
|
if ((strlen(_displayText) < MAX_SAVEGAME_NAME - 1) &&
|
|
(tempLen < _pixelWidth - 12) && (param >= 32) && (param <= 127)) {
|
|
|
|
// Valid displayable character
|
|
if (_cursor < _promptEnd) {
|
|
strcpy(tempStr, _cursor);
|
|
sprintf(_cursor, "%c%s", (char)param, tempStr);
|
|
} else {
|
|
*_cursor = (char)param;
|
|
*(_cursor + 1) = '\0';
|
|
}
|
|
++_cursor;
|
|
++_promptEnd;
|
|
|
|
redrawFlag = true;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (redrawFlag)
|
|
onRefresh();
|
|
|
|
if (callbackFlag)
|
|
onExecute();
|
|
|
|
return handledFlag;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
GUITextField::GUITextField(View *owner, const Common::Rect &bounds): GUIRect(owner, bounds, 0) {
|
|
}
|
|
|
|
void GUITextField::onRefresh() {
|
|
_parent->fillRect(_bounds, _vm->_palette->BLACK);
|
|
_vm->_font->current()->setColors(3, 3, 3);
|
|
_vm->_font->setFont(FONT_INTERFACE);
|
|
_vm->_font->current()->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
GUIButton::GUIButton(View *owner, const Common::Rect &bounds, int tag,
|
|
M4Surface *normalSprite, M4Surface *mouseOverSprite, M4Surface *pressedSprite):
|
|
GUIRect(owner, bounds, tag) {
|
|
|
|
_normalSprite = normalSprite;
|
|
_mouseOverSprite = mouseOverSprite;
|
|
_pressedSprite = pressedSprite;
|
|
_visible = true;
|
|
_tracking = false;
|
|
}
|
|
|
|
void GUIButton::onRefresh() {
|
|
_parent->fillRect(_bounds, _vm->_palette->BLACK);
|
|
|
|
if (_visible) {
|
|
switch (_buttonState) {
|
|
case BUTTON_MOUSEOVER:
|
|
_mouseOverSprite->copyTo(_parent, _bounds.left, _bounds.top, 0);
|
|
break;
|
|
case BUTTON_PRESSED:
|
|
_pressedSprite->copyTo(_parent, _bounds.left, _bounds.top, 0);
|
|
break;
|
|
default:
|
|
_normalSprite->copyTo(_parent, _bounds.left, _bounds.top, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GUIButton::onEvent(M4EventType eventType, int32 param, int x, int y, GUIObject *¤tItem) {
|
|
bool result = false;
|
|
bool isPressed = (eventType == MEVENT_LEFT_CLICK) || (eventType == MEVENT_LEFT_HOLD) ||
|
|
(eventType == MEVENT_LEFT_DRAG);
|
|
bool isOver = isInside(x, y);
|
|
GUIButtonState oldState = _buttonState;
|
|
|
|
if (isOver) {
|
|
if (isPressed) {
|
|
// Button is pressed
|
|
if (!_tracking) {
|
|
_tracking = true;
|
|
_buttonState = BUTTON_PRESSED;
|
|
}
|
|
|
|
if (_vm->isM4())
|
|
_m4Vm->globals()->invSuppressClickSound = false;
|
|
} else {
|
|
// Button isn't pressed
|
|
if (_tracking)
|
|
result = true;
|
|
else {
|
|
result = false;
|
|
_buttonState = BUTTON_MOUSEOVER;
|
|
_tracking = false;
|
|
}
|
|
}
|
|
} else {
|
|
_tracking = false;
|
|
_buttonState = BUTTON_NORMAL;
|
|
}
|
|
|
|
if (oldState != _buttonState)
|
|
onRefresh();
|
|
|
|
return result;
|
|
}
|
|
|
|
} // End of namespace M4
|