diff --git a/Makefile.common b/Makefile.common index a02926a0a3b..b1b090012de 100644 --- a/Makefile.common +++ b/Makefile.common @@ -28,6 +28,7 @@ MODULES += \ engines \ graphics \ common \ + po ifdef USE_MT32EMU MODULES += sound/softsynth/mt32 diff --git a/backends/fs/amigaos4/amigaos4-fs.cpp b/backends/fs/amigaos4/amigaos4-fs.cpp index c0a38817a6c..850121bbd11 100644 --- a/backends/fs/amigaos4/amigaos4-fs.cpp +++ b/backends/fs/amigaos4/amigaos4-fs.cpp @@ -368,7 +368,7 @@ bool AmigaOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, b } if (ERROR_NO_MORE_ENTRIES != IDOS->IoErr() ) { - debug(6, "An error occured during ExamineDir"); + debug(6, "An error occurred during ExamineDir"); ret = false; } else { ret = true; diff --git a/backends/keymapper/remap-dialog.cpp b/backends/keymapper/remap-dialog.cpp index f34454dde33..4a270ad3c59 100644 --- a/backends/keymapper/remap-dialog.cpp +++ b/backends/keymapper/remap-dialog.cpp @@ -31,6 +31,8 @@ #include "gui/ScrollBarWidget.h" #include "gui/ThemeEval.h" +#include "common/translation.h" + namespace Common { enum { @@ -44,12 +46,12 @@ RemapDialog::RemapDialog() _keymapper = g_system->getEventManager()->getKeymapper(); assert(_keymapper); - _kmPopUpDesc = new GUI::StaticTextWidget(this, "KeyMapper.PopupDesc", "Keymap:"); + _kmPopUpDesc = new GUI::StaticTextWidget(this, "KeyMapper.PopupDesc", _("Keymap:")); _kmPopUp = new GUI::PopUpWidget(this, "KeyMapper.Popup"); _scrollBar = new GUI::ScrollBarWidget(this, 0, 0, 0, 0); - new GUI::ButtonWidget(this, "KeyMapper.Close", "Close", kCloseCmd); + new GUI::ButtonWidget(this, "KeyMapper.Close", _("Close"), 0, kCloseCmd); } RemapDialog::~RemapDialog() { @@ -61,7 +63,7 @@ void RemapDialog::open() { const Stack &activeKeymaps = _keymapper->getActiveStack(); if (!(activeKeymaps.size() > 0)) { - _kmPopUp->appendEntry(activeKeymaps.top().keymap->getName() + " (Active)"); + _kmPopUp->appendEntry(activeKeymaps.top().keymap->getName() + _(" (Active)")); divider = true; } @@ -95,7 +97,7 @@ void RemapDialog::open() { if (divider) _kmPopUp->appendEntry(""); for (it = _globalKeymaps->begin(); it != _globalKeymaps->end(); ++it) { - _kmPopUp->appendEntry(it->_value->getName() + " (Global)", idx); + _kmPopUp->appendEntry(it->_value->getName() + _(" (Global)"), idx); _keymapTable[idx++] = it->_value; } divider = true; @@ -105,7 +107,7 @@ void RemapDialog::open() { if (divider) _kmPopUp->appendEntry(""); for (it = _gameKeymaps->begin(); it != _gameKeymaps->end(); ++it) { - _kmPopUp->appendEntry(it->_value->getName() + " (Game)", idx); + _kmPopUp->appendEntry(it->_value->getName() + _(" (Game)"), idx); _keymapTable[idx++] = it->_value; } } @@ -168,7 +170,7 @@ void RemapDialog::reflowLayout() { widg.actionText = new GUI::StaticTextWidget(this, 0, 0, 0, 0, "", Graphics::kTextAlignRight); widg.keyButton = - new GUI::ButtonWidget(this, 0, 0, 0, 0, "", kRemapCmd + i); + new GUI::ButtonWidget(this, 0, 0, 0, 0, "", 0, kRemapCmd + i); _keymapWidgets.push_back(widg); } else { widg = _keymapWidgets[i]; diff --git a/backends/platform/sdl/events.cpp b/backends/platform/sdl/events.cpp index 3b71b65f041..c7ef55aad09 100644 --- a/backends/platform/sdl/events.cpp +++ b/backends/platform/sdl/events.cpp @@ -180,6 +180,7 @@ static void SDLModToOSystemKeyFlags(SDLMod mod, Common::Event &event) { bool OSystem_SDL::pollEvent(Common::Event &event) { SDL_Event ev; + ev.type = SDL_NOEVENT; handleKbdMouse(); /* Residual doesn't support this diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index c4bd4b9e237..829660238a6 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -420,7 +420,7 @@ bool OSystem_SDL::getFeatureState(Feature f) { } } -void OSystem_SDL::quit() { +void OSystem_SDL::deinit() { if (_cdrom) { SDL_CDStop(_cdrom); SDL_CDClose(_cdrom); @@ -438,10 +438,14 @@ void OSystem_SDL::quit() { SDL_Quit(); - // Even Manager requires save manager for storing + // Event Manager requires save manager for storing // recorded events delete getEventManager(); delete _savefile; +} + +void OSystem_SDL::quit() { + deinit(); #if !defined(SAMSUNGTV) exit(0); diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index 271891d8af1..ead1002ffa5 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -135,6 +135,8 @@ public: // Quit virtual void quit(); // overloaded by CE backend + void deinit(); + virtual void getTimeAndDate(TimeDate &t) const; virtual Common::TimerManager *getTimerManager(); diff --git a/base/commandLine.cpp b/base/commandLine.cpp index dc0e2f7ccba..77ab03fd822 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -695,7 +695,7 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha #endif // DISABLE_COMMAND_LINE -bool processSettings(Common::String &command, Common::StringMap &settings) { +Common::Error processSettings(Common::String &command, Common::StringMap &settings) { #ifndef DISABLE_COMMAND_LINE @@ -704,31 +704,31 @@ bool processSettings(Common::String &command, Common::StringMap &settings) { // have been loaded. if (command == "list-targets") { listTargets(); - return false; + return Common::kNoError; } else if (command == "list-games") { listGames(); - return false; + return Common::kNoError; } else if (command == "list-themes") { listThemes(); - return false; + return Common::kNoError; } else if (command == "version") { printf("%s\n", gResidualFullVersion); printf("Features compiled in: %s\n", gResidualFeatures); - return false; + return Common::kNoError; } else if (command == "help") { printf(HELP_STRING, s_appName); - return false; + return Common::kNoError; } #ifdef DETECTOR_TESTING_HACK else if (command == "test-detector") { runDetectorTest(); - return false; + return Common::kNoError; } #endif #ifdef UPGRADE_ALL_TARGETS_HACK else if (command == "upgrade-targets") { upgradeTargets(); - return false; + return Common::kNoError; } #endif @@ -770,7 +770,7 @@ bool processSettings(Common::String &command, Common::StringMap &settings) { // environment variable. This is weaker than a --savepath on the // command line, but overrides the default savepath, hence it is // handled here, just before the command line gets parsed. -#if !defined(MACOS_CARBON) && !defined(_WIN32_WCE) && !defined(PALMOS_MODE) && !defined(__GP32__) +#if !defined(MACOS_CARBON) && !defined(_WIN32_WCE) && !defined(PALMOS_MODE) && !defined(__GP32__) && !defined(ANDROID) if (!settings.contains("savepath")) { const char *dir = getenv("RESIDUAL_SAVEPATH"); if (dir && *dir && strlen(dir) < MAXPATHLEN) { @@ -800,7 +800,7 @@ bool processSettings(Common::String &command, Common::StringMap &settings) { ConfMan.set(key, value, Common::ConfigManager::kTransientDomain); } - return true; + return Common::kArgumentNotProcessed; } } // End of namespace Base diff --git a/base/commandLine.h b/base/commandLine.h index d4e9e455adc..50ad9bf2caa 100644 --- a/base/commandLine.h +++ b/base/commandLine.h @@ -27,13 +27,14 @@ #define BASE_OPTIONS_H #include "common/str.h" +#include "common/error.h" #include "common/config-manager.h" namespace Base { void registerDefaults(); Common::String parseCommandLine(Common::StringMap &settings, int argc, const char * const *argv); -bool processSettings(Common::String &command, Common::StringMap &settings); +Common::Error processSettings(Common::String &command, Common::StringMap &settings); } // End of namespace Base diff --git a/base/main.cpp b/base/main.cpp index 09fc447a33e..278c2b93b10 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -47,6 +47,7 @@ #include "common/fs.h" #include "common/system.h" #include "common/tokenizer.h" +#include "common/translation.h" #include "gui/GuiManager.h" #include "gui/message.h" @@ -101,20 +102,20 @@ static const EnginePlugin *detectPlugin() { ConfMan.set("gameid", gameid); // Query the plugins and find one that will handle the specified gameid - printf("User picked target '%s' (gameid '%s')...\n", ConfMan.getActiveDomainName().c_str(), gameid.c_str()); - printf(" Looking for a plugin supporting this gameid... "); + printf(_t("User picked target '%s' (gameid '%s')...\n"), ConfMan.getActiveDomainName().c_str(), gameid.c_str()); + printf("%s", _t(" Looking for a plugin supporting this gameid... ")); GameDescriptor game = EngineMan.findGame(gameid, &plugin); if (plugin == 0) { - printf("failed\n"); - warning("%s is an invalid gameid. Use the --list-games option to list supported gameid", gameid.c_str()); + printf("%s", _t("failed\n")); + warning(_t("%s is an invalid gameid. Use the --list-games option to list supported gameid"), gameid.c_str()); return 0; } else { printf("%s\n", plugin->getName()); } // FIXME: Do we really need this one? - printf(" Starting '%s'\n", game.description().c_str()); + printf(_t(" Starting '%s'\n"), game.description().c_str()); return plugin; } @@ -141,9 +142,9 @@ static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const // Is a separate dialog here still required? //GUI::displayErrorDialog("ScummVM could not find any game in the specified directory!"); - const char *errMsg = Common::errorToString(err); + const char *errMsg = _(Common::errorToString(err)); - warning("%s failed to instantiate engine: %s (target '%s', path '%s')", + warning(_t("%s failed to instantiate engine: %s (target '%s', path '%s')"), plugin->getName(), errMsg, ConfMan.getActiveDomainName().c_str(), @@ -200,7 +201,7 @@ static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const while (!tokenizer.empty()) { Common::String token = tokenizer.nextToken(); if (!DebugMan.enableDebugChannel(token)) - warning("Engine does not support debug level '%s'", token.c_str()); + warning(_("Engine does not support debug level '%s'"), token.c_str()); } // Inform backend that the engine is about to be run @@ -254,22 +255,22 @@ static void setupKeymapper(OSystem &system) { mapper->registerHardwareKeySet(keySet); // Now create the global keymap - act = new Action(globalMap, "MENU", "Menu", kGenericActionType, kSelectKeyType); + act = new Action(globalMap, "MENU", _("Menu"), kGenericActionType, kSelectKeyType); act->addKeyEvent(KeyState(KEYCODE_F5, ASCII_F5, 0)); - act = new Action(globalMap, "SKCT", "Skip", kGenericActionType, kActionKeyType); + act = new Action(globalMap, "SKCT", _("Skip"), kGenericActionType, kActionKeyType); act->addKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE, 0)); - act = new Action(globalMap, "PAUS", "Pause", kGenericActionType, kStartKeyType); + act = new Action(globalMap, "PAUS", _("Pause"), kGenericActionType, kStartKeyType); act->addKeyEvent(KeyState(KEYCODE_SPACE, ' ', 0)); - act = new Action(globalMap, "SKLI", "Skip line", kGenericActionType, kActionKeyType); + act = new Action(globalMap, "SKLI", _("Skip line"), kGenericActionType, kActionKeyType); act->addKeyEvent(KeyState(KEYCODE_PERIOD, '.', 0)); - act = new Action(globalMap, "VIRT", "Display keyboard", kVirtualKeyboardActionType); + act = new Action(globalMap, "VIRT", _("Display keyboard"), kVirtualKeyboardActionType); act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0)); - act = new Action(globalMap, "REMP", "Remap keys", kKeyRemapActionType); + act = new Action(globalMap, "REMP", _("Remap keys"), kKeyRemapActionType); act->addKeyEvent(KeyState(KEYCODE_F8, ASCII_F8, 0)); mapper->addGlobalKeymap(globalMap); @@ -305,6 +306,8 @@ extern "C" int residual_main(int argc, const char * const argv[]) { // Update the config file ConfMan.set("versioninfo", gResidualVersion, Common::ConfigManager::kApplicationDomain); + // Enable translation + TransMan.setLanguage(ConfMan.get("gui_language").c_str()); // Load and setup the debuglevel and the debug flags. We do this at the // soonest possible moment to ensure debug output starts early on, if @@ -326,8 +329,10 @@ extern "C" int residual_main(int argc, const char * const argv[]) { // Process the remaining command line settings. Must be done after the // config file and the plugins have been loaded. - if (!Base::processSettings(command, settings)) - return 0; + Common::Error res; + + if ((res = Base::processSettings(command, settings)) != Common::kArgumentNotProcessed) + return res; // Init the backend. Must take place after all config data (including // the command line params) was read. @@ -371,7 +376,7 @@ extern "C" int residual_main(int argc, const char * const argv[]) { // Did an error occur ? if (result != Common::kNoError) { // Shows an informative error dialog if starting the selected game failed. - GUI::displayErrorDialog(result, "Error running game:"); + GUI::displayErrorDialog(result, _("Error running game:")); } // Quit unless an error occurred, or Return to launcher was requested @@ -397,8 +402,8 @@ extern "C" int residual_main(int argc, const char * const argv[]) { } else { // A dialog would be nicer, but we don't have any // screen to draw on yet. - warning("Could not find any engine capable of running the selected game"); - GUI::displayErrorDialog("Could not find any engine capable of running the selected game"); + warning("%s", _("Could not find any engine capable of running the selected game")); + GUI::displayErrorDialog(_("Could not find any engine capable of running the selected game")); } // We will destroy the AudioCDManager singleton here to save some memory. diff --git a/common/algorithm.h b/common/algorithm.h index 30051752778..35b292a5f91 100644 --- a/common/algorithm.h +++ b/common/algorithm.h @@ -222,6 +222,21 @@ void sort(T first, T last) { sort(first, last, Common::Less()); } +/** + * Euclid's algorithm to compute the greatest common divisor. + */ +template +T gcd(T a, T b) { + if (a <= 0) a = -a; + if (b <= 0) b = -b; + while (a > 0) { + T tmp = a; + a = b % a; + b = tmp; + } + return b; +} + } // End of namespace Common #endif diff --git a/common/config-file.cpp b/common/config-file.cpp index 4c707e74cbb..f77370577ac 100644 --- a/common/config-file.cpp +++ b/common/config-file.cpp @@ -133,7 +133,7 @@ bool ConfigFile::loadFromStream(SeekableReadStream &stream) { if (*p == '\0') error("ConfigFile::loadFromStream: missing ] in line %d", lineno); else if (*p != ']') - error("ConfigFile::loadFromStream: Invalid character '%c' occured in section name in line %d", *p, lineno); + error("ConfigFile::loadFromStream: Invalid character '%c' occurred in section name in line %d", *p, lineno); // Previous section is finished now, store it. if (!section.name.empty()) diff --git a/common/error.cpp b/common/error.cpp index d6a1f0d1adf..2eaa61e6bb1 100644 --- a/common/error.cpp +++ b/common/error.cpp @@ -26,6 +26,8 @@ #include "common/error.h" #include "common/util.h" +#include "common/translation.h" + namespace Common { /** @@ -38,24 +40,24 @@ struct ErrorMessage { }; static const ErrorMessage _errMsgTable[] = { - { kInvalidPathError, "Invalid Path" }, - { kNoGameDataFoundError, "Game Data not found" }, - { kUnsupportedGameidError, "Game Id not supported" }, - { kUnsupportedColorMode, "Unsupported Color Mode" }, + { kInvalidPathError, _s("Invalid Path") }, + { kNoGameDataFoundError, _s("Game Data not found") }, + { kUnsupportedGameidError, _s("Game Id not supported") }, + { kUnsupportedColorMode, _s("Unsupported Color Mode") }, - { kReadPermissionDenied, "Read permission denied" }, - { kWritePermissionDenied, "Write permission denied" }, + { kReadPermissionDenied, _s("Read permission denied") }, + { kWritePermissionDenied, _s("Write permission denied") }, // The following three overlap a bit with kInvalidPathError and each other. Which to keep? - { kPathDoesNotExist, "Path not exists" }, - { kPathNotDirectory, "Path not a directory" }, - { kPathNotFile, "Path not a file" }, + { kPathDoesNotExist, _s("Path not exists") }, + { kPathNotDirectory, _s("Path not a directory") }, + { kPathNotFile, _s("Path not a file") }, - { kCreatingFileFailed, "Cannot create file" }, - { kReadingFailed, "Reading failed" }, - { kWritingFailed, "Writing data failed" }, + { kCreatingFileFailed, _s("Cannot create file") }, + { kReadingFailed, _s("Reading failed") }, + { kWritingFailed, _s("Writing data failed") }, - { kUnknownError, "Unknown Error" } + { kUnknownError, _s("Unknown Error") } }; const char *errorToString(Error error) { @@ -66,7 +68,7 @@ const char *errorToString(Error error) { } } - return "Unknown Error"; + return _("Unknown Error"); } } // End of namespace Common diff --git a/common/error.h b/common/error.h index fc1e5467691..08ed43bb5ac 100644 --- a/common/error.h +++ b/common/error.h @@ -44,7 +44,7 @@ namespace Common { * kPathInvalid, kPathIsInvalid, kInvalidPathError */ enum Error { - kNoError = 0, ///< No error occured + kNoError = 0, ///< No error occurred kInvalidPathError, ///< Engine initialization: Invalid game path was passed kNoGameDataFoundError, ///< Engine initialization: No game data was found in the specified location kUnsupportedGameidError, ///< Engine initialization: Gameid not supported by this (Meta)Engine @@ -59,10 +59,16 @@ enum Error { kPathNotDirectory, ///< The specified path does not point to a directory kPathNotFile, ///< The specified path does not point to a file - kCreatingFileFailed, - kReadingFailed, ///< Failed creating a (savestate) file + kCreatingFileFailed, ///< Failed creating a (savestate) file + kReadingFailed, ///< Failed to read a file (permission denied?) kWritingFailed, ///< Failure to write data -- disk full? + // The following are used by --list-saves + kPluginNotFound, ///< Failed to find plugin to handle tager + kPluginNotSupportSaves, ///< Failed if plugin does not support saves + kNoSavesError, ///< There are no saves to show + + kArgumentNotProcessed, ///< Used in command line parsing kUnknownError ///< Catch-all error, used if no other error code matches }; diff --git a/common/file.cpp b/common/file.cpp index a3c14bdd3da..2b539a6ddd6 100644 --- a/common/file.cpp +++ b/common/file.cpp @@ -51,11 +51,11 @@ bool File::open(const String &filename, Archive &archive) { SeekableReadStream *stream = 0; if ((stream = archive.createReadStreamForMember(filename))) { - debug(3, "Opening hashed: %s", filename.c_str()); + debug(8, "Opening hashed: %s", filename.c_str()); } else if ((stream = archive.createReadStreamForMember(filename + "."))) { // WORKAROUND: Bug #1458388: "SIMON1: Game Detection fails" // sometimes instead of "GAMEPC" we get "GAMEPC." (note trailing dot) - debug(3, "Opening hashed: %s.", filename.c_str()); + debug(8, "Opening hashed: %s.", filename.c_str()); } return open(stream, filename); diff --git a/common/hashmap.h b/common/hashmap.h index 79cf279cfc5..5fa3e0461d6 100644 --- a/common/hashmap.h +++ b/common/hashmap.h @@ -42,7 +42,7 @@ namespace Common { // The sgi IRIX MIPSpro Compiler has difficulties with nested templates. // This and the other __sgi conditionals below work around these problems. -#ifdef __sgi +#if defined(__sgi) && !defined(__GNUC__) template class IteratorImpl; #endif @@ -131,7 +131,7 @@ public: int lookupAndCreateIfMissing(const Key &key); void expandStorage(uint newCapacity); -#ifndef __sgi +#if !defined(__sgi) || defined(__GNUC__) template friend class IteratorImpl; #endif @@ -141,7 +141,7 @@ public: template class IteratorImpl { friend class HashMap; -#ifdef __sgi +#if defined(__sgi) && !defined(__GNUC__) template friend class Common::IteratorImpl; #else template friend class IteratorImpl; diff --git a/common/macresman.cpp b/common/macresman.cpp index f39dda4fdfe..a882dd2a1ba 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -61,16 +61,15 @@ void MacResManager::close() { _mode = kResForkNone; for (int i = 0; i < _resMap.numTypes; i++) { - for (int j = 0; j < _resTypes[i].items; j++) { - if (_resLists[i][j].nameOffset != -1) { - delete _resLists[i][j].name; - } - } - delete _resLists[i]; + for (int j = 0; j < _resTypes[i].items; j++) + if (_resLists[i][j].nameOffset != -1) + delete[] _resLists[i][j].name; + + delete[] _resLists[i]; } - delete _resLists; _resLists = 0; - delete _resTypes; _resTypes = 0; + delete[] _resLists; _resLists = 0; + delete[] _resTypes; _resTypes = 0; delete _stream; _stream = 0; } @@ -111,6 +110,7 @@ bool MacResManager::open(Common::String filename) { _baseFileName = filename; return true; } + delete macResForkRawStream; #endif Common::File *file = new Common::File(); @@ -120,26 +120,37 @@ bool MacResManager::open(Common::String filename) { _baseFileName = filename; return true; } + file->close(); // Check .bin too if (file->open(filename + ".bin") && loadFromMacBinary(*file)) { _baseFileName = filename; return true; } - + file->close(); + // Maybe we have a dumped fork? if (file->open(filename + ".rsrc") && loadFromRawFork(*file)) { _baseFileName = filename; return true; } + file->close(); // Fine, what about just the data fork? if (file->open(filename)) { _baseFileName = filename; + + if (isMacBinary(*file)) { + file->seek(0, SEEK_SET); + if (loadFromMacBinary(*file)) + return true; + } + + file->seek(0, SEEK_SET); _stream = file; return true; } - + delete file; // The file doesn't exist @@ -158,34 +169,56 @@ bool MacResManager::open(Common::FSNode path, Common::String filename) { _baseFileName = filename; return true; } + delete macResForkRawStream; #endif // First, let's try to see if the Mac converted name exists Common::FSNode fsNode = path.getChild("._" + filename); - if (fsNode.exists() && !fsNode.isDirectory() && loadFromAppleDouble(*fsNode.createReadStream())) { - _baseFileName = filename; - return true; + if (fsNode.exists() && !fsNode.isDirectory()) { + SeekableReadStream *stream = fsNode.createReadStream(); + if (loadFromAppleDouble(*stream)) { + _baseFileName = filename; + return true; + } + delete stream; } // Check .bin too fsNode = path.getChild(filename + ".bin"); - if (fsNode.exists() && !fsNode.isDirectory() && loadFromMacBinary(*fsNode.createReadStream())) { - _baseFileName = filename; - return true; + if (fsNode.exists() && !fsNode.isDirectory()) { + SeekableReadStream *stream = fsNode.createReadStream(); + if (loadFromMacBinary(*stream)) { + _baseFileName = filename; + return true; + } + delete stream; } - + // Maybe we have a dumped fork? fsNode = path.getChild(filename + ".rsrc"); - if (fsNode.exists() && !fsNode.isDirectory() && loadFromRawFork(*fsNode.createReadStream())) { - _baseFileName = filename; - return true; + if (fsNode.exists() && !fsNode.isDirectory()) { + SeekableReadStream *stream = fsNode.createReadStream(); + if (loadFromRawFork(*stream)) { + _baseFileName = filename; + return true; + } + delete stream; } // Fine, what about just the data fork? fsNode = path.getChild(filename); if (fsNode.exists() && !fsNode.isDirectory()) { + SeekableReadStream *stream = fsNode.createReadStream(); _baseFileName = filename; - _stream = fsNode.createReadStream(); + + if (isMacBinary(*stream)) { + stream->seek(0, SEEK_SET); + if (loadFromMacBinary(*stream)) + return true; + } + + stream->seek(0, SEEK_SET); + _stream = stream; return true; } @@ -218,6 +251,34 @@ bool MacResManager::loadFromAppleDouble(Common::SeekableReadStream &stream) { return false; } +bool MacResManager::isMacBinary(Common::SeekableReadStream &stream) { + byte infoHeader[MBI_INFOHDR]; + int resForkOffset = -1; + + stream.read(infoHeader, MBI_INFOHDR); + + if (infoHeader[MBI_ZERO1] == 0 && infoHeader[MBI_ZERO2] == 0 && + infoHeader[MBI_ZERO3] == 0 && infoHeader[MBI_NAMELEN] <= MAXNAMELEN) { + + // Pull out fork lengths + uint32 dataSize = READ_BE_UINT32(infoHeader + MBI_DFLEN); + uint32 rsrcSize = READ_BE_UINT32(infoHeader + MBI_RFLEN); + + uint32 dataSizePad = (((dataSize + 127) >> 7) << 7); + uint32 rsrcSizePad = (((rsrcSize + 127) >> 7) << 7); + + // Length check + if (MBI_INFOHDR + dataSizePad + rsrcSizePad == (uint32)stream.size()) { + resForkOffset = MBI_INFOHDR + dataSizePad; + } + } + + if (resForkOffset < 0) + return false; + + return true; +} + bool MacResManager::loadFromMacBinary(Common::SeekableReadStream &stream) { byte infoHeader[MBI_INFOHDR]; stream.read(infoHeader, MBI_INFOHDR); @@ -275,7 +336,7 @@ bool MacResManager::load(Common::SeekableReadStream &stream) { debug(7, "got header: data %d [%d] map %d [%d]", _dataOffset, _dataLength, _mapOffset, _mapLength); - + _stream = &stream; readMap(); @@ -378,9 +439,33 @@ Common::SeekableReadStream *MacResManager::getResource(uint32 typeID, uint16 res _stream->seek(_dataOffset + _resLists[typeNum][resNum].dataOffset); uint32 len = _stream->readUint32BE(); + + // Ignore resources with 0 length + if (!len) + return 0; + return _stream->readStream(len); } +Common::SeekableReadStream *MacResManager::getResource(const Common::String &filename) { + for (uint32 i = 0; i < _resMap.numTypes; i++) { + for (uint32 j = 0; j < _resTypes[i].items; j++) { + if (_resLists[i][j].nameOffset != -1 && filename.equalsIgnoreCase(_resLists[i][j].name)) { + _stream->seek(_dataOffset + _resLists[i][j].dataOffset); + uint32 len = _stream->readUint32BE(); + + // Ignore resources with 0 length + if (!len) + return 0; + + return _stream->readStream(len); + } + } + } + + return 0; +} + void MacResManager::readMap() { _stream->seek(_mapOffset + 22); diff --git a/common/macresman.h b/common/macresman.h index 1330922294c..29a66f6fbb9 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -45,7 +45,7 @@ class MacResManager { public: MacResManager(); ~MacResManager(); - + bool open(Common::String filename); bool open(Common::FSNode path, Common::String filename); void close(); @@ -53,6 +53,8 @@ public: bool hasDataFork(); bool hasResFork(); + static bool isMacBinary(Common::SeekableReadStream &stream); + /** * Read resource from the Mac Binary file * @param typeID FourCC with type ID @@ -61,11 +63,20 @@ public: */ Common::SeekableReadStream *getResource(uint32 typeID, uint16 resID); + /** + * Read resource from the Mac Binary file + * @param filename filename of the resource + * @return Pointer to a SeekableReadStream with loaded resource + */ + Common::SeekableReadStream *getResource(const Common::String &filename); + Common::SeekableReadStream *getDataFork(); Common::String getResName(uint32 typeID, uint16 resID); uint32 getResForkSize(); bool getResForkMD5(char *md5str, uint32 length); - + + Common::String getBaseFileName() { return _baseFileName; } + /** * Convert cursor from crsr format to format suitable for feeding to CursorMan * @param data Pointer to the cursor data @@ -114,7 +125,7 @@ private: } _mode; void readMap(); - + struct ResMap { uint16 resAttr; uint16 typeOffset; @@ -137,7 +148,7 @@ private: }; typedef Resource *ResPtr; - + int32 _resForkOffset; uint32 _resForkSize; diff --git a/common/memorypool.cpp b/common/memorypool.cpp index 8f0d12f0b69..90a47ae9f22 100644 --- a/common/memorypool.cpp +++ b/common/memorypool.cpp @@ -183,3 +183,4 @@ void MemoryPool::freeUnusedPages() { } } // End of namespace Common + diff --git a/common/module.mk b/common/module.mk index c85522c6a49..9ff2b198b94 100644 --- a/common/module.mk +++ b/common/module.mk @@ -21,6 +21,7 @@ MODULE_OBJS := \ system.o \ textconsole.o \ tokenizer.o \ + translation.o \ unzip.o \ util.o \ xmlparser.o \ diff --git a/common/savefile.h b/common/savefile.h index 21d37277d02..64ac2ea6618 100644 --- a/common/savefile.h +++ b/common/savefile.h @@ -109,14 +109,14 @@ public: /** * Open the savefile with the specified name in the given directory for saving. * @param name the name of the savefile - * @return pointer to an OutSaveFile, or NULL if an error occured. + * @return pointer to an OutSaveFile, or NULL if an error occurred. */ virtual OutSaveFile *openForSaving(const String &name) = 0; /** * Open the file with the specified name in the given directory for loading. * @param name the name of the savefile - * @return pointer to an InSaveFile, or NULL if an error occured. + * @return pointer to an InSaveFile, or NULL if an error occurred. */ virtual InSaveFile *openForLoading(const String &name) = 0; diff --git a/common/str.h b/common/str.h index 7ea93885d56..bcd3dd35ff2 100644 --- a/common/str.h +++ b/common/str.h @@ -222,6 +222,12 @@ public: typedef const char * const_iterator; iterator begin() { + // Since the user could potentially + // change the string via the returned + // iterator we have to assure we are + // pointing to a unique storage. + makeUnique(); + return _str; } diff --git a/common/stream.cpp b/common/stream.cpp index 969ee6d3e9f..18486c6b059 100644 --- a/common/stream.cpp +++ b/common/stream.cpp @@ -152,7 +152,7 @@ char *SeekableReadStream::readLine(char *buf, size_t bufSize) { len++; } - // We always terminate the buffer if no error occured + // We always terminate the buffer if no error occurred *p = 0; return buf; } @@ -303,4 +303,29 @@ bool BufferedSeekableReadStream::seek(int32 offset, int whence) { return true; // FIXME: STREAM REWRITE } +bool MemoryWriteStreamDynamic::seek(int32 offs, int whence) { + // Pre-Condition + assert(_pos <= _size); + switch (whence) { + case SEEK_END: + // SEEK_END works just like SEEK_SET, only 'reversed', + // i.e. from the end. + offs = _size + offs; + // Fall through + case SEEK_SET: + _ptr = _data + offs; + _pos = offs; + break; + + case SEEK_CUR: + _ptr += offs; + _pos += offs; + break; + } + // Post-Condition + assert(_pos <= _size); + + return true; // FIXME: STREAM REWRITE +} + } // End of namespace Common diff --git a/common/stream.h b/common/stream.h index 9dcb91ba96b..afdc201efd0 100644 --- a/common/stream.h +++ b/common/stream.h @@ -156,7 +156,7 @@ public: class ReadStream : virtual public Stream { public: /** - * Returns true if a read failed because the stream has been reached. + * Returns true if a read failed because the stream end has been reached. * This flag is cleared by clearErr(). * For a SeekableReadStream, it is also cleared by a successful seek. */ @@ -687,6 +687,8 @@ public: uint32 size() const { return _size; } byte *getData() { return _data; } + + bool seek(int32 offset, int whence = SEEK_SET); }; } // End of namespace Common diff --git a/common/system.h b/common/system.h index 95ca4f52ac1..037c847d4b8 100644 --- a/common/system.h +++ b/common/system.h @@ -421,7 +421,7 @@ public: /** * Create a new mutex. - * @return the newly created mutex, or 0 if an error occured. + * @return the newly created mutex, or 0 if an error occurred. */ virtual MutexRef createMutex() = 0; diff --git a/common/textconsole.cpp b/common/textconsole.cpp index ba1e3bdb546..6882c21eb1a 100644 --- a/common/textconsole.cpp +++ b/common/textconsole.cpp @@ -43,6 +43,10 @@ extern bool isSmartphone(); #define fputs(str, file) DS::std_fwrite(str, strlen(str), 1, file) #endif +#ifdef ANDROID + #include +#endif + namespace Common { static OutputFormatter s_errorOutputFormatter = 0; @@ -71,7 +75,9 @@ void warning(const char *s, ...) { vsnprintf(buf, STRINGBUFLEN, s, va); va_end(va); -#if !defined (__SYMBIAN32__) +#if defined( ANDROID ) + __android_log_write(ANDROID_LOG_WARN, "ScummVM", buf); +#elif !defined (__SYMBIAN32__) fputs("WARNING: ", stderr); fputs(buf, stderr); fputs("!\n", stderr); @@ -141,6 +147,10 @@ void NORETURN_PRE error(const char *s, ...) { #endif #endif +#ifdef ANDROID + __android_log_assert("Fatal error", "ScummVM", "%s", buf_output); +#endif + #ifdef PALMOS_MODE extern void PalmFatalError(const char *err); PalmFatalError(buf_output); diff --git a/common/translation.cpp b/common/translation.cpp new file mode 100644 index 00000000000..07cde838a91 --- /dev/null +++ b/common/translation.cpp @@ -0,0 +1,265 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "translation.h" + +DECLARE_SINGLETON(Common::TranslationManager) + +#ifdef USE_DETECTLANG +#include +#endif + +#ifdef USE_TERMCONV +#include +#endif + +#ifdef USE_TRANSLATION +#include "messages.cpp" +#endif + +namespace Common { + + +#ifdef USE_TRANSLATION + +// Translation enabled + + +TranslationManager::TranslationManager() { +#ifdef USE_DETECTLANG + // Activating current locale settings + const char *locale = setlocale(LC_ALL, ""); + + // Detect the language from the locale + if (!locale) { + _syslang = "C"; + } else { + int length = 0; + + // Strip out additional information, like + // ".UTF-8" or the like. We do this, since + // our translation languages are usually + // specified without any charset information. + for (int i = 0; locale[i]; ++i) { + // TODO: Check whether "@" should really be checked + // here. + if (locale[i] == '.' || locale[i] == ' ' || locale[i] == '@') { + length = i; + break; + } + + length = i; + } + + _syslang = String(locale, length); + } +#else // USE_DETECTLANG + _syslang = "C"; +#endif // USE_DETECTLANG + +#ifdef USE_TERMCONV + _convmsg = NULL; + _conversion = NULL; +#endif // USE_TERMCONV + + // Set the default language + setLanguage(""); +} + +TranslationManager::~TranslationManager() { +#ifdef USE_TERMCONV + iconv_close(_conversion); + if (_convmsg) + delete[] _convmsg; +#endif // USE_TERMCONV +} + +void TranslationManager::setLanguage(const char *lang) { + if (*lang == '\0') + po2c_setlang(_syslang.c_str()); + else + po2c_setlang(lang); + +#ifdef USE_TERMCONV + // Get the locale character set (for terminal output) + const char *charset_term = nl_langinfo(CODESET); + + // Get the messages character set + const char *charset_po = po2c_getcharset(); + + // Delete previous conversion + if (_conversion) + iconv_close(_conversion); + + // Initialize the conversion + _conversion = iconv_open(charset_term, charset_po); +#endif // USE_TERMCONV +} + +const char *TranslationManager::getTranslation(const char *message) { + return po2c_gettext(message); +} + +String TranslationManager::getTranslation(const String &message) { + return po2c_gettext(message.c_str()); +} + +#ifdef USE_TERMCONV +bool TranslationManager::convert(const char *message) { + // Preparing conversion origin + size_t len = strlen(message) + 1; +#ifdef ICONV_USES_CONST + const char **pmsg = &message; +#else + char *msgcpy = new char[len]; + strcpy(msgcpy, message); + char *msg = msgcpy; + char **pmsg = &msg; +#endif + + // Preparing conversion destination + size_t len2 = _sizeconv; + char *conv = _convmsg; + + // Clean previous conversions + iconv(_conversion, NULL, NULL, &conv, &len2); + + // Do the real conversion + size_t result = iconv(_conversion, pmsg, &len, &conv, &len2); + +#ifndef ICONV_USES_CONST + delete[] msgcpy; +#endif + + return result != ((size_t)-1); +} +#endif // USE_TERMCONV + +const char *TranslationManager::convertTerm(const char *message) { +#ifdef USE_TERMCONV + size_t len = strlen(message) + 1; + if (!_convmsg) { + _sizeconv = len * 2; + _convmsg = new char[_sizeconv]; + } + + if (!convert(message)) { + // Resizing the buffer + delete[] _convmsg; + _sizeconv = len * 2; + _convmsg = new char[_sizeconv]; + + if (!convert(message)) { + printf("Error while converting character sets\n"); + return "Error while converting character sets"; + } + } + + return _convmsg; +#else // USE_TERMCONV + return message; +#endif // USE_TERMCONV +} + +const TLangArray TranslationManager::getSupportedLanguages() const { + TLangArray languages; + + int total = po2c_getnumlangs(); + for (int i = 0; i < total; i++) { + TLanguage lng(po2c_getlang(i), i + 1); + languages.push_back(lng); + } + + //sort(languages.begin(), languages.end()); + + return languages; +} + +int TranslationManager::parseLanguage(const String lang) { + int total = po2c_getnumlangs(); + + for (int i = 0; i < total; i++) { + if (lang == po2c_getlang(i)) + return i + 1; + } + + return kTranslationBuiltinId; +} + + +const char *TranslationManager::getLangById(int id) { + switch (id) { + case kTranslationAutodetectId: + return ""; + case kTranslationBuiltinId: + return "C"; + default: + if (id >= 0 && id - 1 < po2c_getnumlangs()) + return po2c_getlang(id - 1); + } + + // In case an invalid ID was specified, we will output a warning + // and return the same value as the auto detection id. + warning("Invalid language id %d passed to TranslationManager::getLangById", id); + return ""; +} + +#else // USE_TRANSLATION + +// Translation disabled + + +TranslationManager::TranslationManager() {} + +TranslationManager::~TranslationManager() {} + +void TranslationManager::setLanguage(const char *lang) {} + +const char *TranslationManager::getLangById(int id) { + return ""; +} + +int TranslationManager::parseLanguage(const String lang) { + return kTranslationBuiltinId; +} + +const char *TranslationManager::getTranslation(const char *message) { + return message; +} + +String TranslationManager::getTranslation(const String &message) { + return message; +} + +const char *TranslationManager::convertTerm(const char *message) { + return message; +} + +const TLangArray TranslationManager::getSupportedLanguages() const { + return TLangArray(); +} + +#endif // USE_TRANSLATION + +} // End of namespace Common diff --git a/common/translation.h b/common/translation.h new file mode 100644 index 00000000000..6e1666e7ff3 --- /dev/null +++ b/common/translation.h @@ -0,0 +1,160 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef COMMON_TRANSLATION_H +#define COMMON_TRANSLATION_H + +#include "common/singleton.h" +#include "common/str-array.h" + +#ifdef USE_TERMCONV +#include +#endif + +namespace Common { + +enum TranslationIDs { + kTranslationAutodetectId = 0, + kTranslationBuiltinId = 1000 +}; + +struct TLanguage { + const char *name; + int id; + + TLanguage() { + name = 0; + id = 0; + } + + TLanguage(const char *n, int i) { + name = n; + id = i; + } +}; + +typedef Array TLangArray; + +/** + * Message translation manager. + */ +class TranslationManager : public Singleton { +private: + Common::String _syslang; + +#ifdef USE_TERMCONV + iconv_t _conversion; + char *_convmsg; + int _sizeconv; + + bool convert(const char *message); +#endif // USE_TERMCONV + +public: + /** + * The constructor detects the system language and sets default + * language to English. + */ + TranslationManager(); + ~TranslationManager(); + + /** + * Retrieves the language string to the given id. + * + * @param id Id of the language + * @return the matching string description of the language + */ + const char *getLangById(int id); + + /** + * Sets the current translation language to the one specified in the + * parameter. If the parameter is an empty string, it sets the default + * system language. + * + * @param lang Language to setup. + */ + void setLanguage(const char *lang); + + /** + * Sets the current translation language to the one specified by the + * id parameter. + * + * @param id The id of the language. + */ + void setLanguage(int id) { + setLanguage(getLangById(id)); + } + + /** + * Parses a language string and returns an id instead. + * + * @param lang Language string + * @return id of the language or kTranslationBuiltinId in case the + * language could not be found. + */ + int parseLanguage(const String lang); + + /** + * Returns the translation into the current language of the parameter + * message. In case the message isn't found in the translation catalog, + * it returns the original untranslated message. + */ + const char *getTranslation(const char *message); + + /** + * Returns the translation into the current language of the parameter + * message. In case the message isn't found in the translation catalog, + * it returns the original untranslated message. + */ + String getTranslation(const String &message); + + /** + * Converts the message into the terminal character set (which may be + * different than the GUI's "native" one). + */ + const char *convertTerm(const char *message); + + /** + * Returns a list of supported languages. + * + * @return The list of supported languages. + */ + const TLangArray getSupportedLanguages() const; +}; + +} // End of namespace Common + +#define TransMan Common::TranslationManager::instance() + +#ifdef USE_TRANSLATION +#define _(str) TransMan.getTranslation(str) +#define _t(str) TransMan.convertTerm(_(str)) +#else +#define _(str) str +#define _t(str) str +#endif + +#define _s(str) str + +#endif diff --git a/common/unzip.cpp b/common/unzip.cpp index 297e0e9cb60..278fe5736d4 100644 --- a/common/unzip.cpp +++ b/common/unzip.cpp @@ -1433,11 +1433,11 @@ Common::SeekableReadStream *ZipArchive::createReadStreamForMember(const Common:: unz_file_info fileInfo; unzOpenCurrentFile(_zipFile); unzGetCurrentFileInfo(_zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0); - byte *buffer = (byte *)calloc(fileInfo.uncompressed_size+1, 1); + byte *buffer = (byte *)malloc(fileInfo.uncompressed_size); assert(buffer); unzReadCurrentFile(_zipFile, buffer, fileInfo.uncompressed_size); unzCloseCurrentFile(_zipFile); - return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size+1, DisposeAfterUse::YES); + return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size, DisposeAfterUse::YES); // FIXME: instead of reading all into a memory stream, we could // instead create a new ZipStream class. But then we have to be diff --git a/common/util.cpp b/common/util.cpp index fe03114c361..5fa6a33bd8a 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -293,12 +293,20 @@ const struct GameOpt { uint32 option; const char *desc; } g_gameOptions[] = { - { GUIO_NOSUBTITLES, "sndNoSubs" }, - { GUIO_NOMUSIC, "sndNoMusic" }, - { GUIO_NOSPEECH, "sndNoSpeech" }, - { GUIO_NOSFX, "sndNoSFX" }, - { GUIO_NOMIDI, "sndNoMIDI" }, + { GUIO_NOSUBTITLES, "sndNoSubs" }, + { GUIO_NOMUSIC, "sndNoMusic" }, + { GUIO_NOSPEECH, "sndNoSpeech" }, + { GUIO_NOSFX, "sndNoSFX" }, + { GUIO_NOMIDI, "sndNoMIDI" }, { GUIO_NOLAUNCHLOAD, "launchNoLoad" }, + + { GUIO_MIDIPCSPK, "midiPCSpk" }, + { GUIO_MIDICMS, "midiCMS" }, + { GUIO_MIDIPCJR, "midiPCJr" }, + { GUIO_MIDIADLIB, "midiAdLib" }, + { GUIO_MIDITOWNS, "midiTowns" }, + { GUIO_MIDI, "midiMidi" }, + { GUIO_NONE, 0 } }; @@ -314,9 +322,26 @@ bool checkGameGUIOption(GameGUIOption option, const String &str) { return false; } +bool checkGameGUIOptionLanguage(Language lang, const String &str) { + if (!str.contains("lang_")) // If no languages are specified + return true; + + if (str.contains(getGameGUIOptionsDescriptionLanguage(lang))) + return true; + + return false; +} + +const String getGameGUIOptionsDescriptionLanguage(Language lang) { + if (lang == UNK_LANG) + return ""; + + return String(String("lang_") + getLanguageDescription(lang)); +} + uint32 parseGameGUIOptions(const String &str) { uint32 res = 0; - + for (int i = 0; g_gameOptions[i].desc; i++) if (str.contains(g_gameOptions[i].desc)) res |= g_gameOptions[i].option; @@ -324,7 +349,7 @@ uint32 parseGameGUIOptions(const String &str) { return res; } -String getGameGUIOptionsDescription(uint32 options) { +const String getGameGUIOptionsDescription(uint32 options) { String res = ""; for (int i = 0; g_gameOptions[i].desc; i++) diff --git a/common/util.h b/common/util.h index 01a19fafb14..3dab87929db 100644 --- a/common/util.h +++ b/common/util.h @@ -218,14 +218,23 @@ enum GameGUIOption { GUIO_NOSUBTITLES = (1 << 0), GUIO_NOMUSIC = (1 << 1), GUIO_NOSPEECH = (1 << 2), - GUIO_NOSFX = (1 << 3), - GUIO_NOMIDI = (1 << 4), - GUIO_NOLAUNCHLOAD = (1 << 5) + GUIO_NOSFX = (1 << 3), + GUIO_NOMIDI = (1 << 4), + GUIO_NOLAUNCHLOAD = (1 << 5), + + GUIO_MIDIPCSPK = (1 << 6), + GUIO_MIDICMS = (1 << 7), + GUIO_MIDIPCJR = (1 << 8), + GUIO_MIDIADLIB = (1 << 9), + GUIO_MIDITOWNS = (1 << 10), + GUIO_MIDI = (1 << 11) }; bool checkGameGUIOption(GameGUIOption option, const String &str); +bool checkGameGUIOptionLanguage(Language lang, const String &str); uint32 parseGameGUIOptions(const String &str); -String getGameGUIOptionsDescription(uint32 options); +const String getGameGUIOptionsDescription(uint32 options); +const String getGameGUIOptionsDescriptionLanguage(Language lang); /** * Updates the GUI options of the current config manager diff --git a/configure b/configure index e1525aa8b47..8b8cec2c458 100755 --- a/configure +++ b/configure @@ -98,6 +98,8 @@ _enable_prof=no # Default vkeybd/keymapper options _vkeybd=no _keymapper=no +# GUI translation options +_translation=yes # Default platform settings _backend=sdl _endian=unknown @@ -122,7 +124,7 @@ _nasmpath="$PATH" NASMFLAGS="" NASM="" -# Directories for installing Reesidual. +# Directories for installing Residual. # This list is closely based on what GNU autoconf does, # although the default value for datadir differs. # Like GNU autoconf, we distinguish datadir and datarootdir @@ -606,6 +608,7 @@ Optional Features: --enable-plugins enable the support for dynamic plugins --default-dynamic make plugins dynamic by default --disable-mt32emu don't enable the integrated MT-32 emulator + --disable-translation don't build support for translated messages --enable-text-console use text console instead of graphical console --enable-verbose-build enable regular echoing of commands during build process @@ -675,6 +678,8 @@ for ac_option in $@; do --default-dynamic) _plugins_default=dynamic ;; --enable-mt32emu) _mt32emu=yes ;; --disable-mt32emu) _mt32emu=no ;; + --enable-translation) _translation=yes ;; + --disable-translation) _translation=no ;; --enable-vkeybd) _vkeybd=yes ;; --disable-vkeybd) _vkeybd=no ;; --enable-keymapper) _keymapper=yes ;; @@ -909,6 +914,11 @@ wince) _host_cpu=arm _host_alias=arm-wince-mingw32ce ;; +android) + _host_os=android + _host_cpu=arm + _host_alias=arm-android-eabi + ;; *) if test -n "$_host"; then guessed_host=`$_srcdir/config.sub $_host` @@ -987,6 +997,12 @@ psp) exit 1 fi ;; +android) + if test -z "$ANDROID_SDK"; then + echo "Please set ANDROID_SDK in your environment. export ANDROID_SDK=" + exit 1 + fi + ;; *) ;; esac @@ -1290,7 +1306,8 @@ case $_host_os in CXXFLAGS="$CXXFLAGS -Os -mogc -mcpu=750 -meabi -mhard-float" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fmodulo-sched" CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include -I$DEVKITPRO/cube/include" - LDFLAGS="$LDFLAGS -mogc -mcpu=750 -L$DEVKITPRO/libogc/lib/cube -L$DEVKITPRO/cube/lib" + # libogc is required to link the cc tests (includes _start()) + LDFLAGS="$LDFLAGS -mogc -mcpu=750 -L$DEVKITPRO/libogc/lib/cube -L$DEVKITPRO/cube/lib -logc" ;; haiku*) DEFINES="$DEFINES -DUNIX -DSYSTEM_NOT_SUPPORTING_D_TYPE" @@ -1344,13 +1361,19 @@ case $_host_os in CXXFLAGS="$CXXFLAGS -Os -mrvl -mcpu=750 -meabi -mhard-float" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fmodulo-sched" CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include -I$DEVKITPRO/wii/include" - LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -L$DEVKITPRO/libogc/lib/wii -L$DEVKITPRO/wii/lib" + # libogc is required to link the cc tests (includes _start()) + LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -L$DEVKITPRO/libogc/lib/wii -L$DEVKITPRO/wii/lib -logc" ;; wince) CXXFLAGS="$CXXFLAGS -O3 -march=armv4 -mtune=xscale" DEFINES="$DEFINES -D_WIN32_WCE=300 -D__ARM__ -D_ARM_ -DUNICODE -DFPM_DEFAULT -DNONSTANDARD_PORT" DEFINES="$DEFINES -DWIN32 -Dcdecl= -D__cdecl__=" ;; + android) + DEFINES="$DEFINES -DUNIX" + CXXFLAGS="$CXXFLAGS -Os -msoft-float -mtune=xscale -march=armv5te -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__" + add_line_to_config_mk "ANDROID_SDK = $ANDROID_SDK" + ;; # given this is a shell script assume some type of unix *) echo "WARNING: could not establish system type, assuming unix like" @@ -1367,6 +1390,7 @@ if test -n "$_host"; then _need_memalign=yes add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' ;; arm-riscos|linupy) DEFINES="$DEFINES -DUNIX -DLINUPY" @@ -1416,6 +1440,7 @@ if test -n "$_host"; then _build_hq_scalers="no" _mt32emu="no" _vkeybd="yes" + _port_mk="backends/platform/gp2x/gp2x-bundle.mk" ;; gp2xwiz) DEFINES="$DEFINES -DUNIX -DGP2XWIZ -DNDEBUG" @@ -1429,6 +1454,7 @@ if test -n "$_host"; then _build_hq_scalers="no" _mt32emu="no" _vkeybd="yes" + _port_mk="backends/platform/gp2xwiz/gp2xwiz-bundle.mk" ;; iphone) DEFINES="$DEFINES -DIPHONE -DUNIX" @@ -1451,6 +1477,10 @@ if test -n "$_host"; then _ar="$_host-ar cru" _ranlib=$_host-ranlib ;; + mips-sgi*) + _endian=big + _need_memalign=yes + ;; motoezx) DEFINES="$DEFINES -DUNIX -DMOTOEZX" ASFLAGS="$ASFLAGS -mfpu=vfp" @@ -1492,6 +1522,7 @@ if test -n "$_host"; then # TODO: Enable more ARM optimizations -- requires testing! add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + #add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' ;; neuros) DEFINES="$DEFINES -DUNIX -DNEUROS" @@ -1577,6 +1608,16 @@ if test -n "$_host"; then _mt32emu="no" _port_mk="backends/platform/wince/wince.mk" ;; + android) + DEFINES="$DEFINES -DANDROID -DUNIX -DUSE_ARM_SMUSH_ASM" + _need_memalign=yes + add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' + _backend="android" + _port_mk="backends/platform/android/android.mk" + _build_hq_scalers="no" + ;; *) echo "WARNING: Unknown target, continuing with auto-detected values" ;; @@ -1738,7 +1779,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive LIBS += -ldl ' ;; - linux*) + linux*|android) _def_plugin=' #define PLUGIN_PREFIX "lib" #define PLUGIN_SUFFIX ".so" @@ -2106,6 +2147,60 @@ if test "$_keymapper" = yes ; then DEFINES="$DEFINES -DENABLE_KEYMAPPER" fi +# Check whether to build translation support +# +echo_n "Building translation support... " +add_to_config_mk_if_yes $_translation 'USE_TRANSLATION = 1' +add_to_config_h_if_yes $_translation '#define USE_TRANSLATION' +if test "$_translation" = no ; then + echo "no" +else + echo_n "yes (" + + cat > $TMPC << EOF +#include +int main(void) { setlocale(LC_ALL, ""); return 0; } +EOF + _detectlang=no + cc_check $LDFLAGS $CXXFLAGS && _detectlang=yes + + add_to_config_h_if_yes $_detectlang '#define USE_DETECTLANG' + if test "$_detectlang" = yes ; then + echo_n "with runtime language detection, " + + cat > $TMPC << EOF +#include +#include +int main(void) { nl_langinfo(CODESET); iconv_open(0, 0); return 0; } +EOF + _termconv=no + cc_check_no_clean $LDFLAGS $CXXFLAGS && _termconv=yes + cc_check $LDFLAGS $CXXFLAGS -liconv && LIBS="$LIBS -liconv" && _termconv=yes + + add_to_config_mk_if_yes $_termconv 'USE_TERMCONV = 1' + add_to_config_h_if_yes $_termconv '#define USE_TERMCONV' + if test "$_termconv" = yes ; then + uses_const=no + cat > $TMPC << EOF +#include +int main(int argc, char **argv) { + iconv_t iconvP; + const char **inbuf = 0; + iconv(iconvP, inbuf, 0, 0, 0); + return 0; +} +EOF + cc_check $LDFLAGS $LIBS $CXXFLAGS && uses_const=yes + add_to_config_h_if_yes $uses_const '#define ICONV_USES_CONST' + echo "with terminal conversion)" + else + echo "without terminal conversion)" + fi + else + echo "without runtime language detection)" + fi +fi + # # Figure out installation directories # @@ -2199,14 +2294,7 @@ case $_backend in ;; psp) DEFINES="$DEFINES -D__PSP__ -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DDISABLE_DOSBOX_OPL" - INCLUDES="$INCLUDES -I$PSPDEV/psp/include/SDL" - LIBS="$LIBS -lpng -lSDL -Wl,-Map,mapfile.txt" - SDLLIBS=`$PSPDEV/psp/bin/sdl-config --libs` - - if `echo "$SDLLIBS" | grep ".*-lGL.*" 1>/dev/null 2>&1` - then - LIBS="$LIBS -lGL" - fi + LIBS="$LIBS -lpng -Wl,-Map,mapfile.txt" ;; samsungtv) find_sdlconfig @@ -2237,6 +2325,14 @@ case $_backend in INCLUDES="$INCLUDES "'-I$(srcdir) -I$(srcdir)/backends/platform/wince -I$(srcdir)/engines -I$(srcdir)/backends/platform/wince/missing/gcc -I$(srcdir)/backends/platform/wince/CEgui -I$(srcdir)/backends/platform/wince/CEkeys' LIBS="$LIBS -static -lSDL" ;; + android) + # -lgcc is carefully placed here - we want to catch + # all toolchain symbols in *our* libraries rather + # than pick up anything unhygenic from the Android libs. + LIBS="$LIBS -lgcc -lstdc++ -llog -lGLESv1_CM -lEGL" + DEFINES="$DEFINES -D__ANDROID__ -DANDROID_BACKEND -DREDUCE_MEMORY_USAGE" + add_line_to_config_mk 'PLUGIN_LDFLAGS += $(LDFLAGS) -Wl,-shared,-Bsymbolic' + ;; *) echo "support for $_backend backend not implemented in configure script yet" exit 1 @@ -2252,7 +2348,7 @@ if test "$have_gcc" = yes ; then case $_host_os in # newlib-based system include files suppress non-C89 function # declarations under __STRICT_ANSI__ - mingw* | dreamcast | wii | gamecube | psp | wince | amigaos*) + mingw* | dreamcast | wii | gamecube | psp | wince | amigaos* | android) CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter" ;; *) @@ -2273,7 +2369,7 @@ fi; # Some platforms use certain GNU extensions in header files case $_host_os in -gamecube | psp | wii) +gamecube | psp | wii | android) ;; *) CXXFLAGS="$CXXFLAGS -pedantic" diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index 8bd4145043c..c4961747c23 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -208,6 +208,7 @@ static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription * desc["extra"] = realDesc->extra; desc.setGUIOptions(realDesc->guioptions | params.guioptions); + desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language)); } GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const { @@ -340,6 +341,48 @@ static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSiz static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParams ¶ms); +static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, int depth, const char **directoryGlobs) { + if (depth <= 0) + return; + + if (fslist.empty()) + return; + + // First we compose a hashmap of all files in fslist. + // Includes nifty stuff like removing trailing dots and ignoring case. + for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { + if (file->isDirectory()) { + Common::FSList files; + + if (!directoryGlobs) + continue; + + bool matched = false; + for (const char *glob = *directoryGlobs; *glob; glob++) + if (file->getName().matchString(glob, true)) { + matched = true; + break; + } + + if (!matched) + continue; + + if (!file->getChildren(files, Common::FSNode::kListAll)) + continue; + + composeFileHashMap(files, allFiles, depth - 1, directoryGlobs); + } + + Common::String tstr = file->getName(); + + // Strip any trailing dot + if (tstr.lastChar() == '.') + tstr.deleteLastChar(); + + allFiles[tstr] = *file; // Record the presence of this file + } +} + static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams ¶ms, Common::Language language, Common::Platform platform, const Common::String &extra) { FileMap allFiles; SizeMD5Map filesSizeMD5; @@ -355,18 +398,7 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p // First we compose a hashmap of all files in fslist. // Includes nifty stuff like removing trailing dots and ignoring case. - for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { - if (file->isDirectory()) - continue; - - Common::String tstr = file->getName(); - - // Strip any trailing dot - if (tstr.lastChar() == '.') - tstr.deleteLastChar(); - - allFiles[tstr] = *file; // Record the presence of this file - } + composeFileHashMap(fslist, allFiles, (params.depth == 0 ? 1 : params.depth), params.directoryGlobs); // Check which files are included in some ADGameDescription *and* present // in fslist. Compute MD5s and file sizes for these files. diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 9cab961431d..84f42467108 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -190,6 +190,21 @@ struct ADParams { * enum for the list. */ uint32 guioptions; + + /** + * Maximum depth of directories to look up + * If set to 0, the depth is 1 level + */ + uint32 depth; + + /** + * Case-insensitive list of directory globs which could be used for + * going deeper int directory structure. + * @see String::matchString() method for format description. + * + * @note Last item must be 0 + */ + const char **directoryGlobs; }; diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp index aeb3c4ef034..6f206b9e679 100644 --- a/engines/dialogs.cpp +++ b/engines/dialogs.cpp @@ -28,6 +28,7 @@ #include "common/savefile.h" #include "common/system.h" #include "common/events.h" +#include "common/translation.h" #include "graphics/scaler.h" @@ -35,8 +36,9 @@ #include "gui/GuiManager.h" #include "gui/launcher.h" #include "gui/ListWidget.h" -#include "gui/ThemeEval.h" +#include "gui/options.h" #include "gui/saveload.h" +#include "gui/ThemeEval.h" #include "engines/dialogs.h" #include "engines/engine.h" @@ -49,16 +51,17 @@ using GUI::CommandSender; using GUI::StaticTextWidget; -enum { - kSaveCmd = 'SAVE', - kLoadCmd = 'LOAD', - kPlayCmd = 'PLAY', - kOptionsCmd = 'OPTN', - kHelpCmd = 'HELP', - kAboutCmd = 'ABOU', - kQuitCmd = 'QUIT', - kRTLCmd = 'RTL ', - kChooseCmd = 'CHOS' +class ConfigDialog : public GUI::OptionsDialog { +protected: +#ifdef SMALL_SCREEN_DEVICE + GUI::Dialog *_keysDialog; +#endif + +public: + ConfigDialog(bool subtitleControls); + ~ConfigDialog(); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); }; MainMenuDialog::MainMenuDialog(Engine *engine) @@ -72,42 +75,48 @@ MainMenuDialog::MainMenuDialog(Engine *engine) _logo->useThemeTransparency(true); _logo->setGfx(g_gui.theme()->getImageSurface(GUI::ThemeEngine::kImageLogoSmall)); } else { - StaticTextWidget *title = new StaticTextWidget(this, "GlobalMenu.Title", "Residual"); + StaticTextWidget *title = new StaticTextWidget(this, "GlobalMenu.Title", "ScummVM"); title->setAlign(Graphics::kTextAlignCenter); } #else - StaticTextWidget *title = new StaticTextWidget(this, "GlobalMenu.Title", "Residual"); + StaticTextWidget *title = new StaticTextWidget(this, "GlobalMenu.Title", "ScummVM"); title->setAlign(Graphics::kTextAlignCenter); #endif StaticTextWidget *version = new StaticTextWidget(this, "GlobalMenu.Version", gResidualVersionDate); version->setAlign(Graphics::kTextAlignCenter); - new GUI::ButtonWidget(this, "GlobalMenu.Resume", "Resume", kPlayCmd, 'P'); + new GUI::ButtonWidget(this, "GlobalMenu.Resume", _("~R~esume"), 0, kPlayCmd, 'P'); - _loadButton = new GUI::ButtonWidget(this, "GlobalMenu.Load", "Load", kLoadCmd, 'L'); + _loadButton = new GUI::ButtonWidget(this, "GlobalMenu.Load", _("~L~oad"), 0, kLoadCmd); // TODO: setEnabled -> setVisible _loadButton->setEnabled(_engine->hasFeature(Engine::kSupportsLoadingDuringRuntime)); - _saveButton = new GUI::ButtonWidget(this, "GlobalMenu.Save", "Save", kSaveCmd, 'S'); + _saveButton = new GUI::ButtonWidget(this, "GlobalMenu.Save", _("~S~ave"), 0, kSaveCmd); // TODO: setEnabled -> setVisible _saveButton->setEnabled(_engine->hasFeature(Engine::kSupportsSavingDuringRuntime)); - new GUI::ButtonWidget(this, "GlobalMenu.Options", "Options", kOptionsCmd, 'O'); + new GUI::ButtonWidget(this, "GlobalMenu.Options", _("~O~ptions"), 0, kOptionsCmd); - new GUI::ButtonWidget(this, "GlobalMenu.About", "About", kAboutCmd, 'A'); + // The help button is disabled by default. + // To enable "Help", an engine needs to use a subclass of MainMenuDialog + // (at least for now, we might change how this works in the future). + _helpButton = new GUI::ButtonWidget(this, "GlobalMenu.Help", _("~H~elp"), 0, kHelpCmd); + _helpButton->setEnabled(false); - _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", "Return to Launcher", kRTLCmd, 'R'); + new GUI::ButtonWidget(this, "GlobalMenu.About", _("~A~bout"), 0, kAboutCmd); + + _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", _("~R~eturn to Launcher"), 0, kRTLCmd); _rtlButton->setEnabled(_engine->hasFeature(Engine::kSupportsRTL)); - new GUI::ButtonWidget(this, "GlobalMenu.Quit", "Quit", kQuitCmd, 'Q'); + new GUI::ButtonWidget(this, "GlobalMenu.Quit", _("~Q~uit"), 0, kQuitCmd); _aboutDialog = new GUI::AboutDialog(); _optionsDialog = new ConfigDialog(_engine->hasFeature(Engine::kSupportsSubtitleOptions)); - _loadDialog = new GUI::SaveLoadChooser("Load game:", "Load"); + _loadDialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load")); _loadDialog->setSaveMode(false); - _saveDialog = new GUI::SaveLoadChooser("Save game:", "Save"); + _saveDialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save")); _saveDialog->setSaveMode(true); } @@ -135,6 +144,9 @@ void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat case kAboutCmd: _aboutDialog->runModal(); break; + case kHelpCmd: + // Not handled here -- needs to be handled by a subclass (for now) + break; case kRTLCmd: { Common::Event eventRTL; eventRTL.type = Common::EVENT_RTL; diff --git a/engines/dialogs.h b/engines/dialogs.h index 05eb5617ec4..578898fff34 100644 --- a/engines/dialogs.h +++ b/engines/dialogs.h @@ -27,7 +27,6 @@ #include "common/str.h" #include "gui/dialog.h" -#include "gui/options.h" class Engine; @@ -38,6 +37,19 @@ namespace GUI { } class MainMenuDialog : public GUI::Dialog { +public: + enum { + kSaveCmd = 'SAVE', + kLoadCmd = 'LOAD', + kPlayCmd = 'PLAY', + kOptionsCmd = 'OPTN', + kHelpCmd = 'HELP', + kAboutCmd = 'ABOU', + kQuitCmd = 'QUIT', + kRTLCmd = 'RTL ', + kChooseCmd = 'CHOS' + }; + public: MainMenuDialog(Engine *engine); ~MainMenuDialog(); @@ -51,29 +63,20 @@ protected: void load(); protected: - Engine *_engine; + Engine *_engine; - GUI::GraphicsWidget *_logo; - GUI::ButtonWidget *_rtlButton; - GUI::ButtonWidget *_loadButton; - GUI::ButtonWidget *_saveButton; - GUI::Dialog *_aboutDialog; - GUI::Dialog *_optionsDialog; - GUI::SaveLoadChooser *_loadDialog; - GUI::SaveLoadChooser *_saveDialog; -}; + GUI::GraphicsWidget *_logo; -class ConfigDialog : public GUI::OptionsDialog { -protected: -#ifdef SMALL_SCREEN_DEVICE - GUI::Dialog *_keysDialog; -#endif + GUI::ButtonWidget *_rtlButton; + GUI::ButtonWidget *_loadButton; + GUI::ButtonWidget *_saveButton; + GUI::ButtonWidget *_helpButton; -public: - ConfigDialog(bool subtitleControls); - ~ConfigDialog(); + GUI::Dialog *_aboutDialog; + GUI::Dialog *_optionsDialog; - virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + GUI::SaveLoadChooser *_loadDialog; + GUI::SaveLoadChooser *_saveDialog; }; #endif diff --git a/engines/game.cpp b/engines/game.cpp index 9d9219b096f..48d63b5b88b 100644 --- a/engines/game.cpp +++ b/engines/game.cpp @@ -73,6 +73,10 @@ void GameDescriptor::setGUIOptions(uint32 guioptions) { erase("guioptions"); } +void GameDescriptor::appendGUIOptions(const Common::String &str) { + setVal("guioptions", getVal("guioptions", "") + " " + str); +} + void GameDescriptor::updateDesc(const char *extra) { // TODO: The format used here (LANG/PLATFORM/EXTRA) is not set in stone. // We may want to change the order (PLATFORM/EXTRA/LANG, anybody?), or diff --git a/engines/game.h b/engines/game.h index 336fefb9bb1..d3f64adba02 100644 --- a/engines/game.h +++ b/engines/game.h @@ -83,6 +83,7 @@ public: void updateDesc(const char *extra = 0); void setGUIOptions(uint32 options); + void appendGUIOptions(const Common::String &str); Common::String &gameid() { return getVal("gameid"); } Common::String &description() { return getVal("description"); } diff --git a/engines/savestate.cpp b/engines/savestate.cpp index 0c9c29ff8dd..60decfd6ec7 100644 --- a/engines/savestate.cpp +++ b/engines/savestate.cpp @@ -8,18 +8,19 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ + * */ #include "engines/savestate.h" diff --git a/engines/savestate.h b/engines/savestate.h index 19f2ad64eb4..8272d32d55b 100644 --- a/engines/savestate.h +++ b/engines/savestate.h @@ -8,18 +8,19 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ + * */ #ifndef ENGINES_SAVESTATE_H diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index f3bb1d62e72..0b221fdaf7d 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -20,6 +20,7 @@ * * $URL$ * $Id$ + * */ #include "common/util.h" @@ -144,14 +145,14 @@ inline frac_t fp_sqroot(uint32 x) { /** HELPER MACROS for WU's circle drawing algorithm **/ #define __WU_DRAWCIRCLE(ptr1,ptr2,ptr3,ptr4,x,y,px,py,a) { \ - blendPixelPtr(ptr1 + (y) - (px), color, a); \ - blendPixelPtr(ptr1 + (x) - (py), color, a); \ - blendPixelPtr(ptr2 - (x) - (py), color, a); \ - blendPixelPtr(ptr2 - (y) - (px), color, a); \ - blendPixelPtr(ptr3 - (y) + (px), color, a); \ - blendPixelPtr(ptr3 - (x) + (py), color, a); \ - blendPixelPtr(ptr4 + (x) + (py), color, a); \ - blendPixelPtr(ptr4 + (y) + (px), color, a); \ + this->blendPixelPtr(ptr1 + (y) - (px), color, a); \ + this->blendPixelPtr(ptr1 + (x) - (py), color, a); \ + this->blendPixelPtr(ptr2 - (x) - (py), color, a); \ + this->blendPixelPtr(ptr2 - (y) - (px), color, a); \ + this->blendPixelPtr(ptr3 - (y) + (px), color, a); \ + this->blendPixelPtr(ptr3 - (x) + (py), color, a); \ + this->blendPixelPtr(ptr4 + (x) + (py), color, a); \ + this->blendPixelPtr(ptr4 + (y) + (px), color, a); \ } // optimized Wu's algorithm @@ -397,7 +398,7 @@ applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) { template inline void VectorRendererSpec:: -blendPixelPtr(PixelType *ptr, PixelType color, uint8 alpha) { +blendPixelPtr(PixelType *ptr, PixelType color, uint8 alpha) { register int idst = *ptr; register int isrc = color; @@ -1458,8 +1459,8 @@ drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) { ptr += xdir; alpha = (error_acc >> 8); - blendPixelPtr(ptr, color, ~alpha); - blendPixelPtr(ptr + pitch, color, alpha); + this->blendPixelPtr(ptr, color, ~alpha); + this->blendPixelPtr(ptr + pitch, color, alpha); } } else { gradient = (uint32)(dx << 16) / (uint32)dy; @@ -1475,8 +1476,8 @@ drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) { ptr += pitch; alpha = (error_acc >> 8); - blendPixelPtr(ptr, color, ~alpha); - blendPixelPtr(ptr + xdir, color, alpha); + this->blendPixelPtr(ptr, color, ~alpha); + this->blendPixelPtr(ptr + xdir, color, alpha); } } diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h index 5695a153733..90d70749922 100644 --- a/graphics/VectorRendererSpec.h +++ b/graphics/VectorRendererSpec.h @@ -20,6 +20,7 @@ * * $URL$ * $Id$ + * */ #ifndef VECTOR_RENDERER_SPEC_H diff --git a/graphics/font.h b/graphics/font.h index 4bb23bfcc24..48e03942e20 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -67,10 +67,10 @@ public: int getStringWidth(const Common::String &str) const; /** - * Take a text (which may contain newlines characters) and word wrap it so thata + * Take a text (which may contain newline characters) and word wrap it so that * no text line is wider than maxWidth pixels. If necessary, additional line breaks - * are generated, preferably between words (i.e. were whitespaces are). - * The resulting lines are appended to the string list lines. + * are generated, preferably between words (i.e. where whitespaces are). + * The resulting lines are appended to the lines string list. * It returns the maximal width of any of the new lines (i.e. a value which is less * or equal to maxWidth). * diff --git a/graphics/fontman.cpp b/graphics/fontman.cpp index 8c100fea733..05c304bd141 100644 --- a/graphics/fontman.cpp +++ b/graphics/fontman.cpp @@ -57,7 +57,19 @@ FontManager::~FontManager() { g_consolefont = 0; } +const char *builtinFontNames[] = { + "builtinOSD", + "builtinConsole", + "builtinGUI", + "builtinBigGUI", + 0 +}; + const Font *FontManager::getFontByName(const Common::String &name) const { + for (int i = 0; builtinFontNames[i]; i++) + if (!strcmp(name.c_str(), builtinFontNames[i])) + return getFontByUsage((FontUsage)i); + if (!_fontMap.contains(name)) return 0; return _fontMap[name]; diff --git a/graphics/fontman.h b/graphics/fontman.h index 2d58c03e15d..f82bc59e073 100644 --- a/graphics/fontman.h +++ b/graphics/fontman.h @@ -38,10 +38,10 @@ namespace Graphics { class FontManager : public Common::Singleton { public: enum FontUsage { - kOSDFont, - kConsoleFont, - kGUIFont, - kBigGUIFont + kOSDFont = 0, + kConsoleFont = 1, + kGUIFont = 2, + kBigGUIFont = 3 }; /** diff --git a/graphics/scaler.h b/graphics/scaler.h index 6952b4cefa5..cdd9c1f3b48 100644 --- a/graphics/scaler.h +++ b/graphics/scaler.h @@ -42,7 +42,7 @@ enum { * Creates a thumbnail from the current screen (without overlay). * * @param surf a surface (will always have 16 bpp after this for now) - * @return false if a error occured + * @return false if a error occurred */ //extern bool createThumbnailFromScreen(Graphics::Surface *surf); diff --git a/gui/EditTextWidget.cpp b/gui/EditTextWidget.cpp index f86b69f9105..f65d3a8e667 100644 --- a/gui/EditTextWidget.cpp +++ b/gui/EditTextWidget.cpp @@ -30,16 +30,16 @@ namespace GUI { -EditTextWidget::EditTextWidget(GuiObject *boss, int x, int y, int w, int h, const String &text, uint32 cmd) - : EditableWidget(boss, x, y - 1, w, h + 2, cmd) { +EditTextWidget::EditTextWidget(GuiObject *boss, int x, int y, int w, int h, const String &text, const char *tooltip, uint32 cmd) + : EditableWidget(boss, x, y - 1, w, h + 2, tooltip, cmd) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE); _type = kEditTextWidget; setEditString(text); } -EditTextWidget::EditTextWidget(GuiObject *boss, const String &name, const String &text, uint32 cmd) - : EditableWidget(boss, name, cmd) { +EditTextWidget::EditTextWidget(GuiObject *boss, const String &name, const String &text, const char *tooltip, uint32 cmd) + : EditableWidget(boss, name, tooltip, cmd) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE); _type = kEditTextWidget; diff --git a/gui/EditTextWidget.h b/gui/EditTextWidget.h index 688b166b91a..49e0c0482d1 100644 --- a/gui/EditTextWidget.h +++ b/gui/EditTextWidget.h @@ -41,8 +41,8 @@ protected: int _rightPadding; public: - EditTextWidget(GuiObject *boss, int x, int y, int w, int h, const String &text, uint32 cmd = 0); - EditTextWidget(GuiObject *boss, const String &name, const String &text, uint32 cmd = 0); + EditTextWidget(GuiObject *boss, int x, int y, int w, int h, const String &text, const char *tooltip = 0, uint32 cmd = 0); + EditTextWidget(GuiObject *boss, const String &name, const String &text, const char *tooltp = 0, uint32 cmd = 0); void setEditString(const String &str); diff --git a/gui/GuiManager.cpp b/gui/GuiManager.cpp index 0def63444bd..479c05409f2 100644 --- a/gui/GuiManager.cpp +++ b/gui/GuiManager.cpp @@ -27,6 +27,8 @@ #include "common/util.h" #include "common/config-manager.h" #include "common/algorithm.h" +#include "common/timer.h" +#include "common/translation.h" #include "backends/keymapper/keymapper.h" @@ -48,7 +50,7 @@ enum { // Constructor GuiManager::GuiManager() : _redrawStatus(kRedrawDisabled), - _stateIsSaved(false), _cursorAnimateCounter(0), _cursorAnimateTimer(0) { + _stateIsSaved(false), _cursorAnimateCounter(0), _cursorAnimateTimer(0) { _theme = 0; _useStdCursor = false; @@ -71,13 +73,16 @@ GuiManager::GuiManager() : _redrawStatus(kRedrawDisabled), // Loading the theme failed, try to load the built-in theme if (!loadNewTheme("builtin", gfxMode)) { // Loading the built-in theme failed as well. Bail out - error("Failed to load any GUI theme, aborting"); + error("%s", _t("Failed to load any GUI theme, aborting")); } } + + _tooltip = new Tooltip(this); } GuiManager::~GuiManager() { delete _theme; + delete _tooltip; } #ifdef ENABLE_KEYMAPPER @@ -94,27 +99,28 @@ void GuiManager::initKeymap() { Action *act; Keymap *guiMap = new Keymap("gui"); - act = new Action(guiMap, "CLOS", "Close", kGenericActionType, kStartKeyType); + act = new Action(guiMap, "CLOS", _("Close"), kGenericActionType, kStartKeyType); act->addKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE, 0)); - act = new Action(guiMap, "CLIK", "Mouse click"); + act = new Action(guiMap, "CLIK", _("Mouse click")); act->addLeftClickEvent(); - act = new Action(guiMap, "VIRT", "Display keyboard", kVirtualKeyboardActionType); + act = new Action(guiMap, "VIRT", _("Display keyboard"), kVirtualKeyboardActionType); act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0)); - act = new Action(guiMap, "REMP", "Remap keys", kKeyRemapActionType); + act = new Action(guiMap, "REMP", _("Remap keys"), kKeyRemapActionType); act->addKeyEvent(KeyState(KEYCODE_F8, ASCII_F8, 0)); mapper->addGlobalKeymap(guiMap); } #endif -bool GuiManager::loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx) { +bool GuiManager::loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx, bool forced) { // If we are asked to reload the currently active theme, just do nothing // FIXME: Actually, why? It might be desirable at times to force a theme reload... - if (_theme && id == _theme->getThemeId() && gfx == _theme->getGraphicsMode()) - return true; + if (!forced) + if (_theme && id == _theme->getThemeId() && gfx == _theme->getGraphicsMode()) + return true; ThemeEngine *newTheme = 0; @@ -218,6 +224,13 @@ Dialog *GuiManager::getTopDialog() const { return _dialogStack.top(); } +static void tooltipCallback(void *ref) { + GuiManager *guiManager = (GuiManager *)ref; + + guiManager->getTooltip()->setVisible(true); + g_system->getTimerManager()->removeTimerProc(&tooltipCallback); +} + void GuiManager::runLoop() { Dialog *activeDialog = getTopDialog(); bool didSaveState = false; @@ -276,6 +289,8 @@ void GuiManager::runLoop() { } Common::Event event; + + bool eventTookplace = false; while (eventMan->pollEvent(event)) { // The top dialog can change during the event loop. In that case, flush all the @@ -298,16 +313,21 @@ void GuiManager::runLoop() { switch (event.type) { case Common::EVENT_KEYDOWN: activeDialog->handleKeyDown(event.kbd); + eventTookplace = true; break; case Common::EVENT_KEYUP: activeDialog->handleKeyUp(event.kbd); + eventTookplace = true; break; case Common::EVENT_MOUSEMOVE: activeDialog->handleMouseMoved(mouse.x, mouse.y, 0); + _tooltip->setMouseXY(mouse.x, mouse.y); + eventTookplace = true; break; // We don't distinguish between mousebuttons (for now at least) case Common::EVENT_LBUTTONDOWN: case Common::EVENT_RBUTTONDOWN: + eventTookplace = true; button = (event.type == Common::EVENT_LBUTTONDOWN ? 1 : 2); time = _system->getMillis(); if (_lastClick.count && (time < _lastClick.time + kDoubleClickDelay) @@ -324,18 +344,22 @@ void GuiManager::runLoop() { break; case Common::EVENT_LBUTTONUP: case Common::EVENT_RBUTTONUP: + eventTookplace = true; button = (event.type == Common::EVENT_LBUTTONUP ? 1 : 2); activeDialog->handleMouseUp(mouse.x, mouse.y, button, _lastClick.count); break; case Common::EVENT_WHEELUP: + eventTookplace = true; activeDialog->handleMouseWheel(mouse.x, mouse.y, -1); break; case Common::EVENT_WHEELDOWN: + eventTookplace = true; activeDialog->handleMouseWheel(mouse.x, mouse.y, 1); break; case Common::EVENT_QUIT: return; case Common::EVENT_SCREEN_CHANGED: + eventTookplace = true; screenChange(); break; default: @@ -343,6 +367,13 @@ void GuiManager::runLoop() { } } + if (eventTookplace) { + _tooltip->setVisible(false); + + _system->getTimerManager()->removeTimerProc(&tooltipCallback); + _system->getTimerManager()->installTimerProc(&tooltipCallback, 2*1000000, this); + } + // Delay for a moment _system->delayMillis(10); } diff --git a/gui/GuiManager.h b/gui/GuiManager.h index 739574a45f2..97377acb9e3 100644 --- a/gui/GuiManager.h +++ b/gui/GuiManager.h @@ -60,6 +60,7 @@ typedef Common::FixedStack DialogStack; */ class GuiManager : public Common::Singleton { friend class Dialog; + friend class Tooltip; friend class Common::Singleton; GuiManager(); ~GuiManager(); @@ -71,7 +72,7 @@ public: bool isActive() const { return ! _dialogStack.empty(); } - bool loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx = ThemeEngine::kGfxDisabled); + bool loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx = ThemeEngine::kGfxDisabled, bool force = false); ThemeEngine *theme() { return _theme; } ThemeEval *xmlEval() { return _theme->getEvaluator(); } @@ -91,6 +92,9 @@ public: * @return true if the a screen change indeed occurred, false otherwise */ bool checkScreenChange(); + + Tooltip *getTooltip() { return _tooltip; } + protected: enum RedrawStatus { kRedrawDisabled = 0, @@ -114,9 +118,11 @@ protected: bool _useStdCursor; + Tooltip *_tooltip; + // position and time of last mouse click (used to detect double clicks) struct { - int16 x, y; // Position of mouse when the click occured + int16 x, y; // Position of mouse when the click occurred uint32 time; // Time int count; // How often was it already pressed? } _lastClick; diff --git a/gui/KeysDialog.cpp b/gui/KeysDialog.cpp index 42c5d94b695..e5a28060b5c 100644 --- a/gui/KeysDialog.cpp +++ b/gui/KeysDialog.cpp @@ -25,6 +25,7 @@ #include "gui/KeysDialog.h" #include "gui/Actions.h" +#include "common/translation.h" #include #ifdef _WIN32_WCE @@ -40,15 +41,15 @@ enum { KeysDialog::KeysDialog(const Common::String &title) : GUI::Dialog("KeysDialog") { - new ButtonWidget(this, "KeysDialog.Map", "Map", kMapCmd, 0); - new ButtonWidget(this, "KeysDialog.Ok", "OK", kOKCmd, 0); - new ButtonWidget(this, "KeysDialog.Cancel", "Cancel", kCloseCmd, 0); + new ButtonWidget(this, "KeysDialog.Map", _("Map"), 0, kMapCmd); + new ButtonWidget(this, "KeysDialog.Ok", _("OK"), 0, kOKCmd); + new ButtonWidget(this, "KeysDialog.Cancel", _("Cancel"), 0, kCloseCmd); _actionsList = new ListWidget(this, "KeysDialog.List"); _actionsList->setNumberingMode(kListNumberingZero); _actionTitle = new StaticTextWidget(this, "KeysDialog.Action", title); - _keyMapping = new StaticTextWidget(this, "KeysDialog.Mapping", "Select an action and click 'Map'"); + _keyMapping = new StaticTextWidget(this, "KeysDialog.Mapping", _("Select an action and click 'Map'")); _actionTitle->setFlags(WIDGET_CLEARBG); _keyMapping->setFlags(WIDGET_CLEARBG); @@ -79,9 +80,9 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { key = key - Common::ASCII_F1 + SDLK_F1; #endif if (key != 0) - sprintf(selection, "Associated key : %s", SDL_GetKeyName((SDLKey)key)); + sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey)key)); else - sprintf(selection, "Associated key : none"); + sprintf(selection, _("Associated key : none")); _keyMapping->setLabel(selection); _keyMapping->draw(); @@ -89,7 +90,7 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { break; case kMapCmd: if (_actionsList->getSelected() < 0) { - _actionTitle->setLabel("Please select an action"); + _actionTitle->setLabel(_("Please select an action")); } else { char selection[100]; @@ -101,11 +102,11 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { key = key - Common::ASCII_F1 + SDLK_F1; #endif if (key != 0) - sprintf(selection, "Associated key : %s", SDL_GetKeyName((SDLKey)key)); + sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey)key)); else - sprintf(selection, "Associated key : none"); + sprintf(selection, _("Associated key : none")); - _actionTitle->setLabel("Press the key to associate"); + _actionTitle->setLabel(_("Press the key to associate")); _keyMapping->setLabel(selection); _keyMapping->draw(); Actions::Instance()->beginMapping(true); @@ -140,11 +141,11 @@ void KeysDialog::handleKeyUp(Common::KeyState state) { Actions::Instance()->setMapping((ActionType)_actionSelected, state.ascii); if (state.ascii != 0) - sprintf(selection, "Associated key : %s", SDL_GetKeyName((SDLKey) state.keycode)); + sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey) state.keycode)); else - sprintf(selection, "Associated key : none"); + sprintf(selection, _("Associated key : none")); - _actionTitle->setLabel("Choose an action to map"); + _actionTitle->setLabel(_("Choose an action to map")); _keyMapping->setLabel(selection); _keyMapping->draw(); _actionTitle->draw(); diff --git a/gui/ListWidget.cpp b/gui/ListWidget.cpp index 88455154232..86c8d6cd315 100644 --- a/gui/ListWidget.cpp +++ b/gui/ListWidget.cpp @@ -36,8 +36,8 @@ namespace GUI { -ListWidget::ListWidget(GuiObject *boss, const String &name, uint32 cmd) - : EditableWidget(boss, name), _cmd(cmd) { +ListWidget::ListWidget(Dialog *boss, const String &name, const char *tooltip, uint32 cmd) + : EditableWidget(boss, name, tooltip), _cmd(cmd) { _scrollBar = NULL; _textWidth = NULL; @@ -68,8 +68,8 @@ ListWidget::ListWidget(GuiObject *boss, const String &name, uint32 cmd) _editColor = ThemeEngine::kFontColorNormal; } -ListWidget::ListWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd) - : EditableWidget(boss, x, y, w, h), _cmd(cmd) { +ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd) + : EditableWidget(boss, x, y, w, h, tooltip), _cmd(cmd) { _scrollBar = NULL; _textWidth = NULL; @@ -472,6 +472,10 @@ void ListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { if (_currentPos != (int)data) { _currentPos = data; draw(); + + // Scrollbar actions cause list focus (which triggers a redraw) + // NOTE: ListWidget's boss is always GUI::Dialog + ((GUI::Dialog *)_boss)->setFocusWidget(this); } break; } diff --git a/gui/ListWidget.h b/gui/ListWidget.h index d4758b61a38..313a1139c13 100644 --- a/gui/ListWidget.h +++ b/gui/ListWidget.h @@ -87,8 +87,8 @@ protected: ThemeEngine::FontColor _editColor; public: - ListWidget(GuiObject *boss, const String &name, uint32 cmd = 0); - ListWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd = 0); + ListWidget(Dialog *boss, const String &name, const char *tooltip = 0, uint32 cmd = 0); + ListWidget(Dialog *boss, int x, int y, int w, int h, const char *tooltip = 0, uint32 cmd = 0); virtual ~ListWidget(); virtual Widget *findWidget(int x, int y); diff --git a/gui/PopUpWidget.cpp b/gui/PopUpWidget.cpp index 7ced07bdd5e..154b4dd47e6 100644 --- a/gui/PopUpWidget.cpp +++ b/gui/PopUpWidget.cpp @@ -378,8 +378,8 @@ void PopUpDialog::drawMenuEntry(int entry, bool hilite) { // PopUpWidget // -PopUpWidget::PopUpWidget(GuiObject *boss, const String &name) - : Widget(boss, name), CommandSender(boss) { +PopUpWidget::PopUpWidget(GuiObject *boss, const String &name, const char *tooltip) + : Widget(boss, name, tooltip), CommandSender(boss) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_IGNORE_DRAG); _type = kPopUpWidget; diff --git a/gui/PopUpWidget.h b/gui/PopUpWidget.h index 17ae7259b8a..e870677cc58 100644 --- a/gui/PopUpWidget.h +++ b/gui/PopUpWidget.h @@ -59,13 +59,14 @@ protected: int _rightPadding; public: - PopUpWidget(GuiObject *boss, const String &name); + PopUpWidget(GuiObject *boss, const String &name, const char *tooltip = 0); void handleMouseDown(int x, int y, int button, int clickCount); void handleMouseWheel(int x, int y, int direction); void appendEntry(const String &entry, uint32 tag = (uint32)-1); void clearEntries(); + int numEntries() { return _entries.size(); } /** Select the entry at the given index. */ void setSelected(int item); diff --git a/gui/ScrollBarWidget.cpp b/gui/ScrollBarWidget.cpp index 741db9d43db..f510fcfb2b3 100644 --- a/gui/ScrollBarWidget.cpp +++ b/gui/ScrollBarWidget.cpp @@ -26,6 +26,8 @@ #include "gui/dialog.h" #include "gui/GuiManager.h" +#include "common/timer.h" + namespace GUI { #define UP_DOWN_BOX_HEIGHT (_w+1) @@ -47,6 +49,28 @@ ScrollBarWidget::ScrollBarWidget(GuiObject *boss, int x, int y, int w, int h) _currentPos = 0; } +static void upArrowRepeater(void *ref) { + ScrollBarWidget *sb = (ScrollBarWidget *)ref; + int old_pos = sb->_currentPos; + + sb->_currentPos -= 3; + sb->checkBounds(old_pos); + + g_system->getTimerManager()->removeTimerProc(&upArrowRepeater); + g_system->getTimerManager()->installTimerProc(&upArrowRepeater, 1000000/10, ref); +} + +static void downArrowRepeater(void *ref) { + ScrollBarWidget *sb = (ScrollBarWidget *)ref; + int old_pos = sb->_currentPos; + + sb->_currentPos += 3; + sb->checkBounds(old_pos); + + g_system->getTimerManager()->removeTimerProc(&downArrowRepeater); + g_system->getTimerManager()->installTimerProc(&downArrowRepeater, 1000000/10, ref); +} + void ScrollBarWidget::handleMouseDown(int x, int y, int button, int clickCount) { int old_pos = _currentPos; @@ -58,10 +82,12 @@ void ScrollBarWidget::handleMouseDown(int x, int y, int button, int clickCount) // Up arrow _currentPos--; _draggingPart = kUpArrowPart; + g_system->getTimerManager()->installTimerProc(&upArrowRepeater, 1000000/2, this); } else if (y >= _h - UP_DOWN_BOX_HEIGHT) { // Down arrow _currentPos++; _draggingPart = kDownArrowPart; + g_system->getTimerManager()->installTimerProc(&downArrowRepeater, 1000000/2, this); } else if (y < _sliderPos) { _currentPos -= _entriesPerPage - 1; } else if (y >= _sliderPos + _sliderHeight) { @@ -77,6 +103,9 @@ void ScrollBarWidget::handleMouseDown(int x, int y, int button, int clickCount) void ScrollBarWidget::handleMouseUp(int x, int y, int button, int clickCount) { _draggingPart = kNoPart; + + g_system->getTimerManager()->removeTimerProc(&upArrowRepeater); + g_system->getTimerManager()->removeTimerProc(&downArrowRepeater); } void ScrollBarWidget::handleMouseWheel(int x, int y, int direction) { diff --git a/gui/ScrollBarWidget.h b/gui/ScrollBarWidget.h index c14e6d8461d..96761089ada 100644 --- a/gui/ScrollBarWidget.h +++ b/gui/ScrollBarWidget.h @@ -73,9 +73,10 @@ public: // should these accessors force a redraw? void recalc(); + void checkBounds(int old_pos); + protected: void drawWidget(); - void checkBounds(int old_pos); }; } // End of namespace GUI diff --git a/gui/TabWidget.cpp b/gui/TabWidget.cpp index ae1c74816ca..6bc38336fb3 100644 --- a/gui/TabWidget.cpp +++ b/gui/TabWidget.cpp @@ -37,12 +37,12 @@ enum { }; TabWidget::TabWidget(GuiObject *boss, int x, int y, int w, int h) - : Widget(boss, x, y, w, h) { + : Widget(boss, x, y, w, h), _bodyBackgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) { init(); } TabWidget::TabWidget(GuiObject *boss, const String &name) - : Widget(boss, name) { + : Widget(boss, name), _bodyBackgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) { init(); } @@ -56,15 +56,20 @@ void TabWidget::init() { _tabHeight = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Height"); _titleVPad = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Padding.Top"); - _butRP = g_gui.xmlEval()->getVar("Globals.TabWidget.navButtonPadding.Right", 0); + _bodyTP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Top"); + _bodyBP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Bottom"); + _bodyLP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Left"); + _bodyRP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Right"); + + _butRP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButtonPadding.Right", 0); _butTP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Padding.Top", 0); _butW = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Width", 10); _butH = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Height", 10); int x = _w - _butRP - _butW * 2 - 2; int y = _butTP - _tabHeight; - _navLeft = new ButtonWidget(this, x, y, _butW, _butH, "<", kCmdLeft, 0); - _navRight = new ButtonWidget(this, x + _butW + 2, y, _butW, _butH, ">", kCmdRight, 0); + _navLeft = new ButtonWidget(this, x, y, _butW, _butH, "<", 0, kCmdLeft); + _navRight = new ButtonWidget(this, x + _butW + 2, y, _butW, _butH, ">", 0, kCmdRight); } TabWidget::~TabWidget() { @@ -279,11 +284,14 @@ void TabWidget::drawWidget() { for (int i = _firstVisibleTab; i < (int)_tabs.size(); ++i) { tabs.push_back(_tabs[i].title); } + g_gui.theme()->drawDialogBackground(Common::Rect(_x + _bodyLP, _y + _bodyTP, _x+_w-_bodyRP, _y+_h-_bodyBP), _bodyBackgroundType); + g_gui.theme()->drawTab(Common::Rect(_x, _y, _x+_w, _y+_h), _tabHeight, _tabWidth, tabs, _activeTab - _firstVisibleTab, 0, _titleVPad); } void TabWidget::draw() { Widget::draw(); + if (_tabWidth * _tabs.size() > _w) { _navLeft->draw(); _navRight->draw(); diff --git a/gui/TabWidget.h b/gui/TabWidget.h index 8606441782a..febc52448a5 100644 --- a/gui/TabWidget.h +++ b/gui/TabWidget.h @@ -46,6 +46,9 @@ protected: int _tabWidth; int _tabHeight; + int _bodyRP, _bodyTP, _bodyLP, _bodyBP; + ThemeEngine::DialogBackground _bodyBackgroundType; + int _titleVPad; int _butRP, _butTP, _butW, _butH; diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index 874f42afeaf..f978f1006cf 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -30,6 +30,7 @@ #include "common/fs.h" #include "common/unzip.h" #include "common/tokenizer.h" +#include "common/translation.h" #include "graphics/colormasks.h" #include "graphics/cursorman.h" @@ -189,6 +190,10 @@ static const DrawDataInfo kDrawDataDefaults[] = { {kDDCheckboxDisabled, "checkbox_disabled", true, kDDNone}, {kDDCheckboxSelected, "checkbox_selected", false, kDDCheckboxDefault}, + {kDDRadiobuttonDefault, "radiobutton_default", true, kDDNone}, + {kDDRadiobuttonDisabled, "radiobutton_disabled", true, kDDNone}, + {kDDRadiobuttonSelected, "radiobutton_selected", false, kDDRadiobuttonDefault}, + {kDDTabActive, "tab_active", false, kDDTabInactive}, {kDDTabInactive, "tab_inactive", true, kDDNone}, {kDDTabBackground, "tab_background", true, kDDNone}, @@ -329,10 +334,10 @@ ThemeEngine::~ThemeEngine() { * Rendering mode management *********************************************************/ const ThemeEngine::Renderer ThemeEngine::_rendererModes[] = { - { "Disabled GFX", "none", kGfxDisabled }, - { "Standard Renderer (16bpp)", "normal_16bpp", kGfxStandard16bit }, + { _s("Disabled GFX"), "none", kGfxDisabled }, + { _s("Standard Renderer (16bpp)"), "normal_16bpp", kGfxStandard16bit }, #ifndef DISABLE_FANCY_THEMES - { "Antialiased Renderer (16bpp)", "aa_16bpp", kGfxAntialias16bit } + { _s("Antialiased Renderer (16bpp)"), "aa_16bpp", kGfxAntialias16bit } #endif }; @@ -884,6 +889,32 @@ void ThemeEngine::drawCheckbox(const Common::Rect &r, const Common::String &str, queueDDText(getTextData(dd), getTextColor(dd), r2, str, false, false, _widgets[kDDCheckboxDefault]->_textAlignH, _widgets[dd]->_textAlignV); } +void ThemeEngine::drawRadiobutton(const Common::Rect &r, const Common::String &str, bool checked, WidgetStateInfo state) { + if (!ready()) + return; + + Common::Rect r2 = r; + DrawData dd = kDDRadiobuttonDefault; + + if (checked) + dd = kDDRadiobuttonSelected; + + if (state == kStateDisabled) + dd = kDDRadiobuttonDisabled; + + const int checkBoxSize = MIN((int)r.height(), getFontHeight()); + + r2.bottom = r2.top + checkBoxSize; + r2.right = r2.left + checkBoxSize; + + queueDD(dd, r2); + + r2.left = r2.right + checkBoxSize; + r2.right = r.right; + + queueDDText(getTextData(dd), getTextColor(dd), r2, str, false, false, _widgets[kDDRadiobuttonDefault]->_textAlignH, _widgets[dd]->_textAlignV); +} + void ThemeEngine::drawSlider(const Common::Rect &r, int width, WidgetStateInfo state) { if (!ready()) return; @@ -1039,7 +1070,7 @@ void ThemeEngine::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, co } } -void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state, Graphics::TextAlign align, TextInversionState inverted, int deltax, bool useEllipsis, FontStyle font, FontColor color) { +void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state, Graphics::TextAlign align, TextInversionState inverted, int deltax, bool useEllipsis, FontStyle font, FontColor color, bool restore) { if (!ready()) return; @@ -1090,13 +1121,7 @@ void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, Wid return; } - TextData textId = kTextDataNone; - if (font == kFontStyleNormal) - textId = kTextDataNormalFont; - else - textId = kTextDataDefault; - - bool restore = true; + TextData textId = fontStyleToData(font); switch (inverted) { case kTextInversion: @@ -1138,7 +1163,69 @@ void ThemeEngine::debugWidgetPosition(const char *name, const Common::Rect &r) { _screen.vLine(r.right, r.top, r.bottom, 0xFFFF); } +ThemeEngine::StoredState *ThemeEngine::storeState(const Common::Rect &r) { + StoredState *state = new StoredState; + byte *dst; + byte *src; + state->r.top = r.top; + state->r.bottom = r.bottom; + state->r.left = r.left; + state->r.right = r.right; + + state->r.clip(_screen.w, _screen.h); + + state->screen.create(state->r.width(), state->r.height(), _screen.bytesPerPixel); + state->backBuffer.create(state->r.width(), state->r.height(), _backBuffer.bytesPerPixel); + + src = (byte *)_screen.getBasePtr(state->r.left, state->r.top); + dst = (byte *)state->screen.getBasePtr(0, 0); + + for (int i = state->r.height(); i > 0; i--) { + memcpy(dst, src, state->r.width() * _screen.bytesPerPixel); + src += _screen.pitch; + dst += state->screen.pitch; + } + + src = (byte *)_backBuffer.getBasePtr(state->r.left, state->r.top); + dst = (byte *)state->backBuffer.getBasePtr(0, 0); + + for (int i = state->r.height(); i > 0; i--) { + memcpy(dst, src, state->r.width() * _backBuffer.bytesPerPixel); + src += _backBuffer.pitch; + dst += state->backBuffer.pitch; + } + + return state; +} + +void ThemeEngine::restoreState(StoredState *state) { + byte *dst; + byte *src; + + if (!state) + return; + + src = (byte *)state->screen.getBasePtr(0, 0); + dst = (byte *)_screen.getBasePtr(state->r.left, state->r.top); + + for (int i = state->r.height(); i > 0; i--) { + memcpy(dst, src, state->r.width() * _screen.bytesPerPixel); + src += state->screen.pitch; + dst += _screen.pitch; + } + + src = (byte *)state->backBuffer.getBasePtr(0, 0); + dst = (byte *)_backBuffer.getBasePtr(state->r.left, state->r.top); + + for (int i = state->r.height(); i > 0; i--) { + memcpy(dst, src, state->r.width() * _backBuffer.bytesPerPixel); + src += state->backBuffer.pitch; + dst += _backBuffer.pitch; + } + + addDirtyRect(state->r); +} /********************************************************** * Screen/overlay management diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index bc78e736b49..49cfa860c33 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -82,6 +82,10 @@ enum DrawData { kDDCheckboxDisabled, kDDCheckboxSelected, + kDDRadiobuttonDefault, + kDDRadiobuttonDisabled, + kDDRadiobuttonSelected, + kDDTabActive, kDDTabInactive, kDDTabBackground, @@ -108,6 +112,7 @@ enum TextData { kTextDataDefault = 0, kTextDataButton, kTextDataNormalFont, + kTextDataTooltip, kTextDataMAX }; @@ -173,7 +178,7 @@ public: enum TextInversionState { kTextInversionNone, ///< Indicates that the text should not be drawn inverted kTextInversion, ///< Indicates that the text should be drawn inverted, but not focused - kTextInversionFocus ///< Indicates thte the test should be drawn inverted, and focused + kTextInversionFocus ///< Indicates that the text should be drawn inverted, and focused }; enum ScrollbarState { @@ -192,6 +197,7 @@ public: kFontStyleFixedNormal = 3, ///< Fixed size font. kFontStyleFixedBold = 4, ///< Fixed size bold font. kFontStyleFixedItalic = 5, ///< Fixed size italic font. + kFontStyleTooltip = 6, ///< Tiny console font kFontStyleMax }; @@ -255,6 +261,17 @@ public: void enable(); void disable(); + struct StoredState { + Common::Rect r; + Graphics::Surface screen; + Graphics::Surface backBuffer; + + StoredState() {} + }; + + StoredState *storeState(const Common::Rect &r); + void restoreState(StoredState *state); + /** * Implementation of the GUI::Theme API. Called when a * new dialog is opened. Note that the boolean parameter @@ -280,6 +297,8 @@ public: TextData fontStyleToData(FontStyle font) const { if (font == kFontStyleNormal) return kTextDataNormalFont; + if (font == kFontStyleTooltip) + return kTextDataTooltip; return kTextDataDefault; } @@ -312,6 +331,9 @@ public: void drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, WidgetStateInfo state = kStateEnabled); + void drawRadiobutton(const Common::Rect &r, const Common::String &str, + bool checked, WidgetStateInfo state = kStateEnabled); + void drawTab(const Common::Rect &r, int tabHeight, int tabWidth, const Common::Array &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state = kStateEnabled); @@ -329,7 +351,7 @@ public: void drawDialogBackground(const Common::Rect &r, DialogBackground type, WidgetStateInfo state = kStateEnabled); - void drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state = kStateEnabled, Graphics::TextAlign align = Graphics::kTextAlignCenter, TextInversionState inverted = kTextInversionNone, int deltax = 0, bool useEllipsis = true, FontStyle font = kFontStyleBold, FontColor color = kFontColorNormal); + void drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state = kStateEnabled, Graphics::TextAlign align = Graphics::kTextAlignCenter, TextInversionState inverted = kTextInversionNone, int deltax = 0, bool useEllipsis = true, FontStyle font = kFontStyleBold, FontColor color = kFontColorNormal, bool restore = true); void drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, WidgetStateInfo state = kStateEnabled, FontColor color = kFontColorNormal); diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp index b4b64eb5125..cd49ac7771c 100644 --- a/gui/ThemeParser.cpp +++ b/gui/ThemeParser.cpp @@ -42,7 +42,8 @@ struct TextDataInfo { static const TextDataInfo kTextDataDefaults[] = { { kTextDataDefault, "text_default" }, { kTextDataButton, "text_button" }, - { kTextDataNormalFont, "text_normal" } + { kTextDataNormalFont, "text_normal" }, + { kTextDataTooltip, "tooltip_normal" } }; diff --git a/gui/about.cpp b/gui/about.cpp index e03cd5a26c5..cdc7e9dde32 100644 --- a/gui/about.cpp +++ b/gui/about.cpp @@ -27,6 +27,7 @@ #include "base/version.h" #include "common/events.h" #include "common/system.h" +#include "common/translation.h" #include "common/util.h" #include "gui/about.h" #include "gui/GuiManager.h" @@ -81,31 +82,10 @@ AboutDialog::AboutDialog() : Dialog(10, 20, 300, 174), _scrollPos(0), _scrollTime(0), _willClose(false) { + reflowLayout(); + int i; - const int screenW = g_system->getOverlayWidth(); - const int screenH = g_system->getOverlayHeight(); - - _xOff = g_gui.xmlEval()->getVar("Globals.About.XOffset", 5); - _yOff = g_gui.xmlEval()->getVar("Globals.About.YOffset", 5); - int outerBorder = g_gui.xmlEval()->getVar("Globals.About.OuterBorder"); - - _w = screenW - 2 * outerBorder; - _h = screenH - 2 * outerBorder; - - _lineHeight = g_gui.getFontHeight() + 3; - - // Heuristic to compute 'optimal' dialog width - int maxW = _w - 2*_xOff; - _w = 0; - for (i = 0; i < ARRAYSIZE(credits); i++) { - int tmp = g_gui.getStringWidth(credits[i] + 5); - if (_w < tmp && tmp <= maxW) { - _w = tmp; - } - } - _w += 2*_xOff; - for (i = 0; i < 1; i++) _lines.push_back(""); @@ -113,7 +93,7 @@ AboutDialog::AboutDialog() version += gResidualVersion; _lines.push_back(version); - Common::String date("C2""(built on "); + Common::String date(_s("C2""(built on ")); date += gResidualBuildDate; date += ')'; _lines.push_back(date); @@ -121,14 +101,14 @@ AboutDialog::AboutDialog() for (i = 0; i < ARRAYSIZE(copyright_text); i++) addLine(copyright_text[i]); - addLine("C1""Features compiled in:"); + addLine(_s("C1""Features compiled in:")); Common::String features("C0"); features += gResidualFeatures; addLine(features.c_str()); _lines.push_back(""); - addLine("C1""Available engines:"); + addLine(_s("C1""Available engines:")); const EnginePlugin::List &plugins = EngineMan.getPlugins(); EnginePlugin::List::const_iterator iter = plugins.begin(); for (; iter != plugins.end(); ++iter) { @@ -151,10 +131,6 @@ AboutDialog::AboutDialog() for (i = 0; i < ARRAYSIZE(credits); i++) addLine(credits[i]); - - // Center the dialog - _x = (screenW - _w) / 2; - _y = (screenH - _h) / 2; } void AboutDialog::addLine(const char *str) { @@ -294,6 +270,7 @@ void AboutDialog::handleKeyUp(Common::KeyState state) { void AboutDialog::reflowLayout() { Dialog::reflowLayout(); + int i; const int screenW = g_system->getOverlayWidth(); const int screenH = g_system->getOverlayHeight(); @@ -309,7 +286,7 @@ void AboutDialog::reflowLayout() { // Heuristic to compute 'optimal' dialog width int maxW = _w - 2*_xOff; _w = 0; - for (int i = 0; i < ARRAYSIZE(credits); i++) { + for (i = 0; i < ARRAYSIZE(credits); i++) { int tmp = g_gui.getStringWidth(credits[i] + 5); if (_w < tmp && tmp <= maxW) { _w = tmp; @@ -317,8 +294,7 @@ void AboutDialog::reflowLayout() { } _w += 2*_xOff; - _lineHeight = g_gui.getFontHeight() + 3; - + // Center the dialog _x = (screenW - _w) / 2; _y = (screenH - _h) / 2; } diff --git a/gui/browser.cpp b/gui/browser.cpp index 7b540c5bbc2..42a20400def 100644 --- a/gui/browser.cpp +++ b/gui/browser.cpp @@ -30,6 +30,8 @@ #include "common/system.h" #include "common/algorithm.h" +#include "common/translation.h" + namespace GUI { enum { @@ -64,9 +66,9 @@ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) _backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain; // Buttons - new ButtonWidget(this, "Browser.Up", "Go up", kGoUpCmd, 0); - new ButtonWidget(this, "Browser.Cancel", "Cancel", kCloseCmd, 0); - new ButtonWidget(this, "Browser.Choose", "Choose", kChooseCmd, 0); + new ButtonWidget(this, "Browser.Up", _("Go up"), _("Go to previous directory level"), kGoUpCmd); + new ButtonWidget(this, "Browser.Cancel", _("Cancel"), 0, kCloseCmd); + new ButtonWidget(this, "Browser.Choose", _("Choose"), 0, kChooseCmd); } void BrowserDialog::open() { diff --git a/gui/chooser.cpp b/gui/chooser.cpp index d3ec47ec40e..e800c4264f1 100644 --- a/gui/chooser.cpp +++ b/gui/chooser.cpp @@ -23,6 +23,7 @@ */ #include "common/system.h" +#include "common/translation.h" #include "gui/chooser.h" #include "gui/GuiManager.h" #include "gui/ListWidget.h" @@ -45,8 +46,8 @@ ChooserDialog::ChooserDialog(const String &title, String dialogId) _list->setEditable(false); // Buttons - new ButtonWidget(this, dialogId + ".Cancel", "Cancel", kCloseCmd, 0); - _chooseButton = new ButtonWidget(this, dialogId + ".Choose", "Choose", kChooseCmd, 0); + new ButtonWidget(this, dialogId + ".Cancel", _("Cancel"), 0, kCloseCmd); + _chooseButton = new ButtonWidget(this, dialogId + ".Choose", _("Choose"), 0, kChooseCmd); _chooseButton->setEnabled(false); } diff --git a/gui/dialog.cpp b/gui/dialog.cpp index 6d6f692fa57..761fd5dde01 100644 --- a/gui/dialog.cpp +++ b/gui/dialog.cpp @@ -171,7 +171,7 @@ void Dialog::handleMouseDown(int x, int y, int button, int clickCount) { if (w && !(w->getFlags() & WIDGET_IGNORE_DRAG)) _dragWidget = w; - // If the click occured inside a widget which is not the currently + // If the click occurred inside a widget which is not the currently // focused one, change the focus to that widget. if (w && w != _focusedWidget && w->wantsFocus()) { setFocusWidget(w); diff --git a/gui/dialog.h b/gui/dialog.h index 5c8e6214b18..954cd3c44f7 100644 --- a/gui/dialog.h +++ b/gui/dialog.h @@ -44,6 +44,7 @@ enum { class Dialog : public GuiObject { friend class GuiManager; + friend class Tooltip; protected: Widget *_mouseWidget; Widget *_focusedWidget; diff --git a/gui/editable.cpp b/gui/editable.cpp index 302ee45e52c..7434688d47a 100644 --- a/gui/editable.cpp +++ b/gui/editable.cpp @@ -28,13 +28,13 @@ namespace GUI { -EditableWidget::EditableWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd) - : Widget(boss, x, y, w, h), CommandSender(boss), _cmd(cmd) { +EditableWidget::EditableWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd) + : Widget(boss, x, y, w, h, tooltip), CommandSender(boss), _cmd(cmd) { init(); } -EditableWidget::EditableWidget(GuiObject *boss, const String &name, uint32 cmd) - : Widget(boss, name), CommandSender(boss), _cmd(cmd) { +EditableWidget::EditableWidget(GuiObject *boss, const String &name, const char *tooltip, uint32 cmd) + : Widget(boss, name, tooltip), CommandSender(boss), _cmd(cmd) { init(); } @@ -65,7 +65,7 @@ void EditableWidget::setEditString(const String &str) { // TODO: We probably should filter the input string here, // e.g. using tryInsertChar. _editString = str; - _caretPos = _editString.size(); + _caretPos = 0; } bool EditableWidget::tryInsertChar(byte c, int pos) { diff --git a/gui/editable.h b/gui/editable.h index a3e6c6d945b..dc32273c3c0 100644 --- a/gui/editable.h +++ b/gui/editable.h @@ -55,8 +55,8 @@ protected: ThemeEngine::FontStyle _font; public: - EditableWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd = 0); - EditableWidget(GuiObject *boss, const String &name, uint32 cmd = 0); + EditableWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip = 0, uint32 cmd = 0); + EditableWidget(GuiObject *boss, const String &name, const char *tooltip = 0, uint32 cmd = 0); virtual ~EditableWidget(); void init(); diff --git a/gui/error.cpp b/gui/error.cpp index 8a784c8ff25..81bdbdc8ceb 100644 --- a/gui/error.cpp +++ b/gui/error.cpp @@ -27,6 +27,8 @@ #include "gui/message.h" #include "gui/error.h" +#include "common/translation.h" + namespace GUI { void displayErrorDialog(const char *text) { @@ -37,7 +39,7 @@ void displayErrorDialog(const char *text) { void displayErrorDialog(Common::Error error, const char *extraText) { Common::String errorText(extraText); errorText += " "; - errorText += Common::errorToString(error); + errorText += _(Common::errorToString(error)); GUI::MessageDialog alert(errorText); alert.runModal(); } diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 38d61dcc640..bab101c7a62 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -30,6 +30,7 @@ #include "common/util.h" #include "common/savefile.h" #include "common/system.h" +#include "common/translation.h" #include "gui/about.h" #include "gui/browser.h" @@ -87,8 +88,8 @@ enum { */ class DomainEditTextWidget : public EditTextWidget { public: - DomainEditTextWidget(GuiObject *boss, const String &name, const String &text) - : EditTextWidget(boss, name, text) { + DomainEditTextWidget(GuiObject *boss, const String &name, const String &text, const char *tooltip = 0) + : EditTextWidget(boss, name, text, tooltip) { } protected: @@ -166,30 +167,31 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc) // // 1) The game tab // - tab->addTab("Game"); + tab->addTab(_("Game")); // GUI: Label & edit widget for the game ID - new StaticTextWidget(tab, "GameOptions_Game.Id", "ID:"); - _domainWidget = new DomainEditTextWidget(tab, "GameOptions_Game.Domain", _domain); + new StaticTextWidget(tab, "GameOptions_Game.Id", _("ID:"), _("Short game identifier used for referring to savegames and running the game from the command line")); + _domainWidget = new DomainEditTextWidget(tab, "GameOptions_Game.Domain", _domain, _("Short game identifier used for referring to savegames and running the game from the command line")); // GUI: Label & edit widget for the description - new StaticTextWidget(tab, "GameOptions_Game.Name", "Name:"); - _descriptionWidget = new EditTextWidget(tab, "GameOptions_Game.Desc", description); + new StaticTextWidget(tab, "GameOptions_Game.Name", _("Name:"), _("Full title of the game")); + _descriptionWidget = new EditTextWidget(tab, "GameOptions_Game.Desc", description, _("Full title of the game")); // Language popup - _langPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.LangPopupDesc", "Language:"); - _langPopUp = new PopUpWidget(tab, "GameOptions_Game.LangPopup"); - _langPopUp->appendEntry(""); - _langPopUp->appendEntry(""); + _langPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.LangPopupDesc", _("Language:"), _("Language of the game. This will not turn your Spanish game version into English")); + _langPopUp = new PopUpWidget(tab, "GameOptions_Game.LangPopup", _("Language of the game. This will not turn your Spanish game version into English")); + _langPopUp->appendEntry(_(""), 0); + _langPopUp->appendEntry("", 0); const Common::LanguageDescription *l = Common::g_languages; for (; l->code; ++l) { - _langPopUp->appendEntry(l->description, l->id); + if (checkGameGUIOptionLanguage(l->id, _guioptionsString)) + _langPopUp->appendEntry(l->description, l->id); } // Platform popup - _platformPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.PlatformPopupDesc", "Platform:"); - _platformPopUp = new PopUpWidget(tab, "GameOptions_Game.PlatformPopup"); - _platformPopUp->appendEntry(""); + _platformPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.PlatformPopupDesc", _("Platform:"), _("Platform the game was originally designed for")); + _platformPopUp = new PopUpWidget(tab, "GameOptions_Game.PlatformPopup", _("Platform the game was originally designed for")); + _platformPopUp->appendEntry(_("")); _platformPopUp->appendEntry(""); const Common::PlatformDescription *p = Common::g_platforms; for (; p->code; ++p) { @@ -199,36 +201,37 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc) // // 3) The graphics tab // - _graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? "Graphics" : "GFX"); + _graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX")); - _globalGraphicsOverride = new CheckboxWidget(tab, "GameOptions_Graphics.EnableTabCheckbox", "Override global graphic settings", kCmdGlobalGraphicsOverride, 0); + _globalGraphicsOverride = new CheckboxWidget(tab, "GameOptions_Graphics.EnableTabCheckbox", _("Override global graphic settings"), 0, kCmdGlobalGraphicsOverride); addGraphicControls(tab, "GameOptions_Graphics."); // // 4) The audio tab // - tab->addTab("Audio"); + tab->addTab(_("Audio")); - _globalAudioOverride = new CheckboxWidget(tab, "GameOptions_Audio.EnableTabCheckbox", "Override global audio settings", kCmdGlobalAudioOverride, 0); + _globalAudioOverride = new CheckboxWidget(tab, "GameOptions_Audio.EnableTabCheckbox", _("Override global audio settings"), 0, kCmdGlobalAudioOverride); addAudioControls(tab, "GameOptions_Audio."); + addSubtitleControls(tab, "GameOptions_Audio."); // // 5) The volume tab // - tab->addTab("Volume"); + tab->addTab(_("Volume")); - _globalVolumeOverride = new CheckboxWidget(tab, "GameOptions_Volume.EnableTabCheckbox", "Override global volume settings", kCmdGlobalVolumeOverride, 0); + _globalVolumeOverride = new CheckboxWidget(tab, "GameOptions_Volume.EnableTabCheckbox", _("Override global volume settings"), 0, kCmdGlobalVolumeOverride); addVolumeControls(tab, "GameOptions_Volume."); // // 6) The MIDI tab // - tab->addTab("MIDI"); + tab->addTab(_("MIDI")); - _globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", "Override global MIDI settings", kCmdGlobalMIDIOverride, 0); + _globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", _("Override global MIDI settings"), 0, kCmdGlobalMIDIOverride); if (_guioptions & Common::GUIO_NOMIDI) _globalMIDIOverride->setEnabled(false); @@ -238,30 +241,30 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc) // // 2) The 'Path' tab // - tab->addTab("Paths"); + tab->addTab(_("Paths")); // These buttons have to be extra wide, or the text will be truncated // in the small version of the GUI. // GUI: Button + Label for the game path - new ButtonWidget(tab, "GameOptions_Paths.Gamepath", "Game Path:", kCmdGameBrowser, 0); + new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _("Game Path:"), 0, kCmdGameBrowser); _gamePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.GamepathText", gamePath); // GUI: Button + Label for the additional path - new ButtonWidget(tab, "GameOptions_Paths.Extrapath", "Extra Path:", kCmdExtraBrowser, 0); - _extraPathWidget = new StaticTextWidget(tab, "GameOptions_Paths.ExtrapathText", extraPath); + new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _("Extra Path:"), _("Specifies path to additional data used the game"), kCmdExtraBrowser); + _extraPathWidget = new StaticTextWidget(tab, "GameOptions_Paths.ExtrapathText", extraPath, _("Specifies path to additional data used the game")); // GUI: Button + Label for the save path - new ButtonWidget(tab, "GameOptions_Paths.Savepath", "Save Path:", kCmdSaveBrowser, 0); - _savePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.SavepathText", savePath); + new ButtonWidget(tab, "GameOptions_Paths.Savepath", _("Save Path:"), _("Specifies where your savegames are put"), kCmdSaveBrowser); + _savePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.SavepathText", savePath, _("Specifies where your savegames are put")); // Activate the first tab tab->setActiveTab(0); _tabWidget = tab; // Add OK & Cancel buttons - new ButtonWidget(this, "GameOptions.Cancel", "Cancel", kCloseCmd, 0); - new ButtonWidget(this, "GameOptions.Ok", "OK", kOKCmd, 0); + new ButtonWidget(this, "GameOptions.Cancel", _("Cancel"), 0, kCloseCmd); + new ButtonWidget(this, "GameOptions.Ok", _("OK"), 0, kOKCmd); } void EditGameDialog::open() { @@ -269,12 +272,12 @@ void EditGameDialog::open() { String extraPath(ConfMan.get("extrapath", _domain)); if (extraPath.empty() || !ConfMan.hasKey("extrapath", _domain)) { - _extraPathWidget->setLabel("None"); + _extraPathWidget->setLabel(_("None")); } String savePath(ConfMan.get("savepath", _domain)); if (savePath.empty() || !ConfMan.hasKey("savepath", _domain)) { - _savePathWidget->setLabel("Default"); + _savePathWidget->setLabel(_("Default")); } int sel, i; @@ -309,17 +312,16 @@ void EditGameDialog::open() { // TODO: game path - const Common::LanguageDescription *l = Common::g_languages; const Common::Language lang = Common::parseLanguage(ConfMan.get("language", _domain)); - sel = 0; if (ConfMan.hasKey("language", _domain)) { - for (i = 0; l->code; ++l, ++i) { - if (lang == l->id) - sel = i + 2; - } + _langPopUp->setSelectedTag(lang); + } + + if (_langPopUp->numEntries() <= 3) { // If only one language is avaliable + _langPopUpDesc->setEnabled(false); + _langPopUp->setEnabled(false); } - _langPopUp->setSelected(sel); const Common::PlatformDescription *p = Common::g_platforms; @@ -348,11 +350,11 @@ void EditGameDialog::close() { ConfMan.set("path", gamePath, _domain); String extraPath(_extraPathWidget->getLabel()); - if (!extraPath.empty() && (extraPath != "None")) + if (!extraPath.empty() && (extraPath != _("None"))) ConfMan.set("extrapath", extraPath, _domain); String savePath(_savePathWidget->getLabel()); - if (!savePath.empty() && (savePath != "Default")) + if (!savePath.empty() && (savePath != _("Default"))) ConfMan.set("savepath", savePath, _domain); Common::Platform platform = (Common::Platform)_platformPopUp->getSelectedTag(); @@ -385,14 +387,14 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat draw(); break; case kCmdChooseSoundFontCmd: { - BrowserDialog browser("Select SoundFont", false); + BrowserDialog browser(_("Select SoundFont"), false); if (browser.runModal() > 0) { // User made this choice... Common::FSNode file(browser.getResult()); _soundFont->setLabel(file.getPath()); - if (!file.getPath().empty() && (file.getPath() != "None")) + if (!file.getPath().empty() && (file.getPath() != _("None"))) _soundFontClearButton->setEnabled(true); else _soundFontClearButton->setEnabled(false); @@ -404,7 +406,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat // Change path for the game case kCmdGameBrowser: { - BrowserDialog browser("Select directory with game data", true); + BrowserDialog browser(_("Select directory with game data"), true); if (browser.runModal() > 0) { // User made his choice... Common::FSNode dir(browser.getResult()); @@ -422,7 +424,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat // Change path for extra game data (eg, using sword cutscenes when playing via CD) case kCmdExtraBrowser: { - BrowserDialog browser("Select additional game directory", true); + BrowserDialog browser(_("Select additional game directory"), true); if (browser.runModal() > 0) { // User made his choice... Common::FSNode dir(browser.getResult()); @@ -434,7 +436,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat } // Change path for stored save game (perm and temp) data case kCmdSaveBrowser: { - BrowserDialog browser("Select directory for saved games", true); + BrowserDialog browser(_("Select directory for saved games"), true); if (browser.runModal() > 0) { // User made his choice... Common::FSNode dir(browser.getResult()); @@ -453,7 +455,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat || newDomain.hasPrefix("_") || newDomain == ConfigManager::kApplicationDomain || ConfMan.hasGameDomain(newDomain)) { - MessageDialog alert("This game ID is already taken. Please choose another one."); + MessageDialog alert(_("This game ID is already taken. Please choose another one.")); alert.runModal(); return; } @@ -491,42 +493,42 @@ LauncherDialog::LauncherDialog() new StaticTextWidget(this, "Launcher.Version", gResidualFullVersion); #else // Show ScummVM version - new StaticTextWidget(this, "Launcher.Version", gScummVMFullVersion); + new StaticTextWidget(this, "Launcher.Version", gResidualFullVersion); #endif - new ButtonWidget(this, "Launcher.QuitButton", "Quit", kQuitCmd, 'Q'); - new ButtonWidget(this, "Launcher.AboutButton", "About...", kAboutCmd, 'B'); - new ButtonWidget(this, "Launcher.OptionsButton", "Options...", kOptionsCmd, 'O'); + new ButtonWidget(this, "Launcher.QuitButton", _("~Q~uit"), _("Quit Residual"), kQuitCmd); + new ButtonWidget(this, "Launcher.AboutButton", _("A~b~out..."), _("About Residual"), kAboutCmd); + new ButtonWidget(this, "Launcher.OptionsButton", _("~O~ptions..."), _("Change global Residual options"), kOptionsCmd); _startButton = - new ButtonWidget(this, "Launcher.StartButton", "Start", kStartCmd, 'S'); + new ButtonWidget(this, "Launcher.StartButton", _("~S~tart"), _("Start selected game"), kStartCmd); _loadButton = - new ButtonWidget(this, "Launcher.LoadGameButton", "Load...", kLoadGameCmd, 'L'); + new ButtonWidget(this, "Launcher.LoadGameButton", _("~L~oad..."), _("Load savegame for selected game"), kLoadGameCmd); // Above the lowest button rows: two more buttons (directly below the list box) _addButton = - new ButtonWidget(this, "Launcher.AddGameButton", "Add Game...", kAddGameCmd, 'A'); + new ButtonWidget(this, "Launcher.AddGameButton", _("~A~dd Game..."), _("Hold Shift for Mass Add"), kAddGameCmd); _editButton = - new ButtonWidget(this, "Launcher.EditGameButton", "Edit Game...", kEditGameCmd, 'E'); + new ButtonWidget(this, "Launcher.EditGameButton", _("~E~dit Game..."), _("Change game options"), kEditGameCmd); _removeButton = - new ButtonWidget(this, "Launcher.RemoveGameButton", "Remove Game", kRemoveGameCmd, 'R'); + new ButtonWidget(this, "Launcher.RemoveGameButton", _("~R~emove Game"), _("Remove game from the list. The game data files stay intact"), kRemoveGameCmd); // Search box _searchDesc = 0; #ifndef DISABLE_FANCY_THEMES _searchPic = 0; if (g_gui.xmlEval()->getVar("Globals.ShowSearchPic") == 1 && g_gui.theme()->supportsImages()) { - _searchPic = new GraphicsWidget(this, "Launcher.SearchPic"); + _searchPic = new GraphicsWidget(this, "Launcher.SearchPic", _("Search in game list")); _searchPic->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageSearch)); } else #endif - _searchDesc = new StaticTextWidget(this, "Launcher.SearchDesc", "Search:"); + _searchDesc = new StaticTextWidget(this, "Launcher.SearchDesc", _("Search:")); - _searchWidget = new EditTextWidget(this, "Launcher.Search", _search, kSearchCmd); - _searchClearButton = new ButtonWidget(this, "Launcher.SearchClearButton", "C", kSearchClearCmd, 0); + _searchWidget = new EditTextWidget(this, "Launcher.Search", _search, 0, kSearchCmd); + _searchClearButton = new ButtonWidget(this, "Launcher.SearchClearButton", "C", _("Clear value"), kSearchClearCmd); // Add list with game titles - _list = new ListWidget(this, "Launcher.GameList", kListSearchCmd); + _list = new ListWidget(this, "Launcher.GameList", 0, kListSearchCmd); _list->setEditable(false); _list->setNumberingMode(kListNumberingOff); @@ -542,10 +544,10 @@ LauncherDialog::LauncherDialog() updateButtons(); // Create file browser dialog - _browser = new BrowserDialog("Select directory with game data", true); + _browser = new BrowserDialog(_("Select directory with game data"), true); // Create Load dialog - _loadDialog = new SaveLoadChooser("Load game:", "Load"); + _loadDialog = new SaveLoadChooser(_("Load game:"), _("Load")); } void LauncherDialog::selectTarget(const String &target) { @@ -617,8 +619,12 @@ void LauncherDialog::updateListing() { description = g.description(); } - if (description.empty()) - description = "Unknown (target " + iter->_key + ", gameid " + gameid + ")"; + if (description.empty()) { + char tmp[200]; + + snprintf(tmp, 200, "Unknown (target %s, gameid %s)", iter->_key.c_str(), gameid.c_str()); + description = tmp; + } if (!gameid.empty() && !description.empty()) { // Insert the game into the launcher list @@ -650,8 +656,8 @@ void LauncherDialog::addGame() { const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; if (massAdd) { - MessageDialog alert("Do you really want to run the mass game detector? " - "This could potentially add a huge number of games.", "Yes", "No"); + MessageDialog alert(_("Do you really want to run the mass game detector? " + "This could potentially add a huge number of games."), _("Yes"), _("No")); if (alert.runModal() == GUI::kMessageOK && _browser->runModal() > 0) { MassAddDialog massAddDlg(_browser->getResult()); @@ -698,7 +704,7 @@ void LauncherDialog::addGame() { Common::FSNode dir(_browser->getResult()); Common::FSList files; if (!dir.getChildren(files, Common::FSNode::kListAll)) { - MessageDialog alert("ScummVM couldn't open the specified directory!"); + MessageDialog alert("Residual couldn't open the specified directory!"); alert.runModal(); return; } @@ -710,7 +716,7 @@ void LauncherDialog::addGame() { int idx; if (candidates.empty()) { // No game was found in the specified directory - MessageDialog alert("ScummVM could not find any game in the specified directory!"); + MessageDialog alert(_("Residual could not find any game in the specified directory!")); alert.runModal(); idx = -1; @@ -724,7 +730,7 @@ void LauncherDialog::addGame() { for (idx = 0; idx < (int)candidates.size(); idx++) list.push_back(candidates[idx].description()); - ChooserDialog dialog("Pick the game:"); + ChooserDialog dialog(_("Pick the game:")); dialog.setList(list); idx = dialog.runModal(); } @@ -800,7 +806,7 @@ Common::String addGameToConf(const GameDescriptor &result) { } void LauncherDialog::removeGame(int item) { - MessageDialog alert("Do you really want to remove this game configuration?", "Yes", "No"); + MessageDialog alert(_("Do you really want to remove this game configuration?"), _("Yes"), _("No")); if (alert.runModal() == GUI::kMessageOK) { // Remove the currently selected game from the list @@ -863,11 +869,11 @@ void LauncherDialog::loadGame(int item) { } } else { MessageDialog dialog - ("This game does not support loading games from the launcher.", "OK"); + (_("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"); + MessageDialog dialog(_("Residual could not find any engine capable of running the selected game!"), _("OK")); dialog.runModal(); } } @@ -979,8 +985,8 @@ void LauncherDialog::updateButtons() { int modifiers = g_system->getEventManager()->getModifierState(); const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; const char *newAddButtonLabel = massAdd - ? "Mass Add..." - : "Add Game..."; + ? _("Mass Add...") + : _("Add Game..."); if (_addButton->getLabel() != newAddButtonLabel) _addButton->setLabel(newAddButtonLabel); @@ -1027,7 +1033,7 @@ void LauncherDialog::reflowLayout() { } } else { if (!_searchDesc) - _searchDesc = new StaticTextWidget(this, "Launcher.SearchDesc", "Search:"); + _searchDesc = new StaticTextWidget(this, "Launcher.SearchDesc", _("Search:")); if (_searchPic) { removeWidget(_searchPic); diff --git a/gui/massadd.cpp b/gui/massadd.cpp index 2893dbfc4a1..7f95669b573 100644 --- a/gui/massadd.cpp +++ b/gui/massadd.cpp @@ -27,6 +27,7 @@ #include "common/events.h" #include "common/func.h" #include "common/config-manager.h" +#include "common/translation.h" #include "gui/launcher.h" // For addGameToConf() #include "gui/massadd.h" @@ -75,10 +76,10 @@ MassAddDialog::MassAddDialog(const Common::FSNode &startDir) // new StaticTextWidget(this, "massadddialog_caption", "Mass Add Dialog"); _dirProgressText = new StaticTextWidget(this, "MassAdd.DirProgressText", - "... progress ..."); + _("... progress ...")); _gameProgressText = new StaticTextWidget(this, "MassAdd.GameProgressText", - "... progress ..."); + _("... progress ...")); _dirProgressText->setAlign(Graphics::kTextAlignCenter); _gameProgressText->setAlign(Graphics::kTextAlignCenter); @@ -88,10 +89,10 @@ MassAddDialog::MassAddDialog(const Common::FSNode &startDir) _list->setNumberingMode(kListNumberingOff); _list->setList(l); - _okButton = new ButtonWidget(this, "MassAdd.Ok", "OK", kOkCmd, Common::ASCII_RETURN); + _okButton = new ButtonWidget(this, "MassAdd.Ok", _("OK"), 0, kOkCmd, Common::ASCII_RETURN); _okButton->setEnabled(false); - new ButtonWidget(this, "MassAdd.Cancel", "Cancel", kCancelCmd, Common::ASCII_ESCAPE); + new ButtonWidget(this, "MassAdd.Cancel", _("Cancel"), 0, kCancelCmd, Common::ASCII_ESCAPE); // Build a map from all configured game paths to the targets using them const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains(); @@ -240,17 +241,17 @@ void MassAddDialog::handleTickle() { // Enable the OK button _okButton->setEnabled(true); - snprintf(buf, sizeof(buf), "Scan complete!"); + snprintf(buf, sizeof(buf), "%s", _("Scan complete!")); _dirProgressText->setLabel(buf); - snprintf(buf, sizeof(buf), "Discovered %d new games.", _games.size()); + snprintf(buf, sizeof(buf), _("Discovered %d new games."), _games.size()); _gameProgressText->setLabel(buf); } else { - snprintf(buf, sizeof(buf), "Scanned %d directories ...", _dirsScanned); + snprintf(buf, sizeof(buf), _("Scanned %d directories ..."), _dirsScanned); _dirProgressText->setLabel(buf); - snprintf(buf, sizeof(buf), "Discovered %d new games ...", _games.size()); + snprintf(buf, sizeof(buf), _("Discovered %d new games ..."), _games.size()); _gameProgressText->setLabel(buf); } diff --git a/gui/message.cpp b/gui/message.cpp index 0f4320197c5..247d79c00d1 100644 --- a/gui/message.cpp +++ b/gui/message.cpp @@ -89,10 +89,10 @@ MessageDialog::MessageDialog(const Common::String &message, const char *defaultB } if (defaultButton) - new ButtonWidget(this, okButtonPos, _h - buttonHeight - 8, buttonWidth, buttonHeight, defaultButton, kOkCmd, Common::ASCII_RETURN); // Confirm dialog + new ButtonWidget(this, okButtonPos, _h - buttonHeight - 8, buttonWidth, buttonHeight, defaultButton, 0, kOkCmd, Common::ASCII_RETURN); // Confirm dialog if (altButton) - new ButtonWidget(this, cancelButtonPos, _h - buttonHeight - 8, buttonWidth, buttonHeight, altButton, kCancelCmd, Common::ASCII_ESCAPE); // Cancel dialog + new ButtonWidget(this, cancelButtonPos, _h - buttonHeight - 8, buttonWidth, buttonHeight, altButton, 0, kCancelCmd, Common::ASCII_ESCAPE); // Cancel dialog } void MessageDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { diff --git a/gui/options.cpp b/gui/options.cpp index 4795f03d786..e4b3cfea47a 100644 --- a/gui/options.cpp +++ b/gui/options.cpp @@ -35,6 +35,7 @@ #include "common/fs.h" #include "common/config-manager.h" #include "common/system.h" +#include "common/translation.h" #include "graphics/scaler.h" @@ -61,15 +62,21 @@ enum { kChooseThemeCmd = 'chtf' }; +enum { + kSubtitlesSpeech, + kSubtitlesSubs, + kSubtitlesBoth +}; + #ifdef SMALL_SCREEN_DEVICE enum { kChooseKeyMappingCmd = 'chma' }; #endif -static const char *savePeriodLabels[] = { "Never", "every 5 mins", "every 10 mins", "every 15 mins", "every 30 mins", 0 }; +static const char *savePeriodLabels[] = { _s("Never"), _s("every 5 mins"), _s("every 10 mins"), _s("every 15 mins"), _s("every 30 mins"), 0 }; static const int savePeriodValues[] = { 0, 5 * 60, 10 * 60, 15 * 60, 30 * 60, -1 }; -static const char *outputRateLabels[] = { "", "8 kHz", "11kHz", "22 kHz", "44 kHz", "48 kHz", 0 }; +static const char *outputRateLabels[] = { _s(""), _s("8 kHz"), _s("11kHz"), _s("22 kHz"), _s("44 kHz"), _s("48 kHz"), 0 }; static const int outputRateValues[] = { 0, 8000, 11025, 22050, 44100, 48000, -1 }; @@ -84,18 +91,6 @@ OptionsDialog::OptionsDialog(const Common::String &domain, const Common::String init(); } -const char *OptionsDialog::_subModeDesc[] = { - "Speech Only", - "Speech and Subtitles", - "Subtitles Only" -}; - -const char *OptionsDialog::_lowresSubModeDesc[] = { - "Speech Only", - "Speech & Subs", - "Subtitles Only" -}; - void OptionsDialog::init() { _enableGraphicSettings = false; _gfxPopUp = 0; @@ -122,15 +117,20 @@ void OptionsDialog::init() { _speechVolumeLabel = 0; _muteCheckbox = 0; _subToggleDesc = 0; - _subToggleButton = 0; + _subToggleGroup = 0; + _subToggleSubOnly = 0; + _subToggleSpeechOnly = 0; + _subToggleSubBoth = 0; _subSpeedDesc = 0; _subSpeedSlider = 0; _subSpeedLabel = 0; // Retrieve game GUI options _guioptions = 0; - if (ConfMan.hasKey("guioptions", _domain)) - _guioptions = parseGameGUIOptions(ConfMan.get("guioptions", _domain)); + if (ConfMan.hasKey("guioptions", _domain)) { + _guioptionsString = ConfMan.get("guioptions", _domain); + _guioptions = parseGameGUIOptions(_guioptionsString); + } } void OptionsDialog::open() { @@ -141,8 +141,10 @@ void OptionsDialog::open() { // Retrieve game GUI options _guioptions = 0; - if (ConfMan.hasKey("guioptions", _domain)) - _guioptions = parseGameGUIOptions(ConfMan.get("guioptions", _domain)); + if (ConfMan.hasKey("guioptions", _domain)) { + _guioptionsString = ConfMan.get("guioptions", _domain); + _guioptions = parseGameGUIOptions(_guioptionsString); + } // Audio options if (_midiPopUp) { @@ -178,7 +180,7 @@ void OptionsDialog::open() { Common::String soundFont(ConfMan.get("soundfont", _domain)); if (soundFont.empty() || !ConfMan.hasKey("soundfont", _domain)) { - _soundFont->setLabel("None"); + _soundFont->setLabel(_("None")); _soundFontClearButton->setEnabled(false); } else { _soundFont->setLabel(soundFont); @@ -276,7 +278,7 @@ void OptionsDialog::close() { ConfMan.setInt("midi_gain", _midiGainSlider->getValue(), _domain); Common::String soundFont(_soundFont->getLabel()); - if (!soundFont.empty() && (soundFont != "None")) + if (!soundFont.empty() && (soundFont != _("None"))) ConfMan.set("soundfont", soundFont, _domain); else ConfMan.removeKey("soundfont", _domain); @@ -316,24 +318,13 @@ void OptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data _speechVolumeLabel->setValue(_speechVolumeSlider->getValue()); _speechVolumeLabel->draw(); break; - case kSubtitleToggle: - if (_subMode < 2) - _subMode++; - else - _subMode = 0; - - _subToggleButton->setLabel(g_system->getOverlayWidth() > 320 ? _subModeDesc[_subMode] : _lowresSubModeDesc[_subMode]); - _subToggleButton->draw(); - _subSpeedDesc->draw(); - _subSpeedSlider->draw(); - _subSpeedLabel->draw(); break; case kSubtitleSpeedChanged: _subSpeedLabel->setValue(_subSpeedSlider->getValue()); _subSpeedLabel->draw(); break; case kClearSoundFontCmd: - _soundFont->setLabel("None"); + _soundFont->setLabel(_("None")); _soundFontClearButton->setEnabled(false); draw(); break; @@ -358,8 +349,18 @@ void OptionsDialog::setAudioSettingsState(bool enabled) { _midiPopUpDesc->setEnabled(enabled); _midiPopUp->setEnabled(enabled); - _oplPopUpDesc->setEnabled(enabled); - _oplPopUp->setEnabled(enabled); + + uint32 allFlags = MidiDriver::midiDriverFlags2GUIO((uint32)-1); + + if (_domain != Common::ConfigManager::kApplicationDomain && // global dialog + (_guioptions & allFlags) && // No flags are specified + !(_guioptions & Common::GUIO_MIDIADLIB)) { + _oplPopUpDesc->setEnabled(false); + _oplPopUp->setEnabled(false); + } else { + _oplPopUpDesc->setEnabled(enabled); + _oplPopUp->setEnabled(enabled); + } _outputRatePopUpDesc->setEnabled(enabled); _outputRatePopUp->setEnabled(enabled); } @@ -373,7 +374,7 @@ void OptionsDialog::setMIDISettingsState(bool enabled) { _soundFontButton->setEnabled(enabled); _soundFont->setEnabled(enabled); - if (enabled && !_soundFont->getLabel().empty() && (_soundFont->getLabel() != "None")) + if (enabled && !_soundFont->getLabel().empty() && (_soundFont->getLabel() != _("None"))) _soundFontClearButton->setEnabled(enabled); else _soundFontClearButton->setEnabled(false); @@ -424,7 +425,7 @@ void OptionsDialog::setSubtitleSettingsState(bool enabled) { if ((_guioptions & Common::GUIO_NOSUBTITLES) || (_guioptions & Common::GUIO_NOSPEECH)) ena = false; - _subToggleButton->setEnabled(ena); + _subToggleGroup->setEnabled(ena); _subToggleDesc->setEnabled(ena); ena = enabled; @@ -443,33 +444,38 @@ void OptionsDialog::addGraphicControls(GuiObject *boss, const Common::String &pr void OptionsDialog::addAudioControls(GuiObject *boss, const Common::String &prefix) { // The MIDI mode popup & a label - _midiPopUpDesc = new StaticTextWidget(boss, prefix + "auMidiPopupDesc", "Music driver:"); - _midiPopUp = new PopUpWidget(boss, prefix + "auMidiPopup"); + _midiPopUpDesc = new StaticTextWidget(boss, prefix + "auMidiPopupDesc", _("Music driver:"), _("Specifies output sound device or sound card emulator")); + _midiPopUp = new PopUpWidget(boss, prefix + "auMidiPopup", _("Specifies output sound device or sound card emulator")); // Populate it const MidiDriverDescription *md = MidiDriver::getAvailableMidiDrivers(); + uint32 allFlags = MidiDriver::midiDriverFlags2GUIO((uint32)-1); + while (md->name) { - _midiPopUp->appendEntry(md->description, md->id); + if (_domain == Common::ConfigManager::kApplicationDomain || // global dialog + !(_guioptions & allFlags) || // No flags are specified + _guioptions & (MidiDriver::midiDriverFlags2GUIO(md->flags))) // flag is present + _midiPopUp->appendEntry(_(md->description), md->id); md++; } // The OPL emulator popup & a label - _oplPopUpDesc = new StaticTextWidget(boss, prefix + "auOPLPopupDesc", "AdLib emulator:"); - _oplPopUp = new PopUpWidget(boss, prefix + "auOPLPopup"); + _oplPopUpDesc = new StaticTextWidget(boss, prefix + "auOPLPopupDesc", _("AdLib emulator:"), _("AdLib is used for music in many games")); + _oplPopUp = new PopUpWidget(boss, prefix + "auOPLPopup", _("AdLib is used for music in many games")); // Populate it const OPL::Config::EmulatorDescription *ed = OPL::Config::getAvailable(); while (ed->name) { - _oplPopUp->appendEntry(ed->description, ed->id); + _oplPopUp->appendEntry(_(ed->description), ed->id); ++ed; } // Sample rate settings - _outputRatePopUpDesc = new StaticTextWidget(boss, prefix + "auSampleRatePopupDesc", "Output rate:"); - _outputRatePopUp = new PopUpWidget(boss, prefix + "auSampleRatePopup"); + _outputRatePopUpDesc = new StaticTextWidget(boss, prefix + "auSampleRatePopupDesc", _("Output rate:"), _("Higher value specifies better sound quality but may be not supported by your soundcard")); + _outputRatePopUp = new PopUpWidget(boss, prefix + "auSampleRatePopup", _("Higher value specifies better sound quality but may be not supported by your soundcard")); for (int i = 0; outputRateLabels[i]; i++) { - _outputRatePopUp->appendEntry(outputRateLabels[i], outputRateValues[i]); + _outputRatePopUp->appendEntry(_(outputRateLabels[i]), outputRateValues[i]); } _enableAudioSettings = true; @@ -477,22 +483,22 @@ void OptionsDialog::addAudioControls(GuiObject *boss, const Common::String &pref void OptionsDialog::addMIDIControls(GuiObject *boss, const Common::String &prefix) { // SoundFont - _soundFontButton = new ButtonWidget(boss, prefix + "mcFontButton", "SoundFont:", kChooseSoundFontCmd, 0); - _soundFont = new StaticTextWidget(boss, prefix + "mcFontPath", "None"); - _soundFontClearButton = new ButtonWidget(boss, prefix + "mcFontClearButton", "C", kClearSoundFontCmd, 0); + _soundFontButton = new ButtonWidget(boss, prefix + "mcFontButton", _("SoundFont:"), _("SoundFont is supported by some audio cards, Fluidsynth and Timidity"), kChooseSoundFontCmd); + _soundFont = new StaticTextWidget(boss, prefix + "mcFontPath", _("None"), _("SoundFont is supported by some audio cards, Fluidsynth and Timidity")); + _soundFontClearButton = new ButtonWidget(boss, prefix + "mcFontClearButton", "C", _("Clear value"), kClearSoundFontCmd); // Multi midi setting - _multiMidiCheckbox = new CheckboxWidget(boss, prefix + "mcMixedCheckbox", "Mixed AdLib/MIDI mode", 0, 0); + _multiMidiCheckbox = new CheckboxWidget(boss, prefix + "mcMixedCheckbox", _("Mixed AdLib/MIDI mode"), _("Use both MIDI and AdLib sound generation")); // Native mt32 setting - _mt32Checkbox = new CheckboxWidget(boss, prefix + "mcMt32Checkbox", "True Roland MT-32 (disable GM emulation)", 0, 0); + _mt32Checkbox = new CheckboxWidget(boss, prefix + "mcMt32Checkbox", _("True Roland MT-32 (disable GM emulation)"), _("Check if you want to use your real hardware Roland-compatible sound device connected to your computer")); // GS Extensions setting - _enableGSCheckbox = new CheckboxWidget(boss, prefix + "mcGSCheckbox", "Enable Roland GS Mode", 0, 0); + _enableGSCheckbox = new CheckboxWidget(boss, prefix + "mcGSCheckbox", _("Enable Roland GS Mode"), _("Turns off General MIDI mapping for games with Roland MT-32 soundtrack")); // MIDI gain setting (FluidSynth uses this) - _midiGainDesc = new StaticTextWidget(boss, prefix + "mcMidiGainText", "MIDI gain:"); - _midiGainSlider = new SliderWidget(boss, prefix + "mcMidiGainSlider", kMidiGainChanged); + _midiGainDesc = new StaticTextWidget(boss, prefix + "mcMidiGainText", _("MIDI gain:")); + _midiGainSlider = new SliderWidget(boss, prefix + "mcMidiGainSlider", 0, kMidiGainChanged); _midiGainSlider->setMinValue(0); _midiGainSlider->setMaxValue(1000); _midiGainLabel = new StaticTextWidget(boss, prefix + "mcMidiGainLabel", "1.00"); @@ -504,12 +510,25 @@ void OptionsDialog::addMIDIControls(GuiObject *boss, const Common::String &prefi // make use of the widgets. The launcher range is 0-255. SCUMM's 0-9 void OptionsDialog::addSubtitleControls(GuiObject *boss, const Common::String &prefix, int maxSliderVal) { - _subToggleDesc = new StaticTextWidget(boss, prefix + "subToggleDesc", "Text and Speech:"); - _subToggleButton = new ButtonWidget(boss, prefix + "subToggleButton", "", kSubtitleToggle, 0); + _subToggleDesc = new StaticTextWidget(boss, prefix + "subToggleDesc", _("Text and Speech:")); + + if (g_system->getOverlayWidth() > 320) { + _subToggleGroup = new RadiobuttonGroup(boss, kSubtitleToggle); + + _subToggleSpeechOnly = new RadiobuttonWidget(boss, prefix + "subToggleSpeechOnly", _subToggleGroup, kSubtitlesSpeech, _("Speech")); + _subToggleSubOnly = new RadiobuttonWidget(boss, prefix + "subToggleSubOnly", _subToggleGroup, kSubtitlesSubs, _("Subtitles")); + _subToggleSubBoth = new RadiobuttonWidget(boss, prefix + "subToggleSubBoth", _subToggleGroup, kSubtitlesBoth, _("Both")); + } else { + _subToggleGroup = new RadiobuttonGroup(boss, kSubtitleToggle); + + _subToggleSpeechOnly = new RadiobuttonWidget(boss, prefix + "subToggleSpeechOnly", _subToggleGroup, kSubtitlesSpeech, _("Spch"), _("Speech")); + _subToggleSubOnly = new RadiobuttonWidget(boss, prefix + "subToggleSubOnly", _subToggleGroup, kSubtitlesSubs, _("Subs"), _("Subtitles")); + _subToggleSubBoth = new RadiobuttonWidget(boss, prefix + "subToggleSubBoth", _subToggleGroup, kSubtitlesBoth, _("Both"), _("Show subtitles and play speech")); + } // Subtitle speed - _subSpeedDesc = new StaticTextWidget(boss, prefix + "subSubtitleSpeedDesc", "Subtitle speed:"); - _subSpeedSlider = new SliderWidget(boss, prefix + "subSubtitleSpeedSlider", kSubtitleSpeedChanged); + _subSpeedDesc = new StaticTextWidget(boss, prefix + "subSubtitleSpeedDesc", _("Subtitle speed:")); + _subSpeedSlider = new SliderWidget(boss, prefix + "subSubtitleSpeedSlider", 0, kSubtitleSpeedChanged); _subSpeedLabel = new StaticTextWidget(boss, prefix + "subSubtitleSpeedLabel", "100%"); _subSpeedSlider->setMinValue(0); _subSpeedSlider->setMaxValue(maxSliderVal); _subSpeedLabel->setFlags(WIDGET_CLEARBG); @@ -520,22 +539,22 @@ void OptionsDialog::addSubtitleControls(GuiObject *boss, const Common::String &p void OptionsDialog::addVolumeControls(GuiObject *boss, const Common::String &prefix) { // Volume controllers - _musicVolumeDesc = new StaticTextWidget(boss, prefix + "vcMusicText", "Music volume:"); - _musicVolumeSlider = new SliderWidget(boss, prefix + "vcMusicSlider", kMusicVolumeChanged); + _musicVolumeDesc = new StaticTextWidget(boss, prefix + "vcMusicText", _("Music volume:")); + _musicVolumeSlider = new SliderWidget(boss, prefix + "vcMusicSlider", 0, kMusicVolumeChanged); _musicVolumeLabel = new StaticTextWidget(boss, prefix + "vcMusicLabel", "100%"); _musicVolumeSlider->setMinValue(0); _musicVolumeSlider->setMaxValue(Audio::Mixer::kMaxMixerVolume); _musicVolumeLabel->setFlags(WIDGET_CLEARBG); - _sfxVolumeDesc = new StaticTextWidget(boss, prefix + "vcSfxText", "SFX volume:"); - _sfxVolumeSlider = new SliderWidget(boss, prefix + "vcSfxSlider", kSfxVolumeChanged); + _sfxVolumeDesc = new StaticTextWidget(boss, prefix + "vcSfxText", _("SFX volume:"), _("Special sound effects volume")); + _sfxVolumeSlider = new SliderWidget(boss, prefix + "vcSfxSlider", _("Special sound effects volume"), kSfxVolumeChanged); _sfxVolumeLabel = new StaticTextWidget(boss, prefix + "vcSfxLabel", "100%"); _sfxVolumeSlider->setMinValue(0); _sfxVolumeSlider->setMaxValue(Audio::Mixer::kMaxMixerVolume); _sfxVolumeLabel->setFlags(WIDGET_CLEARBG); - _speechVolumeDesc = new StaticTextWidget(boss, prefix + "vcSpeechText" , "Speech volume:"); - _speechVolumeSlider = new SliderWidget(boss, prefix + "vcSpeechSlider", kSpeechVolumeChanged); + _speechVolumeDesc = new StaticTextWidget(boss, prefix + "vcSpeechText" , _("Speech volume:")); + _speechVolumeSlider = new SliderWidget(boss, prefix + "vcSpeechSlider", 0, kSpeechVolumeChanged); _speechVolumeLabel = new StaticTextWidget(boss, prefix + "vcSpeechLabel", "100%"); _speechVolumeSlider->setMinValue(0); _speechVolumeSlider->setMaxValue(Audio::Mixer::kMaxMixerVolume); @@ -546,24 +565,24 @@ void OptionsDialog::addVolumeControls(GuiObject *boss, const Common::String &pre int OptionsDialog::getSubtitleMode(bool subtitles, bool speech_mute) { if (_guioptions & Common::GUIO_NOSUBTITLES) - return 0; // Speech only + return kSubtitlesSpeech; // Speech only if (_guioptions & Common::GUIO_NOSPEECH) - return 2; // Subtitles only + return kSubtitlesSubs; // Subtitles only if (!subtitles && !speech_mute) // Speech only - return 0; + return kSubtitlesSpeech; else if (subtitles && !speech_mute) // Speech and subtitles - return 1; + return kSubtitlesBoth; else if (subtitles && speech_mute) // Subtitles only - return 2; + return kSubtitlesSubs; else warning("Wrong configuration: Both subtitles and speech are off. Assuming subtitles only"); - return 2; + return kSubtitlesSubs; } void OptionsDialog::reflowLayout() { if (_graphicsTabId != -1 && _tabWidget) - _tabWidget->setTabTitle(_graphicsTabId, g_system->getOverlayWidth() > 320 ? "Graphics" : "GFX"); + _tabWidget->setTabTitle(_graphicsTabId, g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX")); Dialog::reflowLayout(); } @@ -580,16 +599,17 @@ GlobalOptionsDialog::GlobalOptionsDialog() // // 1) The graphics tab // - _graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? "Graphics" : "GFX"); + _graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX")); addGraphicControls(tab, "GlobalOptions_Graphics."); // // 2) The audio tab // - tab->addTab("Audio"); + tab->addTab(_("Audio")); addAudioControls(tab, "GlobalOptions_Audio."); + addSubtitleControls(tab, "GlobalOptions_Audio."); - tab->addTab("Volume"); + tab->addTab(_("Volume")); addVolumeControls(tab, "GlobalOptions_Volume."); // TODO: cd drive setting @@ -597,67 +617,85 @@ GlobalOptionsDialog::GlobalOptionsDialog() // // 3) The MIDI tab // - tab->addTab("MIDI"); + tab->addTab(_("MIDI")); addMIDIControls(tab, "GlobalOptions_MIDI."); // // 4) The miscellaneous tab // - tab->addTab("Paths"); + tab->addTab(_("Paths")); #if !( defined(__DC__) || defined(__GP32__) ) // These two buttons have to be extra wide, or the text will be // truncated in the small version of the GUI. // Save game path - new ButtonWidget(tab, "GlobalOptions_Paths.SaveButton", "Save Path: ", kChooseSaveDirCmd, 0); - _savePath = new StaticTextWidget(tab, "GlobalOptions_Paths.SavePath", "/foo/bar"); + new ButtonWidget(tab, "GlobalOptions_Paths.SaveButton", _("Save Path: "), _("Specifies where your savegames are put"), kChooseSaveDirCmd); + _savePath = new StaticTextWidget(tab, "GlobalOptions_Paths.SavePath", "/foo/bar", _("Specifies where your savegames are put")); - new ButtonWidget(tab, "GlobalOptions_Paths.ThemeButton", "Theme Path:", kChooseThemeDirCmd, 0); - _themePath = new StaticTextWidget(tab, "GlobalOptions_Paths.ThemePath", "None"); + new ButtonWidget(tab, "GlobalOptions_Paths.ThemeButton", _("Theme Path:"), 0, kChooseThemeDirCmd); + _themePath = new StaticTextWidget(tab, "GlobalOptions_Paths.ThemePath", _("None")); - new ButtonWidget(tab, "GlobalOptions_Paths.ExtraButton", "Extra Path:", kChooseExtraDirCmd, 0); - _extraPath = new StaticTextWidget(tab, "GlobalOptions_Paths.ExtraPath", "None"); + new ButtonWidget(tab, "GlobalOptions_Paths.ExtraButton", _("Extra Path:"), _("Specifies path to additional data used by all games or ScummVM"), kChooseExtraDirCmd); + _extraPath = new StaticTextWidget(tab, "GlobalOptions_Paths.ExtraPath", _("None"), _("Specifies path to additional data used by all games or ScummVM")); #ifdef DYNAMIC_MODULES - new ButtonWidget(tab, "GlobalOptions_Paths.PluginsButton", "Plugins Path:", kChoosePluginsDirCmd, 0); - _pluginsPath = new StaticTextWidget(tab, "GlobalOptions_Paths.PluginsPath", "None"); + new ButtonWidget(tab, "GlobalOptions_Paths.PluginsButton", _("Plugins Path:"), 0, kChoosePluginsDirCmd); + _pluginsPath = new StaticTextWidget(tab, "GlobalOptions_Paths.PluginsPath", _("None")); #endif #endif - tab->addTab("Misc"); + tab->addTab(_("Misc")); - new ButtonWidget(tab, "GlobalOptions_Misc.ThemeButton", "Theme:", kChooseThemeCmd, 0); + new ButtonWidget(tab, "GlobalOptions_Misc.ThemeButton", _("Theme:"), 0, kChooseThemeCmd); _curTheme = new StaticTextWidget(tab, "GlobalOptions_Misc.CurTheme", g_gui.theme()->getThemeName()); - _rendererPopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Misc.RendererPopupDesc", "GUI Renderer:"); + _rendererPopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Misc.RendererPopupDesc", _("GUI Renderer:")); _rendererPopUp = new PopUpWidget(tab, "GlobalOptions_Misc.RendererPopup"); for (uint i = 1; i < GUI::ThemeEngine::_rendererModesSize; ++i) - _rendererPopUp->appendEntry(GUI::ThemeEngine::_rendererModes[i].name, GUI::ThemeEngine::_rendererModes[i].mode); + _rendererPopUp->appendEntry(_(GUI::ThemeEngine::_rendererModes[i].name), GUI::ThemeEngine::_rendererModes[i].mode); - _autosavePeriodPopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Misc.AutosavePeriodPopupDesc", "Autosave:"); + _autosavePeriodPopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Misc.AutosavePeriodPopupDesc", _("Autosave:")); _autosavePeriodPopUp = new PopUpWidget(tab, "GlobalOptions_Misc.AutosavePeriodPopup"); for (int i = 0; savePeriodLabels[i]; i++) { - _autosavePeriodPopUp->appendEntry(savePeriodLabels[i], savePeriodValues[i]); + _autosavePeriodPopUp->appendEntry(_(savePeriodLabels[i]), savePeriodValues[i]); } #ifdef SMALL_SCREEN_DEVICE - new ButtonWidget(tab, "GlobalOptions_Misc.KeysButton", "Keys", kChooseKeyMappingCmd, 0); + new ButtonWidget(tab, "GlobalOptions_Misc.KeysButton", _("Keys"), 0, kChooseKeyMappingCmd); #endif // TODO: joystick setting +#ifdef USE_TRANSLATION + _guiLanguagePopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Misc.GuiLanguagePopupDesc", _("GUI Language:"), _("Language of ScummVM GUI")); + _guiLanguagePopUp = new PopUpWidget(tab, "GlobalOptions_Misc.GuiLanguagePopup"); +#ifdef USE_DETECTLANG + _guiLanguagePopUp->appendEntry(_(""), Common::kTranslationAutodetectId); +#endif // USE_DETECTLANG + _guiLanguagePopUp->appendEntry(_("English"), Common::kTranslationBuiltinId); + _guiLanguagePopUp->appendEntry("", 0); + Common::TLangArray languages = TransMan.getSupportedLanguages(); + Common::TLangArray::iterator lang = languages.begin(); + while (lang != languages.end()) { + _guiLanguagePopUp->appendEntry(lang->name, lang->id); + lang++; + } + _guiLanguagePopUp->setSelectedTag(TransMan.parseLanguage(ConfMan.get("gui_language").c_str())); + +#endif // USE_TRANSLATION + // Activate the first tab tab->setActiveTab(0); _tabWidget = tab; // Add OK & Cancel buttons - new ButtonWidget(this, "GlobalOptions.Cancel", "Cancel", kCloseCmd, 0); - new ButtonWidget(this, "GlobalOptions.Ok", "OK", kOKCmd, 0); + new ButtonWidget(this, "GlobalOptions.Cancel", _("Cancel"), 0, kCloseCmd); + new ButtonWidget(this, "GlobalOptions.Ok", _("OK"), 0, kOKCmd); #ifdef SMALL_SCREEN_DEVICE _keysDialog = new KeysDialog(); @@ -680,19 +718,19 @@ void GlobalOptionsDialog::open() { Common::String extraPath(ConfMan.get("extrapath", _domain)); if (savePath.empty() || !ConfMan.hasKey("savepath", _domain)) { - _savePath->setLabel("None"); + _savePath->setLabel(_("None")); } else { _savePath->setLabel(savePath); } if (themePath.empty() || !ConfMan.hasKey("themepath", _domain)) { - _themePath->setLabel("None"); + _themePath->setLabel(_("None")); } else { _themePath->setLabel(themePath); } if (extraPath.empty() || !ConfMan.hasKey("extrapath", _domain)) { - _extraPath->setLabel("None"); + _extraPath->setLabel(_("None")); } else { _extraPath->setLabel(extraPath); } @@ -700,7 +738,7 @@ void GlobalOptionsDialog::open() { #ifdef DYNAMIC_MODULES Common::String pluginsPath(ConfMan.get("pluginspath", _domain)); if (pluginsPath.empty() || !ConfMan.hasKey("pluginspath", _domain)) { - _pluginsPath->setLabel("None"); + _pluginsPath->setLabel(_("None")); } else { _pluginsPath->setLabel(pluginsPath); } @@ -724,24 +762,24 @@ void GlobalOptionsDialog::open() { void GlobalOptionsDialog::close() { if (getResult()) { Common::String savePath(_savePath->getLabel()); - if (!savePath.empty() && (savePath != "None")) + if (!savePath.empty() && (savePath != _("None"))) ConfMan.set("savepath", savePath, _domain); Common::String themePath(_themePath->getLabel()); - if (!themePath.empty() && (themePath != "None")) + if (!themePath.empty() && (themePath != _("None"))) ConfMan.set("themepath", themePath, _domain); else ConfMan.removeKey("themepath", _domain); Common::String extraPath(_extraPath->getLabel()); - if (!extraPath.empty() && (extraPath != "None")) + if (!extraPath.empty() && (extraPath != _("None"))) ConfMan.set("extrapath", extraPath, _domain); else ConfMan.removeKey("extrapath", _domain); #ifdef DYNAMIC_MODULES Common::String pluginsPath(_pluginsPath->getLabel()); - if (!pluginsPath.empty() && (pluginsPath != "None")) + if (!pluginsPath.empty() && (pluginsPath != _("None"))) ConfMan.set("pluginspath", pluginsPath, _domain); else ConfMan.removeKey("pluginspath", _domain); @@ -757,6 +795,28 @@ void GlobalOptionsDialog::close() { g_gui.loadNewTheme(g_gui.theme()->getThemeId(), selected); ConfMan.set("gui_renderer", cfg, _domain); } +#ifdef USE_TRANSLATION + Common::String oldLang = ConfMan.get("gui_language"); + int selLang = _guiLanguagePopUp->getSelectedTag(); + + ConfMan.set("gui_language", TransMan.getLangById(selLang)); + + Common::String newLang = ConfMan.get("gui_language").c_str(); + if (newLang != oldLang) { +#if 0 + // Activate the selected language + TransMan.setLanguage(selLang); + + // FIXME: Actually, any changes (including the theme change) should + // only become active *after* the options dialog has closed. + g_gui.loadNewTheme(g_gui.theme()->getThemeId(), ThemeEngine::kGfxDisabled, true); +#else + MessageDialog error(_("You have to restart ScummVM to take the effect.")); + error.runModal(); +#endif + } +#endif // USE_TRANSLATION + } OptionsDialog::close(); } @@ -764,14 +824,14 @@ void GlobalOptionsDialog::close() { void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { switch (cmd) { case kChooseSaveDirCmd: { - BrowserDialog browser("Select directory for savegames", true); + BrowserDialog browser(_("Select directory for savegames"), true); if (browser.runModal() > 0) { // User made his choice... Common::FSNode dir(browser.getResult()); if (dir.isWritable()) { _savePath->setLabel(dir.getPath()); } else { - MessageDialog error("The chosen directory cannot be written to. Please select another one."); + MessageDialog error(_("The chosen directory cannot be written to. Please select another one.")); error.runModal(); return; } @@ -780,7 +840,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3 break; } case kChooseThemeDirCmd: { - BrowserDialog browser("Select directory for GUI themes", true); + BrowserDialog browser(_("Select directory for GUI themes"), true); if (browser.runModal() > 0) { // User made his choice... Common::FSNode dir(browser.getResult()); @@ -790,7 +850,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3 break; } case kChooseExtraDirCmd: { - BrowserDialog browser("Select directory for extra files", true); + BrowserDialog browser(_("Select directory for extra files"), true); if (browser.runModal() > 0) { // User made his choice... Common::FSNode dir(browser.getResult()); @@ -801,7 +861,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3 } #ifdef DYNAMIC_MODULES case kChoosePluginsDirCmd: { - BrowserDialog browser("Select directory for plugins", true); + BrowserDialog browser(_("Select directory for plugins"), true); if (browser.runModal() > 0) { // User made his choice... Common::FSNode dir(browser.getResult()); @@ -812,13 +872,13 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3 } #endif case kChooseSoundFontCmd: { - BrowserDialog browser("Select SoundFont", false); + BrowserDialog browser(_("Select SoundFont"), false); if (browser.runModal() > 0) { // User made his choice... Common::FSNode file(browser.getResult()); _soundFont->setLabel(file.getPath()); - if (!file.getPath().empty() && (file.getPath() != "None")) + if (!file.getPath().empty() && (file.getPath() != _("None"))) _soundFontClearButton->setEnabled(true); else _soundFontClearButton->setEnabled(false); diff --git a/gui/options.h b/gui/options.h index 59d9eddd68a..f4520047278 100644 --- a/gui/options.h +++ b/gui/options.h @@ -121,7 +121,10 @@ private: int getSubtitleMode(bool subtitles, bool speech_mute); bool _enableSubtitleSettings; StaticTextWidget *_subToggleDesc; - ButtonWidget *_subToggleButton; + RadiobuttonGroup *_subToggleGroup; + RadiobuttonWidget *_subToggleSubOnly; + RadiobuttonWidget *_subToggleSpeechOnly; + RadiobuttonWidget *_subToggleSubBoth; int _subMode; static const char *_subModeDesc[]; static const char *_lowresSubModeDesc[]; @@ -153,6 +156,7 @@ protected: // Game GUI options // uint32 _guioptions; + Common::String _guioptionsString; }; @@ -184,6 +188,8 @@ protected: PopUpWidget *_rendererPopUp; StaticTextWidget *_autosavePeriodPopUpDesc; PopUpWidget *_autosavePeriodPopUp; + StaticTextWidget *_guiLanguagePopUpDesc; + PopUpWidget *_guiLanguagePopUp; }; } // End of namespace GUI diff --git a/gui/saveload.cpp b/gui/saveload.cpp index b04b9b1844a..1c3f411fc4b 100644 --- a/gui/saveload.cpp +++ b/gui/saveload.cpp @@ -23,6 +23,7 @@ */ #include "common/config-manager.h" +#include "common/translation.h" #include "gui/ListWidget.h" #include "gui/message.h" @@ -56,16 +57,16 @@ SaveLoadChooser::SaveLoadChooser(const String &title, const String &buttonLabel) _gfxWidget = new GUI::GraphicsWidget(this, 0, 0, 10, 10); - _date = new StaticTextWidget(this, 0, 0, 10, 10, "No date saved", Graphics::kTextAlignCenter); - _time = new StaticTextWidget(this, 0, 0, 10, 10, "No time saved", Graphics::kTextAlignCenter); - _playtime = new StaticTextWidget(this, 0, 0, 10, 10, "No playtime saved", Graphics::kTextAlignCenter); + _date = new StaticTextWidget(this, 0, 0, 10, 10, _("No date saved"), Graphics::kTextAlignCenter); + _time = new StaticTextWidget(this, 0, 0, 10, 10, _("No time saved"), Graphics::kTextAlignCenter); + _playtime = new StaticTextWidget(this, 0, 0, 10, 10, _("No playtime saved"), Graphics::kTextAlignCenter); // Buttons - new GUI::ButtonWidget(this, "SaveLoadChooser.Cancel", "Cancel", kCloseCmd, 0); - _chooseButton = new GUI::ButtonWidget(this, "SaveLoadChooser.Choose", buttonLabel, kChooseCmd, 0); + new GUI::ButtonWidget(this, "SaveLoadChooser.Cancel", _("Cancel"), 0, kCloseCmd); + _chooseButton = new GUI::ButtonWidget(this, "SaveLoadChooser.Choose", buttonLabel, 0, kChooseCmd); _chooseButton->setEnabled(false); - _deleteButton = new GUI::ButtonWidget(this, "SaveLoadChooser.Delete", "Delete", kDelCmd, 0); + _deleteButton = new GUI::ButtonWidget(this, "SaveLoadChooser.Delete", _("Delete"), 0, kDelCmd); _deleteButton->setEnabled(false); _delSupport = _metaInfoSupport = _thumbnailSupport = false; @@ -152,8 +153,8 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da break; case kDelCmd: if (selItem >= 0 && _delSupport) { - MessageDialog alert("Do you really want to delete this savegame?", - "Delete", "Cancel"); + 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())); @@ -237,9 +238,9 @@ void SaveLoadChooser::updateSelection(bool redraw) { bool startEditMode = _list->isEditable(); _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB); - _date->setLabel("No date saved"); - _time->setLabel("No time saved"); - _playtime->setLabel("No playtime saved"); + _date->setLabel(_("No date saved")); + _time->setLabel(_("No time saved")); + _playtime->setLabel(_("No playtime saved")); if (selItem >= 0 && !_list->getSelectedString().empty() && _metaInfoSupport) { SaveStateDescriptor desc = (*_plugin)->querySaveMetaInfos(_target.c_str(), atoi(_saveList[selItem].save_slot().c_str())); @@ -261,15 +262,15 @@ void SaveLoadChooser::updateSelection(bool redraw) { if (_saveDateSupport) { if (desc.contains("save_date")) - _date->setLabel("Date: " + desc.getVal("save_date")); + _date->setLabel(_("Date: ") + desc.getVal("save_date")); if (desc.contains("save_time")) - _time->setLabel("Time: " + desc.getVal("save_time")); + _time->setLabel(_("Time: ") + desc.getVal("save_time")); } if (_playTimeSupport) { if (desc.contains("play_time")) - _playtime->setLabel("Playtime: " + desc.getVal("play_time")); + _playtime->setLabel(_("Playtime: ") + desc.getVal("play_time")); } } @@ -282,7 +283,7 @@ void SaveLoadChooser::updateSelection(bool redraw) { if (startEditMode) { _list->startEditMode(); - if (_chooseButton->isEnabled() && _list->getSelectedString() == "Untitled savestate" && + if (_chooseButton->isEnabled() && _list->getSelectedString() == _("Untitled savestate") && _list->getSelectionColor() == ThemeEngine::kFontColorAlternate) { _list->setEditString(""); _list->setEditColor(ThemeEngine::kFontColorNormal); @@ -349,7 +350,7 @@ void SaveLoadChooser::updateSaveList() { Common::String trimmedDescription = description; trimmedDescription.trim(); if (trimmedDescription.empty()) { - description = "Untitled savestate"; + description = _("Untitled savestate"); colors.push_back(ThemeEngine::kFontColorAlternate); } else { colors.push_back(ThemeEngine::kFontColorNormal); diff --git a/gui/themebrowser.cpp b/gui/themebrowser.cpp index e0db7569546..9ac55b9af59 100644 --- a/gui/themebrowser.cpp +++ b/gui/themebrowser.cpp @@ -26,6 +26,8 @@ #include "gui/ListWidget.h" #include "gui/widget.h" +#include "common/translation.h" + namespace GUI { enum { @@ -41,7 +43,7 @@ enum { ThemeBrowser::ThemeBrowser() : Dialog("Browser") { _fileList = 0; - new StaticTextWidget(this, "Browser.Headline", "Select a Theme"); + new StaticTextWidget(this, "Browser.Headline", _("Select a Theme")); // Add file list _fileList = new ListWidget(this, "Browser.List"); @@ -51,8 +53,8 @@ ThemeBrowser::ThemeBrowser() : Dialog("Browser") { _backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain; // Buttons - new ButtonWidget(this, "Browser.Cancel", "Cancel", kCloseCmd, 0); - new ButtonWidget(this, "Browser.Choose", "Choose", kChooseCmd, 0); + new ButtonWidget(this, "Browser.Cancel", _("Cancel"), 0, kCloseCmd); + new ButtonWidget(this, "Browser.Choose", _("Choose"), 0, kChooseCmd); } void ThemeBrowser::open() { diff --git a/gui/themes/default.inc b/gui/themes/default.inc index 75c4fb73deb..a360c95a00d 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -1,1514 +1,4 @@ "" -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " " " " " " " +" " " " @@ -1859,9 +352,1591 @@ "fill='none' " "/> " " " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " " " " " " " +" " +" " +" " " " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " diff --git a/gui/themes/modern.zip b/gui/themes/modern.zip index d53a13b0169..616c1b122ab 100644 Binary files a/gui/themes/modern.zip and b/gui/themes/modern.zip differ diff --git a/gui/themes/modern/modern_gfx.stx b/gui/themes/modern/modern_gfx.stx index f835fe9f7e1..9d94aaba312 100644 --- a/gui/themes/modern/modern_gfx.stx +++ b/gui/themes/modern/modern_gfx.stx @@ -98,6 +98,8 @@ + + @@ -112,6 +114,9 @@ + + + + + + + + + + + + + + + + + + + @@ -611,10 +652,8 @@ /> - - + + + + + @@ -89,6 +96,11 @@ size = '75, 27' padding = '0, 0, 8, 0' /> + + + - + + @@ -419,6 +436,14 @@ type = 'PopUp' /> + + + + @@ -595,6 +620,10 @@ width = '150' height = 'Globals.Button.Height' /> + - - - - - - - - - - - - - - - - + @@ -691,9 +691,17 @@ - + + diff --git a/gui/themes/modern/modern_layout_lowres.stx b/gui/themes/modern/modern_layout_lowres.stx index cd32cf5e6e7..ca3f92e5e46 100644 --- a/gui/themes/modern/modern_layout_lowres.stx +++ b/gui/themes/modern/modern_layout_lowres.stx @@ -48,6 +48,10 @@ + + + + + @@ -80,6 +87,9 @@ size = '45, 16' padding = '0, 0, 2, 0' /> + - + - + + @@ -389,6 +404,7 @@ + + + + @@ -561,7 +588,7 @@ - + + - - - - - - - - - - - - - - - - + - + - + + + + + + getFontHeight(ThemeEngine::kFontStyleTooltip) + 2; + + // Make Rect bigger for compensating the shadow + _storedState = g_gui.theme()->storeState(Common::Rect(_x - 5, _y - 5, _x + _w + 5, _y + _h + 5)); + + g_gui.theme()->startBuffering(); + g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h), 0, ThemeEngine::kWidgetBackgroundBorderSmall); + + for (Common::StringArray::const_iterator i = _wrappedLines.begin(); i != _wrappedLines.end(); ++i, ++num) { + g_gui.theme()->drawText(Common::Rect(_x + 1, _y + 1 + num * h, _x + 1 +_w, _y + 1+ (num + 1) * h), *i, ThemeEngine::kStateEnabled, Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, 0, false, ThemeEngine::kFontStyleTooltip, ThemeEngine::kFontColorNormal, false); + } + g_gui.theme()->finishBuffering(); +} + +void Tooltip::reflowLayout() { +} + +void Tooltip::setMouseXY(int x, int y) { + _mouseX = x; + _mouseY = y; +} + +void Tooltip::setVisible(bool state) { + if (state == _visible) + return; + + if (state) { + if (!_guiManager->getTopDialog()) + return; + + Widget *wdg = _guiManager->getTopDialog()->findWidget(_mouseX, _mouseY); + + if (!wdg) + return; + + if (wdg->getTooltip()) { + _visible = state; + + // Cache config values. + // NOTE: we cannot do it in the consturctor + if (_maxWidth == -1) { + _maxWidth = g_gui.xmlEval()->getVar("Globals.Tooltip.MaxWidth", 100); + _xdelta = g_gui.xmlEval()->getVar("Globals.Tooltip.XDelta", 0); + _ydelta = g_gui.xmlEval()->getVar("Globals.Tooltip.YDelta", 0); + } + + const Graphics::Font *tooltipFont = g_gui.theme()->getFont(ThemeEngine::kFontStyleTooltip); + + _wrappedLines.clear(); + _w = tooltipFont->wordWrapText(wdg->getTooltip(), _maxWidth - 4, _wrappedLines); + _h = (tooltipFont->getFontHeight() + 2) * _wrappedLines.size(); + + _x = MIN(_guiManager->getTopDialog()->_x + _mouseX + _xdelta, g_gui.getWidth() - _w - 3); + _y = MIN(_guiManager->getTopDialog()->_y + _mouseY + _ydelta, g_gui.getHeight() - _h - 3); + + draw(); + } + } else { + _visible = state; + + g_gui.theme()->restoreState(_storedState); + delete _storedState; + } +} + +#pragma mark - + +StaticTextWidget::StaticTextWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &text, Graphics::TextAlign align, const char *tooltip) + : Widget(boss, x, y, w, h, tooltip), _align(align) { setFlags(WIDGET_ENABLED); _type = kStaticTextWidget; _label = text; } -StaticTextWidget::StaticTextWidget(GuiObject *boss, const Common::String &name, const Common::String &text) - : Widget(boss, name) { +StaticTextWidget::StaticTextWidget(GuiObject *boss, const Common::String &name, const Common::String &text, const char *tooltip) + : Widget(boss, name, tooltip) { setFlags(WIDGET_ENABLED); _type = kStaticTextWidget; _label = text; @@ -227,16 +349,22 @@ void StaticTextWidget::drawWidget() { #pragma mark - -ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, uint32 cmd, uint8 hotkey) - : StaticTextWidget(boss, x, y, w, h, label, Graphics::kTextAlignCenter), CommandSender(boss), - _cmd(cmd), _hotkey(hotkey) { +ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey) + : StaticTextWidget(boss, x, y, w, h, cleanupHotkey(label), Graphics::kTextAlignCenter, tooltip), CommandSender(boss), + _cmd(cmd) { + + if (hotkey == 0) + _hotkey = parseHotkey(label); + setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); _type = kButtonWidget; } -ButtonWidget::ButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, uint32 cmd, uint8 hotkey) - : StaticTextWidget(boss, name, label), CommandSender(boss), - _cmd(cmd), _hotkey(hotkey) { +ButtonWidget::ButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey) + : StaticTextWidget(boss, name, cleanupHotkey(label), tooltip), CommandSender(boss), + _cmd(cmd) { + if (hotkey == 0) + _hotkey = parseHotkey(label); setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); _type = kButtonWidget; } @@ -252,14 +380,14 @@ void ButtonWidget::drawWidget() { #pragma mark - -CheckboxWidget::CheckboxWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, uint32 cmd, uint8 hotkey) - : ButtonWidget(boss, x, y, w, h, label, cmd, hotkey), _state(false) { +CheckboxWidget::CheckboxWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey) + : ButtonWidget(boss, x, y, w, h, label, tooltip, cmd, hotkey), _state(false) { setFlags(WIDGET_ENABLED); _type = kCheckboxWidget; } -CheckboxWidget::CheckboxWidget(GuiObject *boss, const Common::String &name, const Common::String &label, uint32 cmd, uint8 hotkey) - : ButtonWidget(boss, name, label, cmd, hotkey), _state(false) { +CheckboxWidget::CheckboxWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey) + : ButtonWidget(boss, name, label, tooltip, cmd, hotkey), _state(false) { setFlags(WIDGET_ENABLED); _type = kCheckboxWidget; } @@ -284,16 +412,84 @@ void CheckboxWidget::drawWidget() { } #pragma mark - +RadiobuttonGroup::RadiobuttonGroup(GuiObject *boss, uint32 cmd) : CommandSender(boss) { + _value = -1; + _cmd = cmd; +} -SliderWidget::SliderWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd) - : Widget(boss, x, y, w, h), CommandSender(boss), +void RadiobuttonGroup::setValue(int value) { + Common::Array::iterator button = _buttons.begin(); + while (button != _buttons.end()) { + (*button)->setState((*button)->getValue() == value, false); + + button++; + } + + _value = value; + + sendCommand(_cmd, _value); +} + +void RadiobuttonGroup::setEnabled(bool ena) { + Common::Array::iterator button = _buttons.begin(); + while (button != _buttons.end()) { + (*button)->setEnabled(ena); + + button++; + } +} + +#pragma mark - + +RadiobuttonWidget::RadiobuttonWidget(GuiObject *boss, int x, int y, int w, int h, RadiobuttonGroup *group, int value, const Common::String &label, const char *tooltip, uint8 hotkey) + : ButtonWidget(boss, x, y, w, h, label, tooltip, 0, hotkey), _state(false), _value(value), _group(group) { + setFlags(WIDGET_ENABLED); + _type = kRadiobuttonWidget; + _group->addButton(this); +} + +RadiobuttonWidget::RadiobuttonWidget(GuiObject *boss, const Common::String &name, RadiobuttonGroup *group, int value, const Common::String &label, const char *tooltip, uint8 hotkey) + : ButtonWidget(boss, name, label, tooltip, 0, hotkey), _state(false), _value(value), _group(group) { + setFlags(WIDGET_ENABLED); + _type = kRadiobuttonWidget; + _group->addButton(this); +} + +void RadiobuttonWidget::handleMouseUp(int x, int y, int button, int clickCount) { + if (isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) { + toggleState(); + } +} + +void RadiobuttonWidget::setState(bool state, bool setGroup) { + if (setGroup) { + _group->setValue(_value); + return; + } + + if (_state != state) { + _state = state; + //_flags ^= WIDGET_INV_BORDER; + draw(); + } + sendCommand(_cmd, _state); +} + +void RadiobuttonWidget::drawWidget() { + g_gui.theme()->drawRadiobutton(Common::Rect(_x, _y, _x+_w, _y+_h), _label, _state, Widget::_state); +} + +#pragma mark - + +SliderWidget::SliderWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd) + : Widget(boss, x, y, w, h, tooltip), CommandSender(boss), _cmd(cmd), _value(0), _oldValue(0), _valueMin(0), _valueMax(100), _isDragging(false) { setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_CLEARBG); _type = kSliderWidget; } -SliderWidget::SliderWidget(GuiObject *boss, const Common::String &name, uint32 cmd) - : Widget(boss, name), CommandSender(boss), +SliderWidget::SliderWidget(GuiObject *boss, const Common::String &name, const char *tooltip, uint32 cmd) + : Widget(boss, name, tooltip), CommandSender(boss), _cmd(cmd), _value(0), _oldValue(0), _valueMin(0), _valueMax(100), _isDragging(false) { setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_CLEARBG); _type = kSliderWidget; @@ -365,14 +561,14 @@ int SliderWidget::posToValue(int pos) { #pragma mark - -GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h) - : Widget(boss, x, y, w, h), _gfx(), _alpha(256), _transparency(false) { +GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip) + : Widget(boss, x, y, w, h, tooltip), _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG); _type = kGraphicsWidget; } -GraphicsWidget::GraphicsWidget(GuiObject *boss, const Common::String &name) - : Widget(boss, name), _gfx(), _alpha(256), _transparency(false) { +GraphicsWidget::GraphicsWidget(GuiObject *boss, const Common::String &name, const char *tooltip) + : Widget(boss, name, tooltip), _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG); _type = kGraphicsWidget; } diff --git a/gui/widget.h b/gui/widget.h index d0b3c3781e5..8003931d9e1 100644 --- a/gui/widget.h +++ b/gui/widget.h @@ -27,6 +27,7 @@ #include "common/sys.h" #include "common/str.h" +#include "common/str-array.h" #include "common/keyboard.h" #include "graphics/surface.h" #include "gui/object.h" @@ -62,6 +63,7 @@ enum { kEditTextWidget = 'EDIT', kButtonWidget = 'BTTN', kCheckboxWidget = 'CHKB', + kRadiobuttonWidget = 'RDBT', kSliderWidget = 'SLDE', kListWidget = 'LIST', kScrollBarWidget = 'SCRB', @@ -85,6 +87,7 @@ protected: uint16 _id; bool _hasFocus; ThemeEngine::WidgetStateInfo _state; + const char *_tooltip; private: uint16 _flags; @@ -94,8 +97,8 @@ public: static Widget *findWidgetInChain(Widget *start, const char *name); public: - Widget(GuiObject *boss, int x, int y, int w, int h); - Widget(GuiObject *boss, const Common::String &name); + Widget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip = 0); + Widget(GuiObject *boss, const Common::String &name, const char *tooltip = 0); virtual ~Widget(); void init(); @@ -135,6 +138,11 @@ public: void setVisible(bool e); bool isVisible() const; + uint8 parseHotkey(const Common::String &label); + Common::String cleanupHotkey(const Common::String &label); + + const char *getTooltip() const { return _tooltip; } + protected: void updateState(int oldFlags, int newFlags); @@ -151,14 +159,39 @@ protected: void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { assert(_boss); _boss->handleCommand(sender, cmd, data); } }; +class GuiManager; + +class Tooltip : public GuiObject { +public: + Tooltip(GuiManager *guiManager); + + bool isVisible() const { return _visible; } + void draw(); + void reflowLayout(); + void releaseFocus() {} + void setVisible(bool state); + void setMouseXY(int x, int y); + +protected: + Common::String _text; + GuiManager *_guiManager; + bool _visible; + int _mouseX, _mouseY; + int _maxWidth; + int _xdelta, _ydelta; + + Common::StringArray _wrappedLines; + ThemeEngine::StoredState *_storedState; +}; + /* StaticTextWidget */ class StaticTextWidget : public Widget { protected: Common::String _label; Graphics::TextAlign _align; public: - StaticTextWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &text, Graphics::TextAlign align); - StaticTextWidget(GuiObject *boss, const Common::String &name, const Common::String &text); + StaticTextWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &text, Graphics::TextAlign align, const char *tooltip = 0); + StaticTextWidget(GuiObject *boss, const Common::String &name, const Common::String &text, const char *tooltip = 0); void setValue(int value); void setLabel(const Common::String &label); const Common::String &getLabel() const { return _label; } @@ -176,8 +209,8 @@ protected: uint32 _cmd; uint8 _hotkey; public: - ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, uint32 cmd = 0, uint8 hotkey = 0); - ButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, uint32 cmd = 0, uint8 hotkey = 0); + ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip = 0, uint32 cmd = 0, uint8 hotkey = 0); + ButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip = 0, uint32 cmd = 0, uint8 hotkey = 0); void setCmd(uint32 cmd) { _cmd = cmd; } uint32 getCmd() const { return _cmd; } @@ -195,8 +228,8 @@ class CheckboxWidget : public ButtonWidget { protected: bool _state; public: - CheckboxWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, uint32 cmd = 0, uint8 hotkey = 0); - CheckboxWidget(GuiObject *boss, const Common::String &name, const Common::String &label, uint32 cmd = 0, uint8 hotkey = 0); + CheckboxWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip = 0, uint32 cmd = 0, uint8 hotkey = 0); + CheckboxWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip = 0, uint32 cmd = 0, uint8 hotkey = 0); void handleMouseUp(int x, int y, int button, int clickCount); virtual void handleMouseEntered(int button) { setFlags(WIDGET_HILITED); draw(); } @@ -210,6 +243,55 @@ protected: void drawWidget(); }; +class RadiobuttonWidget; + +class RadiobuttonGroup : public CommandSender { +public: + RadiobuttonGroup(GuiObject *boss, uint32 cmd = 0); + ~RadiobuttonGroup() {} + + void addButton(RadiobuttonWidget *button) { _buttons.push_back(button); } + Common::Array getButtonList() const { return _buttons; } + + void setValue(int state); + int getValue() const { return _value; } + + void setEnabled(bool ena); + + void setCmd(uint32 cmd) { _cmd = cmd; } + uint32 getCmd() const { return _cmd; } + +protected: + Common::Array _buttons; + int _value; + uint32 _cmd; +}; + +/* RadiobuttonWidget */ +class RadiobuttonWidget : public ButtonWidget { +protected: + bool _state; + int _value; + +public: + RadiobuttonWidget(GuiObject *boss, int x, int y, int w, int h, RadiobuttonGroup *group, int value, const Common::String &label, const char *tooltip = 0, uint8 hotkey = 0); + RadiobuttonWidget(GuiObject *boss, const Common::String &name, RadiobuttonGroup *group, int value, const Common::String &label, const char *tooltip = 0, uint8 hotkey = 0); + + void handleMouseUp(int x, int y, int button, int clickCount); + virtual void handleMouseEntered(int button) { setFlags(WIDGET_HILITED); draw(); } + virtual void handleMouseLeft(int button) { clearFlags(WIDGET_HILITED); draw(); } + + void setState(bool state, bool setGroup = true); + void toggleState() { setState(!_state); } + bool getState() const { return _state; } + int getValue() const { return _value; } + +protected: + void drawWidget(); + + RadiobuttonGroup *_group; +}; + /* SliderWidget */ class SliderWidget : public Widget, public CommandSender { protected: @@ -219,8 +301,8 @@ protected: bool _isDragging; uint _labelWidth; public: - SliderWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd = 0); - SliderWidget(GuiObject *boss, const Common::String &name, uint32 cmd = 0); + SliderWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip = 0, uint32 cmd = 0); + SliderWidget(GuiObject *boss, const Common::String &name, const char *tooltip = 0, uint32 cmd = 0); void setCmd(uint32 cmd) { _cmd = cmd; } uint32 getCmd() const { return _cmd; } @@ -251,8 +333,8 @@ protected: /* GraphicsWidget */ class GraphicsWidget : public Widget { public: - GraphicsWidget(GuiObject *boss, int x, int y, int w, int h); - GraphicsWidget(GuiObject *boss, const Common::String &name); + GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip = 0); + GraphicsWidget(GuiObject *boss, const Common::String &name, const char *tooltip = 0); ~GraphicsWidget(); void setGfx(const Graphics::Surface *gfx); diff --git a/po/POTFILES b/po/POTFILES new file mode 100644 index 00000000000..d2afe71f539 --- /dev/null +++ b/po/POTFILES @@ -0,0 +1,24 @@ +#TODO: help_string & usage_string: + +gui/about.cpp +gui/browser.cpp +gui/chooser.cpp +gui/error.cpp +gui/GuiManager.cpp +gui/KeysDialog.cpp +gui/launcher.cpp +gui/massadd.cpp +gui/options.cpp +gui/saveload.cpp +gui/themebrowser.cpp +gui/ThemeEngine.cpp + +base/main.cpp + +common/error.cpp + +engines/dialogs.cpp + +sound/mididrv.cpp + +backends/keymapper/remap-dialog.cpp diff --git a/po/module.mk b/po/module.mk new file mode 100644 index 00000000000..93d8e26197d --- /dev/null +++ b/po/module.mk @@ -0,0 +1,42 @@ +POTFILE := $(srcdir)/po/residual.pot +POFILES := $(wildcard $(srcdir)/po/*.po) + +updatepot: + xgettext -f $(srcdir)/po/POTFILES -D $(srcdir) -d residual --c++ -k_ -k_t -k_s -o $(POTFILE) \ + "--copyright-holder=Residual Team" --package-name=Residual \ + --package-version=$(VERSION) --msgid-bugs-address=residual-devel@lists.sf.net -o $(POTFILE)_ + + sed -e 's/SOME DESCRIPTIVE TITLE/LANGUAGE translation for Residual/' \ + -e 's/UTF-8/CHARSET/' -e 's/PACKAGE/Residual/' $(POTFILE)_ > $(POTFILE).new + + rm $(POTFILE)_ + if test -f $(POTFILE); then \ + sed -f $(srcdir)/po/remove-potcdate.sed < $(POTFILE) > $(POTFILE).1 && \ + sed -f $(srcdir)/po/remove-potcdate.sed < $(POTFILE).new > $(POTFILE).2 && \ + if cmp $(POTFILE).1 $(POTFILE).2 >/dev/null 2>&1; then \ + rm -f $(POTFILE).new; \ + else \ + rm -f $(POTFILE) && \ + mv -f $(POTFILE).new $(POTFILE); \ + fi; \ + rm -f $(POTFILE).1 $(POTFILE).2; \ + else \ + mv -f $(POTFILE).new $(POTFILE); \ + fi; + +%.po: $(POTFILE) + msgmerge $@ $(POTFILE) -o $@.new + if cmp $@ $@.new >/dev/null 2>&1; then \ + rm -f $@.new; \ + else \ + mv -f $@.new $@; \ + fi; + +$(srcdir)/common/messages.cpp: $(POFILES) + perl $(srcdir)/tools/po2c $^ > $(srcdir)/common/messages.cpp + +update-translations: updatepot $(POFILES) $(srcdir)/common/messages.cpp + @$(foreach file, $(POFILES), echo -n $(notdir $(basename $(file)))": ";msgfmt --statistic $(file);) + @rm -f messages.mo + +.PHONY: updatepot update-translations diff --git a/po/remove-potcdate.sed b/po/remove-potcdate.sed new file mode 100644 index 00000000000..2436c49e786 --- /dev/null +++ b/po/remove-potcdate.sed @@ -0,0 +1,19 @@ +# Sed script that remove the POT-Creation-Date line in the header entry +# from a POT file. +# +# The distinction between the first and the following occurrences of the +# pattern is achieved by looking at the hold space. +/^"POT-Creation-Date: .*"$/{ +x +# Test if the hold space is empty. +s/P/P/ +ta +# Yes it was empty. First occurrence. Remove the line. +g +d +bb +:a +# The hold space was nonempty. Following occurrences. Do nothing. +x +:b +} diff --git a/ports.mk b/ports.mk index 8aa5804714c..b8b3b01b932 100644 --- a/ports.mk +++ b/ports.mk @@ -51,7 +51,9 @@ bundle: residual-static cp $(srcdir)/icons/residual.icns $(bundle_name)/Contents/Resources/ cp $(DIST_FILES_DOCS) $(bundle_name)/ cp $(DIST_FILES_THEMES) $(bundle_name)/Contents/Resources/ - #cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/Contents/Resources/ +ifdef DIST_FILES_ENGINEDATA + cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/Contents/Resources/ +endif $(srcdir)/tools/credits.pl --rtf > $(bundle_name)/Contents/Resources/Credits.rtf chmod 644 $(bundle_name)/Contents/Resources/* cp residual-static $(bundle_name)/Contents/MacOS/residual @@ -63,8 +65,12 @@ iphonebundle: iphone cp $(srcdir)/dists/iphone/Info.plist $(bundle_name)/ cp $(DIST_FILES_DOCS) $(bundle_name)/ cp $(DIST_FILES_THEMES) $(bundle_name)/ - #cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/ +ifdef DIST_FILES_ENGINEDATA + cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/ +endif + $(STRIP) residual ldid -S residual + chmod 755 residual cp residual $(bundle_name)/Residual cp $(srcdir)/dists/iphone/icon.png $(bundle_name)/icon.png cp $(srcdir)/dists/iphone/Default.png $(bundle_name)/Default.png @@ -102,6 +108,10 @@ ifdef USE_ZLIB OSX_ZLIB ?= -lz endif +ifdef USE_TERMCONV +OSX_ICONV ?= -liconv +endif + # Special target to create a static linked binary for Mac OS X. # We use -force_cpusubtype_ALL to ensure the binary runs on every # PowerPC machine. @@ -155,7 +165,9 @@ win32dist: $(EXECUTABLE) mkdir -p $(WIN32PATH) $(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE) cp $(DIST_FILES_THEMES) $(WIN32PATH) - #cp $(DIST_FILES_ENGINEDATA) $(WIN32PATH) +ifdef DIST_FILES_ENGINEDATA + cp $(DIST_FILES_ENGINEDATA) $(WIN32PATH) +endif cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt cp $(srcdir)/COPYING $(WIN32PATH)/COPYING.txt cp $(srcdir)/COPYING.LGPL $(WIN32PATH)/COPYING.LGPL.txt @@ -170,7 +182,9 @@ crosswin32dist: $(EXECUTABLE) mkdir -p ResidualWin32 $(STRIP) $(EXECUTABLE) -o ResidualWin32/$(EXECUTABLE) cp $(DIST_FILES_THEMES) ResidualWin32 - #cp $(DIST_FILES_ENGINEDATA) ResidualWin32 +ifdef DIST_FILES_ENGINEDATA + cp $(DIST_FILES_ENGINEDATA) ResidualWin32 +endif cp $(srcdir)/AUTHORS ResidualWin32/AUTHORS.txt cp $(srcdir)/COPYING ResidualWin32/COPYING.txt cp $(srcdir)/COPYING.LGPL ResidualWin32/COPYING.LGPL.txt @@ -192,7 +206,9 @@ aos4dist: $(EXECUTABLE) $(STRIP) $(EXECUTABLE) -o $(AOS4PATH)/$(EXECUTABLE) cp icons/residual.info $(AOS4PATH)/$(EXECUTABLE).info cp $(DIST_FILES_THEMES) $(AOS4PATH)/themes/ - #cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/ +ifdef DIST_FILES_ENGINEDATA + cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/ +endif cp $(srcdir)/AUTHORS $(AOS4PATH)/AUTHORS.txt cp $(srcdir)/COPYING $(AOS4PATH)/COPYING.txt cp $(srcdir)/COPYING.LGPL $(AOS4PATH)/COPYING.LGPL.txt diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index 377f5aa2287..3e1a3d04b02 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -95,6 +95,12 @@ SeekableAudioStream *SeekableAudioStream::openStreamFile(const Common::String &b LoopingAudioStream::LoopingAudioStream(RewindableAudioStream *stream, uint loops, DisposeAfterUse::Flag disposeAfterUse) : _parent(stream), _disposeAfterUse(disposeAfterUse), _loops(loops), _completeIterations(0) { + assert(stream); + + if (!stream->rewind()) { + // TODO: Properly indicate error + _loops = _completeIterations = 1; + } } LoopingAudioStream::~LoopingAudioStream() { diff --git a/sound/audiostream.h b/sound/audiostream.h index 02c54ed6c32..af92d263c4a 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -109,6 +109,9 @@ public: /** * Creates a looping audio stream object. * + * Note that on creation of the LoopingAudioStream object + * the underlying stream will be rewound. + * * @see makeLoopingAudioStream * * @param stream Stream to loop @@ -126,6 +129,7 @@ public: /** * Returns number of loops the stream has played. + * * @param numLoops number of loops to play, 0 - infinite */ uint getCompleteIterations() const { return _completeIterations; } @@ -145,8 +149,8 @@ private: * overhead down, when the code does not require any functionality only offered * by LoopingAudioStream. * - * @param stream Stream to loop (will be automatically destroyed, when the looping is done) - * @param loops How often to loop (0 = infinite) + * @param stream Stream to loop (will be automatically destroyed, when the looping is done) + * @param loops How often to loop (0 = infinite) * @return A new AudioStream, which offers the desired functionality. */ AudioStream *makeLoopingAudioStream(RewindableAudioStream *stream, uint loops); @@ -162,7 +166,8 @@ public: * Tries to load a file by trying all available formats. * In case of an error, the file handle will be closed, but deleting * it is still the responsibility of the caller. - * @param basename a filename without an extension + * + * @param basename a filename without an extension * @return an SeekableAudioStream ready to use in case of success; * NULL in case of an error (e.g. invalid/nonexisting file) */ @@ -208,9 +213,9 @@ public: * by LoopingAudioStream. * * @param stream Stream to loop (will be automatically destroyed, when the looping is done) - * @param start Starttime of the stream interval to be looped - * @param end End of the stream interval to be looped (a zero time, means till end) - * @param loops How often to loop (0 = infinite) + * @param start Starttime of the stream interval to be looped + * @param end End of the stream interval to be looped (a zero time, means till end) + * @param loops How often to loop (0 = infinite) * @return A new AudioStream, which offers the desired functionality. */ AudioStream *makeLoopingAudioStream(SeekableAudioStream *stream, Timestamp start, Timestamp end, uint loops); @@ -232,10 +237,13 @@ public: /** * Constructor for a SubLoopingAudioStream. * - * @param stream Stream to loop - * @param loops How often the stream should be looped (0 means infinite) - * @param loopStart Start of the loop (this must be smaller than loopEnd) - * @param loopEnd End of the loop (thus must be greater than loopStart) + * Note that on creation of the SubLoopingAudioStream object + * the underlying stream will be rewound. + * + * @param stream Stream to loop + * @param loops How often the stream should be looped (0 means infinite) + * @param loopStart Start of the loop (this must be smaller than loopEnd) + * @param loopEnd End of the loop (thus must be greater than loopStart) * @param disposeAfterUse Whether the stream should be disposed, when the * SubLoopingAudioStream is destroyed. */ @@ -279,9 +287,9 @@ public: /** * Creates a new SubSeekableAudioStream. * - * @param parent parent stream object. - * @param start Start time. - * @param end End time. + * @param parent parent stream object. + * @param start Start time. + * @param end End time. * @param disposeAfterUse Whether the parent stream object should be destroyed on destruction of the SubSeekableAudioStream. */ SubSeekableAudioStream(SeekableAudioStream *parent, const Timestamp start, const Timestamp end, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); @@ -317,7 +325,7 @@ public: * contained in it has been played. */ virtual void queueAudioStream(Audio::AudioStream *audStream, - DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) = 0; + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) = 0; /** * Queue a block of raw audio data for playback. This stream plays all @@ -327,10 +335,10 @@ public: * * @note Make sure to allocate the data block with malloc(), not with new[]. * - * @param data pointer to the audio data block - * @param size length of the audio data block - * @param disposeAfterUse if equal to DisposeAfterUse::YES, the block is released using free() after use. - * @param flags a bit-ORed combination of RawFlags describing the audio data format + * @param data pointer to the audio data block + * @param size length of the audio data block + * @param disposeAfterUse if equal to DisposeAfterUse::YES, the block is released using free() after use. + * @param flags a bit-ORed combination of RawFlags describing the audio data format */ void queueBuffer(byte *data, uint32 size, DisposeAfterUse::Flag disposeAfterUse, byte flags); @@ -357,8 +365,8 @@ QueuingAudioStream *makeQueuingAudioStream(int rate, bool stereo); * Converts a point in time to a precise sample offset * with the given parameters. * - * @param where Point in time. - * @param rate Rate of the stream. + * @param where Point in time. + * @param rate Rate of the stream. * @param isStereo Is the stream a stereo stream? */ Timestamp convertTimeToStreamPos(const Timestamp &where, int rate, bool isStereo); diff --git a/sound/decoders/flac.cpp b/sound/decoders/flac.cpp index cd6d45d1109..5eeb8a2c3cc 100644 --- a/sound/decoders/flac.cpp +++ b/sound/decoders/flac.cpp @@ -230,7 +230,7 @@ FLACStream::FLACStream(Common::SeekableReadStream *inStream, bool dispose) if (processUntilEndOfMetadata() && _streaminfo.channels > 0) { _lastSample = _streaminfo.total_samples + 1; _length = Timestamp(0, _lastSample - 1, getRate()); - return; // no error occured + return; // no error occurred } } @@ -355,7 +355,7 @@ int FLACStream::readBuffer(int16 *buffer, const int numSamples) { break; default: decoderOk = false; - warning("FLACStream: An error occured while decoding. DecoderState is: %s", + warning("FLACStream: An error occurred while decoding. DecoderState is: %s", FLAC__StreamDecoderStateString[getStreamDecoderState()]); } @@ -668,7 +668,7 @@ inline void FLACStream::callbackMetadata(const ::FLAC__StreamMetadata *metadata) } inline void FLACStream::callbackError(::FLAC__StreamDecoderErrorStatus status) { // some of these are non-critical-Errors - debug(1, "FLACStream: An error occured while decoding. DecoderState is: %s", + debug(1, "FLACStream: An error occurred while decoding. DecoderState is: %s", FLAC__StreamDecoderErrorStatusString[status]); } diff --git a/sound/decoders/flac.h b/sound/decoders/flac.h index 93a163d0def..354775d4ff1 100644 --- a/sound/decoders/flac.h +++ b/sound/decoders/flac.h @@ -41,8 +41,8 @@ #ifndef SOUND_FLAC_H #define SOUND_FLAC_H -#include "common/types.h" #include "common/sys.h" +#include "common/types.h" #ifdef USE_FLAC @@ -61,7 +61,7 @@ class SeekableAudioStream; * * @param stream the SeekableReadStream from which to read the FLAC data * @param disposeAfterUse whether to delete the stream after use - * @return a new SeekableAudioStream, or NULL, if an error occured + * @return a new SeekableAudioStream, or NULL, if an error occurred */ SeekableAudioStream *makeFLACStream( Common::SeekableReadStream *stream, diff --git a/sound/decoders/mp3.cpp b/sound/decoders/mp3.cpp index e694b67d6d0..bc306d68be7 100644 --- a/sound/decoders/mp3.cpp +++ b/sound/decoders/mp3.cpp @@ -36,7 +36,9 @@ #include - +#if defined(__PSP__) + #include "backends/platform/psp/mp3.h" +#endif namespace Audio { @@ -347,7 +349,18 @@ int MP3Stream::readBuffer(int16 *buffer, const int numSamples) { SeekableAudioStream *makeMP3Stream( Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + +#if defined(__PSP__) + SeekableAudioStream *s = 0; + + if (Mp3PspStream::isOkToCreateStream()) + s = new Mp3PspStream(stream, disposeAfterUse); + + if (!s) // go to regular MAD mp3 stream if ME fails + s = new MP3Stream(stream, disposeAfterUse); +#else SeekableAudioStream *s = new MP3Stream(stream, disposeAfterUse); +#endif if (s && s->endOfData()) { delete s; return 0; diff --git a/sound/decoders/mp3.h b/sound/decoders/mp3.h index 5abd3ba4b1c..335f43c0cf1 100644 --- a/sound/decoders/mp3.h +++ b/sound/decoders/mp3.h @@ -41,8 +41,8 @@ #ifndef SOUND_MP3_H #define SOUND_MP3_H -#include "common/types.h" #include "common/sys.h" +#include "common/types.h" #ifdef USE_MAD @@ -61,7 +61,7 @@ class SeekableAudioStream; * * @param stream the SeekableReadStream from which to read the MP3 data * @param disposeAfterUse whether to delete the stream after use - * @return a new SeekableAudioStream, or NULL, if an error occured + * @return a new SeekableAudioStream, or NULL, if an error occurred */ SeekableAudioStream *makeMP3Stream( Common::SeekableReadStream *stream, diff --git a/sound/decoders/vorbis.cpp b/sound/decoders/vorbis.cpp index 3aee5190771..332ee1baf19 100644 --- a/sound/decoders/vorbis.cpp +++ b/sound/decoders/vorbis.cpp @@ -35,7 +35,7 @@ #include "sound/audiocd.h" #ifdef USE_TREMOR -#ifdef __GP32__ // GP32 uses custom libtremor +#if defined(ANDROID) || defined(__GP32__) // custom libtremor locations #include #else #include diff --git a/sound/decoders/vorbis.h b/sound/decoders/vorbis.h index 64508856a0e..16c2de00b3c 100644 --- a/sound/decoders/vorbis.h +++ b/sound/decoders/vorbis.h @@ -41,8 +41,8 @@ #ifndef SOUND_VORBIS_H #define SOUND_VORBIS_H -#include "common/types.h" #include "common/sys.h" +#include "common/types.h" #ifdef USE_VORBIS @@ -61,7 +61,7 @@ class SeekableAudioStream; * * @param stream the SeekableReadStream from which to read the Ogg Vorbis data * @param disposeAfterUse whether to delete the stream after use - * @return a new SeekableAudioStream, or NULL, if an error occured + * @return a new SeekableAudioStream, or NULL, if an error occurred */ SeekableAudioStream *makeVorbisStream( Common::SeekableReadStream *stream, diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp index 14fa1b276db..6e511067cc4 100644 --- a/sound/mididrv.cpp +++ b/sound/mididrv.cpp @@ -27,70 +27,92 @@ #include "common/config-manager.h" #include "common/str.h" #include "common/system.h" +#include "common/translation.h" #include "common/util.h" #include "sound/mididrv.h" +static const uint32 GUIOMapping[] = { + MDT_PCSPK, Common::GUIO_MIDIPCSPK, + MDT_CMS, Common::GUIO_MIDICMS, + MDT_PCJR, Common::GUIO_MIDIPCJR, + MDT_ADLIB, Common::GUIO_MIDIADLIB, + MDT_TOWNS, Common::GUIO_MIDITOWNS, + MDT_MIDI, Common::GUIO_MIDI, + 0, 0 +}; + +uint32 MidiDriver::midiDriverFlags2GUIO(uint32 flags) { + uint32 res = 0; + + for (int i = 0; GUIOMapping[i] || GUIOMapping[i + 1]; i += 2) { + if (flags & GUIOMapping[i]) + res |= GUIOMapping[i + 1]; + } + + return res; +} + /** Internal list of all available 'midi' drivers. */ static const MidiDriverDescription s_musicDrivers[] = { // The flags for the "auto" & "null" drivers indicate that they are anything // you want it to be. - {"auto", "", MD_AUTO, MDT_MIDI | MDT_PCSPK | MDT_ADLIB | MDT_TOWNS}, - {"null", "No music", MD_NULL, MDT_MIDI | MDT_PCSPK | MDT_ADLIB | MDT_TOWNS}, + {"auto", _s(""), MD_AUTO, MDT_MIDI | MDT_PCSPK | MDT_ADLIB | MDT_TOWNS}, + {"null", _s("No music"), MD_NULL, MDT_MIDI | MDT_PCSPK | MDT_ADLIB | MDT_TOWNS}, #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) - {"windows", "Windows MIDI", MD_WINDOWS, MDT_MIDI}, + {"windows", _s("Windows MIDI"), MD_WINDOWS, MDT_MIDI}, #endif #if defined(UNIX) && defined(USE_ALSA) - {"alsa", "ALSA", MD_ALSA, MDT_MIDI}, + {"alsa", _s("ALSA"), MD_ALSA, MDT_MIDI}, #endif #if defined(__MINT__) - {"stmidi", "Atari ST MIDI", MD_STMIDI, MDT_MIDI}, + {"stmidi", _s("Atari ST MIDI"), MD_STMIDI, MDT_MIDI}, #endif #if defined(UNIX) && !defined(__BEOS__) && !defined(MACOSX) && !defined(__MAEMO__) && !defined(__MINT__) - {"seq", "SEQ", MD_SEQ, MDT_MIDI}, + {"seq", _s("SEQ"), MD_SEQ, MDT_MIDI}, #endif #if defined(IRIX) - {"dmedia", "DMedia", MD_DMEDIA, MDT_MIDI}, + {"dmedia", _s("DMedia"), MD_DMEDIA, MDT_MIDI}, #endif #if defined(__amigaos4__) - {"camd", "CAMD", MD_CAMD, MDT_MIDI}, + {"camd", _s("CAMD"), MD_CAMD, MDT_MIDI}, #endif #if defined(MACOSX) - {"core", "CoreAudio", MD_COREAUDIO, MDT_MIDI}, + {"core", _s("CoreAudio"), MD_COREAUDIO, MDT_MIDI}, // {"coreaudio", "CoreAudio", MD_COREAUDIO, MDT_MIDI}, - {"coremidi", "CoreMIDI", MD_COREMIDI, MDT_MIDI}, + {"coremidi", _s("CoreMIDI"), MD_COREMIDI, MDT_MIDI}, #endif #if defined(PALMOS_MODE) # if defined(COMPILE_CLIE) - {"ypa1", "Yamaha Pa1", MD_YPA1, MDT_MIDI}, + {"ypa1", _s("Yamaha Pa1"), MD_YPA1, MDT_MIDI}, # elif defined(COMPILE_ZODIAC) && (!defined(ENABLE_SCUMM) || !defined(PALMOS_ARM)) - {"zodiac", "Tapwave Zodiac", MD_ZODIAC, MDT_MIDI}, + {"zodiac", _s("Tapwave Zodiac"), MD_ZODIAC, MDT_MIDI}, # endif #endif #ifdef USE_FLUIDSYNTH - {"fluidsynth", "FluidSynth", MD_FLUIDSYNTH, MDT_MIDI}, + {"fluidsynth", _s("FluidSynth"), MD_FLUIDSYNTH, MDT_MIDI}, #endif #ifdef USE_MT32EMU - {"mt32", "MT-32 Emulation", MD_MT32, MDT_MIDI}, + {"mt32", _s("MT-32 Emulation"), MD_MT32, MDT_MIDI}, #endif // The flags for the "adlib" driver indicates that it can do AdLib and MIDI. - {"adlib", "AdLib", MD_ADLIB, MDT_ADLIB}, - {"pcspk", "PC Speaker", MD_PCSPK, MDT_PCSPK}, - {"pcjr", "IBM PCjr", MD_PCJR, MDT_PCSPK}, - {"cms", "Creative Music System", MD_CMS, MDT_CMS}, - {"towns", "FM Towns", MD_TOWNS, MDT_TOWNS}, + {"adlib", _s("AdLib"), MD_ADLIB, MDT_ADLIB}, + {"pcspk", _s("PC Speaker"), MD_PCSPK, MDT_PCSPK}, + {"pcjr", _s("IBM PCjr"), MD_PCJR, MDT_PCSPK | MDT_PCJR}, + {"cms", _s("Creative Music System"), MD_CMS, MDT_CMS}, + {"towns", _s("FM Towns"), MD_TOWNS, MDT_TOWNS}, #if defined(UNIX) - {"timidity", "TiMidity", MD_TIMIDITY, MDT_MIDI}, + {"timidity", _s("TiMidity"), MD_TIMIDITY, MDT_MIDI}, #endif {0, 0, MD_NULL, MDT_NONE} diff --git a/sound/mididrv.h b/sound/mididrv.h index 3b66697c777..619560541b4 100644 --- a/sound/mididrv.h +++ b/sound/mididrv.h @@ -100,10 +100,11 @@ enum MidiDriverFlags { MDT_NONE = 0, MDT_PCSPK = 1 << 0, // PC Speaker: Maps to MD_PCSPK and MD_PCJR MDT_CMS = 1 << 1, // Creative Music System / Gameblaster: Maps to MD_CMS - MDT_ADLIB = 1 << 2, // AdLib: Maps to MD_ADLIB - MDT_TOWNS = 1 << 3, // FM-TOWNS: Maps to MD_TOWNS - MDT_MIDI = 1 << 4, // Real MIDI - MDT_PREFER_MIDI = 1 << 5 // Real MIDI output is preferred + MDT_PCJR = 1 << 2, // Tandy/PC Junior driver + MDT_ADLIB = 1 << 3, // AdLib: Maps to MD_ADLIB + MDT_TOWNS = 1 << 4, // FM-TOWNS: Maps to MD_TOWNS + MDT_MIDI = 1 << 5, // Real MIDI + MDT_PREFER_MIDI = 1 << 6 // Real MIDI output is preferred }; /** @@ -133,6 +134,8 @@ public: /** Get the id of the music driver matching the given driver name, or MD_AUTO if there is no match. */ static MidiDriverType parseMusicDriver(const Common::String &str); + static uint32 midiDriverFlags2GUIO(uint32 flags); + /** * Get a list of all available MidiDriver types. * @return list of all available midi drivers, terminated by a zero entry diff --git a/sound/midiparser.cpp b/sound/midiparser.cpp index 117bd14f865..5c56a2fdd63 100644 --- a/sound/midiparser.cpp +++ b/sound/midiparser.cpp @@ -47,6 +47,10 @@ _num_tracks(0), _active_track(255), _abort_parse(0) { memset(_active_notes, 0, sizeof(_active_notes)); + _next_event.start = NULL; + _next_event.delta = 0; + _next_event.event = 0; + _next_event.length = 0; } void MidiParser::property(int prop, int value) { @@ -63,6 +67,10 @@ void MidiParser::property(int prop, int value) { } } +void MidiParser::sendToDriver(uint32 b) { + _driver->send(b); +} + void MidiParser::setTempo(uint32 tempo) { _tempo = tempo; if (_ppqn) @@ -123,7 +131,7 @@ void MidiParser::hangingNote(byte channel, byte note, uint32 time_left, bool rec best = ptr; if (ptr->time_left) { if (recycle) - _driver->send(0x80 | channel, note, 0); + sendToDriver(0x80 | channel, note, 0); --_hanging_notes_count; } break; @@ -168,7 +176,7 @@ void MidiParser::onTimer() { for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) { if (ptr->time_left) { if (ptr->time_left <= _timer_rate) { - _driver->send(0x80 | ptr->channel, ptr->note, 0); + sendToDriver(0x80 | ptr->channel, ptr->note, 0); ptr->time_left = 0; --_hanging_notes_count; } else { @@ -228,7 +236,7 @@ void MidiParser::onTimer() { else activeNote(info.channel(), info.basic.param1, true); } - _driver->send(info.event, info.basic.param1, info.basic.param2); + sendToDriver(info.event, info.basic.param1, info.basic.param2); } @@ -254,7 +262,7 @@ void MidiParser::allNotesOff() { for (i = 0; i < 128; ++i) { for (j = 0; j < 16; ++j) { if (_active_notes[i] & (1 << j)) { - _driver->send(0x80 | j, i, 0); + sendToDriver(0x80 | j, i, 0); } } } @@ -262,7 +270,7 @@ void MidiParser::allNotesOff() { // Turn off all hanging notes for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) { if (_hanging_notes[i].time_left) { - _driver->send(0x80 | _hanging_notes[i].channel, _hanging_notes[i].note, 0); + sendToDriver(0x80 | _hanging_notes[i].channel, _hanging_notes[i].note, 0); _hanging_notes[i].time_left = 0; } } @@ -272,7 +280,7 @@ void MidiParser::allNotesOff() { // support this...). for (i = 0; i < 16; ++i) { - _driver->send(0xB0 | i, 0x7b, 0); // All notes off + sendToDriver(0xB0 | i, 0x7b, 0); // All notes off } memset(_active_notes, 0, sizeof(_active_notes)); @@ -344,7 +352,7 @@ void MidiParser::hangAllActiveNotes() { for (j = 0; j < 16; ++j) { if (temp_active[i] & (1 << j)) { activeNote(j, i, false); - _driver->send(0x80 | j, i, 0); + sendToDriver(0x80 | j, i, 0); } } } @@ -395,7 +403,7 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes) { else _driver->sysEx(info.ext.data, (uint16)info.length); } else - _driver->send(info.event, info.basic.param1, info.basic.param2); + sendToDriver(info.event, info.basic.param1, info.basic.param2); } parseNextEvent(_next_event); @@ -437,7 +445,7 @@ void MidiParser::unloadMusic() { if (_driver) { for (int i = 0; i < 16; ++i) { - _driver->send(0xE0 | i, 0, 0x40); + sendToDriver(0xE0 | i, 0, 0x40); } } } diff --git a/sound/midiparser.h b/sound/midiparser.h index e4f33ca82ec..687ed1da055 100644 --- a/sound/midiparser.h +++ b/sound/midiparser.h @@ -267,13 +267,12 @@ struct NoteTimer { * memory block containing the music data.) */ class MidiParser { -private: +protected: uint16 _active_notes[128]; ///< Each uint16 is a bit mask for channels that have that note on. NoteTimer _hanging_notes[32]; ///< Maintains expiration info for up to 32 notes. ///< Used for "Smart Jump" and MIDI formats that do not include explicit Note Off events. byte _hanging_notes_count; ///< Count of hanging notes, used to optimize expiration. -protected: MidiDriver *_driver; ///< The device to which all events will be transmitted. uint32 _timer_rate; ///< The time in microseconds between onTimer() calls. Obtained from the MidiDriver. uint32 _ppqn; ///< Pulses Per Quarter Note. (We refer to "pulses" as "ticks".) @@ -303,6 +302,11 @@ protected: void hangingNote(byte channel, byte note, uint32 ticks_left, bool recycle = true); void hangAllActiveNotes(); + virtual void sendToDriver(uint32 b); + void sendToDriver(byte status, byte firstOp, byte secondOp) { + sendToDriver(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16)); + } + /** * Platform independent BE uint32 read-and-advance. * This helper function reads Big Endian 32-bit numbers diff --git a/sound/softsynth/mt32.cpp b/sound/softsynth/mt32.cpp index af4d09569c6..3d1516de963 100644 --- a/sound/softsynth/mt32.cpp +++ b/sound/softsynth/mt32.cpp @@ -164,6 +164,8 @@ static void drawProgress(float progress) { else col = 2; // TODO implement in Residual + //screen->fillRect(r, col); + //g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, x, y, surf.w, surf.h); //g_system->updateScreen(); } @@ -220,6 +222,7 @@ static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) { drawMessage(1, buf); } + //vdebug(0, fmt, list); // FIXME: Use a higher debug level } diff --git a/sound/softsynth/sid.cpp b/sound/softsynth/sid.cpp index a1cf83e695a..d7aa58387d9 100644 --- a/sound/softsynth/sid.cpp +++ b/sound/softsynth/sid.cpp @@ -506,7 +506,7 @@ Filter::Filter() { + sizeof(f0_points_6581)/sizeof(*f0_points_6581) - 1, PointPlotter(f0_6581), 1.0); - mixer_DC = -0xfff*0xff/18 >> 7; + mixer_DC = (-0xfff*0xff/18) >> 7; f0 = f0_6581; f0_points = f0_points_6581; diff --git a/sound/softsynth/wave6581.cpp b/sound/softsynth/wave6581.cpp index 0bbedd481d4..2a743ee15c5 100644 --- a/sound/softsynth/wave6581.cpp +++ b/sound/softsynth/wave6581.cpp @@ -28,6 +28,8 @@ * Copyright (C) 2004 Dag Lem */ +#ifndef DISABLE_SID + #include "sid.h" namespace Resid @@ -2098,3 +2100,4 @@ reg8 WaveformGenerator::wave6581_PST[] = }; } +#endif diff --git a/sound/timestamp.cpp b/sound/timestamp.cpp index 7c029b12ab5..002f59a5ea9 100644 --- a/sound/timestamp.cpp +++ b/sound/timestamp.cpp @@ -24,23 +24,15 @@ */ #include "sound/timestamp.h" +#include "common/algorithm.h" namespace Audio { -static uint gcd(uint a, uint b) { - while (a > 0) { - int tmp = a; - a = b % a; - b = tmp; - } - return b; -} - Timestamp::Timestamp(uint ms, uint fr) { assert(fr > 0); _secs = ms / 1000; - _framerateFactor = 1000 / gcd(1000, fr); + _framerateFactor = 1000 / Common::gcd(1000, fr); _framerate = fr * _framerateFactor; // Note that _framerate is always divisible by 1000. @@ -51,7 +43,7 @@ Timestamp::Timestamp(uint s, uint frames, uint fr) { assert(fr > 0); _secs = s; - _framerateFactor = 1000 / gcd(1000, fr); + _framerateFactor = 1000 / Common::gcd(1000, fr); _framerate = fr * _framerateFactor; _numFrames = frames * _framerateFactor; @@ -62,10 +54,10 @@ Timestamp Timestamp::convertToFramerate(uint newFramerate) const { Timestamp ts(*this); if (ts.framerate() != newFramerate) { - ts._framerateFactor = 1000 / gcd(1000, newFramerate); + ts._framerateFactor = 1000 / Common::gcd(1000, newFramerate); ts._framerate = newFramerate * ts._framerateFactor; - const uint g = gcd(_framerate, ts._framerate); + const uint g = Common::gcd(_framerate, ts._framerate); const uint p = _framerate / g; const uint q = ts._framerate / g; @@ -122,7 +114,7 @@ bool Timestamp::operator>=(const Timestamp &ts) const { int Timestamp::cmp(const Timestamp &ts) const { int delta = _secs - ts._secs; if (!delta) { - const uint g = gcd(_framerate, ts._framerate); + const uint g = Common::gcd(_framerate, ts._framerate); const uint p = _framerate / g; const uint q = ts._framerate / g; @@ -164,7 +156,7 @@ void Timestamp::addIntern(const Timestamp &ts) { // We need to multiply by the quotient of the two framerates. // We cancel the GCD in this fraction to reduce the risk of // overflows. - const uint g = gcd(_framerate, ts._framerate); + const uint g = Common::gcd(_framerate, ts._framerate); const uint p = _framerate / g; const uint q = ts._framerate / g; @@ -227,7 +219,7 @@ int Timestamp::frameDiff(const Timestamp &ts) const { // We need to multiply by the quotient of the two framerates. // We cancel the GCD in this fraction to reduce the risk of // overflows. - const uint g = gcd(_framerate, ts._framerate); + const uint g = Common::gcd(_framerate, ts._framerate); const uint p = _framerate / g; const uint q = ts._framerate / g;