mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 09:49:11 +00:00
1428 lines
34 KiB
C++
1428 lines
34 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.
|
|
*
|
|
* Additional copyright for this file:
|
|
* Copyright (C) 1994-1998 Revolution Software Ltd.
|
|
*
|
|
* 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/rect.h"
|
|
#include "common/system.h"
|
|
|
|
#include "sword2/sword2.h"
|
|
#include "sword2/defs.h"
|
|
#include "sword2/header.h"
|
|
#include "sword2/controls.h"
|
|
#include "sword2/mouse.h"
|
|
#include "sword2/resman.h"
|
|
#include "sword2/screen.h"
|
|
#include "sword2/sound.h"
|
|
|
|
#define MAX_STRING_LEN 64 // 20 was too low; better to be safe ;)
|
|
#define CHARACTER_OVERLAP 2 // overlap characters by 3 pixels
|
|
|
|
// our fonts start on SPACE character (32)
|
|
#define SIZE_OF_CHAR_SET (256 - 32)
|
|
|
|
namespace Sword2 {
|
|
|
|
static int baseSlot = 0;
|
|
|
|
class Widget;
|
|
|
|
/**
|
|
* Base class for all widgets.
|
|
*/
|
|
|
|
class Widget {
|
|
protected:
|
|
Sword2Engine *_vm;
|
|
Dialog *_parent;
|
|
|
|
SpriteInfo *_sprites;
|
|
|
|
struct WidgetSurface {
|
|
byte *_surface;
|
|
bool _original;
|
|
};
|
|
|
|
WidgetSurface *_surfaces;
|
|
int _numStates;
|
|
int _state;
|
|
|
|
Common::Rect _hitRect;
|
|
|
|
public:
|
|
Widget(Dialog *parent, int states);
|
|
|
|
virtual ~Widget();
|
|
|
|
void createSurfaceImage(int state, uint32 res, int x, int y, uint32 pc);
|
|
void linkSurfaceImage(Widget *from, int state, int x, int y);
|
|
|
|
void createSurfaceImages(uint32 res, int x, int y);
|
|
void linkSurfaceImages(Widget *from, int x, int y);
|
|
|
|
void setHitRect(int x, int y, int width, int height);
|
|
bool isHit(int16 x, int16 y);
|
|
|
|
void setState(int state);
|
|
int getState();
|
|
|
|
virtual void paint(Common::Rect *clipRect = NULL);
|
|
|
|
virtual void onMouseEnter() {}
|
|
virtual void onMouseExit() {}
|
|
virtual void onMouseMove(int x, int y) {}
|
|
virtual void onMouseDown(int x, int y) {}
|
|
virtual void onMouseUp(int x, int y) {}
|
|
virtual void onWheelUp(int x, int y) {}
|
|
virtual void onWheelDown(int x, int y) {}
|
|
virtual void onKey(KeyboardEvent *ke) {}
|
|
virtual void onTick() {}
|
|
|
|
virtual void releaseMouse(int x, int y) {}
|
|
};
|
|
|
|
/**
|
|
* This class is used to draw text in dialogs, buttons, etc.
|
|
*/
|
|
|
|
class FontRendererGui {
|
|
private:
|
|
Sword2Engine *_vm;
|
|
|
|
struct Glyph {
|
|
byte *_data;
|
|
int _width;
|
|
int _height;
|
|
};
|
|
|
|
Glyph _glyph[SIZE_OF_CHAR_SET];
|
|
|
|
int _fontId;
|
|
|
|
public:
|
|
enum {
|
|
kAlignLeft,
|
|
kAlignRight,
|
|
kAlignCenter
|
|
};
|
|
|
|
FontRendererGui(Sword2Engine *vm, int fontId);
|
|
~FontRendererGui();
|
|
|
|
void fetchText(uint32 textId, byte *buf);
|
|
|
|
int getCharWidth(byte c);
|
|
int getCharHeight(byte c);
|
|
|
|
int getTextWidth(byte *text);
|
|
int getTextWidth(uint32 textId);
|
|
|
|
void drawText(byte *text, int x, int y, int alignment = kAlignLeft);
|
|
void drawText(uint32 textId, int x, int y, int alignment = kAlignLeft);
|
|
};
|
|
|
|
FontRendererGui::FontRendererGui(Sword2Engine *vm, int fontId)
|
|
: _vm(vm), _fontId(fontId) {
|
|
byte *font = _vm->_resman->openResource(fontId);
|
|
SpriteInfo sprite;
|
|
|
|
sprite.type = RDSPR_NOCOMPRESSION | RDSPR_TRANS;
|
|
|
|
for (int i = 0; i < SIZE_OF_CHAR_SET; i++) {
|
|
byte *frame = _vm->fetchFrameHeader(font, i);
|
|
|
|
FrameHeader frame_head;
|
|
|
|
frame_head.read(frame);
|
|
|
|
sprite.data = frame + FrameHeader::size();
|
|
sprite.w = frame_head.width;
|
|
sprite.h = frame_head.height;
|
|
_vm->_screen->createSurface(&sprite, &_glyph[i]._data);
|
|
_glyph[i]._width = frame_head.width;
|
|
_glyph[i]._height = frame_head.height;
|
|
}
|
|
|
|
_vm->_resman->closeResource(fontId);
|
|
}
|
|
|
|
FontRendererGui::~FontRendererGui() {
|
|
for (int i = 0; i < SIZE_OF_CHAR_SET; i++)
|
|
_vm->_screen->deleteSurface(_glyph[i]._data);
|
|
}
|
|
|
|
void FontRendererGui::fetchText(uint32 textId, byte *buf) {
|
|
byte *data = _vm->fetchTextLine(_vm->_resman->openResource(textId / SIZE), textId & 0xffff);
|
|
int i;
|
|
|
|
if (buf) {
|
|
for (i = 0; data[i + 2]; i++)
|
|
buf[i] = data[i + 2];
|
|
buf[i] = 0;
|
|
}
|
|
|
|
_vm->_resman->closeResource(textId / SIZE);
|
|
}
|
|
|
|
int FontRendererGui::getCharWidth(byte c) {
|
|
if (c < 32)
|
|
return 0;
|
|
return _glyph[c - 32]._width;
|
|
}
|
|
|
|
int FontRendererGui::getCharHeight(byte c) {
|
|
if (c < 32)
|
|
return 0;
|
|
return _glyph[c - 32]._height;
|
|
}
|
|
|
|
int FontRendererGui::getTextWidth(byte *text) {
|
|
int textWidth = 0;
|
|
|
|
for (int i = 0; text[i]; i++)
|
|
if (text[i] >= ' ')
|
|
textWidth += (getCharWidth(text[i]) - CHARACTER_OVERLAP);
|
|
return textWidth;
|
|
}
|
|
|
|
int FontRendererGui::getTextWidth(uint32 textId) {
|
|
byte text[MAX_STRING_LEN];
|
|
|
|
fetchText(textId, text);
|
|
return getTextWidth(text);
|
|
}
|
|
|
|
void FontRendererGui::drawText(byte *text, int x, int y, int alignment) {
|
|
SpriteInfo sprite;
|
|
int i;
|
|
|
|
if (alignment != kAlignLeft) {
|
|
int textWidth = getTextWidth(text);
|
|
|
|
switch (alignment) {
|
|
case kAlignRight:
|
|
x -= textWidth;
|
|
break;
|
|
case kAlignCenter:
|
|
x -= (textWidth / 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
sprite.x = x;
|
|
sprite.y = y;
|
|
|
|
for (i = 0; text[i]; i++) {
|
|
if (text[i] >= ' ') {
|
|
sprite.w = getCharWidth(text[i]);
|
|
sprite.h = getCharHeight(text[i]);
|
|
|
|
_vm->_screen->drawSurface(&sprite, _glyph[text[i] - 32]._data);
|
|
|
|
sprite.x += (getCharWidth(text[i]) - CHARACTER_OVERLAP);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FontRendererGui::drawText(uint32 textId, int x, int y, int alignment) {
|
|
byte text[MAX_STRING_LEN];
|
|
|
|
fetchText(textId, text);
|
|
drawText(text, x, y, alignment);
|
|
}
|
|
|
|
//
|
|
// Dialog class functions
|
|
//
|
|
|
|
Dialog::Dialog(Sword2Engine *vm)
|
|
: _numWidgets(0), _finish(false), _result(0), _vm(vm) {
|
|
_vm->_screen->setFullPalette(CONTROL_PANEL_PALETTE);
|
|
_vm->_screen->clearScene();
|
|
_vm->_screen->updateDisplay();
|
|
|
|
// Usually the mouse pointer will already be "normal", but not always.
|
|
_vm->_mouse->setMouse(NORMAL_MOUSE_ID);
|
|
|
|
// Force mouse mode as system menu: normally not needed,
|
|
// but value is not correct in case of game start dialog
|
|
// (when asking to restart or load a game).
|
|
// This is forced to avoid GMM loading/saving being enabled
|
|
// during initial dialog.
|
|
_vm->_mouse->setMouseMode(MOUSE_system_menu);
|
|
}
|
|
|
|
Dialog::~Dialog() {
|
|
for (int i = 0; i < _numWidgets; i++)
|
|
delete _widgets[i];
|
|
_vm->_screen->clearScene();
|
|
_vm->_screen->updateDisplay();
|
|
}
|
|
|
|
void Dialog::registerWidget(Widget *widget) {
|
|
if (_numWidgets < MAX_WIDGETS)
|
|
_widgets[_numWidgets++] = widget;
|
|
}
|
|
|
|
void Dialog::paint() {
|
|
_vm->_screen->clearScene();
|
|
for (int i = 0; i < _numWidgets; i++)
|
|
_widgets[i]->paint();
|
|
}
|
|
|
|
void Dialog::setResult(int result) {
|
|
_result = result;
|
|
_finish = true;
|
|
}
|
|
|
|
int Dialog::runModal() {
|
|
uint32 oldFilter = _vm->setInputEventFilter(0);
|
|
|
|
int i;
|
|
|
|
paint();
|
|
|
|
int oldMouseX = -1;
|
|
int oldMouseY = -1;
|
|
|
|
while (!_finish) {
|
|
// So that the menu icons will reach their full size
|
|
_vm->_mouse->processMenu();
|
|
_vm->_screen->updateDisplay(false);
|
|
|
|
int newMouseX, newMouseY;
|
|
|
|
_vm->_mouse->getPos(newMouseX, newMouseY);
|
|
|
|
newMouseY += 40;
|
|
|
|
MouseEvent *me = _vm->mouseEvent();
|
|
KeyboardEvent *ke = _vm->keyboardEvent();
|
|
|
|
if (ke) {
|
|
if (ke->kbd.keycode == Common::KEYCODE_ESCAPE)
|
|
setResult(0);
|
|
else if (ke->kbd.keycode == Common::KEYCODE_RETURN || ke->kbd.keycode == Common::KEYCODE_KP_ENTER)
|
|
setResult(1);
|
|
}
|
|
|
|
int oldHit = -1;
|
|
int newHit = -1;
|
|
|
|
// Find out which widget the mouse was over the last time, and
|
|
// which it is currently over. This assumes the widgets do not
|
|
// overlap.
|
|
|
|
for (i = 0; i < _numWidgets; i++) {
|
|
if (_widgets[i]->isHit(oldMouseX, oldMouseY))
|
|
oldHit = i;
|
|
if (_widgets[i]->isHit(newMouseX, newMouseY))
|
|
newHit = i;
|
|
}
|
|
|
|
// Was the mouse inside a widget the last time?
|
|
|
|
if (oldHit >= 0) {
|
|
if (newHit != oldHit)
|
|
_widgets[oldHit]->onMouseExit();
|
|
}
|
|
|
|
// Is the mouse currently in a widget?
|
|
|
|
if (newHit >= 0) {
|
|
if (newHit != oldHit)
|
|
_widgets[newHit]->onMouseEnter();
|
|
|
|
if (me) {
|
|
switch (me->buttons) {
|
|
case RD_LEFTBUTTONDOWN:
|
|
_widgets[newHit]->onMouseDown(newMouseX, newMouseY);
|
|
break;
|
|
case RD_LEFTBUTTONUP:
|
|
_widgets[newHit]->onMouseUp(newMouseX, newMouseY);
|
|
break;
|
|
case RD_WHEELUP:
|
|
_widgets[newHit]->onWheelUp(newMouseX, newMouseY);
|
|
break;
|
|
case RD_WHEELDOWN:
|
|
_widgets[newHit]->onWheelDown(newMouseX, newMouseY);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Some events are passed to the widgets regardless of where
|
|
// the mouse cursor is.
|
|
|
|
for (i = 0; i < _numWidgets; i++) {
|
|
if (me && me->buttons == RD_LEFTBUTTONUP) {
|
|
// So that slider widgets will know when the
|
|
// user releases the mouse button, even if the
|
|
// cursor is outside of the slider's hit area.
|
|
_widgets[i]->releaseMouse(newMouseX, newMouseY);
|
|
}
|
|
|
|
// This is to make it easier to drag the slider widget
|
|
|
|
if (newMouseX != oldMouseX || newMouseY != oldMouseY)
|
|
_widgets[i]->onMouseMove(newMouseX, newMouseY);
|
|
|
|
if (ke)
|
|
_widgets[i]->onKey(ke);
|
|
|
|
_widgets[i]->onTick();
|
|
}
|
|
|
|
oldMouseX = newMouseX;
|
|
oldMouseY = newMouseY;
|
|
|
|
_vm->_system->delayMillis(20);
|
|
|
|
if (_vm->shouldQuit())
|
|
setResult(0);
|
|
}
|
|
|
|
_vm->setInputEventFilter(oldFilter);
|
|
return _result;
|
|
}
|
|
|
|
//
|
|
// Widget functions
|
|
//
|
|
|
|
Widget::Widget(Dialog *parent, int states)
|
|
: _vm(parent->_vm), _parent(parent), _numStates(states), _state(0) {
|
|
_sprites = (SpriteInfo *)calloc(states, sizeof(SpriteInfo));
|
|
_surfaces = (WidgetSurface *)calloc(states, sizeof(WidgetSurface));
|
|
|
|
_hitRect.left = _hitRect.right = _hitRect.top = _hitRect.bottom = -1;
|
|
}
|
|
|
|
Widget::~Widget() {
|
|
for (int i = 0; i < _numStates; i++) {
|
|
if (_surfaces[i]._original)
|
|
_vm->_screen->deleteSurface(_surfaces[i]._surface);
|
|
}
|
|
free(_sprites);
|
|
free(_surfaces);
|
|
}
|
|
|
|
void Widget::createSurfaceImage(int state, uint32 res, int x, int y, uint32 pc) {
|
|
byte *file, *colTablePtr = NULL;
|
|
AnimHeader anim_head;
|
|
FrameHeader frame_head;
|
|
CdtEntry cdt_entry;
|
|
uint32 spriteType = RDSPR_TRANS;
|
|
|
|
// open anim resource file, point to base
|
|
file = _vm->_resman->openResource(res);
|
|
|
|
byte *frame = _vm->fetchFrameHeader(file, pc);
|
|
|
|
anim_head.read(_vm->fetchAnimHeader(file));
|
|
cdt_entry.read(_vm->fetchCdtEntry(file, pc));
|
|
frame_head.read(frame);
|
|
|
|
// If the frame is flipped. (Only really applicable to frames using
|
|
// offsets.)
|
|
|
|
if (cdt_entry.frameType & FRAME_FLIPPED)
|
|
spriteType |= RDSPR_FLIP;
|
|
|
|
// Which compression was used?
|
|
|
|
switch (anim_head.runTimeComp) {
|
|
case NONE:
|
|
spriteType |= RDSPR_NOCOMPRESSION;
|
|
break;
|
|
case RLE256:
|
|
spriteType |= RDSPR_RLE256;
|
|
break;
|
|
case RLE16:
|
|
spriteType |= RDSPR_RLE256;
|
|
// Points to just after last cdt_entry, i.e. start of color
|
|
// table
|
|
colTablePtr = _vm->fetchAnimHeader(file) + AnimHeader::size()
|
|
+ anim_head.noAnimFrames * CdtEntry::size();
|
|
break;
|
|
}
|
|
|
|
_sprites[state].x = x;
|
|
_sprites[state].y = y;
|
|
_sprites[state].w = frame_head.width;
|
|
_sprites[state].h = frame_head.height;
|
|
_sprites[state].scale = 0;
|
|
_sprites[state].type = spriteType;
|
|
_sprites[state].blend = anim_head.blend;
|
|
|
|
// Points to just after frame header, ie. start of sprite data
|
|
_sprites[state].data = frame + FrameHeader::size();
|
|
|
|
_vm->_screen->createSurface(&_sprites[state], &_surfaces[state]._surface);
|
|
_surfaces[state]._original = true;
|
|
|
|
// Release the anim resource
|
|
_vm->_resman->closeResource(res);
|
|
}
|
|
|
|
void Widget::linkSurfaceImage(Widget *from, int state, int x, int y) {
|
|
_sprites[state].x = x;
|
|
_sprites[state].y = y;
|
|
_sprites[state].w = from->_sprites[state].w;
|
|
_sprites[state].h = from->_sprites[state].h;
|
|
_sprites[state].scale = from->_sprites[state].scale;
|
|
_sprites[state].type = from->_sprites[state].type;
|
|
_sprites[state].blend = from->_sprites[state].blend;
|
|
|
|
_surfaces[state]._surface = from->_surfaces[state]._surface;
|
|
_surfaces[state]._original = false;
|
|
}
|
|
|
|
void Widget::createSurfaceImages(uint32 res, int x, int y) {
|
|
for (int i = 0; i < _numStates; i++)
|
|
createSurfaceImage(i, res, x, y, i);
|
|
}
|
|
|
|
void Widget::linkSurfaceImages(Widget *from, int x, int y) {
|
|
for (int i = 0; i < from->_numStates; i++)
|
|
linkSurfaceImage(from, i, x, y);
|
|
}
|
|
|
|
void Widget::setHitRect(int x, int y, int width, int height) {
|
|
_hitRect.left = x;
|
|
_hitRect.right = x + width;
|
|
_hitRect.top = y;
|
|
_hitRect.bottom = y + height;
|
|
}
|
|
|
|
bool Widget::isHit(int16 x, int16 y) {
|
|
return _hitRect.left >= 0 && _hitRect.contains(x, y);
|
|
}
|
|
|
|
void Widget::setState(int state) {
|
|
if (state != _state) {
|
|
_state = state;
|
|
paint();
|
|
}
|
|
}
|
|
|
|
int Widget::getState() {
|
|
return _state;
|
|
}
|
|
|
|
void Widget::paint(Common::Rect *clipRect) {
|
|
_vm->_screen->drawSurface(&_sprites[_state], _surfaces[_state]._surface, clipRect);
|
|
}
|
|
|
|
/**
|
|
* Standard button class.
|
|
*/
|
|
|
|
class Button : public Widget {
|
|
public:
|
|
Button(Dialog *parent, int x, int y, int w, int h)
|
|
: Widget(parent, 2) {
|
|
setHitRect(x, y, w, h);
|
|
}
|
|
|
|
virtual void onMouseExit() {
|
|
setState(0);
|
|
}
|
|
|
|
virtual void onMouseDown(int x, int y) {
|
|
setState(1);
|
|
}
|
|
|
|
virtual void onMouseUp(int x, int y) {
|
|
if (getState() != 0) {
|
|
setState(0);
|
|
_parent->onAction(this);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Scroll buttons are used to scroll the savegame list. The difference between
|
|
* this and a normal button is that we want this to repeat.
|
|
*/
|
|
|
|
class ScrollButton : public Widget {
|
|
private:
|
|
uint32 _holdCounter;
|
|
|
|
public:
|
|
ScrollButton(Dialog *parent, int x, int y, int w, int h)
|
|
: Widget(parent, 2), _holdCounter(0) {
|
|
setHitRect(x, y, w, h);
|
|
}
|
|
|
|
virtual void onMouseExit() {
|
|
setState(0);
|
|
}
|
|
|
|
virtual void onMouseDown(int x, int y) {
|
|
setState(1);
|
|
_parent->onAction(this);
|
|
_holdCounter = 0;
|
|
}
|
|
|
|
virtual void onMouseUp(int x, int y) {
|
|
setState(0);
|
|
}
|
|
|
|
virtual void onTick() {
|
|
if (getState() != 0) {
|
|
_holdCounter++;
|
|
if (_holdCounter > 16 && (_holdCounter % 4) == 0)
|
|
_parent->onAction(this);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* A switch is a button that changes state when clicked, and keeps that state
|
|
* until clicked again.
|
|
*/
|
|
|
|
class Switch : public Widget {
|
|
private:
|
|
bool _holding, _value;
|
|
int _upState, _downState;
|
|
|
|
public:
|
|
Switch(Dialog *parent, int x, int y, int w, int h)
|
|
: Widget(parent, 2), _holding(false), _value(false),
|
|
_upState(0), _downState(1) {
|
|
setHitRect(x, y, w, h);
|
|
}
|
|
|
|
// The sound mute switches have 0 as their "down" state and 1 as
|
|
// their "up" state, so this function is needed to get consistent
|
|
// behavior.
|
|
|
|
void reverseStates() {
|
|
_upState = 1;
|
|
_downState = 0;
|
|
}
|
|
|
|
void setValue(bool value) {
|
|
_value = value;
|
|
if (_value)
|
|
setState(_downState);
|
|
else
|
|
setState(_upState);
|
|
}
|
|
|
|
bool getValue() {
|
|
return _value;
|
|
}
|
|
|
|
virtual void onMouseExit() {
|
|
if (_holding && !_value)
|
|
setState(_upState);
|
|
_holding = false;
|
|
}
|
|
|
|
virtual void onMouseDown(int x, int y) {
|
|
_holding = true;
|
|
setState(_downState);
|
|
}
|
|
|
|
virtual void onMouseUp(int x, int y) {
|
|
if (_holding) {
|
|
_holding = false;
|
|
_value = !_value;
|
|
if (_value)
|
|
setState(_downState);
|
|
else
|
|
setState(_upState);
|
|
_parent->onAction(this, getState());
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* A slider is used to specify a value within a pre-defined range.
|
|
*/
|
|
|
|
class Slider : public Widget {
|
|
private:
|
|
Widget *_background;
|
|
bool _dragging;
|
|
int _value, _targetValue;
|
|
int _maxValue;
|
|
int _valueStep;
|
|
int _dragOffset;
|
|
|
|
int posFromValue(int value) {
|
|
return _hitRect.left + (value * (_hitRect.width() - 38)) / _maxValue;
|
|
}
|
|
|
|
int valueFromPos(int x) {
|
|
return (int)((double)(_maxValue * (x - _hitRect.left)) / (double)(_hitRect.width() - 38) + 0.5);
|
|
}
|
|
|
|
public:
|
|
Slider(Dialog *parent, Widget *background, int max,
|
|
int x, int y, int w, int h, int step, Widget *base = NULL)
|
|
: Widget(parent, 1), _background(background),
|
|
_dragging(false), _value(0), _targetValue(0),
|
|
_maxValue(max), _valueStep(step) {
|
|
setHitRect(x, y, w, h);
|
|
|
|
if (_valueStep <= 0)
|
|
_valueStep = 1;
|
|
|
|
if (base)
|
|
linkSurfaceImages(base, x, y);
|
|
else
|
|
createSurfaceImages(3406, x, y);
|
|
}
|
|
|
|
virtual void paint(Common::Rect *clipRect = NULL) {
|
|
// This will redraw a bit more than is strictly necessary,
|
|
// but I doubt that will make any noticeable difference.
|
|
|
|
_background->paint(&_hitRect);
|
|
Widget::paint(clipRect);
|
|
}
|
|
|
|
void setValue(int value) {
|
|
_value = value;
|
|
_targetValue = value;
|
|
_sprites[0].x = posFromValue(_value);
|
|
paint();
|
|
}
|
|
|
|
int getValue() {
|
|
return _value;
|
|
}
|
|
|
|
virtual void onMouseMove(int x, int y) {
|
|
if (_dragging) {
|
|
int newX = x - _dragOffset;
|
|
int newValue;
|
|
|
|
if (newX < _hitRect.left)
|
|
newX = _hitRect.left;
|
|
else if (newX + 38 > _hitRect.right)
|
|
newX = _hitRect.right - 38;
|
|
|
|
_sprites[0].x = newX;
|
|
|
|
newValue = valueFromPos(newX);
|
|
if (newValue != _value) {
|
|
_value = newValue;
|
|
_targetValue = newValue;
|
|
_parent->onAction(this, newValue);
|
|
}
|
|
|
|
paint();
|
|
}
|
|
}
|
|
|
|
virtual void onMouseDown(int x, int y) {
|
|
if (x >= _sprites[0].x && x < _sprites[0].x + 38) {
|
|
_dragging = true;
|
|
_dragOffset = x - _sprites[0].x;
|
|
} else if (x < _sprites[0].x) {
|
|
if (_targetValue >= _valueStep)
|
|
_targetValue -= _valueStep;
|
|
else
|
|
_targetValue = 0;
|
|
} else {
|
|
if (_targetValue < _maxValue - _valueStep)
|
|
_targetValue += _valueStep;
|
|
else
|
|
_targetValue = _maxValue;
|
|
}
|
|
}
|
|
|
|
virtual void releaseMouse(int x, int y) {
|
|
if (_dragging)
|
|
_dragging = false;
|
|
}
|
|
|
|
virtual void onTick() {
|
|
if (!_dragging) {
|
|
int target = posFromValue(_targetValue);
|
|
|
|
if (target != _sprites[0].x) {
|
|
if (target < _sprites[0].x) {
|
|
_sprites[0].x -= 4;
|
|
if (_sprites[0].x < target)
|
|
_sprites[0].x = target;
|
|
} else if (target > _sprites[0].x) {
|
|
_sprites[0].x += 4;
|
|
if (_sprites[0].x > target)
|
|
_sprites[0].x = target;
|
|
}
|
|
|
|
int newValue = valueFromPos(_sprites[0].x);
|
|
if (newValue != _value) {
|
|
_value = newValue;
|
|
_parent->onAction(this, newValue);
|
|
}
|
|
|
|
paint();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "mini" dialog.
|
|
*/
|
|
|
|
MiniDialog::MiniDialog(Sword2Engine *vm, uint32 headerTextId, uint32 okTextId, uint32 cancelTextId) : Dialog(vm) {
|
|
_headerTextId = headerTextId;
|
|
_okTextId = okTextId;
|
|
_cancelTextId = cancelTextId;
|
|
|
|
_fr = new FontRendererGui(_vm, _vm->_controlsFontId);
|
|
|
|
_panel = new Widget(this, 1);
|
|
_panel->createSurfaceImages(1996, 203, 104);
|
|
|
|
_okButton = new Button(this, 243, 214, 24, 24);
|
|
_okButton->createSurfaceImages(2002, 243, 214);
|
|
|
|
_cancelButton = new Button(this, 243, 276, 24, 24);
|
|
_cancelButton->linkSurfaceImages(_okButton, 243, 276);
|
|
|
|
registerWidget(_panel);
|
|
registerWidget(_okButton);
|
|
registerWidget(_cancelButton);
|
|
}
|
|
|
|
MiniDialog::~MiniDialog() {
|
|
delete _fr;
|
|
}
|
|
|
|
void MiniDialog::paint() {
|
|
Dialog::paint();
|
|
|
|
if (_headerTextId)
|
|
_fr->drawText(_headerTextId, 310, 134, FontRendererGui::kAlignCenter);
|
|
_fr->drawText(_okTextId, 270, 214);
|
|
_fr->drawText(_cancelTextId, 270, 276);
|
|
}
|
|
|
|
void MiniDialog::onAction(Widget *widget, int result) {
|
|
if (widget == _okButton)
|
|
setResult(1);
|
|
else if (widget == _cancelButton)
|
|
setResult(0);
|
|
}
|
|
|
|
StartDialog::StartDialog(Sword2Engine *vm) : MiniDialog(vm, 0) {}
|
|
|
|
int StartDialog::runModal() {
|
|
while (1) {
|
|
MiniDialog startDialog(_vm, 0, TEXT_RESTART, TEXT_RESTORE);
|
|
|
|
if (startDialog.runModal())
|
|
return 1;
|
|
|
|
if (_vm->shouldQuit())
|
|
return 0;
|
|
|
|
RestoreDialog restoreDialog(_vm);
|
|
|
|
if (restoreDialog.runModal())
|
|
return 0;
|
|
|
|
if (_vm->shouldQuit())
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* The restart dialog.
|
|
*/
|
|
|
|
RestartDialog::RestartDialog(Sword2Engine *vm) : MiniDialog(vm, TEXT_RESTART) {}
|
|
|
|
int RestartDialog::runModal() {
|
|
int result = MiniDialog::runModal();
|
|
|
|
if (result)
|
|
_vm->restartGame();
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* The quit dialog.
|
|
*/
|
|
|
|
QuitDialog::QuitDialog(Sword2Engine *vm) : MiniDialog(vm, TEXT_QUIT) {}
|
|
|
|
int QuitDialog::runModal() {
|
|
int result = MiniDialog::runModal();
|
|
|
|
if (result)
|
|
_vm->quitGame();
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* The game settings dialog.
|
|
*/
|
|
|
|
OptionsDialog::OptionsDialog(Sword2Engine *vm) : Dialog(vm) {
|
|
_fr = new FontRendererGui(_vm, _vm->_controlsFontId);
|
|
|
|
_mixer = _vm->_mixer;
|
|
|
|
_panel = new Widget(this, 1);
|
|
_panel->createSurfaceImages(3405, 0, 40);
|
|
|
|
_objectLabelsSwitch = new Switch(this, 304, 100, 53, 32);
|
|
_objectLabelsSwitch->createSurfaceImages(3687, 304, 100);
|
|
|
|
_subtitlesSwitch = new Switch(this, 510, 100, 53, 32);
|
|
_subtitlesSwitch->linkSurfaceImages(_objectLabelsSwitch, 510, 100);
|
|
|
|
_reverseStereoSwitch = new Switch(this, 304, 293, 53, 32);
|
|
_reverseStereoSwitch->linkSurfaceImages(_objectLabelsSwitch, 304, 293);
|
|
|
|
_musicSwitch = new Switch(this, 516, 157, 40, 32);
|
|
_musicSwitch->createSurfaceImages(3315, 516, 157);
|
|
_musicSwitch->reverseStates();
|
|
|
|
_speechSwitch = new Switch(this, 516, 205, 40, 32);
|
|
_speechSwitch->linkSurfaceImages(_musicSwitch, 516, 205);
|
|
_speechSwitch->reverseStates();
|
|
|
|
_fxSwitch = new Switch(this, 516, 250, 40, 32);
|
|
_fxSwitch->linkSurfaceImages(_musicSwitch, 516, 250);
|
|
_fxSwitch->reverseStates();
|
|
|
|
int volStep = Audio::Mixer::kMaxMixerVolume / 10;
|
|
|
|
_musicSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 161, 170, 27, volStep);
|
|
_speechSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 208, 170, 27, volStep, _musicSlider);
|
|
_fxSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 254, 170, 27, volStep, _musicSlider);
|
|
_gfxSlider = new Slider(this, _panel, 3, 309, 341, 170, 27, 1, _musicSlider);
|
|
|
|
_gfxPreview = new Widget(this, 4);
|
|
_gfxPreview->createSurfaceImages(256, 495, 310);
|
|
|
|
_okButton = new Button(this, 203, 382, 53, 32);
|
|
_okButton->createSurfaceImages(901, 203, 382);
|
|
|
|
_cancelButton = new Button(this, 395, 382, 53, 32);
|
|
_cancelButton->linkSurfaceImages(_okButton, 395, 382);
|
|
|
|
registerWidget(_panel);
|
|
registerWidget(_objectLabelsSwitch);
|
|
registerWidget(_subtitlesSwitch);
|
|
registerWidget(_reverseStereoSwitch);
|
|
registerWidget(_musicSwitch);
|
|
registerWidget(_speechSwitch);
|
|
registerWidget(_fxSwitch);
|
|
registerWidget(_musicSlider);
|
|
registerWidget(_speechSlider);
|
|
registerWidget(_fxSlider);
|
|
registerWidget(_gfxSlider);
|
|
registerWidget(_gfxPreview);
|
|
registerWidget(_okButton);
|
|
registerWidget(_cancelButton);
|
|
|
|
_objectLabelsSwitch->setValue(_vm->_mouse->getObjectLabels());
|
|
_subtitlesSwitch->setValue(_vm->getSubtitles());
|
|
_reverseStereoSwitch->setValue(_vm->_sound->isReverseStereo());
|
|
_musicSwitch->setValue(!_vm->_sound->isMusicMute());
|
|
_speechSwitch->setValue(!_vm->_sound->isSpeechMute());
|
|
_fxSwitch->setValue(!_vm->_sound->isFxMute());
|
|
|
|
_musicSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType));
|
|
_speechSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType));
|
|
_fxSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType));
|
|
|
|
_gfxSlider->setValue(_vm->_screen->getRenderLevel());
|
|
_gfxPreview->setState(_vm->_screen->getRenderLevel());
|
|
}
|
|
|
|
OptionsDialog::~OptionsDialog() {
|
|
delete _fr;
|
|
}
|
|
|
|
void OptionsDialog::paint() {
|
|
Dialog::paint();
|
|
|
|
int maxWidth = 0;
|
|
int width;
|
|
|
|
uint32 alignTextIds[] = {
|
|
TEXT_OBJECT_LABELS,
|
|
TEXT_MUSIC_VOLUME,
|
|
TEXT_SPEECH_VOLUME,
|
|
TEXT_FX_VOLUME,
|
|
TEXT_GFX_QUALITY,
|
|
TEXT_REVERSE_STEREO
|
|
};
|
|
|
|
for (int i = 0; i < ARRAYSIZE(alignTextIds); i++) {
|
|
width = _fr->getTextWidth(alignTextIds[i]);
|
|
if (width > maxWidth)
|
|
maxWidth = width;
|
|
}
|
|
|
|
_fr->drawText(TEXT_OPTIONS, 321, 55, FontRendererGui::kAlignCenter);
|
|
_fr->drawText(TEXT_SUBTITLES, 500, 103, FontRendererGui::kAlignRight);
|
|
_fr->drawText(TEXT_OBJECT_LABELS, 299 - maxWidth, 103);
|
|
_fr->drawText(TEXT_MUSIC_VOLUME, 299 - maxWidth, 161);
|
|
_fr->drawText(TEXT_SPEECH_VOLUME, 299 - maxWidth, 208);
|
|
_fr->drawText(TEXT_FX_VOLUME, 299 - maxWidth, 254);
|
|
_fr->drawText(TEXT_REVERSE_STEREO, 299 - maxWidth, 296);
|
|
_fr->drawText(TEXT_GFX_QUALITY, 299 - maxWidth, 341);
|
|
_fr->drawText(TEXT_OK, 193, 382, FontRendererGui::kAlignRight);
|
|
_fr->drawText(TEXT_CANCEL, 385, 382, FontRendererGui::kAlignRight);
|
|
}
|
|
|
|
void OptionsDialog::onAction(Widget *widget, int result) {
|
|
// Since there is music playing while the dialog is displayed we need
|
|
// to update music volume immediately.
|
|
|
|
if (widget == _musicSwitch) {
|
|
_vm->_sound->muteMusic(result != 0);
|
|
} else if (widget == _musicSlider) {
|
|
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, result);
|
|
_vm->_sound->muteMusic(result == 0);
|
|
_musicSwitch->setValue(result != 0);
|
|
} else if (widget == _speechSlider) {
|
|
_speechSwitch->setValue(result != 0);
|
|
} else if (widget == _fxSlider) {
|
|
_fxSwitch->setValue(result != 0);
|
|
} else if (widget == _gfxSlider) {
|
|
_gfxPreview->setState(result);
|
|
_vm->_screen->setRenderLevel(result);
|
|
} else if (widget == _okButton) {
|
|
// Apply the changes
|
|
_vm->setSubtitles(_subtitlesSwitch->getValue());
|
|
_vm->_mouse->setObjectLabels(_objectLabelsSwitch->getValue());
|
|
_vm->_sound->muteMusic(!_musicSwitch->getValue());
|
|
_vm->_sound->muteSpeech(!_speechSwitch->getValue());
|
|
_vm->_sound->muteFx(!_fxSwitch->getValue());
|
|
_vm->_sound->setReverseStereo(_reverseStereoSwitch->getValue());
|
|
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _musicSlider->getValue());
|
|
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _speechSlider->getValue());
|
|
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _fxSlider->getValue());
|
|
_vm->_screen->setRenderLevel(_gfxSlider->getValue());
|
|
|
|
_vm->writeSettings();
|
|
setResult(1);
|
|
} else if (widget == _cancelButton) {
|
|
// Revert the changes
|
|
_vm->readSettings();
|
|
setResult(0);
|
|
}
|
|
}
|
|
|
|
// Slot button actions. Note that keyboard input generates positive actions
|
|
|
|
enum {
|
|
kSelectSlot = -1,
|
|
kDeselectSlot = -2,
|
|
kWheelDown = -3,
|
|
kWheelUp = -4,
|
|
kStartEditing = -5,
|
|
kCursorTick = -6
|
|
};
|
|
|
|
class Slot : public Widget {
|
|
private:
|
|
int _mode;
|
|
FontRendererGui *_fr;
|
|
byte _text[SAVE_DESCRIPTION_LEN];
|
|
bool _clickable;
|
|
bool _editable;
|
|
|
|
public:
|
|
Slot(Dialog *parent, int x, int y, int w, int h)
|
|
: Widget(parent, 2), _clickable(false), _editable(false) {
|
|
setHitRect(x, y, w, h);
|
|
_text[0] = 0;
|
|
}
|
|
|
|
void setMode(int mode) {
|
|
_mode = mode;
|
|
}
|
|
|
|
void setClickable(bool clickable) {
|
|
_clickable = clickable;
|
|
}
|
|
|
|
void setEditable(bool editable) {
|
|
_editable = editable;
|
|
_vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, editable);
|
|
}
|
|
|
|
bool isEditable() {
|
|
return _editable;
|
|
}
|
|
|
|
void setText(FontRendererGui *fr, int slot, byte *text) {
|
|
_fr = fr;
|
|
if (text)
|
|
sprintf((char *)_text, "%d. %s", slot, text);
|
|
else
|
|
sprintf((char *)_text, "%d. ", slot);
|
|
}
|
|
|
|
byte *getText() {
|
|
return &_text[0];
|
|
}
|
|
|
|
virtual void paint(Common::Rect *clipRect = NULL) {
|
|
Widget::paint();
|
|
|
|
// HACK: The main dialog is responsible for drawing the text
|
|
// when in editing mode.
|
|
|
|
if (!_editable)
|
|
_fr->drawText(_text, _sprites[0].x + 16, _sprites[0].y + 4 + 2 * getState());
|
|
}
|
|
|
|
virtual void onMouseDown(int x, int y) {
|
|
if (_clickable) {
|
|
if (getState() == 0) {
|
|
setState(1);
|
|
_parent->onAction(this, kSelectSlot);
|
|
if (_mode == kSaveDialog)
|
|
_parent->onAction(this, kStartEditing);
|
|
} else if (_mode == kRestoreDialog) {
|
|
setState(0);
|
|
_parent->onAction(this, kDeselectSlot);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void onWheelUp(int x, int y) {
|
|
_parent->onAction(this, kWheelUp);
|
|
}
|
|
|
|
virtual void onWheelDown(int x, int y) {
|
|
_parent->onAction(this, kWheelDown);
|
|
}
|
|
|
|
virtual void onKey(KeyboardEvent *ke) {
|
|
if (_editable) {
|
|
if (ke->kbd.keycode == Common::KEYCODE_BACKSPACE)
|
|
_parent->onAction(this, Common::KEYCODE_BACKSPACE);
|
|
else if (ke->kbd.ascii >= ' ' && ke->kbd.ascii <= 255) {
|
|
// Accept the character if the font renderer
|
|
// has what looks like a valid glyph for it.
|
|
if (_fr->getCharWidth(ke->kbd.ascii))
|
|
_parent->onAction(this, ke->kbd.ascii);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void onTick() {
|
|
if (_editable)
|
|
_parent->onAction(this, kCursorTick);
|
|
}
|
|
|
|
void setY(int y) {
|
|
for (int i = 0; i < _numStates; i++)
|
|
_sprites[i].y = y;
|
|
setHitRect(_hitRect.left, y, _hitRect.width(), _hitRect.height());
|
|
}
|
|
|
|
int getY() {
|
|
return _sprites[0].y;
|
|
}
|
|
};
|
|
|
|
SaveRestoreDialog::SaveRestoreDialog(Sword2Engine *vm, int mode) : Dialog(vm) {
|
|
int i;
|
|
|
|
_mode = mode;
|
|
_selectedSlot = -1;
|
|
|
|
// FIXME: The "control font" and the "red font" are currently always
|
|
// the same font, so one should be eliminated.
|
|
|
|
_fr1 = new FontRendererGui(_vm, _vm->_controlsFontId);
|
|
_fr2 = new FontRendererGui(_vm, _vm->_redFontId);
|
|
|
|
_panel = new Widget(this, 1);
|
|
_panel->createSurfaceImages(2016, 0, 40);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
_slotButton[i] = new Slot(this, 114, 0, 384, 36);
|
|
_slotButton[i]->createSurfaceImages(2006 + i, 114, 0);
|
|
_slotButton[i]->setMode(mode);
|
|
_slotButton[i + 4] = new Slot(this, 114, 0, 384, 36);
|
|
_slotButton[i + 4]->linkSurfaceImages(_slotButton[i], 114, 0);
|
|
_slotButton[i + 4]->setMode(mode);
|
|
}
|
|
|
|
updateSlots();
|
|
|
|
_zupButton = new ScrollButton(this, 516, 65, 17, 17);
|
|
_zupButton->createSurfaceImages(1982, 516, 65);
|
|
|
|
_upButton = new ScrollButton(this, 516, 85, 17, 17);
|
|
_upButton->createSurfaceImages(2067, 516, 85);
|
|
|
|
_downButton = new ScrollButton(this, 516, 329, 17, 17);
|
|
_downButton->createSurfaceImages(1986, 516, 329);
|
|
|
|
_zdownButton = new ScrollButton(this, 516, 350, 17, 17);
|
|
_zdownButton->createSurfaceImages(1988, 516, 350);
|
|
|
|
_okButton = new Button(this, 130, 377, 24, 24);
|
|
_okButton->createSurfaceImages(2002, 130, 377);
|
|
|
|
_cancelButton = new Button(this, 350, 377, 24, 24);
|
|
_cancelButton->linkSurfaceImages(_okButton, 350, 377);
|
|
|
|
registerWidget(_panel);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
registerWidget(_slotButton[i]);
|
|
|
|
registerWidget(_zupButton);
|
|
registerWidget(_upButton);
|
|
registerWidget(_downButton);
|
|
registerWidget(_zdownButton);
|
|
registerWidget(_okButton);
|
|
registerWidget(_cancelButton);
|
|
}
|
|
|
|
SaveRestoreDialog::~SaveRestoreDialog() {
|
|
delete _fr1;
|
|
delete _fr2;
|
|
}
|
|
|
|
// There aren't really a hundred different button objects of course, there are
|
|
// only eight. Re-arrange them to simulate scrolling.
|
|
|
|
void SaveRestoreDialog::updateSlots() {
|
|
for (int i = 0; i < 8; i++) {
|
|
Slot *slot = _slotButton[(baseSlot + i) % 8];
|
|
FontRendererGui *fr;
|
|
byte description[SAVE_DESCRIPTION_LEN];
|
|
|
|
slot->setY(72 + i * 36);
|
|
|
|
if (baseSlot + i == _selectedSlot) {
|
|
slot->setEditable(_mode == kSaveDialog);
|
|
slot->setState(1);
|
|
fr = _fr2;
|
|
} else {
|
|
slot->setEditable(false);
|
|
slot->setState(0);
|
|
fr = _fr1;
|
|
}
|
|
|
|
if (_vm->getSaveDescription(baseSlot + i, description) == SR_OK) {
|
|
slot->setText(fr, baseSlot + i, description);
|
|
slot->setClickable(true);
|
|
} else {
|
|
slot->setText(fr, baseSlot + i, NULL);
|
|
slot->setClickable(_mode == kSaveDialog);
|
|
}
|
|
|
|
if (slot->isEditable())
|
|
drawEditBuffer(slot);
|
|
else
|
|
slot->paint();
|
|
}
|
|
}
|
|
|
|
void SaveRestoreDialog::drawEditBuffer(Slot *slot) {
|
|
if (_selectedSlot == -1)
|
|
return;
|
|
|
|
// This will redraw a bit more than is strictly necessary, but I doubt
|
|
// that will make any noticeable difference.
|
|
|
|
slot->paint();
|
|
_fr2->drawText(_editBuffer, 130, 78 + (_selectedSlot - baseSlot) * 36);
|
|
}
|
|
|
|
void SaveRestoreDialog::onAction(Widget *widget, int result) {
|
|
if (widget == _zupButton) {
|
|
if (baseSlot > 0) {
|
|
if (baseSlot >= 8)
|
|
baseSlot -= 8;
|
|
else
|
|
baseSlot = 0;
|
|
updateSlots();
|
|
}
|
|
} else if (widget == _upButton) {
|
|
if (baseSlot > 0) {
|
|
baseSlot--;
|
|
updateSlots();
|
|
}
|
|
} else if (widget == _downButton) {
|
|
if (baseSlot < 92) {
|
|
baseSlot++;
|
|
updateSlots();
|
|
}
|
|
} else if (widget == _zdownButton) {
|
|
if (baseSlot < 92) {
|
|
if (baseSlot <= 84)
|
|
baseSlot += 8;
|
|
else
|
|
baseSlot = 92;
|
|
updateSlots();
|
|
}
|
|
} else if (widget == _okButton) {
|
|
setResult(1);
|
|
} else if (widget == _cancelButton) {
|
|
setResult(0);
|
|
} else {
|
|
Slot *slot = (Slot *)widget;
|
|
int textWidth;
|
|
byte tmp;
|
|
int i;
|
|
int j;
|
|
|
|
switch (result) {
|
|
case kWheelUp:
|
|
onAction(_upButton);
|
|
break;
|
|
case kWheelDown:
|
|
onAction(_downButton);
|
|
break;
|
|
case kSelectSlot:
|
|
case kDeselectSlot:
|
|
if (result == kSelectSlot)
|
|
_selectedSlot = baseSlot + (slot->getY() - 72) / 35;
|
|
else if (result == kDeselectSlot)
|
|
_selectedSlot = -1;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
if (widget == _slotButton[i])
|
|
break;
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
if (j != i) {
|
|
_slotButton[j]->setEditable(false);
|
|
_slotButton[j]->setState(0);
|
|
}
|
|
}
|
|
break;
|
|
case kStartEditing:
|
|
if (_selectedSlot >= 10)
|
|
_firstPos = 5;
|
|
else
|
|
_firstPos = 4;
|
|
|
|
strcpy((char *)_editBuffer, (char *)slot->getText());
|
|
_editPos = strlen((char *)_editBuffer);
|
|
_cursorTick = 0;
|
|
_editBuffer[_editPos] = '_';
|
|
_editBuffer[_editPos + 1] = 0;
|
|
slot->setEditable(true);
|
|
drawEditBuffer(slot);
|
|
break;
|
|
case kCursorTick:
|
|
_cursorTick++;
|
|
if (_cursorTick == 7) {
|
|
_editBuffer[_editPos] = ' ';
|
|
drawEditBuffer(slot);
|
|
} else if (_cursorTick == 14) {
|
|
_cursorTick = 0;
|
|
_editBuffer[_editPos] = '_';
|
|
drawEditBuffer(slot);
|
|
}
|
|
break;
|
|
case Common::KEYCODE_BACKSPACE:
|
|
if (_editPos > _firstPos) {
|
|
_editBuffer[_editPos - 1] = _editBuffer[_editPos];
|
|
_editBuffer[_editPos--] = 0;
|
|
drawEditBuffer(slot);
|
|
}
|
|
break;
|
|
default:
|
|
tmp = _editBuffer[_editPos];
|
|
_editBuffer[_editPos] = 0;
|
|
textWidth = _fr2->getTextWidth(_editBuffer);
|
|
_editBuffer[_editPos] = tmp;
|
|
|
|
if (textWidth < 340 && _editPos < SAVE_DESCRIPTION_LEN - 2) {
|
|
_editBuffer[_editPos + 1] = _editBuffer[_editPos];
|
|
_editBuffer[_editPos + 2] = 0;
|
|
_editBuffer[_editPos++] = result;
|
|
drawEditBuffer(slot);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SaveRestoreDialog::paint() {
|
|
Dialog::paint();
|
|
|
|
_fr1->drawText((_mode == kRestoreDialog) ? TEXT_RESTORE : TEXT_SAVE, 165, 377);
|
|
_fr1->drawText(TEXT_CANCEL, 382, 377);
|
|
}
|
|
|
|
void SaveRestoreDialog::setResult(int result) {
|
|
if (result) {
|
|
if (_selectedSlot == -1)
|
|
return;
|
|
|
|
if (_mode == kSaveDialog) {
|
|
if (_editPos <= _firstPos)
|
|
return;
|
|
}
|
|
}
|
|
|
|
Dialog::setResult(result);
|
|
}
|
|
|
|
int SaveRestoreDialog::runModal() {
|
|
int result = Dialog::runModal();
|
|
|
|
if (result) {
|
|
switch (_mode) {
|
|
case kSaveDialog:
|
|
// Remove the cursor character from the savegame name
|
|
_editBuffer[_editPos] = 0;
|
|
|
|
if (_vm->saveGame(_selectedSlot, (byte *)&_editBuffer[_firstPos]) != SR_OK)
|
|
result = 0;
|
|
break;
|
|
case kRestoreDialog:
|
|
if (_vm->restoreGame(_selectedSlot) != SR_OK)
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // End of namespace Sword2
|