458 lines
15 KiB
C++
458 lines
15 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 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/translation.h"
|
|
#include "gui/saveload.h"
|
|
#include "sherlock/tattoo/widget_files.h"
|
|
#include "sherlock/tattoo/tattoo.h"
|
|
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
|
#include "sherlock/tattoo/tattoo_scene.h"
|
|
#include "sherlock/tattoo/tattoo_user_interface.h"
|
|
|
|
#include "backends/keymapper/keymapper.h"
|
|
|
|
namespace Sherlock {
|
|
|
|
namespace Tattoo {
|
|
|
|
#define FILES_LINES_COUNT 5
|
|
|
|
WidgetFiles::WidgetFiles(SherlockEngine *vm, const Common::String &target) :
|
|
SaveManager(vm, target), WidgetBase(vm), _vm(vm) {
|
|
_fileMode = SAVEMODE_NONE;
|
|
_selector = _oldSelector = -1;
|
|
}
|
|
|
|
void WidgetFiles::show(SaveMode mode) {
|
|
Events &events = *_vm->_events;
|
|
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
|
Common::Point mousePos = events.mousePos();
|
|
|
|
if (_vm->_showOriginalSavesDialog) {
|
|
// Render and display the file dialog
|
|
_fileMode = mode;
|
|
ui._menuMode = FILES_MODE;
|
|
_selector = _oldSelector = -1;
|
|
_scroll = true;
|
|
createSavegameList();
|
|
|
|
// Set up the display area
|
|
_bounds = Common::Rect(SHERLOCK_SCREEN_WIDTH * 2 / 3, (_surface.fontHeight() + 1) *
|
|
(FILES_LINES_COUNT + 1) + 17);
|
|
_bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
|
|
|
|
// Create the surface and render its contents
|
|
_surface.create(_bounds.width(), _bounds.height());
|
|
render(RENDER_ALL);
|
|
|
|
summonWindow();
|
|
ui._menuMode = FILES_MODE;
|
|
|
|
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
|
keymapper->getKeymap("tattoo")->setEnabled(false);
|
|
keymapper->getKeymap("tattoo-scrolling")->setEnabled(true);
|
|
keymapper->getKeymap("tattoo-files")->setEnabled(true);
|
|
} else if (mode == SAVEMODE_LOAD) {
|
|
showScummVMRestoreDialog();
|
|
} else {
|
|
showScummVMSaveDialog();
|
|
}
|
|
}
|
|
|
|
void WidgetFiles::showScummVMSaveDialog() {
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
|
|
|
|
int slot = dialog->runModalWithCurrentTarget();
|
|
if (slot >= 0) {
|
|
Common::String desc = dialog->getResultString();
|
|
|
|
if (desc.empty()) {
|
|
// create our own description for the saved game, the user didn't enter it
|
|
desc = dialog->createDefaultSaveDescription(slot);
|
|
}
|
|
|
|
_vm->saveGameState(slot, desc);
|
|
}
|
|
|
|
close();
|
|
delete dialog;
|
|
}
|
|
|
|
void WidgetFiles::showScummVMRestoreDialog() {
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
|
|
int slot = dialog->runModalWithCurrentTarget();
|
|
close();
|
|
delete dialog;
|
|
|
|
if (slot >= 0) {
|
|
_vm->loadGameState(slot);
|
|
}
|
|
}
|
|
|
|
void WidgetFiles::render(FilesRenderMode mode) {
|
|
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
|
ImageFile &images = *ui._interfaceImages;
|
|
byte color;
|
|
|
|
if (mode == RENDER_ALL) {
|
|
_surface.clear(TRANSPARENCY);
|
|
makeInfoArea();
|
|
|
|
switch (_fileMode) {
|
|
case SAVEMODE_LOAD:
|
|
_surface.writeString(FIXED(LoadGame),
|
|
Common::Point((_surface.width() - _surface.stringWidth(FIXED(LoadGame))) / 2, 5), INFO_TOP);
|
|
break;
|
|
|
|
case SAVEMODE_SAVE:
|
|
_surface.writeString(FIXED(SaveGame),
|
|
Common::Point((_surface.width() - _surface.stringWidth(FIXED(SaveGame))) / 2, 5), INFO_TOP);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_surface.hLine(3, _surface.fontHeight() + 7, _surface.width() - 4, INFO_TOP);
|
|
_surface.hLine(3, _surface.fontHeight() + 8, _surface.width() - 4, INFO_MIDDLE);
|
|
_surface.hLine(3, _surface.fontHeight() + 9, _surface.width() - 4, INFO_BOTTOM);
|
|
_surface.SHtransBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 6));
|
|
_surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, _surface.fontHeight() + 6));
|
|
|
|
int xp = _surface.width() - BUTTON_SIZE - 6;
|
|
_surface.vLine(xp, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_TOP);
|
|
_surface.vLine(xp + 1, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_MIDDLE);
|
|
_surface.vLine(xp + 2, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_BOTTOM);
|
|
_surface.SHtransBlitFrom(images[6], Common::Point(xp - 1, _surface.fontHeight() + 8));
|
|
_surface.SHtransBlitFrom(images[7], Common::Point(xp - 1, _bounds.height() - 4));
|
|
}
|
|
|
|
int xp = _surface.stringWidth("00.") + _surface.widestChar() + 5;
|
|
int yp = _surface.fontHeight() + 14;
|
|
|
|
for (int idx = _savegameIndex; idx < (_savegameIndex + FILES_LINES_COUNT); ++idx) {
|
|
if (idx == _selector && mode != RENDER_ALL)
|
|
color = COMMAND_HIGHLIGHTED;
|
|
else
|
|
color = INFO_TOP;
|
|
|
|
if (mode == RENDER_NAMES_AND_SCROLLBAR)
|
|
_surface.fillRect(Common::Rect(4, yp, _surface.width() - BUTTON_SIZE - 9, yp + _surface.fontHeight()), TRANSPARENCY);
|
|
|
|
Common::String numStr = Common::String::format("%d.", idx + 1);
|
|
_surface.writeString(numStr, Common::Point(_surface.widestChar(), yp), color);
|
|
_surface.writeString(_savegames[idx], Common::Point(xp, yp), color);
|
|
|
|
yp += _surface.fontHeight() + 1;
|
|
}
|
|
|
|
// Draw the Scrollbar if necessary
|
|
if (mode != RENDER_NAMES)
|
|
drawScrollBar(_savegameIndex, FILES_LINES_COUNT, _savegames.size());
|
|
}
|
|
|
|
void WidgetFiles::handleEvents() {
|
|
Events &events = *_vm->_events;
|
|
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
|
Common::Point mousePos = events.mousePos();
|
|
Common::CustomEventType action = ui._action;
|
|
|
|
// Handle scrollbar events
|
|
ScrollHighlight oldHighlight = ui._scrollHighlight;
|
|
handleScrollbarEvents(_savegameIndex, FILES_LINES_COUNT, _savegames.size());
|
|
|
|
int oldScrollIndex = _savegameIndex;
|
|
handleScrolling(_savegameIndex, FILES_LINES_COUNT, _savegames.size());
|
|
|
|
// See if the mouse is pointing at any filenames in the window
|
|
if (Common::Rect(_bounds.left, _bounds.top + _surface.fontHeight() + 14,
|
|
_bounds.right - BUTTON_SIZE - 5, _bounds.bottom - 5).contains(mousePos)) {
|
|
_selector = (mousePos.y - _bounds.top - _surface.fontHeight() - 14) / (_surface.fontHeight() + 1) +
|
|
_savegameIndex;
|
|
} else {
|
|
_selector = -1;
|
|
}
|
|
|
|
// Check for the slot change actions
|
|
if (action == kActionTattooFilesNextSlot || action == kActionTattooFilesNextPageSlot) {
|
|
// If the mouse is not over any of the filenames, move the mouse so that it points to the first one
|
|
if (_selector == -1) {
|
|
events.warpMouse(Common::Point(_bounds.right - BUTTON_SIZE - 20,
|
|
_bounds.top + _surface.fontHeight() * 2 + 8));
|
|
} else {
|
|
// See if we're doing Next Page Slot action
|
|
if (action == kActionTattooFilesNextPageSlot) {
|
|
// We're doing Shift Tab
|
|
if (_selector == _savegameIndex)
|
|
_selector = _savegameIndex + 4;
|
|
else
|
|
--_selector;
|
|
} else {
|
|
// We're doing Next Slot action
|
|
++_selector;
|
|
if (_selector >= _savegameIndex + 5)
|
|
_selector = _savegameIndex;
|
|
}
|
|
|
|
events.warpMouse(Common::Point(mousePos.x, _bounds.top + _surface.fontHeight() * 2
|
|
+ 8 + (_selector - _savegameIndex) * (_surface.fontHeight() + 1)));
|
|
}
|
|
}
|
|
|
|
// Only redraw the window if the scrollbar position has changed
|
|
if (ui._scrollHighlight != oldHighlight || oldScrollIndex != _savegameIndex || _selector != _oldSelector)
|
|
render(RENDER_NAMES_AND_SCROLLBAR);
|
|
_oldSelector = _selector;
|
|
|
|
if (events._firstPress && !_bounds.contains(mousePos))
|
|
_outsideMenu = true;
|
|
|
|
if (events._released || events._rightReleased || action == kActionTattooFilesSelect) {
|
|
ui._scrollHighlight = SH_NONE;
|
|
|
|
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
|
|
|
if (_outsideMenu && !_bounds.contains(mousePos)) {
|
|
keymapper->getKeymap("tattoo-scrolling")->setEnabled(false);
|
|
keymapper->getKeymap("tattoo-files")->setEnabled(false);
|
|
keymapper->getKeymap("tattoo")->setEnabled(true);
|
|
close();
|
|
} else {
|
|
_outsideMenu = false;
|
|
|
|
if (_selector != -1) {
|
|
if (_fileMode == SAVEMODE_LOAD) {
|
|
// We're in Load Mode
|
|
_vm->loadGameState(_selector);
|
|
} else if (_fileMode == SAVEMODE_SAVE) {
|
|
keymapper->getKeymap("tattoo-scrolling")->setEnabled(false);
|
|
keymapper->getKeymap("tattoo-files")->setEnabled(false);
|
|
keymapper->getKeymap("tattoo-files-name")->setEnabled(true);
|
|
keymapper->getKeymap("tattoo-exit")->setEnabled(true);
|
|
// We're in Save Mode
|
|
if (getFilename())
|
|
_vm->saveGameState(_selector, _savegames[_selector]);
|
|
|
|
keymapper->getKeymap("tattoo-files-name")->setEnabled(false);
|
|
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
|
keymapper->getKeymap("tattoo")->setEnabled(true);
|
|
close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool WidgetFiles::getFilename() {
|
|
Events &events = *_vm->_events;
|
|
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
|
Screen &screen = *_vm->_screen;
|
|
Talk &talk = *_vm->_talk;
|
|
int index = 0;
|
|
int done = 0;
|
|
bool blinkFlag = false;
|
|
int blinkCountdown = 0;
|
|
int cursorColor = 192;
|
|
byte color, textColor;
|
|
bool insert = true;
|
|
|
|
assert(_selector != -1);
|
|
Common::Point pt(_surface.stringWidth("00.") + _surface.widestChar() + 5,
|
|
_surface.fontHeight() + 14 + (_selector - _savegameIndex) * (_surface.fontHeight() + 1));
|
|
|
|
Common::String numStr = Common::String::format("%d.", _selector + 1);
|
|
_surface.writeString(numStr, Common::Point(_surface.widestChar(), pt.y), COMMAND_HIGHLIGHTED);
|
|
|
|
Common::String filename = _savegames[_selector];
|
|
|
|
if (isSlotEmpty(_selector)) {
|
|
index = 0;
|
|
_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight()), TRANSPARENCY);
|
|
filename = "";
|
|
} else {
|
|
index = filename.size();
|
|
_surface.writeString(filename, pt, COMMAND_HIGHLIGHTED);
|
|
pt.x = _surface.stringWidth("00.") + _surface.stringWidth(filename) + _surface.widestChar() + 5;
|
|
}
|
|
|
|
do {
|
|
scene.doBgAnim();
|
|
|
|
if (talk._talkToAbort)
|
|
return false;
|
|
|
|
char currentChar = (index == (int)filename.size()) ? ' ' : filename[index];
|
|
Common::String charString = Common::String::format("%c", currentChar);
|
|
int width = screen.charWidth(currentChar);
|
|
|
|
// Wait for keypress or action
|
|
while (!events.kbHit() && !events.actionHit()) {
|
|
events.pollEventsAndWait();
|
|
events.setButtonState();
|
|
|
|
scene.doBgAnim();
|
|
|
|
if (talk._talkToAbort)
|
|
return false;
|
|
|
|
if (--blinkCountdown <= 0) {
|
|
blinkCountdown = 3;
|
|
blinkFlag = !blinkFlag;
|
|
if (blinkFlag) {
|
|
textColor = 236;
|
|
color = cursorColor;
|
|
} else {
|
|
textColor = COMMAND_HIGHLIGHTED;
|
|
color = TRANSPARENCY;
|
|
}
|
|
|
|
_surface.fillRect(Common::Rect(pt.x, pt.y, pt.x + width, pt.y + _surface.fontHeight()), color);
|
|
if (currentChar != ' ')
|
|
_surface.writeString(charString, pt, textColor);
|
|
}
|
|
if (_vm->shouldQuit())
|
|
return false;
|
|
}
|
|
|
|
while (events.kbHit()) {
|
|
Common::KeyState keyState = events.getKey();
|
|
if (keyState.keycode == Common::KEYCODE_BACKSPACE && index > 0) {
|
|
pt.x -= _surface.charWidth(filename[index - 1]);
|
|
--index;
|
|
|
|
if (insert) {
|
|
filename.deleteChar(index);
|
|
} else {
|
|
filename.setChar(' ', index);
|
|
}
|
|
|
|
_surface.fillRect(Common::Rect(pt.x, pt.y, _surface.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight()), TRANSPARENCY);
|
|
_surface.writeString(filename.c_str() + index, pt, COMMAND_HIGHLIGHTED);
|
|
} else if (keyState.keycode == Common::KEYCODE_DELETE && index < (int)filename.size()) {
|
|
filename.deleteChar(index);
|
|
|
|
_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight()), TRANSPARENCY);
|
|
_surface.writeString(Common::String(filename.c_str() + index), pt, COMMAND_HIGHLIGHTED);
|
|
} else if (keyState.keycode == Common::KEYCODE_RETURN) {
|
|
done = 1;
|
|
}
|
|
|
|
if ((keyState.ascii >= ' ') && (keyState.ascii <= 'z') && (index < 50)) {
|
|
if (pt.x + _surface.charWidth(keyState.ascii) < _surface.w - BUTTON_SIZE - 20) {
|
|
if (insert)
|
|
filename.insertChar(keyState.ascii, index);
|
|
else
|
|
filename.setChar(keyState.ascii, index);
|
|
|
|
_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.width() - BUTTON_SIZE - 9,
|
|
pt.y + _surface.fontHeight()), TRANSPARENCY);
|
|
_surface.writeString(filename.c_str() + index, pt, COMMAND_HIGHLIGHTED);
|
|
pt.x += _surface.charWidth(keyState.ascii);
|
|
++index;
|
|
}
|
|
}
|
|
|
|
currentChar = (index == (int)filename.size()) ? ' ' : filename[index];
|
|
charString = Common::String::format("%c", currentChar);
|
|
width = screen.charWidth(currentChar);
|
|
}
|
|
|
|
while (events.actionHit()) {
|
|
Common::CustomEventType action = events.getAction();
|
|
|
|
if ((action == kActionTattooFilesNameLeft && index > 0)
|
|
|| (action == kActionTattooFilesNameRight && index < (int)filename.size() && pt.x < (_bounds.right - BUTTON_SIZE - 20))
|
|
|| (action == kActionTattooFilesNameStart && index > 0)
|
|
|| (action == kActionTattooFilesNameEnd)) {
|
|
_surface.fillRect(Common::Rect(pt.x, pt.y, pt.x + width, pt.y + _surface.fontHeight()), TRANSPARENCY);
|
|
if (currentChar)
|
|
_surface.writeString(charString, pt, COMMAND_HIGHLIGHTED);
|
|
|
|
switch (action) {
|
|
case kActionTattooFilesNameLeft:
|
|
pt.x -= _surface.charWidth(filename[index - 1]);
|
|
--index;
|
|
break;
|
|
|
|
case kActionTattooFilesNameRight:
|
|
pt.x += _surface.charWidth(filename[index]);
|
|
++index;
|
|
break;
|
|
|
|
case kActionTattooFilesNameStart:
|
|
pt.x = _surface.stringWidth("00.") + _surface.widestChar() + 5;
|
|
index = 0;
|
|
break;
|
|
|
|
case kActionTattooFilesNameEnd:
|
|
pt.x = _surface.stringWidth("00.") + _surface.stringWidth(filename) + _surface.widestChar() + 5;
|
|
index = filename.size();
|
|
|
|
while (filename[index - 1] == ' ' && index > 0) {
|
|
pt.x -= _surface.charWidth(filename[index - 1]);
|
|
--index;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} else if (action == kActionTattooFilesNameToggleInsertMode) {
|
|
insert = !insert;
|
|
if (insert)
|
|
cursorColor = 192;
|
|
else
|
|
cursorColor = 200;
|
|
} else if (action == kActionTattooExit) {
|
|
_selector = -1;
|
|
render(RENDER_NAMES_AND_SCROLLBAR);
|
|
done = -1;
|
|
}
|
|
|
|
currentChar = (index == (int)filename.size()) ? ' ' : filename[index];
|
|
charString = Common::String::format("%c", currentChar);
|
|
width = screen.charWidth(currentChar);
|
|
}
|
|
|
|
} while (!done && !_vm->shouldQuit());
|
|
|
|
scene.doBgAnim();
|
|
|
|
if (talk._talkToAbort)
|
|
return false;
|
|
|
|
if (done == 1)
|
|
_savegames[_selector] = filename;
|
|
|
|
return done == 1;
|
|
}
|
|
|
|
Common::Rect WidgetFiles::getScrollBarBounds() const {
|
|
Common::Rect scrollRect(BUTTON_SIZE, _bounds.height() - _surface.fontHeight() - 13);
|
|
scrollRect.moveTo(_bounds.width() - BUTTON_SIZE - 3, _surface.fontHeight() + 10);
|
|
|
|
return scrollRect;
|
|
}
|
|
|
|
} // End of namespace Tattoo
|
|
|
|
} // End of namespace Sherlock
|