diff options
Diffstat (limited to 'gui/ThemeEngine.cpp')
-rw-r--r-- | gui/ThemeEngine.cpp | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp new file mode 100644 index 0000000000..5ed142cb31 --- /dev/null +++ b/gui/ThemeEngine.cpp @@ -0,0 +1,1070 @@ +/* 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. |