diff options
Diffstat (limited to 'gui/launcher.cpp')
-rw-r--r-- | gui/launcher.cpp | 356 |
1 files changed, 348 insertions, 8 deletions
diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 34c4ebf474..e9f718f4f0 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -29,6 +29,7 @@ #include "common/events.h" #include "common/fs.h" #include "common/util.h" +#include "common/savefile.h" #include "common/system.h" #include "gui/about.h" @@ -45,6 +46,7 @@ #include "gui/TabWidget.h" #include "gui/PopUpWidget.h" #include "graphics/cursorman.h" +#include "graphics/scaler.h" #include "sound/mididrv.h" @@ -61,7 +63,10 @@ enum { kAddGameCmd = 'ADDG', kEditGameCmd = 'EDTG', kRemoveGameCmd = 'REMG', + kLoadGameCmd = 'LOAD', kQuitCmd = 'QUIT', + kChooseCmd = 'CHOS', + kDelCmd = 'DEL ', kCmdGlobalGraphicsOverride = 'OGFX', @@ -390,7 +395,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat if (browser.runModal() > 0) { // User made this choice... - FilesystemNode file(browser.getResult()); + Common::FilesystemNode file(browser.getResult()); _soundFont->setLabel(file.getPath()); if (!file.getPath().empty() && (file.getPath() != "None")) @@ -408,7 +413,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat BrowserDialog browser("Select directory with game data", true); if (browser.runModal() > 0) { // User made his choice... - FilesystemNode dir(browser.getResult()); + Common::FilesystemNode dir(browser.getResult()); // TODO: Verify the game can be found in the new directory... Best // done with optional specific gameid to pluginmgr detectgames? @@ -426,7 +431,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat BrowserDialog browser("Select additional game directory", true); if (browser.runModal() > 0) { // User made his choice... - FilesystemNode dir(browser.getResult()); + Common::FilesystemNode dir(browser.getResult()); _extraPathWidget->setLabel(dir.getPath()); draw(); } @@ -438,7 +443,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat BrowserDialog browser("Select directory for saved games", true); if (browser.runModal() > 0) { // User made his choice... - FilesystemNode dir(browser.getResult()); + Common::FilesystemNode dir(browser.getResult()); _savePathWidget->setLabel(dir.getPath()); draw(); } @@ -450,7 +455,10 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat // Write back changes made to config object String newDomain(_domainWidget->getEditString()); if (newDomain != _domain) { - if (newDomain.empty() || ConfMan.hasGameDomain(newDomain)) { + if (newDomain.empty() + || newDomain.hasPrefix("_") + || newDomain == ConfigManager::kApplicationDomain + || ConfMan.hasGameDomain(newDomain)) { MessageDialog alert("This game ID is already taken. Please choose another one."); alert.runModal(); return; @@ -465,6 +473,293 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat } } +class SaveLoadChooser : public GUI::Dialog { + typedef Common::String String; + typedef Common::StringList StringList; +protected: + GUI::ListWidget *_list; + GUI::ButtonWidget *_chooseButton; + GUI::ButtonWidget *_deleteButton; + GUI::GraphicsWidget *_gfxWidget; + GUI::ContainerWidget *_container; + GUI::StaticTextWidget *_date; + GUI::StaticTextWidget *_time; + GUI::StaticTextWidget *_playtime; + + const EnginePlugin *_plugin; + bool _delSupport; + bool _metaInfoSupport; + bool _thumbnailSupport; + bool _saveDateSupport; + bool _playTimeSupport; + String _target; + SaveStateList _saveList; + + uint8 _fillR, _fillG, _fillB; + + void updateSaveList(); + void updateSelection(bool redraw); +public: + SaveLoadChooser(const String &title, const String &buttonLabel); + ~SaveLoadChooser(); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + void setList(const StringList& list); + int runModal(const EnginePlugin *plugin, const String &target); + + virtual void reflowLayout(); + + virtual void close(); +}; + +SaveLoadChooser::SaveLoadChooser(const String &title, const String &buttonLabel) + : Dialog("scummsaveload"), _delSupport(0), _list(0), _chooseButton(0), _deleteButton(0), _gfxWidget(0) { + _delSupport = _metaInfoSupport = _thumbnailSupport = _saveDateSupport = _playTimeSupport = false; + + _drawingHints |= GUI::THEME_HINT_SPECIAL_COLOR; + + new StaticTextWidget(this, "scummsaveload_title", title); + + // Add choice list + _list = new GUI::ListWidget(this, "scummsaveload_list"); + _list->setNumberingMode(GUI::kListNumberingOff); + + _gfxWidget = new GUI::GraphicsWidget(this, 0, 0, 10, 10); + + _date = new StaticTextWidget(this, 0, 0, 10, 10, "No date saved", kTextAlignCenter); + _time = new StaticTextWidget(this, 0, 0, 10, 10, "No time saved", kTextAlignCenter); + _playtime = new StaticTextWidget(this, 0, 0, 10, 10, "No playtime saved", kTextAlignCenter); + + // Buttons + new GUI::ButtonWidget(this, "scummsaveload_cancel", "Cancel", kCloseCmd, 0); + _chooseButton = new GUI::ButtonWidget(this, "scummsaveload_choose", buttonLabel, kChooseCmd, 0); + _chooseButton->setEnabled(false); + + _deleteButton = new GUI::ButtonWidget(this, "scummsaveload_delete", "Delete", kDelCmd, 0); + _deleteButton->setEnabled(false); + + _delSupport = _metaInfoSupport = _thumbnailSupport = false; + + _container = new GUI::ContainerWidget(this, 0, 0, 10, 10); + _container->setHints(GUI::THEME_HINT_USE_SHADOW); +} + +SaveLoadChooser::~SaveLoadChooser() { +} + +int SaveLoadChooser::runModal(const EnginePlugin *plugin, const String &target) { + if (_gfxWidget) + _gfxWidget->setGfx(0); + + _plugin = plugin; + _target = target; + _delSupport = (*_plugin)->hasFeature(MetaEngine::kSupportsDeleteSave); + _metaInfoSupport = (*_plugin)->hasFeature(MetaEngine::kSupportsMetaInfos); + _thumbnailSupport = _metaInfoSupport && (*_plugin)->hasFeature(MetaEngine::kSupportsThumbnails); + _saveDateSupport = _metaInfoSupport && (*_plugin)->hasFeature(MetaEngine::kSupportsSaveDate); + _playTimeSupport = _metaInfoSupport && (*_plugin)->hasFeature(MetaEngine::kSupportsSavePlayTime); + reflowLayout(); + updateSaveList(); + + int ret = Dialog::runModal(); + return ret; +} + +void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + int selItem = _list->getSelected(); + + switch (cmd) { + case GUI::kListItemActivatedCmd: + case GUI::kListItemDoubleClickedCmd: + if (selItem >= 0) { + if (!_list->getSelectedString().empty()) { + _list->endEditMode(); + setResult(atoi(_saveList[selItem].save_slot().c_str())); + close(); + } + } + break; + case kChooseCmd: + setResult(atoi(_saveList[selItem].save_slot().c_str())); + close(); + break; + case GUI::kListSelectionChangedCmd: { + updateSelection(true); + } break; + case kDelCmd: + if (selItem >= 0 && _delSupport) { + MessageDialog alert("Do you really want to delete this savegame?", + "Delete", "Cancel"); + if (alert.runModal() == GUI::kMessageOK) { + (*_plugin)->removeSaveState(_target.c_str(), atoi(_saveList[selItem].save_slot().c_str())); + + setResult(-1); + _list->setSelected(-1); + + updateSaveList(); + updateSelection(true); + } + } + break; + case kCloseCmd: + setResult(-1); + default: + Dialog::handleCommand(sender, cmd, data); + } +} + +void SaveLoadChooser::reflowLayout() { + if (g_gui.evaluator()->getVar("scummsaveload_extinfo.visible") == 1 && _thumbnailSupport) { + int thumbX = g_gui.evaluator()->getVar("scummsaveload_thumbnail.x"); + int thumbY = g_gui.evaluator()->getVar("scummsaveload_thumbnail.y"); + int hPad = g_gui.evaluator()->getVar("scummsaveload_thumbnail.hPad"); + int vPad = g_gui.evaluator()->getVar("scummsaveload_thumbnail.vPad"); + int thumbH = ((g_system->getHeight() % 200 && g_system->getHeight() != 350) ? kThumbnailHeight2 : kThumbnailHeight1); + + int textLines = 0; + if (_saveDateSupport) + textLines += 2; + if (_playTimeSupport) + textLines += 1; + + if (textLines) + ++textLines; + + _container->resize(thumbX - hPad, thumbY - vPad, kThumbnailWidth + hPad * 2, thumbH + vPad * 2 + kLineHeight * textLines); + + // Add the thumbnail display + _gfxWidget->resize(thumbX, thumbY, kThumbnailWidth, thumbH); + + int height = thumbY + thumbH + kLineHeight; + + if (_saveDateSupport) { + _date->resize(thumbX, height, kThumbnailWidth, kLineHeight); + height += kLineHeight; + _time->resize(thumbX, height, kThumbnailWidth, kLineHeight); + height += kLineHeight; + } + + if (_playTimeSupport) + _playtime->resize(thumbX, height, kThumbnailWidth, kLineHeight); + + _container->clearFlags(GUI::WIDGET_INVISIBLE); + _gfxWidget->clearFlags(GUI::WIDGET_INVISIBLE); + + if (_saveDateSupport) { + _date->clearFlags(GUI::WIDGET_INVISIBLE); + _time->clearFlags(GUI::WIDGET_INVISIBLE); + } else { + _date->setFlags(GUI::WIDGET_INVISIBLE); + _time->setFlags(GUI::WIDGET_INVISIBLE); + } + + if (_playTimeSupport) + _playtime->clearFlags(GUI::WIDGET_INVISIBLE); + else + _playtime->setFlags(GUI::WIDGET_INVISIBLE); + + _fillR = g_gui.evaluator()->getVar("scummsaveload_thumbnail.fillR"); + _fillG = g_gui.evaluator()->getVar("scummsaveload_thumbnail.fillG"); + _fillB = g_gui.evaluator()->getVar("scummsaveload_thumbnail.fillB"); + updateSelection(false); + } else { + _container->setFlags(GUI::WIDGET_INVISIBLE); + _gfxWidget->setFlags(GUI::WIDGET_INVISIBLE); + _date->setFlags(GUI::WIDGET_INVISIBLE); + _time->setFlags(GUI::WIDGET_INVISIBLE); + _playtime->setFlags(GUI::WIDGET_INVISIBLE); + } + + Dialog::reflowLayout(); +} + +void SaveLoadChooser::updateSelection(bool redraw) { + int selItem = _list->getSelected(); + + bool isDeletable = _delSupport; + + if (selItem >= 0 && !_list->getSelectedString().empty() && _metaInfoSupport) { + SaveStateDescriptor desc = (*_plugin)->querySaveMetaInfos(_target.c_str(), atoi(_saveList[selItem].save_slot().c_str())); + + isDeletable = desc.getBool("is_deletable") && _delSupport; + + if (_thumbnailSupport) { + const Graphics::Surface *thumb = desc.getThumbnail(); + if (thumb) { + _gfxWidget->setGfx(thumb); + _gfxWidget->useAlpha(256); + } else { + _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB); + } + } + + if (_saveDateSupport) { + Common::String date = "Date: "; + if (desc.contains("save_date")) + date += desc.getVal("save_date"); + else + date = "No date saved"; + + Common::String time = "Time: "; + if (desc.contains("save_time")) + time += desc.getVal("save_time"); + else + time = "No time saved"; + + _date->setLabel(date); + _time->setLabel(time); + } + + if (_playTimeSupport) { + Common::String time = "Playtime: "; + if (desc.contains("play_time")) + time += desc.getVal("play_time"); + else + time = "No playtime saved"; + + _playtime->setLabel(time); + } + } + + + // Disable these buttons if nothing is selected, or if an empty + // list item is selected. + _chooseButton->setEnabled(selItem >= 0 && (!_list->getSelectedString().empty())); + // Delete will always be disabled if the engine doesn't support it. + _deleteButton->setEnabled(isDeletable && (selItem >= 0) && (!_list->getSelectedString().empty())); + + if (redraw) { + _gfxWidget->draw(); + _date->draw(); + _time->draw(); + _playtime->draw(); + _chooseButton->draw(); + _deleteButton->draw(); + } +} + +void SaveLoadChooser::close() { + _plugin = 0; + _target.clear(); + _saveList.clear(); + _list->setList(StringList()); + + Dialog::close(); +} + +void SaveLoadChooser::updateSaveList() { + _saveList = (*_plugin)->listSaves(_target.c_str()); + + StringList saveNames; + for (SaveStateList::const_iterator x = _saveList.begin(); x != _saveList.end(); ++x) { + Common::String description = x->save_slot(); + description += ". "; + description += x->description(); + + saveNames.push_back(description); + } + _list->setList(saveNames); +} #pragma mark - @@ -499,6 +794,9 @@ LauncherDialog::LauncherDialog() _startButton = new ButtonWidget(this, "launcher_start_button", "Start", kStartCmd, 'S'); + _loadButton = + new ButtonWidget(this, "launcher_loadGame_button", "Load", kLoadGameCmd, 'L'); + // Above the lowest button rows: two more buttons (directly below the list box) _addButton = new ButtonWidget(this, "launcher_addGame_button", "Add Game...", kAddGameCmd, 'A'); @@ -526,6 +824,9 @@ LauncherDialog::LauncherDialog() // Create file browser dialog _browser = new BrowserDialog("Select directory with game data", true); + + // Create Load dialog + _loadDialog = new SaveLoadChooser("Load game:", "Load"); } void LauncherDialog::selectGame(const String &name) { @@ -543,6 +844,7 @@ void LauncherDialog::selectGame(const String &name) { LauncherDialog::~LauncherDialog() { delete _browser; + delete _loadDialog; } void LauncherDialog::open() { @@ -651,9 +953,9 @@ void LauncherDialog::addGame() { if (_browser->runModal() > 0) { // User made his choice... - FilesystemNode dir(_browser->getResult()); - FSList files; - if (!dir.getChildren(files, FilesystemNode::kListAll)) { + Common::FilesystemNode dir(_browser->getResult()); + Common::FSList files; + if (!dir.getChildren(files, Common::FilesystemNode::kListAll)) { error("browser returned a node that is not a directory: '%s'", dir.getPath().c_str()); } @@ -792,6 +1094,37 @@ void LauncherDialog::editGame(int item) { } } +void LauncherDialog::loadGame(int item) { + String gameId = ConfMan.get("gameid", _domains[item]); + if (gameId.empty()) + gameId = _domains[item]; + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + String target = _domains[item]; + target.toLowercase(); + + if (plugin) { + if ((*plugin)->hasFeature(MetaEngine::kSupportsListSaves) && + (*plugin)->hasFeature(MetaEngine::kSupportsDirectLoad)) { + int slot = _loadDialog->runModal(plugin, target); + if (slot >= 0) { + ConfMan.setActiveDomain(_domains[item]); + ConfMan.setInt("save_slot", slot, Common::ConfigManager::kTransientDomain); + close(); + } + } else { + MessageDialog dialog + ("This game does not support loading games from the launcher.", "OK"); + dialog.runModal(); + } + } else { + MessageDialog dialog("ScummVM could not find any engine capable of running the selected game!", "OK"); + dialog.runModal(); + } +} + void LauncherDialog::handleKeyDown(Common::KeyState state) { Dialog::handleKeyDown(state); updateButtons(); @@ -815,6 +1148,9 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat case kEditGameCmd: editGame(item); break; + case kLoadGameCmd: + loadGame(item); + break; case kOptionsCmd: { GlobalOptionsDialog options; options.runModal(); @@ -863,6 +1199,10 @@ void LauncherDialog::updateButtons() { _removeButton->setEnabled(enable); _removeButton->draw(); } + if (enable != _loadButton->isEnabled()) { + _loadButton->setEnabled(enable); + _loadButton->draw(); + } // Update the label of the "Add" button depending on whether shift is pressed or not int modifiers = g_system->getEventManager()->getModifierState(); |