Files
scummvm/gui/ThemeEngine.cpp
T
Vicent Marti 5f30b1f320 Merged several revisions from SVNMerge.
Fixed theme loading to use the new ArchiveMember class.

Original SVNMerge.py log:
=====================
Merged revisions 34686,34688-34689,34691-34698,34700-34705,34707-34713,34715-34722,34725-34727,34731-34732,34734-34738,34746 via svnmerge from 
https://tanoku@scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk

........
  r34686 | cyx | 2008-09-29 21:09:56 +0200 (Mon, 29 Sep 2008) | 1 line
  
  add constructor to SearchSet::Node for proper init of structure members with gcc 3.4.x (see tracker items #2120595, #2106292)
........
  r34688 | cyx | 2008-09-29 22:08:26 +0200 (Mon, 29 Sep 2008) | 1 line
  
  restrict _heXmapNum to HE versions, should fix #2135822
........
  r34689 | eriktorbjorn | 2008-09-29 22:40:58 +0200 (Mon, 29 Sep 2008) | 4 lines
  
  Since no one has come up with any better suggestion... this should fix #2123258
  ("COMI: Crash after video (SAN) play"). The eos() function won't return true
  until we've tried to read *past* the end of the stream.
........
  r34691 | tanoku | 2008-09-30 00:29:07 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Ported ZipArchive::getAllNames() implementation from GUI branch.
........
  r34692 | fingolfin | 2008-09-30 11:07:03 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Order alphabetically
........
  r34693 | fingolfin | 2008-09-30 11:08:17 +0200 (Tue, 30 Sep 2008) | 1 line
  
  SAGA: Removed patchesCount from SAGAGameDescription
........
  r34694 | fingolfin | 2008-09-30 11:09:39 +0200 (Tue, 30 Sep 2008) | 1 line
  
  SCUMM HE: Use FilesystemNode::openForReading() instead of 'new Common::File' (didn't cover all instances, though)
........
  r34695 | fingolfin | 2008-09-30 11:11:25 +0200 (Tue, 30 Sep 2008) | 1 line
  
  SAGA: Forgot to commit saga.h
........
  r34696 | fingolfin | 2008-09-30 11:12:02 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Added multiple new open() methods to Common::File, which make it possible to use this class as a generic convenience wrapper around all kinds of SeekableReadStream; also renamed the name() method to the less confusing getName()
........
  r34697 | fingolfin | 2008-09-30 13:33:43 +0200 (Tue, 30 Sep 2008) | 1 line
  
  cleanup
........
  r34698 | tanoku | 2008-09-30 13:53:37 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Fixed functionality of ZipArchive::getAllNames(). Now it expects a possibly non-empty string list and returns the amount of new filenames added to it.
........
  r34700 | fingolfin | 2008-09-30 14:27:38 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Renamed Engine::quit to Engine::shouldQuit (previously, it was easily confused with Engine::quitGame); also cleaned up engine.h a bit
........
  r34701 | fingolfin | 2008-09-30 14:37:28 +0200 (Tue, 30 Sep 2008) | 1 line
  
  cleanup
........
  r34702 | fingolfin | 2008-09-30 14:38:44 +0200 (Tue, 30 Sep 2008) | 1 line
  
  AdvancedDetector: Do not (ab)use paths to keep track of files, rather, use FSNodes -- partial (?) fix for bug #2137680
........
  r34703 | fingolfin | 2008-09-30 14:58:27 +0200 (Tue, 30 Sep 2008) | 1 line
  
  SAGA: Some code cleanup
........
  r34704 | fingolfin | 2008-09-30 14:59:29 +0200 (Tue, 30 Sep 2008) | 1 line
  
  SKY: Fix detector to not (ab)use FSNode::getPath; if you want to open a FSNode, just pass it to File::open
........
  r34705 | thebluegr | 2008-09-30 15:19:14 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Fixed crash when starting IHNM, a regression from commit #34693
........
  r34707 | fingolfin | 2008-09-30 17:42:19 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Fixing ConfigManager::loadConfigFile
........
  r34708 | fingolfin | 2008-09-30 18:23:35 +0200 (Tue, 30 Sep 2008) | 1 line
  
  AGI: Another case where it is better to use FSNodes directly, instead of converting them to path strings
........
  r34709 | fingolfin | 2008-09-30 18:34:38 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Modified FilePluginProvider to use FSNodes (instead of raw filenames / paths) in its API
........
  r34710 | fingolfin | 2008-09-30 18:38:46 +0200 (Tue, 30 Sep 2008) | 1 line
  
  AGI: Got rid of yet another unwarranted use of FSNode::getPath
........
  r34711 | fingolfin | 2008-09-30 18:53:04 +0200 (Tue, 30 Sep 2008) | 1 line
  
  AGI: Simplify WagFileParser by not reading data into a memory stream first (this was there to improve performance on systems with slow seeking; those systems should use another approach, see scummvm-devel)
........
  r34712 | thebluegr | 2008-09-30 18:55:10 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Fix crash when starting ITE, a regression from commit #34705
........
  r34713 | fingolfin | 2008-09-30 19:09:41 +0200 (Tue, 30 Sep 2008) | 1 line
  
  Fix ThemeBrowser to use FSNodes, not getPath()
........
  r34715 | Kirben | 2008-10-02 16:41:50 +0200 (Thu, 02 Oct 2008) | 1 line
  
  Correct HE version for Putt-Putt Enters the Race (French/Windows).
........
  r34716 | fingolfin | 2008-10-02 18:58:59 +0200 (Thu, 02 Oct 2008) | 1 line
  
  Renamed FilesystemNode -> FSNode
........
  r34717 | fingolfin | 2008-10-02 19:08:15 +0200 (Thu, 02 Oct 2008) | 1 line
  
  cleanup
........
  r34718 | fingolfin | 2008-10-02 19:17:18 +0200 (Thu, 02 Oct 2008) | 1 line
  
  Ignore String::deleteLastChar when called on an empty string
........
  r34719 | fingolfin | 2008-10-02 19:20:21 +0200 (Thu, 02 Oct 2008) | 1 line
  
  Fix for bug #2142743: Assertion on clicking "Theme" in Options
........
  r34720 | fingolfin | 2008-10-02 19:48:01 +0200 (Thu, 02 Oct 2008) | 7 lines
  
  Engine class changed:
  - Moved initCommonGFX() && GUIErrorMessage() out of class Engine
  - got rid of the _autosavePeriod member (this prevented users from
    changing the autosave period during runtime)
  - Got rid of an evil 'using GUI::Dialog' statement
  - Clarified some Doxygen comments
........
  r34721 | fingolfin | 2008-10-02 19:52:29 +0200 (Thu, 02 Oct 2008) | 1 line
  
  Add the current dir to the global SearchSet, i.e. to SearchMan (this should fix the problems on Windows, and maybe other systems, see 'bug' #2137680)
........
  r34722 | fingolfin | 2008-10-02 19:55:08 +0200 (Thu, 02 Oct 2008) | 1 line
  
  Forgot to mention yet another Engine class change: mainMenuDialog -> openMainMenuDialog; and also forgot to commit this related file :/
........
  r34725 | fingolfin | 2008-10-02 20:11:40 +0200 (Thu, 02 Oct 2008) | 1 line
  
  typo
........
  r34726 | thebluegr | 2008-10-03 00:04:34 +0200 (Fri, 03 Oct 2008) | 1 line
  
  Merged loadHotSpotsMads() and loadHotSpotsM4()
........
  r34727 | lordhoto | 2008-10-03 02:16:21 +0200 (Fri, 03 Oct 2008) | 2 lines
  
  Fixed typos (thanks to Raziel^ for spotting them).
........
  r34731 | fingolfin | 2008-10-03 18:07:57 +0200 (Fri, 03 Oct 2008) | 1 line
  
  Fixed Engine::hasFeature to use proper types (i.e., MetaEngine::MetaEngineFeature instead of int)
........
  r34732 | fingolfin | 2008-10-03 18:14:12 +0200 (Fri, 03 Oct 2008) | 1 line
  
  SCUMM: Added french Monkey VGA variant, see bug #2129199
........
  r34734 | lordhoto | 2008-10-03 18:57:40 +0200 (Fri, 03 Oct 2008) | 2 lines
  
  Committed slightly modified patch #2034983 "Case-insensitivy and directory handling for engines".
........
  r34735 | lordhoto | 2008-10-03 20:18:42 +0200 (Fri, 03 Oct 2008) | 2 lines
  
  Check all 'kyra.dat' files in setup paths instead of only the first one found.
........
  r34736 | lordhoto | 2008-10-03 20:23:57 +0200 (Fri, 03 Oct 2008) | 2 lines
  
  Cleanup.
........
  r34737 | dreammaster | 2008-10-04 13:10:25 +0200 (Sat, 04 Oct 2008) | 1 line
  
  Bugfix to prevent NPC characters managing to walk beyond room exits without leaving the room, and thus getting stuck
........
  r34738 | fingolfin | 2008-10-04 15:09:01 +0200 (Sat, 04 Oct 2008) | 1 line
  
  Renamed some MetaEngine feature flags; removed explicit numbers from this feature flag list (nothing should rely on their specific values, anyway); added a note that Engine::hasFeature should become independant of MetaEngine::hasFeature
........
  r34746 | john_doe | 2008-10-04 23:40:14 +0200 (Sat, 04 Oct 2008) | 3 lines
  
  - Declared all stack functions as inline
  - Sleep some ms after 500 opcodes to reduce CPU load
  - Fixed odd bug in LGoP2 where text disappeared quickly without waiting for user input by returning 0x38 in sfShowPage
........

svn-id: r34749
2008-10-05 11:21:07 +00:00

1071 lines
29 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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 "common/util.h"
#include "graphics/surface.h"
#include "graphics/colormasks.h"
#include "common/system.h"
#include "common/events.h"
#include "common/config-manager.h"
#include "common/fs.h"
#include "common/unzip.h"
#include "graphics/imageman.h"
#include "graphics/cursorman.h"
#include "gui/launcher.h"
#include "gui/ThemeEngine.h"
#include "gui/ThemeEval.h"
#include "graphics/VectorRenderer.h"
#define GUI_ENABLE_BUILTIN_THEME
namespace GUI {
using namespace Graphics;
const char *ThemeEngine::rendererModeLabels[] = {
"Disabled GFX",
"Stardard Renderer (16bpp)",
"Antialiased Renderer (16bpp)"
};
/**********************************************************
* ThemeItem functions for drawing queues.
*********************************************************/
void ThemeItemDrawData::drawSelf(bool draw, bool restore) {
Common::Rect extendedRect = _area;
extendedRect.grow(_engine->kDirtyRectangleThreshold + _data->_backgroundOffset);
if (restore)
_engine->restoreBackground(extendedRect);
if (draw) {
Common::List<Graphics::DrawStep>::const_iterator step;
for (step = _data->_steps.begin(); step != _data->_steps.end(); ++step)
_engine->renderer()->drawStep(_area, *step, _dynamicData);
}
_engine->addDirtyRect(extendedRect);
}
void ThemeItemTextData::drawSelf(bool draw, bool restore) {
if (_restoreBg || restore)
_engine->restoreBackground(_area);
if (draw) {
_engine->renderer()->setFgColor(_data->_color.r, _data->_color.g, _data->_color.b);
_engine->renderer()->drawString(_data->_fontPtr, _text, _area, _alignH, _alignV, _deltax, _ellipsis);
}
_engine->addDirtyRect(_area);
}
void ThemeItemBitmap::drawSelf(bool draw, bool restore) {
if (restore)
_engine->restoreBackground(_area);
if (draw) {
if (_alpha)
_engine->renderer()->blitAlphaBitmap(_bitmap, _area);
else
_engine->renderer()->blitSubSurface(_bitmap, _area);
}
_engine->addDirtyRect(_area);
}
/**********************************************************
* Data definitions for theme engine elements
*********************************************************/
const ThemeEngine::DrawDataInfo ThemeEngine::kDrawDataDefaults[] = {
{kDDMainDialogBackground, "mainmenu_bg", true, kDDNone},
{kDDSpecialColorBackground, "special_bg", true, kDDNone},
{kDDPlainColorBackground, "plain_bg", true, kDDNone},
{kDDDefaultBackground, "default_bg", true, kDDNone},
{kDDTextSelectionBackground, "text_selection", false, kDDNone},
{kDDWidgetBackgroundDefault, "widget_default", true, kDDNone},
{kDDWidgetBackgroundSmall, "widget_small", true, kDDNone},
{kDDWidgetBackgroundEditText, "widget_textedit", true, kDDNone},
{kDDWidgetBackgroundSlider, "widget_slider", true, kDDNone},
{kDDButtonIdle, "button_idle", true, kDDWidgetBackgroundSlider},
{kDDButtonHover, "button_hover", false, kDDButtonIdle},
{kDDButtonDisabled, "button_disabled", true, kDDNone},
{kDDSliderFull, "slider_full", false, kDDNone},
{kDDSliderHover, "slider_hover", false, kDDNone},
{kDDSliderDisabled, "slider_disabled", true, kDDNone},
{kDDCheckboxDefault, "checkbox_default", true, kDDNone},
{kDDCheckboxDisabled, "checkbox_disabled", true, kDDNone},
{kDDCheckboxSelected, "checkbox_selected", false, kDDCheckboxDefault},
{kDDTabActive, "tab_active", false, kDDTabInactive},
{kDDTabInactive, "tab_inactive", true, kDDNone},
{kDDTabBackground, "tab_background", true, kDDNone},
{kDDScrollbarBase, "scrollbar_base", true, kDDNone},
{kDDScrollbarButtonIdle, "scrollbar_button_idle", true, kDDNone},
{kDDScrollbarButtonHover, "scrollbar_button_hover", false, kDDScrollbarButtonIdle},
{kDDScrollbarHandleIdle, "scrollbar_handle_idle", false, kDDNone},
{kDDScrollbarHandleHover, "scrollbar_handle_hover", false, kDDScrollbarBase},
{kDDPopUpIdle, "popup_idle", true, kDDNone},
{kDDPopUpHover, "popup_hover", false, kDDPopUpIdle},
{kDDCaret, "caret", false, kDDNone},
{kDDSeparator, "separator", true, kDDNone},
};
const ThemeEngine::TextDataInfo ThemeEngine::kTextDataDefaults[] = {
{kTextDataDefault, "text_default"},
{kTextDataHover, "text_hover"},
{kTextDataDisabled, "text_disabled"},
{kTextDataInverted, "text_inverted"},
{kTextDataButton, "text_button"},
{kTextDataButtonHover, "text_button_hover"},
{kTextDataNormalFont, "text_normal"}
};
/**********************************************************
* ThemeEngine class
*********************************************************/
ThemeEngine::ThemeEngine(Common::String fileName, GraphicsMode mode) :
_vectorRenderer(0), _system(0), _graphicsMode(kGfxDisabled), _font(0),
_screen(0), _backBuffer(0), _bytesPerPixel(0), _initOk(false),
_themeOk(false), _enabled(false), _buffering(false), _cursor(0) {
_system = g_system;
_parser = new ThemeParser(this);
_themeEval = new GUI::ThemeEval();
_useCursor = false;
for (int i = 0; i < kDrawDataMAX; ++i) {
_widgets[i] = 0;
}
for (int i = 0; i < kTextDataMAX; ++i) {
_texts[i] = 0;
}
_graphicsMode = mode;
_themeFileName = fileName;
_initOk = false;
}
ThemeEngine::~ThemeEngine() {
freeRenderer();
freeScreen();
freeBackbuffer();
unloadTheme();
delete _parser;
delete _themeEval;
delete[] _cursor;
for (ImagesMap::iterator i = _bitmaps.begin(); i != _bitmaps.end(); ++i)
ImageMan.unregisterSurface(i->_key);
}
/**********************************************************
* Theme setup/initialization
*********************************************************/
bool ThemeEngine::init() {
// reset everything and reload the graphics
deinit();
setGraphicsMode(_graphicsMode);
if (_screen->pixels && _backBuffer->pixels) {
_initOk = true;
clearAll();
resetDrawArea();
}
if (_screen->w >= 400 && _screen->h >= 300) {
_font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
} else {
_font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
}
if (isThemeLoadingRequired() || !_themeOk) {
loadTheme(_themeFileName);
}
return true;
}
void ThemeEngine::deinit() {
if (_initOk) {
_system->hideOverlay();
freeRenderer();
freeScreen();
freeBackbuffer();
_initOk = false;
}
}
void ThemeEngine::unloadTheme() {
if (!_themeOk)
return;
for (int i = 0; i < kDrawDataMAX; ++i) {
delete _widgets[i];
_widgets[i] = 0;
}
for (int i = 0; i < kTextDataMAX; ++i) {
delete _texts[i];
_texts[i] = 0;
}
for (ImagesMap::iterator i = _bitmaps.begin(); i != _bitmaps.end(); ++i)
ImageMan.unregisterSurface(i->_key);
if (_themeFileName.hasSuffix(".zip"))
ImageMan.removeArchive(_themeFileName);
Common::File::resetDefaultDirectories();
_themeEval->reset();
_themeOk = false;
}
void ThemeEngine::clearAll() {
if (!_initOk)
return;
_system->clearOverlay();
_system->grabOverlay((OverlayColor*)_screen->pixels, _screen->w);
}
void ThemeEngine::refresh() {
init();
if (_enabled) {
_system->showOverlay();
if (_useCursor) {
CursorMan.replaceCursorPalette(_cursorPal, 0, MAX_CURS_COLORS);
CursorMan.replaceCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, _cursorTargetScale);
}
}
}
void ThemeEngine::enable() {
init();
resetDrawArea();
if (_useCursor)
setUpCursor();
_system->showOverlay();
clearAll();
_enabled = true;
}
void ThemeEngine::disable() {
_system->hideOverlay();
if (_useCursor) {
CursorMan.popCursorPalette();
CursorMan.popCursor();
}
_enabled = false;
}
template<typename PixelType>
void ThemeEngine::screenInit(bool backBuffer) {
uint32 width = _system->getOverlayWidth();
uint32 height = _system->getOverlayHeight();
if (backBuffer) {
freeBackbuffer();
_backBuffer = new Surface;
_backBuffer->create(width, height, sizeof(PixelType));
}
freeScreen();
_screen = new Surface;
_screen->create(width, height, sizeof(PixelType));
_system->clearOverlay();
}
void ThemeEngine::setGraphicsMode(GraphicsMode mode) {
switch (mode) {
case kGfxStandard16bit:
case kGfxAntialias16bit:
_bytesPerPixel = sizeof(uint16);
screenInit<uint16>(kEnableBackCaching);
break;
default:
error("Invalid graphics mode");
}
freeRenderer();
_vectorRenderer = createRenderer(mode);
_vectorRenderer->setSurface(_screen);
}
bool ThemeEngine::isWidgetCached(DrawData type, const Common::Rect &r) {
return _widgets[type] && _widgets[type]->_cached &&
_widgets[type]->_surfaceCache->w == r.width() &&
_widgets[type]->_surfaceCache->h == r.height();
}
void ThemeEngine::drawCached(DrawData type, const Common::Rect &r) {
assert(_widgets[type]->_surfaceCache->bytesPerPixel == _screen->bytesPerPixel);
_vectorRenderer->blitSurface(_widgets[type]->_surfaceCache, r);
}
void ThemeEngine::calcBackgroundOffset(DrawData type) {
uint maxShadow = 0;
for (Common::List<Graphics::DrawStep>::const_iterator step = _widgets[type]->_steps.begin();
step != _widgets[type]->_steps.end(); ++step) {
if ((step->autoWidth || step->autoHeight) && step->shadow > maxShadow)
maxShadow = step->shadow;
if (step->drawingCall == &Graphics::VectorRenderer::drawCallback_BEVELSQ && step->bevel > maxShadow)
maxShadow = step->bevel;
}
_widgets[type]->_backgroundOffset = maxShadow;
}
void ThemeEngine::restoreBackground(Common::Rect r, bool special) {
r.clip(_screen->w, _screen->h); // AHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA... Oh god. :(
_vectorRenderer->blitSurface(_backBuffer, r);
}
/**********************************************************
* Theme elements management
*********************************************************/
void ThemeEngine::addDrawStep(const Common::String &drawDataId, Graphics::DrawStep step) {
DrawData id = getDrawDataId(drawDataId);
assert(_widgets[id] != 0);
_widgets[id]->_steps.push_back(step);
}
bool ThemeEngine::addTextData(const Common::String &drawDataId, const Common::String &textDataId, TextAlign alignH, TextAlignVertical alignV) {
DrawData id = getDrawDataId(drawDataId);
TextData textId = getTextDataId(textDataId);
if (id == -1 || textId == -1 || !_widgets[id])
return false;
_widgets[id]->_textDataId = textId;
_widgets[id]->_textAlignH = alignH;
_widgets[id]->_textAlignV = alignV;
return true;
}
bool ThemeEngine::addFont(const Common::String &fontId, const Common::String &file, int r, int g, int b) {
TextData textId = getTextDataId(fontId);
if (textId == -1)
return false;
if (_texts[textId] != 0)
delete _texts[textId];
_texts[textId] = new TextDrawData;
if (file == "default") {
_texts[textId]->_fontPtr = _font;
} else {
_texts[textId]->_fontPtr = FontMan.getFontByName(file);
if (!_texts[textId]->_fontPtr) {
_texts[textId]->_fontPtr = loadFont(file.c_str());
if (!_texts[textId]->_fontPtr)
error("Couldn't load %s font '%s'", fontId.c_str(), file.c_str());
FontMan.assignFontToName(file, _texts[textId]->_fontPtr);
}
}
_texts[textId]->_color.r = r;
_texts[textId]->_color.g = g;
_texts[textId]->_color.b = b;
return true;
}
bool ThemeEngine::addBitmap(const Common::String &filename) {
if (_bitmaps.contains(filename)) {
ImageMan.unregisterSurface(filename);
}
ImageMan.registerSurface(filename, 0);
_bitmaps[filename] = ImageMan.getSurface(filename);
return _bitmaps[filename] != 0;
}
bool ThemeEngine::addDrawData(const Common::String &data, bool cached) {
DrawData data_id = getDrawDataId(data);
if (data_id == -1)
return false;
if (_widgets[data_id] != 0)
delete _widgets[data_id];
_widgets[data_id] = new WidgetDrawData;
_widgets[data_id]->_cached = cached;
_widgets[data_id]->_buffer = kDrawDataDefaults[data_id].buffer;
_widgets[data_id]->_surfaceCache = 0;
_widgets[data_id]->_textDataId = -1;
return true;
}
/**********************************************************
* Theme XML loading
*********************************************************/
bool ThemeEngine::loadTheme(Common::String fileName) {
unloadTheme();
if (fileName != "builtin") {
if (fileName.hasSuffix(".zip"))
ImageMan.addArchive(fileName);
else
Common::File::addDefaultDirectory(fileName);
}
if (fileName == "builtin") {
if (!loadDefaultXML())
error("Could not load default embeded theme");
}
else if (!loadThemeXML(fileName)) {
warning("Could not parse custom theme '%s'. Falling back to default theme", fileName.c_str());
if (!loadDefaultXML()) // if we can't load the embeded theme, this is a complete failure
error("Could not load default embeded theme");
}
for (int i = 0; i < kDrawDataMAX; ++i) {
if (_widgets[i] == 0) {
warning("Missing data asset: '%s'", kDrawDataDefaults[i].name);
} else {
calcBackgroundOffset((DrawData)i);
// TODO: draw the cached widget to the cache surface
if (_widgets[i]->_cached) {}
}
}
_themeOk = true;
return true;
}
bool ThemeEngine::loadDefaultXML() {
// The default XML theme is included on runtime from a pregenerated
// file inside the themes directory.
// Use the Python script "makedeftheme.py" to convert a normal XML theme
// into the "default.inc" file, which is ready to be included in the code.
bool result;
#ifdef GUI_ENABLE_BUILTIN_THEME
const char *defaultXML =
#include "themes/default.inc"
;
if (!parser()->loadBuffer((const byte*)defaultXML, strlen(defaultXML), false))
return false;
_themeName = "ScummVM Classic Theme (Builtin Version)";
_themeFileName = "builtin";
result = parser()->parse();
parser()->close();
return result;
#else
warning("The built-in theme is not enabled in the current build. Please load an external theme");
return false;
#endif
}
bool ThemeEngine::loadThemeXML(Common::String themeName) {
assert(_parser);
_themeName.clear();
char fileNameBuffer[32];
Common::String stxHeader;
int parseCount = 0;
bool failed = false;
#ifdef USE_ZLIB
Common::ZipArchive zipFile(themeName.c_str());
Common::ArchiveMemberList zipContents;
if (zipFile.isOpen() && zipFile.listMembers(zipContents)) {
// for (uint i = 0; i < zipContents.size(); ++i) {
for (Common::ArchiveMemberList::iterator za = zipContents.begin(); za != zipContents.end(); ++za) {
if (!failed && matchString((*za)->getName().c_str(), "*.stx")) {
if (parser()->loadStream((*za)->open()) == false) {
warning("Failed to load stream for zipped file '%s'", fileNameBuffer);
failed = true;
}
if (parser()->parse() == false) {
warning("Theme parsing failed on zipped file '%s'.", fileNameBuffer);
failed = true;
}
parser()->close();
parseCount++;
} else if ((*za)->getName() == "THEMERC") {
Common::SeekableReadStream *stream = (*za)->open();
stxHeader = stream->readLine();
if (!themeConfigParseHeader(stxHeader.c_str(), _themeName)) {
warning("Corrupted 'THEMERC' file in theme '%s'", _themeFileName.c_str());
failed = true;
}
delete stream;
}
}
} else {
#endif
FSNode node(themeName);
if (node.exists() && node.isReadable() && node.isDirectory()) {
FSList fslist;
if (!node.getChildren(fslist, FSNode::kListFilesOnly))
return false;
for (FSList::const_iterator i = fslist.begin(); i != fslist.end(); ++i) {
if (!failed && i->getName().hasSuffix(".stx")) {
parseCount++;
if (parser()->loadFile(*i) == false) {
warning("Failed to load STX file '%s'", i->getName().c_str());
failed = true;
}
if (parser()->parse() == false) {
warning("Failed to parse STX file '%s'", i->getName().c_str());
failed = true;
}
parser()->close();
} else if (i->getName() == "THEMERC") {
Common::File f;
f.open(*i);
stxHeader = f.readLine();
if (!themeConfigParseHeader(stxHeader.c_str(), _themeName)) {
warning("Corrupted 'THEMERC' file in theme '%s'", _themeFileName.c_str());
failed = true;
}
}
}
}
#ifdef USE_ZLIB
}
#endif
return (parseCount > 0 && _themeName.empty() == false && failed == false);
}
/**********************************************************
* Drawing Queue management
*********************************************************/
void ThemeEngine::queueDD(DrawData type, const Common::Rect &r, uint32 dynamic) {
if (_widgets[type] == 0)
return;
Common::Rect area = r;
area.clip(_screen->w, _screen->h);
ThemeItemDrawData *q = new ThemeItemDrawData(this, _widgets[type], area, dynamic);
if (_buffering) {
if (_widgets[type]->_buffer) {
_bufferQueue.push_back(q);
} else {
if (kDrawDataDefaults[type].parent != kDDNone && kDrawDataDefaults[type].parent != type)
queueDD(kDrawDataDefaults[type].parent, r);
_screenQueue.push_back(q);
}
} else {
q->drawSelf(!_widgets[type]->_buffer, _widgets[type]->_buffer);
delete q;
}
}
void ThemeEngine::queueDDText(TextData type, const Common::Rect &r, const Common::String &text, bool restoreBg,
bool ellipsis, TextAlign alignH, TextAlignVertical alignV, int deltax) {
if (_texts[type] == 0)
return;
Common::Rect area = r;
area.clip(_screen->w, _screen->h);
ThemeItemTextData *q = new ThemeItemTextData(this, _texts[type], area, text, alignH, alignV, ellipsis, restoreBg, deltax);
if (_buffering) {
_screenQueue.push_back(q);
} else {
q->drawSelf(true, false);
delete q;
}
}
void ThemeEngine::queueBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha) {
Common::Rect area = r;
area.clip(_screen->w, _screen->h);
ThemeItemBitmap *q = new ThemeItemBitmap(this, area, bitmap, alpha);
if (_buffering) {
_bufferQueue.push_back(q);
} else {
q->drawSelf(true, false);
delete q;
}
}
/**********************************************************
* Widget drawing functions
*********************************************************/
void ThemeEngine::drawButton(const Common::Rect &r, const Common::String &str, WidgetStateInfo state, uint16 hints) {
if (!ready())
return;
DrawData dd = kDDButtonIdle;
if (state == kStateEnabled)
dd = kDDButtonIdle;
else if (state == kStateHighlight)
dd = kDDButtonHover;
else if (state == kStateDisabled)
dd = kDDButtonDisabled;
queueDD(dd, r);
queueDDText(getTextData(dd), r, str, false, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV);
}
void ThemeEngine::drawLineSeparator(const Common::Rect &r, WidgetStateInfo state) {
if (!ready())
return;
queueDD(kDDSeparator, r);
}
void ThemeEngine::drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, WidgetStateInfo state) {
if (!ready())
return;
Common::Rect r2 = r;
DrawData dd = kDDCheckboxDefault;
if (checked)
dd = kDDCheckboxSelected;
if (state == kStateDisabled)
dd = kDDCheckboxDisabled;
TextData td = (state == kStateHighlight) ? kTextDataHover : getTextData(dd);
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(td, r2, str, false, false, _widgets[kDDCheckboxDefault]->_textAlignH, _widgets[dd]->_textAlignV);
}
void ThemeEngine::drawSlider(const Common::Rect &r, int width, WidgetStateInfo state) {
if (!ready())
return;
DrawData dd = kDDSliderFull;
if (state == kStateHighlight)
dd = kDDSliderHover;
else if (state == kStateDisabled)
dd = kDDSliderDisabled;
Common::Rect r2 = r;
r2.setWidth(MIN((int16)width, r.width()));
// r2.top++; r2.bottom--; r2.left++; r2.right--;
drawWidgetBackground(r, 0, kWidgetBackgroundSlider, kStateEnabled);
if (width > r.width() * 5 / 100)
queueDD(dd, r2);
}
void ThemeEngine::drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, ScrollbarState scrollState, WidgetStateInfo state) {
if (!ready())
return;
queueDD(kDDScrollbarBase, r);
Common::Rect r2 = r;
const int buttonExtra = (r.width() * 120) / 100;
r2.bottom = r2.top + buttonExtra;
queueDD(scrollState == kScrollbarStateUp ? kDDScrollbarButtonHover : kDDScrollbarButtonIdle, r2, Graphics::VectorRenderer::kTriangleUp);
r2.translate(0, r.height() - r2.height());
queueDD(scrollState == kScrollbarStateDown ? kDDScrollbarButtonHover : kDDScrollbarButtonIdle, r2, Graphics::VectorRenderer::kTriangleDown);
r2 = r;
r2.left += 1;
r2.right -= 1;
r2.top += sliderY;
r2.bottom = r2.top + sliderHeight - 1;
r2.top += r.width() / 5;
r2.bottom -= r.width() / 5;
queueDD(scrollState == kScrollbarStateSlider ? kDDScrollbarHandleHover : kDDScrollbarHandleIdle, r2);
}
void ThemeEngine::drawDialogBackground(const Common::Rect &r, DialogBackground bgtype, WidgetStateInfo state) {
if (!ready())
return;
switch (bgtype) {
case kDialogBackgroundMain:
queueDD(kDDMainDialogBackground, r);
break;
case kDialogBackgroundSpecial:
queueDD(kDDSpecialColorBackground, r);
break;
case kDialogBackgroundPlain:
queueDD(kDDPlainColorBackground, r);
break;
case kDialogBackgroundDefault:
queueDD(kDDDefaultBackground, r);
break;
}
}
void ThemeEngine::drawCaret(const Common::Rect &r, bool erase, WidgetStateInfo state) {
if (!ready())
return;
if (erase) {
restoreBackground(r);
addDirtyRect(r);
} else
queueDD(kDDCaret, r);
}
void ThemeEngine::drawPopUpWidget(const Common::Rect &r, const Common::String &sel, int deltax, WidgetStateInfo state, TextAlign align) {
if (!ready())
return;
DrawData dd = (state == kStateHighlight) ? kDDPopUpHover : kDDPopUpIdle;
queueDD(dd, r);
if (!sel.empty()) {
Common::Rect text(r.left, r.top, r.right - 16, r.bottom);
queueDDText(getTextData(dd), text, sel, false, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV, deltax);
}
}
void ThemeEngine::drawSurface(const Common::Rect &r, const Graphics::Surface &surface, WidgetStateInfo state, int alpha, bool themeTrans) {
if (!ready())
return;
queueBitmap(&surface, r, themeTrans);
}
void ThemeEngine::drawWidgetBackground(const Common::Rect &r, uint16 hints, WidgetBackground background, WidgetStateInfo state) {
if (!ready())
return;
switch (background) {
case kWidgetBackgroundBorderSmall:
queueDD(kDDWidgetBackgroundSmall, r);
break;
case kWidgetBackgroundEditText:
queueDD(kDDWidgetBackgroundEditText, r);
break;
case kWidgetBackgroundSlider:
queueDD(kDDWidgetBackgroundSlider, r);
break;
default:
queueDD(kDDWidgetBackgroundDefault, r);
break;
}
}
void ThemeEngine::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, const Common::Array<Common::String> &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state) {
if (!ready())
return;
const int tabOffset = 2;
tabWidth -= tabOffset;
queueDD(kDDTabBackground, Common::Rect(r.left, r.top, r.right, r.top + tabHeight));
for (int i = 0; i < (int)tabs.size(); ++i) {
if (i == active)
continue;
Common::Rect tabRect(r.left + i * (tabWidth + tabOffset), r.top, r.left + i * (tabWidth + tabOffset) + tabWidth, r.top + tabHeight);
queueDD(kDDTabInactive, tabRect);
queueDDText(getTextData(kDDTabInactive), tabRect, tabs[i], false, false, _widgets[kDDTabInactive]->_textAlignH, _widgets[kDDTabInactive]->_textAlignV);
}
if (active >= 0) {
Common::Rect tabRect(r.left + active * (tabWidth + tabOffset), r.top, r.left + active * (tabWidth + tabOffset) + tabWidth, r.top + tabHeight);
const uint16 tabLeft = active * (tabWidth + tabOffset);
const uint16 tabRight = MAX(r.right - tabRect.right, 0);
queueDD(kDDTabActive, tabRect, (tabLeft << 16) | (tabRight & 0xFFFF));
queueDDText(getTextData(kDDTabActive), tabRect, tabs[active], false, false, _widgets[kDDTabActive]->_textAlignH, _widgets[kDDTabActive]->_textAlignV);
}
}
void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state, TextAlign align, bool inverted, int deltax, bool useEllipsis, FontStyle font) {
if (!ready())
return;
if (inverted) {
queueDD(kDDTextSelectionBackground, r);
queueDDText(kTextDataInverted, r, str, false, useEllipsis, align, kTextAlignVCenter, deltax);
return;
}
switch (font) {
case kFontStyleNormal:
queueDDText(kTextDataNormalFont, r, str, true, useEllipsis, align, kTextAlignVCenter, deltax);
return;
default:
break;
}
switch (state) {
case kStateDisabled:
queueDDText(kTextDataDisabled, r, str, true, useEllipsis, align, kTextAlignVCenter, deltax);
return;
case kStateHighlight:
queueDDText(kTextDataHover, r, str, true, useEllipsis, align, kTextAlignVCenter, deltax);
return;
case kStateEnabled:
queueDDText(kTextDataDefault, r, str, true, useEllipsis, align, kTextAlignVCenter, deltax);
return;
}
}
void ThemeEngine::drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, WidgetStateInfo state) {
if (!ready())
return;
restoreBackground(r);
font->drawChar(_screen, ch, r.left, r.top, 0);
addDirtyRect(r);
}
void ThemeEngine::debugWidgetPosition(const char *name, const Common::Rect &r) {
_font->drawString(_screen, name, r.left, r.top, r.width(), 0xFFFF, Graphics::kTextAlignRight, 0, true);
_screen->hLine(r.left, r.top, r.right, 0xFFFF);
_screen->hLine(r.left, r.bottom, r.right, 0xFFFF);
_screen->vLine(r.left, r.top, r.bottom, 0xFFFF);
_screen->vLine(r.right, r.top, r.bottom, 0xFFFF);
}
/**********************************************************
* Screen/overlay management
*********************************************************/
void ThemeEngine::updateScreen() {
if (!_bufferQueue.empty()) {
_vectorRenderer->setSurface(_backBuffer);
for (Common::List<ThemeItem*>::iterator q = _bufferQueue.begin(); q != _bufferQueue.end(); ++q) {
(*q)->drawSelf(true, false);
delete *q;
}
_vectorRenderer->setSurface(_screen);
_vectorRenderer->blitSurface(_backBuffer, Common::Rect(0, 0, _screen->w, _screen->h));
_bufferQueue.clear();
}
if (!_screenQueue.empty()) {
_vectorRenderer->disableShadows();
for (Common::List<ThemeItem*>::iterator q = _screenQueue.begin(); q != _screenQueue.end(); ++q) {
(*q)->drawSelf(true, false);
delete *q;
}
_vectorRenderer->enableShadows();
_screenQueue.clear();
}
renderDirtyScreen();
}
void ThemeEngine::renderDirtyScreen() {
if (_dirtyScreen.empty())
return;
Common::List<Common::Rect>::const_iterator i, j;
for (i = _dirtyScreen.begin(); i != _dirtyScreen.end(); ++i) {
for (j = i; j != _dirtyScreen.end(); ++j)
if (j != i && i->contains(*j))
j = _dirtyScreen.reverse_erase(j);
_vectorRenderer->copyFrame(_system, *i);
}
_dirtyScreen.clear();
}
void ThemeEngine::openDialog(bool doBuffer, ShadingStyle style) {
if (doBuffer)
_buffering = true;
if (style != kShadingNone) {
_vectorRenderer->applyScreenShading(style);
addDirtyRect(Common::Rect(0, 0, _screen->w, _screen->h));
}
_vectorRenderer->setSurface(_backBuffer);
_vectorRenderer->blitSurface(_screen, Common::Rect(0, 0, _screen->w, _screen->h));
_vectorRenderer->setSurface(_screen);
}
void ThemeEngine::setUpCursor() {
CursorMan.pushCursorPalette(_cursorPal, 0, MAX_CURS_COLORS);
CursorMan.pushCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, _cursorTargetScale);
CursorMan.showMouse(true);
}
bool ThemeEngine::createCursor(const Common::String &filename, int hotspotX, int hotspotY, int scale) {
if (!_system->hasFeature(OSystem::kFeatureCursorHasPalette))
return true;
const Surface *cursor = _bitmaps[filename];
if (!cursor)
return false;
_cursorHotspotX = hotspotX;
_cursorHotspotY = hotspotY;
_cursorTargetScale = scale;
_cursorWidth = cursor->w;
_cursorHeight = cursor->h;
uint colorsFound = 0;
const OverlayColor *src = (const OverlayColor*)cursor->pixels;
byte *table = new byte[65536];
assert(table);
memset(table, 0, sizeof(byte)*65536);
byte r, g, b;
uint16 transparency = RGBToColor<ColorMasks<565> >(255, 0, 255);
delete[] _cursor;
_cursor = new byte[_cursorWidth * _cursorHeight];
assert(_cursor);
memset(_cursor, 255, sizeof(byte)*_cursorWidth*_cursorHeight);
for (uint y = 0; y < _cursorHeight; ++y) {
for (uint x = 0; x < _cursorWidth; ++x) {
_system->colorToRGB(src[x], r, g, b);
uint16 col = RGBToColor<ColorMasks<565> >(r, g, b);
if (!table[col] && col != transparency) {
table[col] = colorsFound++;
uint index = table[col];
_cursorPal[index * 4 + 0] = r;
_cursorPal[index * 4 + 1] = g;
_cursorPal[index * 4 + 2] = b;
_cursorPal[index * 4 + 3] = 0xFF;
if (colorsFound > MAX_CURS_COLORS) {
warning("Cursor contains too much colors (%d, but only %d are allowed)", colorsFound, MAX_CURS_COLORS);
return false;
}
}
if (col != transparency) {
uint index = table[col];
_cursor[y * _cursorWidth + x] = index;
}
}
src += _cursorWidth;
}
_useCursor = true;
delete[] table;
return true;
}
} // end of namespace GUI.