aboutsummaryrefslogtreecommitdiff
path: root/gui/ThemeEngine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/ThemeEngine.cpp')
-rw-r--r--gui/ThemeEngine.cpp286
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)