diff options
Diffstat (limited to 'gui/ThemeEngine.cpp')
-rw-r--r-- | gui/ThemeEngine.cpp | 286 |
1 files changed, 228 insertions, 58 deletions
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index b472ad0535..3a50b2c69c 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" @@ -43,10 +44,6 @@ #include "gui/ThemeEval.h" #include "gui/ThemeParser.h" -#if defined(MACOSX) || defined(IPHONE) -#include <CoreFoundation/CoreFoundation.h> -#endif - #define GUI_ENABLE_BUILTIN_THEME namespace GUI { @@ -168,6 +165,7 @@ static const DrawDataInfo kDrawDataDefaults[] = { {kDDMainDialogBackground, "mainmenu_bg", true, kDDNone}, {kDDSpecialColorBackground, "special_bg", true, kDDNone}, {kDDPlainColorBackground, "plain_bg", true, kDDNone}, + {kDDTooltipBackground, "tooltip_bg", true, kDDNone}, {kDDDefaultBackground, "default_bg", true, kDDNone}, {kDDTextSelectionBackground, "text_selection", false, kDDNone}, {kDDTextSelectionFocusBackground, "text_selection_focus", false, kDDNone}, @@ -189,6 +187,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 +331,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 }; @@ -391,20 +393,23 @@ bool ThemeEngine::init() { // Try to create a Common::Archive with the files of the theme. if (!_themeArchive && !_themeFile.empty()) { Common::FSNode node(_themeFile); - if (node.getName().hasSuffix(".zip") && !node.isDirectory()) { -#ifdef USE_ZLIB - Common::Archive *zipArchive = Common::makeZipArchive(node); - - if (!zipArchive) { - warning("Failed to open Zip archive '%s'.", node.getPath().c_str()); - } - _themeArchive = zipArchive; -#else - warning("Trying to load theme '%s' in a Zip archive without zLib support", _themeFile.c_str()); - return false; -#endif - } else if (node.isDirectory()) { + if (node.isDirectory()) { _themeArchive = new Common::FSDirectory(node); + } else if (_themeFile.hasSuffix(".zip")) { + // TODO: Also use "node" directly? + // Look for the zip file via SearchMan + Common::ArchiveMemberPtr member = SearchMan.getMember(_themeFile); + if (member) { + _themeArchive = Common::makeZipArchive(member->createReadStream()); + if (!_themeArchive) { + warning("Failed to open Zip archive '%s'.", member->getDisplayName().c_str()); + } + } else { + _themeArchive = Common::makeZipArchive(node); + if (!_themeArchive) { + warning("Failed to open Zip archive '%s'.", node.getPath().c_str()); + } + } } } @@ -563,10 +568,17 @@ bool ThemeEngine::addFont(TextData textId, const Common::String &file) { if (file == "default") { _texts[textId]->_fontPtr = _font; } else { - _texts[textId]->_fontPtr = FontMan.getFontByName(file); + Common::String localized = genLocalizedFontFilename(file.c_str()); + // Try built-in fonts + _texts[textId]->_fontPtr = FontMan.getFontByName(localized); if (!_texts[textId]->_fontPtr) { - _texts[textId]->_fontPtr = loadFont(file); + // First try to load localized font + _texts[textId]->_fontPtr = loadFont(localized); + + // Fallback to non-localized font + if (!_texts[textId]->_fontPtr) + _texts[textId]->_fontPtr = loadFont(file); if (!_texts[textId]->_fontPtr) error("Couldn't load font '%s'", file.c_str()); @@ -884,6 +896,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; @@ -947,6 +985,10 @@ void ThemeEngine::drawDialogBackground(const Common::Rect &r, DialogBackground b queueDD(kDDPlainColorBackground, r); break; + case kDialogBackgroundTooltip: + queueDD(kDDTooltipBackground, r); + break; + case kDialogBackgroundDefault: queueDD(kDDDefaultBackground, r); break; @@ -1025,12 +1067,16 @@ void ThemeEngine::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, co if (i == active) continue; + if (r.left + i * tabWidth > r.right || r.left + (i + 1) * tabWidth > r.right) + continue; + Common::Rect tabRect(r.left + i * tabWidth, r.top, r.left + (i + 1) * tabWidth, r.top + tabHeight); queueDD(kDDTabInactive, tabRect); queueDDText(getTextData(kDDTabInactive), getTextColor(kDDTabInactive), tabRect, tabs[i], false, false, _widgets[kDDTabInactive]->_textAlignH, _widgets[kDDTabInactive]->_textAlignV); } - if (active >= 0) { + if (active >= 0 && + (r.left + active * tabWidth < r.right) && (r.left + (active + 1) * tabWidth < r.right)) { Common::Rect tabRect(r.left + active * tabWidth, r.top, r.left + (active + 1) * tabWidth, r.top + tabHeight); const uint16 tabLeft = active * tabWidth; const uint16 tabRight = MAX(r.right - tabRect.right, 0); @@ -1039,7 +1085,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 +1136,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 +1178,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 @@ -1349,6 +1451,20 @@ const Graphics::Font *ThemeEngine::loadFontFromArchive(const Common::String &fil if (_themeArchive) stream = _themeArchive->createReadStreamForMember(filename); if (stream) { + font = Graphics::NewFont::loadFont(*stream); + delete stream; + } + + return font; +} + +const Graphics::Font *ThemeEngine::loadCachedFontFromArchive(const Common::String &filename) { + Common::SeekableReadStream *stream = 0; + const Graphics::Font *font = 0; + + if (_themeArchive) + stream = _themeArchive->createReadStreamForMember(filename); + if (stream) { font = Graphics::NewFont::loadFromCache(*stream); delete stream; } @@ -1362,13 +1478,14 @@ const Graphics::Font *ThemeEngine::loadFont(const Common::String &filename) { Common::File fontFile; if (!cacheFilename.empty()) { - if (fontFile.open(cacheFilename)) + if (fontFile.open(cacheFilename)) { font = Graphics::NewFont::loadFromCache(fontFile); + } if (font) return font; - if ((font = loadFontFromArchive(cacheFilename))) + if ((font = loadCachedFontFromArchive(cacheFilename))) return font; } @@ -1383,7 +1500,7 @@ const Graphics::Font *ThemeEngine::loadFont(const Common::String &filename) { if (font) { if (!cacheFilename.empty()) { - if (!Graphics::NewFont::cacheFontData(*(const Graphics::NewFont*)font, cacheFilename)) { + if (!Graphics::NewFont::cacheFontData(*(const Graphics::NewFont *)font, cacheFilename)) { warning("Couldn't create cache file for font '%s'", filename.c_str()); } } @@ -1408,6 +1525,34 @@ Common::String ThemeEngine::genCacheFilename(const char *filename) { return Common::String(); } +Common::String ThemeEngine::genLocalizedFontFilename(const char *filename) { +#ifndef USE_TRANSLATION + return Common::String(filename); +#else + + Common::String result; + bool pointPassed = false; + + for (const char *p = filename; *p != 0; p++) { + if (!pointPassed) { + if (*p != '.') { + result += *p; + } else { + result += "-"; + result += TransMan.getCurrentCharset(); + result += *p; + + pointPassed = true; + } + } else { + result += *p; + } + } + + return result; +#endif +} + /********************************************************** * Static Theme XML functions @@ -1436,12 +1581,33 @@ bool ThemeEngine::themeConfigParseHeader(Common::String header, Common::String & return tok.empty(); } +bool ThemeEngine::themeConfigUsable(const Common::ArchiveMember &member, Common::String &themeName) { + Common::File stream; + bool foundHeader = false; + + if (member.getName().hasSuffix(".zip")) { + Common::Archive *zipArchive = Common::makeZipArchive(member.createReadStream()); + + if (zipArchive && zipArchive->hasFile("THEMERC")) { + stream.open("THEMERC", *zipArchive); + } + + delete zipArchive; + } + + if (stream.isOpen()) { + Common::String stxHeader = stream.readLine(); + foundHeader = themeConfigParseHeader(stxHeader, themeName); + } + + return foundHeader; +} + bool ThemeEngine::themeConfigUsable(const Common::FSNode &node, Common::String &themeName) { Common::File stream; bool foundHeader = false; if (node.getName().hasSuffix(".zip") && !node.isDirectory()) { -#ifdef USE_ZLIB Common::Archive *zipArchive = Common::makeZipArchive(node); if (zipArchive && zipArchive->hasFile("THEMERC")) { // Open THEMERC from the ZIP file. @@ -1454,7 +1620,6 @@ bool ThemeEngine::themeConfigUsable(const Common::FSNode &node, Common::String & // reference to zipArchive anywhere. This could change if we // ever modify ZipArchive::createReadStreamForMember. delete zipArchive; -#endif } else if (node.isDirectory()) { Common::FSNode headerfile = node.getChild("THEMERC"); if (!headerfile.exists() || !headerfile.isReadable() || headerfile.isDirectory()) @@ -1493,26 +1658,7 @@ void ThemeEngine::listUsableThemes(Common::List<ThemeDescriptor> &list) { if (ConfMan.hasKey("themepath")) listUsableThemes(Common::FSNode(ConfMan.get("themepath")), list); -#ifdef DATA_PATH - listUsableThemes(Common::FSNode(DATA_PATH), list); -#endif - -#if defined(MACOSX) || defined(IPHONE) - CFURLRef resourceUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()); - if (resourceUrl) { - char buf[256]; - if (CFURLGetFileSystemRepresentation(resourceUrl, true, (UInt8 *)buf, 256)) { - Common::FSNode resourcePath(buf); - listUsableThemes(resourcePath, list); - } - CFRelease(resourceUrl); - } -#endif - - if (ConfMan.hasKey("extrapath")) - listUsableThemes(Common::FSNode(ConfMan.get("extrapath")), list); - - listUsableThemes(Common::FSNode("."), list, 1); + listUsableThemes(SearchMan, list); // Now we need to strip all duplicates // TODO: It might not be the best idea to strip duplicates. The user might @@ -1531,6 +1677,32 @@ void ThemeEngine::listUsableThemes(Common::List<ThemeDescriptor> &list) { output.clear(); } +void ThemeEngine::listUsableThemes(Common::Archive &archive, Common::List<ThemeDescriptor> &list) { + ThemeDescriptor td; + + Common::ArchiveMemberList fileList; + archive.listMatchingMembers(fileList, "*.zip"); + for (Common::ArchiveMemberList::iterator i = fileList.begin(); + i != fileList.end(); ++i) { + td.name.clear(); + if (themeConfigUsable(**i, td.name)) { + td.filename = (*i)->getName(); + td.id = (*i)->getDisplayName(); + + // If the name of the node object also contains + // the ".zip" suffix, we will strip it. + if (td.id.hasSuffix(".zip")) { + for (int j = 0; j < 4; ++j) + td.id.deleteLastChar(); + } + + list.push_back(td); + } + } + + fileList.clear(); +} + void ThemeEngine::listUsableThemes(const Common::FSNode &node, Common::List<ThemeDescriptor> &list, int depth) { if (!node.exists() || !node.isReadable() || !node.isDirectory()) return; @@ -1550,7 +1722,6 @@ void ThemeEngine::listUsableThemes(const Common::FSNode &node, Common::List<Them } Common::FSList fileList; -#ifdef USE_ZLIB // Check all files. We need this to find all themes inside ZIP archives. if (!node.getChildren(fileList, Common::FSNode::kListFilesOnly)) return; @@ -1577,7 +1748,6 @@ void ThemeEngine::listUsableThemes(const Common::FSNode &node, Common::List<Them } fileList.clear(); -#endif // Check if we exceeded the given recursion depth if (depth - 1 == -1) |