diff options
Diffstat (limited to 'gui/ThemeModern.cpp')
-rw-r--r-- | gui/ThemeModern.cpp | 1597 |
1 files changed, 1597 insertions, 0 deletions
diff --git a/gui/ThemeModern.cpp b/gui/ThemeModern.cpp new file mode 100644 index 0000000000..6f12f856cd --- /dev/null +++ b/gui/ThemeModern.cpp @@ -0,0 +1,1597 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef DISABLE_FANCY_THEMES + +#include "gui/theme.h" +#include "gui/eval.h" + +#include "graphics/imageman.h" +#include "graphics/imagedec.h" +#include "graphics/colormasks.h" +#include "graphics/cursorman.h" +#include "graphics/paletteman.h" + +#include "common/config-manager.h" +#include "common/file.h" + +#define kShadowTr0 8 +#define kShadowTr1 16 +#define kShadowTr2 32 +#define kShadowTr3 64 +#define kShadowTr35 96 +#define kShadowTr4 128 +#define kShadowTr5 192 + +using Graphics::Surface; + +/** Specifies the currently active 16bit pixel format, 555 or 565. */ +extern int gBitFormat; + +namespace GUI { + +OverlayColor getColorAlpha(OverlayColor col1, OverlayColor col2, int alpha); +OverlayColor calcGradient(OverlayColor start, OverlayColor end, int pos, int max, uint factor); + +#pragma mark - + +ThemeModern::ThemeModern(OSystem *system, const Common::String &stylefile, const Common::ConfigFile *cfg) : Theme(), _system(system), _screen(), _initOk(false), +_lastUsedBitMask(0), _forceRedraw(false), _imageHandles(0), _images(0), _colors(), _fonts(), _cursor(0), _gradientFactors() { + _stylefile = stylefile; + _initOk = false; + _useCursor = false; + memset(&_screen, 0, sizeof(_screen)); + memset(&_dialog, 0, sizeof(_dialog)); + memset(&_colors, 0, sizeof(_colors)); + memset(&_gradientFactors, 0, sizeof(_gradientFactors)); + + _screen.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor)); + if (_screen.pixels) { + _initOk = true; + clearAll(); + } + + if (cfg) { + _configFile = *cfg; + } else { + if (!loadConfigFile(stylefile)) { + warning("Can not find theme config file '%s'", (stylefile + ".ini").c_str()); + return; + } + } + + ImageMan.addArchive(stylefile + ".zip"); + + Common::String temp; + _configFile.getKey("version", "theme", temp); + if (atoi(temp.c_str()) != THEME_VERSION) { + // TODO: improve this detection and handle it nicer + warning("Theme config uses a different version (you have: '%s', needed is: '%d')", temp.c_str(), THEME_VERSION); + return; + } + + temp.clear(); + _configFile.getKey("type", "theme", temp); + if (0 != temp.compareToIgnoreCase("modern")) { + warning("Theme config is not for the modern style theme"); + return; + } + + if (_configFile.hasKey("name", "theme")) + _configFile.getKey("name", "theme", _stylename); + else + _stylename = _stylefile; + + _images = new const Graphics::Surface*[kImageHandlesMax]; + assert(_images); + + _lastUsedBitMask = gBitFormat; +} + +ThemeModern::~ThemeModern() { + deleteFonts(); + deinit(); + delete [] _images; + delete [] _cursor; + _images = 0; + if (_imageHandles) { + for (int i = 0; i < kImageHandlesMax; ++i) { + ImageMan.unregisterSurface(_imageHandles[i]); + } + } + ImageMan.remArchive(_stylefile + ".zip"); +} + +bool ThemeModern::init() { + if (!_images) + return false; + + deinit(); + _screen.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor)); + if (_screen.pixels) { + _initOk = true; + clearAll(); + resetDrawArea(); + } + + if (isThemeLoadingRequired()) { + loadTheme(_defaultConfig); + loadTheme(_configFile, false); // Don't reset + + processExtraValues(); + } + + for (int i = 0; i < kImageHandlesMax; ++i) { + if (!_images[i]) { + return false; + } + } + + return true; +} + +void ThemeModern::deinit() { + if (_initOk) { + _system->hideOverlay(); + _screen.free(); + _initOk = false; + } +} + +void ThemeModern::refresh() { + init(); + resetupGuiRenderer(); + if (_enabled) { + _system->showOverlay(); + PaletteMan.replaceCursorPalette(_cursorPal, 0, MAX_CURS_COLORS); + CursorMan.replaceCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, _cursorTargetScale); + } +} + +void ThemeModern::enable() { + init(); + resetupGuiRenderer(); + resetDrawArea(); + setUpCursor(); + _system->showOverlay(); + clearAll(); + _enabled = true; +} + +void ThemeModern::disable() { + _system->hideOverlay(); + PaletteMan.popCursorPalette(); + _enabled = false; +} + +void ThemeModern::openDialog(bool topDialog) { + if (!_dialog) { + _dialog = new DialogState; + assert(_dialog); + // first dialog + _dialog->screen.create(_screen.w, _screen.h, sizeof(OverlayColor)); + } + + if (_dialogShadingCallback && topDialog) { + OverlayColor *col = (OverlayColor*)_screen.pixels; + for (int y = 0; y < _screen.h; ++y) { + for (int x = 0; x < _screen.w; ++x) { + col[x] = (this->*(_dialogShadingCallback))(col[x]); + } + col += _screen.w; + } + } + + memcpy(_dialog->screen.pixels, _screen.pixels, _screen.pitch*_screen.h); + + if ((_dialogShadingCallback) && topDialog) + addDirtyRect(Common::Rect(0, 0, _screen.w, _screen.h), false, false); +} + +void ThemeModern::closeDialog() { + if (_dialog) { + _dialog->screen.free(); + delete _dialog; + _dialog = 0; + } + _forceRedraw = true; +} + +void ThemeModern::clearAll() { + if (!_initOk) + return; + _system->clearOverlay(); + // FIXME: problem with the 'pitch' + _system->grabOverlay((OverlayColor*)_screen.pixels, _screen.w); +} + +void ThemeModern::drawAll() { + // TODO: see ThemeModern::addDirtyRect + _forceRedraw = false; +} + +void ThemeModern::setDrawArea(const Common::Rect &r) { + if (_initOk) { + _drawArea = r; + _shadowDrawArea = Common::Rect(r.left-_shadowLeftWidth, r.top-_shadowTopHeight, r.right+_shadowRightWidth, r.bottom+_shadowBottomHeight); + _drawArea.clip(_screen.w, _screen.h); + _shadowDrawArea.clip(_screen.w, _screen.h); + } +} + +void ThemeModern::resetDrawArea() { + if (_initOk) { + _drawArea = Common::Rect(0, 0, _screen.w, _screen.h); + _shadowDrawArea = _drawArea; + } +} + +#define surface(x) (_images[x]) + +void ThemeModern::drawDialogBackground(const Common::Rect &r, uint16 hints, State state) { + if (!_initOk) + return; + + Common::Rect r2 = shadowRect(r, kShadowFull); + + if ((hints & THEME_HINT_SAVE_BACKGROUND) && !(hints & THEME_HINT_FIRST_DRAW) && !_forceRedraw) { + restoreBackground(r2, true); + return; + } + + if (hints & THEME_HINT_MAIN_DIALOG) { + colorFade(r, _colors[kMainDialogStart], _colors[kMainDialogEnd], _gradientFactors[kMainDialogFactor]); + } else if (hints & THEME_HINT_SPECIAL_COLOR) { + drawShadow(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), kShadowFull); + + drawRectMasked(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), + 256, _colors[kMainDialogStart], _colors[kMainDialogEnd], _gradientFactors[kDialogSpecialFactor]); + } else if (hints & THEME_HINT_PLAIN_COLOR) { + drawShadow(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), kShadowFull); + + drawRectMasked(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), + 256, _colors[kDialogEnd], _colors[kDialogEnd], _gradientFactors[kDialogFactor]); + } else { + drawShadow(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), kShadowFull); + + drawRectMasked(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), + 256, _colors[kDialogStart], _colors[kDialogEnd], _gradientFactors[kDialogFactor]); + } + + addDirtyRect(r2, (hints & THEME_HINT_SAVE_BACKGROUND) != 0, true); +} + +void ThemeModern::drawText(const Common::Rect &r, const Common::String &str, State state, TextAlign align, bool inverted, int deltax, bool useEllipsis, FontStyle font) { + if (!_initOk) + return; + + Common::Rect r2(r.left, r.top, r.right, r.top+getFontHeight(font)); + uint32 color; + + restoreBackground(r2); + r2.bottom += 4; + + if (inverted) { + _screen.fillRect(r, _colors[kTextInvertedBackground]); + color = _colors[kTextInvertedColor]; + } else { + color = getColor(state); + } + + getFont(font)->drawString(&_screen, str, r.left, r.top, r.width(), color, convertAligment(align), deltax, useEllipsis); + addDirtyRect(r2); +} + +void ThemeModern::drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, State state) { + if (!_initOk) + return; + restoreBackground(r); + font->drawChar(&_screen, ch, r.left, r.top, getColor(state)); + addDirtyRect(r); +} + +void ThemeModern::drawWidgetBackground(const Common::Rect &r, uint16 hints, WidgetBackground background, State state) { + if (!_initOk) + return; + + Common::Rect r2; + + ImageHandles corner, top, left, bkgd; + ShadowStyles shadow; + ColorHandles start, end; + GradientFactors factor; + + switch (background) { + case kWidgetBackgroundBorderSmall: + corner = kWidgetSmallBkgdCorner; + top = kWidgetSmallBkgdTop; + left = kWidgetSmallBkgdLeft; + bkgd = kWidgetSmallBkgd; + shadow = kShadowSmall; + start = kWidgetBackgroundSmallStart; + end = kWidgetBackgroundSmallEnd; + factor = kWidgetSmallFactor; + break; + case kWidgetBackgroundEditText: + corner = kEditTextBkgdCorner; + top = kEditTextBkgdTop; + left = kEditTextBkgdLeft; + bkgd = kEditTextBkgd; + shadow = kShadowEmboss; + start = kEditTextBackgroundStart; + end = kEditTextBackgroundEnd; + factor = kEditTextFactor; + break; + case kWidgetBackgroundSlider: + corner = kSliderBkgdCorner; + top = kSliderBkgdTop; + left = kSliderBkgdLeft; + bkgd = kSliderBkgd; + shadow = kShadowEmboss; + start = kSliderBackgroundStart; + end = kSliderBackgroundEnd; + factor = kSliderBackground; + break; + default: + corner = kWidgetBkgdCorner; + top = kWidgetBkgdTop; + left = kWidgetBkgdLeft; + bkgd = kWidgetBkgd; + shadow = kShadowFull; + if (hints & THEME_HINT_PLAIN_COLOR) + start = kWidgetBackgroundEnd; + else + start = kWidgetBackgroundStart; + end = kWidgetBackgroundEnd; + factor = kWidgetFactor; + break; + } + + if ((hints & THEME_HINT_SAVE_BACKGROUND) && !(hints & THEME_HINT_FIRST_DRAW) && !_forceRedraw) { + restoreBackground((hints & THEME_HINT_USE_SHADOW) ? r2 : r); + return; + } + + if ((hints & THEME_HINT_USE_SHADOW)) { + r2 = shadowRect(r, shadow); + restoreBackground(r2); + // shadow + drawShadow(r, surface(corner), surface(top), surface(left), surface(bkgd), shadow); + } + + drawRectMasked(r, surface(corner), surface(top), surface(left), surface(bkgd), + (state == kStateDisabled) ? -30 : 256, _colors[start], _colors[end], + _gradientFactors[factor]); + + addDirtyRect((hints & THEME_HINT_USE_SHADOW) ? r2 : r, (hints & THEME_HINT_SAVE_BACKGROUND) != 0); +} + +void ThemeModern::drawButton(const Common::Rect &r, const Common::String &str, State state, uint16 hints) { + if (!_initOk) + return; + + Common::Rect r2 = shadowRect(r, kShadowButton); + + if (!(hints & THEME_HINT_NO_BACKGROUND_RESTORE) || state == kStateDisabled) + restoreBackground(r2); + + // shadow + drawShadow(r, surface(kButtonBkgdCorner), surface(kButtonBkgdTop), surface(kButtonBkgdLeft), surface(kButtonBkgd), kShadowButton); + + if (state == kStateHighlight) { + drawRectMasked(r, surface(kButtonBkgdCorner), surface(kButtonBkgdTop), surface(kButtonBkgdLeft), surface(kButtonBkgd), + 256, _colors[kButtonBackgroundHighlightStart], _colors[kButtonBackgroundHighlightEnd], + _gradientFactors[kButtonFactor]); + } else { + drawRectMasked(r, surface(kButtonBkgdCorner), surface(kButtonBkgdTop), surface(kButtonBkgdLeft), surface(kButtonBkgd), + (state == kStateDisabled) ? -30 : 256, _colors[kButtonBackgroundStart], _colors[kButtonBackgroundEnd], + _gradientFactors[kButtonFactor]); + } + + const int off = (r.height() - getFontHeight()) / 2; + + OverlayColor col = 0; + switch (state) { + case kStateEnabled: + col = _colors[kButtonTextEnabled]; + break; + + case kStateHighlight: + col = _colors[kButtonTextHighlight]; + break; + + default: + col = _colors[kButtonTextDisabled]; + break; + }; + + getFont()->drawString(&_screen, str, r.left, r.top + off, r.width(), col, Graphics::kTextAlignCenter, 0, true); + + addDirtyRect(r2); +} + +void ThemeModern::drawSurface(const Common::Rect &r, const Graphics::Surface &surface, State state, int alpha, bool themeTrans) { + if (!_initOk) + return; + + Common::Rect rect(r.left, r.top, r.left + surface.w, r.top + surface.h); + rect.clip(_screen.w, _screen.h); + + if (!rect.isValidRect()) + return; + + assert(surface.bytesPerPixel == sizeof(OverlayColor)); + + if (alpha != 256) + restoreBackground(rect); + + if (themeTrans) + drawSurface(rect, &surface, false, false, alpha); + else { + OverlayColor *dst = (OverlayColor*)_screen.getBasePtr(rect.left, rect.top); + const OverlayColor *src = (OverlayColor*)surface.pixels; + + int h = rect.height(); + if (alpha == 256) { + while (h--) { + memcpy(dst, src, surface.pitch); + dst += _screen.w; + src += surface.w; + } + } else { + int w = rect.width(); + while (h--) { + for (int i = 0; i < w; ++i) { + *dst = getColorAlpha(*src, *dst, alpha); + } + dst += _screen.w; + src += surface.w; + } + } + } + + addDirtyRect(rect); +} + +void ThemeModern::drawSlider(const Common::Rect &rr, int width, State state) { + if (!_initOk) + return; + + Common::Rect r = rr; + + r.left++; + r.right++; + + drawWidgetBackground(r, THEME_HINT_USE_SHADOW, kWidgetBackgroundSlider, kStateEnabled); + + Common::Rect r2 = r; + r2.left = r.left; + r2.top = r.top; + r2.bottom = r.bottom-1; + r2.right = r2.left + width-1; + if (r2.right > r.right) { + r2.right = r.right; + } + + drawShadow(r2, surface(kButtonBkgdCorner), surface(kButtonBkgdTop), surface(kButtonBkgdLeft), surface(kButtonBkgd), kShadowButton); + if (state == kStateHighlight) { + drawRectMasked(r2, surface(kSliderCorner), surface(kSliderTop), surface(kSliderLeft), surface(kSliderBkgd), + 256, _colors[kSliderHighStart], _colors[kSliderHighEnd], _gradientFactors[kSliderFactor]); + } else { + drawRectMasked(r2, surface(kSliderCorner), surface(kSliderTop), surface(kSliderLeft), surface(kSliderBkgd), + (state == kStateDisabled) ? -30 : 256, _colors[kSliderStart], _colors[kSliderEnd], _gradientFactors[kSliderFactor]); + } + + addDirtyRect(r); +} + +void ThemeModern::drawPopUpWidget(const Common::Rect &r, const Common::String &sel, int deltax, State state, TextAlign align) { + if (!_initOk) + return; + + Common::Rect r2 = shadowRect(r, kShadowSmall); + restoreBackground(r2); + + OverlayColor start = _colors[kPopUpWidgetStart], end = _colors[kPopUpWidgetEnd]; + if (state == kStateHighlight) { + start = _colors[kPopUpWidgetHighlightStart]; + end = _colors[kPopUpWidgetHighlightEnd]; + } + + drawShadow(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), kShadowPopUp); + + drawRectMasked(r, surface(kPopUpWidgetBkgdCorner), surface(kPopUpWidgetBkgdTop), surface(kPopUpWidgetBkgdLeft), surface(kPopUpWidgetBkgd), + (state == kStateDisabled) ? -30 : 256, start, end, _gradientFactors[kPopUpWidgetFactor]); + + const Graphics::Surface *arrow = surface(kWidgetArrow); + + int yOff = r.height() / 2 - arrow->h; + if (yOff < 0) + yOff = 0; + + Common::Rect arrowRect(r.right - 4 - arrow->w, r.top + yOff, r.right - 4, r.top + yOff + arrow->h); + arrowRect.clip(r); + + drawSurface(arrowRect, arrow, false, false, (state == kStateDisabled) ? -30 : 256); + + arrowRect.top += arrow->h; + arrowRect.bottom += arrow->h; + arrowRect.clip(r); + drawSurface(arrowRect, arrow, true, false, (state == kStateDisabled) ? -30 : 256); + + if (!sel.empty()) { + Common::Rect text(r.left + 2, r.top + 3, r.right - 4, r.top + 3 + getFont()->getFontHeight()); + getFont()->drawString(&_screen, sel, text.left, text.top, text.width(), getColor(state), convertAligment(align), deltax, false); + } + + addDirtyRect(r2); +} + +void ThemeModern::drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, State state) { + if (!_initOk) + return; + Common::Rect r2 = r; + + const Graphics::Surface *checkBox = surface(checked ? kCheckboxChecked : kCheckboxEmpty); + int checkBoxSize = checkBox->w; + + restoreBackground(Common::Rect(r.left, r.top, r.left+checkBox->w, r.top+checkBox->h)); + + drawSurface(Common::Rect(r.left, r.top, r.left+checkBox->w, r.top+checkBox->h), checkBox, false, false, (state == kStateDisabled) ? 128 : 256); + + r2.left += checkBoxSize + 5; + getFont()->drawString(&_screen, str, r2.left, r2.top, r2.width(), getColor(state), Graphics::kTextAlignLeft, 0, false); + + addDirtyRect(r); +} + +void ThemeModern::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, const Common::Array<Common::String> &tabs, int active, uint16 hints, int titleVPad, State state) { + if (!_initOk) + return; + + restoreBackground(r); + + // whole tab background + drawRectMasked(r, surface(kTabBkgdCorner), surface(kTabBkgdTop), surface(kTabBkgdLeft), surface(kTabBkgd), + /*(state == kStateDisabled) ? -30 : */256, _colors[kTabBackgroundStart], _colors[kTabBackgroundEnd], + _gradientFactors[kTabFactor]); + + OverlayColor tabEnd = calcGradient(_colors[kTabActiveStart], _colors[kTabActiveEnd], tabHeight, r.height(), _gradientFactors[kTabFactor]); + + const int tabOffset = 1; + + // tab shadows + for (int i = 0; i < (int)tabs.size(); ++i) { + Common::Rect tabRect(r.left + i * (tabWidth + tabOffset), r.top, r.left + i * (tabWidth + tabOffset) + tabWidth, r.top + tabHeight); + drawShadow(tabRect, surface(kTabBkgdCorner), surface(kTabBkgdTop), surface(kTabBkgdLeft), surface(kTabBkgd), + kShadowSmall, true); + } + + // inactive tabs + 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 + 5); + drawRectMasked(tabRect, surface(kTabBkgdCorner), surface(kTabBkgdTop), surface(kTabBkgdLeft), surface(kTabBkgd), + 256, _colors[kTabInactiveStart], _colors[kTabInactiveEnd], _gradientFactors[kTabFactor], true); + + getFont()->drawString(&_screen, tabs[i], tabRect.left, tabRect.top+titleVPad, tabRect.width(), getColor(kStateEnabled), Graphics::kTextAlignCenter, 0, true); + } + + // area shadow + Common::Rect widgetBackground = Common::Rect(r.left, r.top + tabHeight - 1, r.right, r.top + + tabHeight + 20); + drawShadow(widgetBackground, surface(kTabBkgdCorner), surface(kTabBkgdTop), surface(kTabBkgdLeft), surface(kTabBkgd), + kShadowSmall); + + // area itself + widgetBackground = Common::Rect(r.left, r.top + tabHeight, r.right, r.bottom); + drawRectMasked(widgetBackground, surface(kTabBkgdCorner), surface(kTabBkgdTop), surface(kTabBkgdLeft), surface(kTabBkgd), + /*(state == kStateDisabled) ? -30 : */256, tabEnd, _colors[kTabActiveEnd], + _gradientFactors[kTabFactor]); + addDirtyRect(widgetBackground, true); + + // active tab + if (active >= 0) { + Common::Rect tabRect(r.left + active * (tabWidth + tabOffset), r.top, r.left + active * (tabWidth + tabOffset) + tabWidth, r.top + tabHeight + 5); + + Common::Rect shadowRect2(tabRect.left, r.top, tabRect.right, tabRect.bottom - 6); + drawShadow(shadowRect2, surface(kTabBkgdCorner), surface(kTabBkgdTop), surface(kTabBkgdLeft), surface(kTabBkgd), kShadowSmall, false, true); + + drawRectMasked(tabRect, surface(kTabBkgdCorner), surface(kTabBkgdTop), surface(kTabBkgdLeft), surface(kTabBkgd), + 256, _colors[kTabActiveStart], tabEnd, _gradientFactors[kTabFactor], true); + + + getFont()->drawString(&_screen, tabs[active], tabRect.left, tabRect.top+titleVPad, tabRect.width(), getColor(kStateEnabled), Graphics::kTextAlignCenter, 0, true); + } + + addDirtyRect(Common::Rect(r.left, r.top-2, r.right, r.bottom)); +} + +void ThemeModern::drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, ScrollbarState scrollState, State state) { + if (!_initOk) + return; + const int UP_DOWN_BOX_HEIGHT = r.width() + 1; + Common::Rect r2 = r; + + // draws the scrollbar background + drawRectMasked(r2, surface(kScrollbarBkgdCorner), surface(kScrollbarBkgdTop), surface(kScrollbarBkgdLeft), surface(kScrollbarBkgd), 256, + _colors[kScrollbarBackgroundStart], _colors[kScrollbarBackgroundEnd], _gradientFactors[kScrollbarBkgdFactor]); + + // draws the 'up' button + OverlayColor buttonStart = 0; + OverlayColor buttonEnd = 0; + + if (scrollState == kScrollbarStateUp) { + buttonStart = _colors[kScrollbarButtonHighlightStart]; + buttonEnd = _colors[kScrollbarButtonHighlightEnd]; + } else { + buttonStart = _colors[kScrollbarButtonStart]; + buttonEnd = _colors[kScrollbarButtonEnd]; + } + + r2.bottom = r2.top + UP_DOWN_BOX_HEIGHT; + drawRectMasked(r2, surface(kScrollbarBkgdCorner), surface(kScrollbarBkgdTop), surface(kScrollbarBkgdLeft), surface(kScrollbarBkgd), 256, + buttonStart, buttonEnd, _gradientFactors[kScrollbarBkgdFactor]); + + const Graphics::Surface *arrow = surface(kWidgetArrow); + r2.left += 1 + (r2.width() - arrow->w) / 2; + r2.right = r2.left + arrow->w; + r2.top += (r2.height() - arrow->h) / 2; + r2.bottom = r2.top + arrow->h; + drawSurface(r2, arrow, false, false, 256); + + // draws the slider + OverlayColor sliderStart = 0; + OverlayColor sliderEnd = 0; + + if (scrollState == kScrollbarStateSlider) { + sliderStart = _colors[kScrollbarSliderHighlightStart]; + sliderEnd = _colors[kScrollbarSliderHighlightEnd]; + } else { + sliderStart = _colors[kScrollbarSliderStart]; + sliderEnd = _colors[kScrollbarSliderEnd]; + } + + r2 = r; + r2.left += 1; + r2.right -= 1; + r2.top += sliderY; + r2.bottom = r2.top + sliderHeight - 1; + + drawShadow(r2, surface(kSliderCorner), surface(kSliderTop), surface(kSliderLeft), surface(kSliderBkgd), kShadowSmall); + + r2.left += 1; + r2.right -= 1; + + r2.bottom = r2.top + sliderHeight / 2 + surface(kScrollbarCorner)->h + 4; + drawRectMasked(r2, surface(kScrollbarCorner), surface(kScrollbarTop), surface(kScrollbarLeft), surface(kScrollbarBkgd), 256, + sliderStart, sliderEnd, _gradientFactors[kScrollbarFactor]); + r2.top += sliderHeight / 2; + r2.bottom += sliderHeight / 2 - surface(kScrollbarCorner)->h - 4; + drawRectMasked(r2, surface(kScrollbarCorner), surface(kScrollbarTop), surface(kScrollbarLeft), surface(kScrollbarBkgd), 256, + sliderEnd, sliderStart, _gradientFactors[kScrollbarFactor]); + + // draws the 'down' button + + if (scrollState == kScrollbarStateDown) { + buttonStart = _colors[kScrollbarButtonHighlightStart]; + buttonEnd = _colors[kScrollbarButtonHighlightEnd]; + } else { + buttonStart = _colors[kScrollbarButtonStart]; + buttonEnd = _colors[kScrollbarButtonEnd]; + } + + r2 = r; + r2.top = r2.bottom - UP_DOWN_BOX_HEIGHT; + drawRectMasked(r2, surface(kScrollbarBkgdCorner), surface(kScrollbarBkgdTop), surface(kScrollbarBkgdLeft), surface(kScrollbarBkgd), 256, + buttonStart, buttonEnd, _gradientFactors[kScrollbarBkgdFactor]); + + r2.left += 1 + (r2.width() - arrow->w) / 2; + r2.right = r2.left + arrow->w; + r2.top += (r2.height() - arrow->h) / 2; + r2.bottom = r2.top + arrow->h; + drawSurface(r2, arrow, true, false, 256); + + addDirtyRect(r); +} + +void ThemeModern::drawCaret(const Common::Rect &r, bool erase, State state) { + if (!_initOk) + return; + + restoreBackground(r); + if (!erase) { + _screen.vLine(r.left, r.top, r.bottom-1, _colors[kCaretColor]); + } else if (r.top >= 0) { + // FIXME: hack to restore the caret background correctly + const OverlayColor search = _colors[kTextInvertedBackground]; + const OverlayColor *src = (const OverlayColor*)_screen.getBasePtr(r.left-1, r.top-1); + int height = r.height() + 2; + if (r.top + height > _screen.h) { + height = _screen.h - r.top; + } + bool drawInvBackground = false; + while (height--) { + if (src[0] == search || src[1] == search || src[2] == search) { + drawInvBackground = true; + } + src += _screen.w; + } + if (drawInvBackground) { + _screen.vLine(r.left, r.top, r.bottom-1, search); + } + } + addDirtyRect(r); +} + +void ThemeModern::drawLineSeparator(const Common::Rect &r, State state) { + if (!_initOk) + return; + _screen.hLine(r.left - 1, r.top + r.height() / 2, r.right, _system->RGBToColor(0, 0, 0)); + addDirtyRect(r); +} + +int ThemeModern::getTabSpacing() const { + return 0; +} +int ThemeModern::getTabPadding() const { + return 3; +} + +#pragma mark - intern drawing + +void ThemeModern::restoreBackground(Common::Rect r, bool special) { + r.clip(_screen.w, _screen.h); + if (special) { + r.clip(_shadowDrawArea); + } else { + r.clip(_drawArea); + } + if (_dialog) { + if (!_dialog->screen.pixels) { + return; + } + const OverlayColor *src = (const OverlayColor*)_dialog->screen.getBasePtr(r.left, r.top); + OverlayColor *dst = (OverlayColor*)_screen.getBasePtr(r.left, r.top); + + int h = r.height(); + int w = r.width(); + while (h--) { + memcpy(dst, src, w*sizeof(OverlayColor)); + src += _dialog->screen.w; + dst += _screen.w; + } + } +} + +bool ThemeModern::addDirtyRect(Common::Rect r, bool backup, bool special) { + // TODO: implement proper dirty rect handling + // FIXME: problem with the 'pitch' + r.clip(_screen.w, _screen.h); + if (special) { + r.clip(_shadowDrawArea); + } else { + r.clip(_drawArea); + } + _system->copyRectToOverlay((OverlayColor*)_screen.getBasePtr(r.left, r.top), _screen.w, r.left, r.top, r.width(), r.height()); + if (_dialog && backup) { + if (_dialog->screen.pixels) { + OverlayColor *dst = (OverlayColor*)_dialog->screen.getBasePtr(r.left, r.top); + const OverlayColor *src = (const OverlayColor*)_screen.getBasePtr(r.left, r.top); + int h = r.height(); + while (h--) { + memcpy(dst, src, r.width()*sizeof(OverlayColor)); + dst += _dialog->screen.w; + src += _screen.w; + } + } + } + return true; +} + +void ThemeModern::colorFade(const Common::Rect &r, OverlayColor start, OverlayColor end, uint factor) { + OverlayColor *ptr = (OverlayColor*)_screen.getBasePtr(r.left, r.top); + int h = r.height(); + while (h--) { + OverlayColor col = calcGradient(start, end, r.height()-h, r.height(), factor); + for (int i = 0; i < r.width(); ++i) { + ptr[i] = col; + } + ptr += _screen.w; + } +} + +void ThemeModern::drawRect(const Common::Rect &r, const Surface *corner, const Surface *top, const Surface *left, const Surface *fill, int alpha, bool skipLastRow) { + drawRectMasked(r, corner, top, left, fill, alpha, _system->RGBToColor(255, 255, 255), _system->RGBToColor(255, 255, 255), 1, skipLastRow); +} + +void ThemeModern::drawRectMasked(const Common::Rect &r, const Graphics::Surface *corner, const Graphics::Surface *top, + const Graphics::Surface *left, const Graphics::Surface *fill, int alpha, + OverlayColor start, OverlayColor end, uint factor, bool skipLastRow, bool skipTopRow) { + int drawWidth = MIN(corner->w, MIN(top->w, MIN(left->w, fill->w))); + int drawHeight = MIN(corner->h, MIN(top->h, MIN(left->h, fill->h))); + int partsH = r.height() / drawHeight; + int partsW = r.width() / drawWidth; + int yPos = r.top; + + int specialHeight = 0; + int specialWidth = 0; + + if (drawHeight*2 > r.height()) { + drawHeight = r.height() / 2; + partsH = 2; + } else { + specialHeight = r.height() % drawHeight; + if (specialHeight != 0) + ++partsH; + } + + if (drawWidth*2 > r.width()) { + drawWidth = r.width() / 2; + partsW = 2; + } else { + specialWidth = r.width() % drawWidth; + if (specialWidth != 0) + ++partsW; + } + + for (int y = 0; y < partsH; ++y) { + int xPos = r.left; + bool upDown = (y == partsH - 1); + + // calculate the correct drawing height + int usedHeight = drawHeight; + if (specialHeight && y == 1) { + usedHeight = specialHeight; + } + + OverlayColor startCol = calcGradient(start, end, yPos-r.top, r.height(), factor); + OverlayColor endCol = calcGradient(start, end, yPos-r.top+usedHeight, r.height(), factor); + + for (int i = 0; i < partsW; ++i) { + // calculate the correct drawing width + int usedWidth = drawWidth; + if (specialWidth && i == 1) { + usedWidth = specialWidth; + } + + // draw the right surface + if (!i || i == partsW - 1) { + if ((!y && !skipTopRow) || (y == partsH - 1 && !skipLastRow)) { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), corner, upDown, (i == partsW - 1), alpha, startCol, endCol); + } else { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), left, upDown, (i == partsW - 1), alpha, startCol, endCol); + } + } else if (!y || (y == partsH - 1 && !skipLastRow)) { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), top, upDown, false, alpha, startCol, endCol); + } else { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), fill, false, false, alpha, startCol, endCol); + } + xPos += usedWidth; + } + + yPos += usedHeight; + } +} + +Common::Rect ThemeModern::shadowRect(const Common::Rect &r, uint32 shadowStyle) { + switch (shadowStyle) { + case kShadowButton: + return Common::Rect(r.left - 1, r.top - 1, r.right + 1, r.bottom + 1); + + case kShadowEmboss: + return Common::Rect(r.left - 1, r.top - 1, r.right + 1, r.bottom + 1); + + case kShadowPopUp: + return Common::Rect(r.left - 1, r.top - 1, r.right + 3, r.bottom + 3); + + case kShadowFull: + return Common::Rect(r.left - 2, r.top - 2, r.right + 4, r.bottom + 4); + + default: + return Common::Rect(r.left - _shadowLeftWidth/2, r.top - _shadowTopHeight/2, r.right + _shadowRightWidth/2 + 1, r.bottom + _shadowBottomHeight/2 + 1); + } + + return Common::Rect(); +} + +void ThemeModern::drawShadow(const Common::Rect &r, const Graphics::Surface *corner, const Graphics::Surface *top, + const Graphics::Surface *left, const Graphics::Surface *fill, uint32 shadowStyle, bool skipLastRow, bool skipTopRow) { + switch (shadowStyle) { + case kShadowFull: { + Common::Rect r2(r.left-1, r.top-1, r.right + 4, r.bottom + 4); + Common::Rect r3(r.left, r.top+1, r.right + 3, r.bottom + 3); + Common::Rect r4(r.left, r.top+1, r.right + 2, r.bottom + 2); + Common::Rect r5(r.left, r.top, r.right + 1, r.bottom + 1); + + drawShadowRect(r2, r, corner, top, left, fill, kShadowTr0, skipLastRow, skipTopRow); + drawShadowRect(r3, r, corner, top, left, fill, kShadowTr1, skipLastRow, skipTopRow); + drawShadowRect(r4, r, corner, top, left, fill, kShadowTr2, skipLastRow, skipTopRow); + drawShadowRect(r5, r, corner, top, left, fill, kShadowTr3, skipLastRow, skipTopRow); + //drawShadowRect(r5, r, corner, top, left, fill, kShadowTr35, skipLastRow); + } break; + + case kShadowSmall: { + Common::Rect r3(r.left - _shadowLeftWidth/2, r.top - _shadowTopHeight/2, r.right + _shadowRightWidth/2, r.bottom + _shadowBottomHeight/2); + Common::Rect r4(r.left - _shadowLeftWidth/2 + 1, r.top - _shadowTopHeight/2 + 1, r.right + _shadowRightWidth/2-1, r.bottom + _shadowBottomHeight/2-1); + + drawShadowRect(r3, r, corner, top, left, fill, kShadowTr1, skipLastRow, skipTopRow); + drawShadowRect(r4, r, corner, top, left, fill, kShadowTr2, skipLastRow, skipTopRow); + } break; + + case kShadowButton: { + Common::Rect r2(r.left-1, r.top - 1, r.right, r.bottom); + Common::Rect r4(r.left, r.top, r.right + 1, r.bottom + 1); + + drawShadowRect(r2, r, corner, top, left, fill, -kShadowTr35-256, skipLastRow, skipTopRow); + drawShadowRect(r4, r, corner, top, left, fill, kShadowTr4, skipLastRow, skipTopRow); + } break; + + case kShadowEmboss: { + Common::Rect r2(r.left - 1, r.top - 1, r.right, r.bottom); + Common::Rect r4(r.left + 1, r.top + 1, r.right + 1, r.bottom + 1); + + drawShadowRect(r2, r, corner, top, left, fill, kShadowTr5, skipLastRow, skipTopRow); + drawShadowRect(r4, r, corner, top, left, fill, kShadowTr1, skipLastRow, skipTopRow); + } break; + + case kShadowPopUp: { + Common::Rect r2(r.left, r.top, r.right + 3, r.bottom + 3); + Common::Rect r25(r.left-1, r.top-1, r.right + 2, r.bottom + 2); + Common::Rect r3(r.left - 1, r.top-1, r.right, r.bottom); + Common::Rect r4(r.left, r.top, r.right + 1, r.bottom + 1); + + drawShadowRect(r2, r, corner, top, left, fill, kShadowTr1, skipLastRow, skipTopRow); + drawShadowRect(r25, r, corner, top, left, fill, kShadowTr2, skipLastRow, skipTopRow); + drawShadowRect(r4, r, corner, top, left, fill, kShadowTr3, skipLastRow, skipTopRow); + drawShadowRect(r3, r, corner, top, left, fill, -kShadowTr35-256, skipLastRow, skipTopRow); + } break; + + default: + break; + } +} + +void ThemeModern::drawShadowRect(const Common::Rect &r, const Common::Rect &area, const Graphics::Surface *corner, + const Graphics::Surface *top, const Graphics::Surface *left, const Graphics::Surface *fill, + int alpha, bool skipLastRow, bool skipTopRow) { + int drawWidth = MIN(corner->w, MIN(top->w, MIN(left->w, fill->w))); + int drawHeight = MIN(corner->h, MIN(top->h, MIN(left->h, fill->h))); + int partsH = r.height() / drawHeight; + int partsW = r.width() / drawWidth; + int yPos = r.top; + + int yDrawTilesTop = 1 + (ABS(area.top - r.top) % drawHeight); + int xDrawTilesLeft = 1 + (ABS(area.left - r.left) % drawWidth); + int yDrawTilesBottom = 1 + (ABS(area.bottom - r.bottom) % drawHeight); + int xDrawTilesRight = 1 + (ABS(area.right - r.right) % drawWidth); + + int specialHeight = 0; + int specialWidth = 0; + + if (drawHeight*2 > r.height()) { + drawHeight = r.height() / 2; + partsH = 2; + } else { + specialHeight = r.height() % drawHeight; + if (specialHeight != 0) + ++partsH; + } + + if (drawWidth*2 > r.width()) { + drawWidth = r.width() / 2; + partsW = 2; + } else { + specialWidth = r.width() % drawWidth; + if (specialWidth != 0) + ++partsW; + } + + OverlayColor startCol = g_system->RGBToColor(0, 0, 0); + OverlayColor endCol = g_system->RGBToColor(0, 0, 0); + + for (int y = 0; y < partsH; ++y) { + // calculate the correct drawing height + int usedHeight = drawHeight; + if (specialHeight && y == 1) { + usedHeight = specialHeight; + } + + int xPos = r.left; + bool upDown = (y == partsH - 1); + + for (int i = 0; i < partsW; ++i) { + // calculate the correct drawing width + int usedWidth = drawWidth; + if (specialWidth && i == 1) { + usedWidth = specialWidth; + } + + if (i >= xDrawTilesLeft && i <= partsW - xDrawTilesRight && y >= yDrawTilesTop && y <= partsH - yDrawTilesBottom) { + xPos += usedWidth; + continue; + } + + // draw the right surface + if (!i || i == partsW - 1) { + if ((!y && !skipTopRow) || (y == partsH - 1 && !skipLastRow)) { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), corner, upDown, (i == partsW - 1), alpha, startCol, endCol); + } else { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), left, upDown, (i == partsW - 1), alpha, startCol, endCol); + } + } else if (!y || (y == partsH - 1 && !skipLastRow)) { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), top, upDown, false, alpha, startCol, endCol); + } else { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), fill, upDown, false, alpha, startCol, endCol); + } + xPos += usedWidth; + } + yPos += usedHeight; + } +} + +void ThemeModern::drawSurface(const Common::Rect &r, const Surface *surf, bool upDown, bool leftRight, int alpha) { + drawSurfaceMasked(r, surf, upDown, leftRight, alpha, _system->RGBToColor(255, 255, 255), _system->RGBToColor(255, 255, 255)); +} + +void ThemeModern::drawSurfaceMasked(const Common::Rect &r, const Graphics::Surface *surf, bool upDown, bool leftRight, + int alpha, OverlayColor start, OverlayColor end, uint factor) { + OverlayColor *dst = (OverlayColor*)_screen.getBasePtr(r.left, r.top); + const OverlayColor *src = 0; + + const OverlayColor transparency = _colors[kColorTransparency]; + int drawWidth = (r.width() < surf->w) ? r.width() : surf->w; + if (r.left + drawWidth > _screen.w) + drawWidth = _screen.w - r.left; + + int srcAdd = 0; + if (upDown) { + src = (const OverlayColor*)surf->pixels + (surf->h - 1) * surf->w; + srcAdd = -surf->w; + } else { + src = (const OverlayColor*)surf->pixels; + srcAdd = surf->w; + } + + int h = r.height(); + if (r.top + h > _screen.h) + h = _screen.h - r.top; + + while (dst < _screen.pixels) { + dst += _screen.w; + src += srcAdd; + --h; + } + + if (h <= 0) + return; + +#define NO_EFFECT(x, y) (src[x] & rowColor) +#define ALPHA_EFFECT(x, y) (getColorAlpha(src[x] & rowColor, dst[y], alpha)) +#define DARKEN_EFFECT(x, y) (calcDimColor(src[x] & rowColor)) + +#define LEFT_RIGHT_OFFSET(x) (drawWidth-x-1) +#define NORMAL_OFFSET(x) (x) + +#define blitSurface(a, b) \ + for (int i = 0; i < h; ++i) { \ + OverlayColor rowColor = calcGradient(start, end, i, r.height(), factor); \ + for (int x = 0; x < drawWidth; ++x) { \ + if (src[a(x)] != transparency) \ + dst[x] = b(a(x), x); \ + } \ + dst += _screen.w; \ + src += srcAdd; \ + } + if (alpha >= 256) { + if (leftRight) { + blitSurface(LEFT_RIGHT_OFFSET, NO_EFFECT); + } else { + blitSurface(NORMAL_OFFSET, NO_EFFECT); + } + } else if (alpha < 0 && alpha >= -256) { + int backUp = _dimPercentValue; + _dimPercentValue = 256 * (100 - (-alpha)) / 100; + + if (leftRight) { + blitSurface(LEFT_RIGHT_OFFSET, DARKEN_EFFECT); + } else { + blitSurface(NORMAL_OFFSET, DARKEN_EFFECT); + } + + _dimPercentValue = backUp; + } else { + if (leftRight) { + blitSurface(LEFT_RIGHT_OFFSET, ALPHA_EFFECT); + } else { + blitSurface(NORMAL_OFFSET, ALPHA_EFFECT); + } + } +#undef blitSurface + +#undef NORMAL_OFFSET +#undef LEFT_RIGHT_OFFSET + +#undef DARKEN_EFFECT +#undef ALPHA_EFFECT +#undef NO_EFFECT +} + +OverlayColor ThemeModern::getColor(State state) { + switch (state) { + case kStateDisabled: + return _colors[kColorStateDisabled]; + + case kStateHighlight: + return _colors[kColorStateHighlight]; + + default: + break; + }; + return _colors[kColorStateEnabled]; +} + +void ThemeModern::resetupGuiRenderer() { + if (_lastUsedBitMask == gBitFormat || !_initOk) { + // ok same format no need to reload + return; + } + + _lastUsedBitMask = gBitFormat; + + int i; + for (i = 0; i < kImageHandlesMax; ++i) { + ImageMan.unregisterSurface(_imageHandles[i]); + } + + for (i = 0; i < kImageHandlesMax; ++i) { + ImageMan.registerSurface(_imageHandles[i], 0); + _images[i] = ImageMan.getSurface(_imageHandles[i]); + } + + setupColors(); +} + +void ThemeModern::setupColors() { + // load the colors from the config file + getColorFromConfig("main_dialog_start", _colors[kMainDialogStart]); + getColorFromConfig("main_dialog_end", _colors[kMainDialogEnd]); + + getColorFromConfig("dialog_start", _colors[kDialogStart]); + getColorFromConfig("dialog_end", _colors[kDialogEnd]); + + getColorFromConfig("color_state_disabled", _colors[kColorStateDisabled]); + getColorFromConfig("color_state_highlight", _colors[kColorStateHighlight]); + getColorFromConfig("color_state_enabled", _colors[kColorStateEnabled]); + getColorFromConfig("color_transparency", _colors[kColorTransparency]); + + getColorFromConfig("text_inverted_background", _colors[kTextInvertedBackground]); + getColorFromConfig("text_inverted_color", _colors[kTextInvertedColor]); + + getColorFromConfig("widget_bkgd_start", _colors[kWidgetBackgroundStart]); + getColorFromConfig("widget_bkgd_end", _colors[kWidgetBackgroundEnd]); + getColorFromConfig("widget_bkgd_small_start", _colors[kWidgetBackgroundSmallStart]); + getColorFromConfig("widget_bkgd_small_end", _colors[kWidgetBackgroundSmallEnd]); + + getColorFromConfig("button_bkgd_start", _colors[kButtonBackgroundStart]); + getColorFromConfig("button_bkgd_end", _colors[kButtonBackgroundEnd]); + getColorFromConfig("button_bkgd_highlight_start", _colors[kButtonBackgroundHighlightStart]); + getColorFromConfig("button_bkgd_highlight_end", _colors[kButtonBackgroundHighlightEnd]); + getColorFromConfig("button_text_enabled", _colors[kButtonTextEnabled]); + getColorFromConfig("button_text_disabled", _colors[kButtonTextDisabled]); + getColorFromConfig("button_text_highlight", _colors[kButtonTextHighlight]); + + getColorFromConfig("slider_background_start", _colors[kSliderBackgroundStart]); + getColorFromConfig("slider_background_end", _colors[kSliderBackgroundEnd]); + getColorFromConfig("slider_start", _colors[kSliderStart]); + getColorFromConfig("slider_end", _colors[kSliderEnd]); + getColorFromConfig("slider_highlight_start", _colors[kSliderHighStart]); + getColorFromConfig("slider_highlight_end", _colors[kSliderHighEnd]); + + getColorFromConfig("tab_background_start", _colors[kTabBackgroundStart]); + getColorFromConfig("tab_background_end", _colors[kTabBackgroundEnd]); + + getColorFromConfig("tab_active_start", _colors[kTabActiveStart]); + getColorFromConfig("tab_active_end", _colors[kTabActiveEnd]); + getColorFromConfig("tab_inactive_start", _colors[kTabInactiveStart]); + getColorFromConfig("tab_inactive_end", _colors[kTabInactiveEnd]); + + getColorFromConfig("scrollbar_background_start", _colors[kScrollbarBackgroundStart]); + getColorFromConfig("scrollbar_background_end", _colors[kScrollbarBackgroundEnd]); + getColorFromConfig("scrollbar_button_start", _colors[kScrollbarButtonStart]); + getColorFromConfig("scrollbar_button_end", _colors[kScrollbarButtonEnd]); + getColorFromConfig("scrollbar_slider_start", _colors[kScrollbarSliderStart]); + getColorFromConfig("scrollbar_slider_end", _colors[kScrollbarSliderEnd]); + getColorFromConfig("scrollbar_button_highlight_start", _colors[kScrollbarButtonHighlightStart]); + getColorFromConfig("scrollbar_button_highlight_end", _colors[kScrollbarButtonHighlightEnd]); + getColorFromConfig("scrollbar_slider_highlight_start", _colors[kScrollbarSliderHighlightStart]); + getColorFromConfig("scrollbar_slider_highlight_end", _colors[kScrollbarSliderHighlightEnd]); + + getColorFromConfig("caret_color", _colors[kCaretColor]); + + getColorFromConfig("popupwidget_start", _colors[kPopUpWidgetStart]); + getColorFromConfig("popupwidget_end", _colors[kPopUpWidgetEnd]); + getColorFromConfig("popupwidget_highlight_start", _colors[kPopUpWidgetHighlightStart]); + getColorFromConfig("popupwidget_highlight_end", _colors[kPopUpWidgetHighlightEnd]); + + getColorFromConfig("edittext_background_start", _colors[kEditTextBackgroundStart]); + getColorFromConfig("edittext_background_end", _colors[kEditTextBackgroundEnd]); +} + +#define FONT_NAME_NORMAL "newgui_normal" +#define FONT_NAME_BOLD "newgui_bold" +#define FONT_NAME_ITALIC "newgui_italic" +#define FONT_NAME_FIXED_NORMAL "newgui_fixed_normal" +#define FONT_NAME_FIXED_BOLD "newgui_fixed_bold" +#define FONT_NAME_FIXED_ITALIC "newgui_fixed_italic" + +void ThemeModern::setupFonts() { + if (_screen.w >= 400 && _screen.h >= 270) { + setupFont("fontfile_bold", FONT_NAME_BOLD, kFontStyleBold); + setupFont("fontfile_normal", FONT_NAME_NORMAL, kFontStyleNormal); + setupFont("fontfile_italic", FONT_NAME_ITALIC, kFontStyleItalic); + + setupFont("fontfile_fixed_bold", FONT_NAME_FIXED_BOLD, kFontStyleFixedBold); + setupFont("fontfile_fixed_normal", FONT_NAME_FIXED_NORMAL, kFontStyleFixedNormal); + setupFont("fontfile_fixed_italic", FONT_NAME_FIXED_ITALIC, kFontStyleFixedItalic); + } else { + _fonts[kFontStyleBold] = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + _fonts[kFontStyleNormal] = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + _fonts[kFontStyleItalic] = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + } +} + +void ThemeModern::deleteFonts() { + const Graphics::Font *normal = FontMan.getFontByName(FONT_NAME_NORMAL); + const Graphics::Font *bold = FontMan.getFontByName(FONT_NAME_BOLD); + const Graphics::Font *italic = FontMan.getFontByName(FONT_NAME_ITALIC); + + delete normal; + delete bold; + delete italic; + + FontMan.removeFontName(FONT_NAME_NORMAL); + FontMan.removeFontName(FONT_NAME_BOLD); + FontMan.removeFontName(FONT_NAME_ITALIC); +} + +void ThemeModern::setupFont(const String &key, const String &name, FontStyle style) { + if (_evaluator->getVar(key) == EVAL_STRING_VAR) { + _fonts[style] = FontMan.getFontByName(name); + + if (!_fonts[style]) { + Common::String temp(_evaluator->getStringVar(key)); + + _fonts[style] = loadFont(temp.c_str()); + if (!_fonts[style]) + error("Couldn't load %s font '%s'", key.c_str(), temp.c_str()); + + FontMan.assignFontToName(name, _fonts[style]); + } + } else { + _fonts[style] = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + } +} + +#pragma mark - + +void ThemeModern::processExtraValues() { + static Common::String imageHandlesTable[kImageHandlesMax]; + + // load the pixmap filenames from the config file + imageHandlesTable[kDialogBkgdCorner] = _evaluator->getStringVar("pix_dialog_corner"); + imageHandlesTable[kDialogBkgdTop] = _evaluator->getStringVar("pix_dialog_top"); + imageHandlesTable[kDialogBkgdLeft] = _evaluator->getStringVar("pix_dialog_left"); + imageHandlesTable[kDialogBkgd] = _evaluator->getStringVar("pix_dialog_bkgd"); + + imageHandlesTable[kWidgetBkgdCorner] = _evaluator->getStringVar("pix_widget_corner"); + imageHandlesTable[kWidgetBkgdTop] = _evaluator->getStringVar("pix_widget_top"); + imageHandlesTable[kWidgetBkgdLeft] = _evaluator->getStringVar("pix_widget_left"); + imageHandlesTable[kWidgetBkgd] = _evaluator->getStringVar("pix_widget_bkgd"); + + imageHandlesTable[kWidgetSmallBkgdCorner] = _evaluator->getStringVar("pix_widget_small_corner"); + imageHandlesTable[kWidgetSmallBkgdTop] = _evaluator->getStringVar("pix_widget_small_top"); + imageHandlesTable[kWidgetSmallBkgdLeft] = _evaluator->getStringVar("pix_widget_small_left"); + imageHandlesTable[kWidgetSmallBkgd] = _evaluator->getStringVar("pix_widget_small_bkgd"); + + imageHandlesTable[kCheckboxEmpty] = _evaluator->getStringVar("pix_checkbox_empty"); + imageHandlesTable[kCheckboxChecked] = _evaluator->getStringVar("pix_checkbox_checked"); + + imageHandlesTable[kWidgetArrow] = _evaluator->getStringVar("pix_widget_arrow"); + + imageHandlesTable[kTabBkgdCorner] = _evaluator->getStringVar("pix_tab_corner"); + imageHandlesTable[kTabBkgdTop] = _evaluator->getStringVar("pix_tab_top"); + imageHandlesTable[kTabBkgdLeft] = _evaluator->getStringVar("pix_tab_left"); + imageHandlesTable[kTabBkgd] = _evaluator->getStringVar("pix_tab_bkgd"); + + imageHandlesTable[kSliderBkgdCorner] = _evaluator->getStringVar("pix_slider_bkgd_corner"); + imageHandlesTable[kSliderBkgdTop] = _evaluator->getStringVar("pix_slider_bkgd_top"); + imageHandlesTable[kSliderBkgdLeft] = _evaluator->getStringVar("pix_slider_bkgd_left"); + imageHandlesTable[kSliderBkgd] = _evaluator->getStringVar("pix_slider_bkgd_bkgd"); + + imageHandlesTable[kSliderCorner] = _evaluator->getStringVar("pix_slider_corner"); + imageHandlesTable[kSliderTop] = _evaluator->getStringVar("pix_slider_top"); + imageHandlesTable[kSliderLeft] = _evaluator->getStringVar("pix_slider_left"); + imageHandlesTable[kSlider] = _evaluator->getStringVar("pix_slider_bkgd"); + + imageHandlesTable[kScrollbarBkgdCorner] = _evaluator->getStringVar("pix_scrollbar_bkgd_corner"); + imageHandlesTable[kScrollbarBkgdTop] = _evaluator->getStringVar("pix_scrollbar_bkgd_top"); + imageHandlesTable[kScrollbarBkgdLeft] = _evaluator->getStringVar("pix_scrollbar_bkgd_left"); + imageHandlesTable[kScrollbarBkgd] = _evaluator->getStringVar("pix_scrollbar_bkgd_bkgd"); + + imageHandlesTable[kScrollbarCorner] = _evaluator->getStringVar("pix_scrollbar_corner"); + imageHandlesTable[kScrollbarTop] = _evaluator->getStringVar("pix_scrollbar_top"); + imageHandlesTable[kScrollbarLeft] = _evaluator->getStringVar("pix_scrollbar_left"); + imageHandlesTable[kScrollbar] = _evaluator->getStringVar("pix_scrollbar_bkgd"); + + imageHandlesTable[kButtonBkgdCorner] = _evaluator->getStringVar("pix_button_corner"); + imageHandlesTable[kButtonBkgdTop] = _evaluator->getStringVar("pix_button_top"); + imageHandlesTable[kButtonBkgdLeft] = _evaluator->getStringVar("pix_button_left"); + imageHandlesTable[kButtonBkgd] = _evaluator->getStringVar("pix_button_bkgd"); + + imageHandlesTable[kThemeLogo] = _evaluator->getStringVar("pix_theme_logo"); + + imageHandlesTable[kPopUpWidgetBkgdCorner] = _evaluator->getStringVar("pix_popupwidget_corner"); + imageHandlesTable[kPopUpWidgetBkgdTop] = _evaluator->getStringVar("pix_popupwidget_top"); + imageHandlesTable[kPopUpWidgetBkgdLeft] = _evaluator->getStringVar("pix_popupwidget_left"); + imageHandlesTable[kPopUpWidgetBkgd] = _evaluator->getStringVar("pix_popupwidget_bkgd"); + + imageHandlesTable[kEditTextBkgdCorner] = _evaluator->getStringVar("pix_edittext_bkgd_corner"); + imageHandlesTable[kEditTextBkgdTop] = _evaluator->getStringVar("pix_edittext_bkgd_top"); + imageHandlesTable[kEditTextBkgdLeft] = _evaluator->getStringVar("pix_edittext_bkgd_left"); + imageHandlesTable[kEditTextBkgd] = _evaluator->getStringVar("pix_edittext_bkgd"); + + imageHandlesTable[kGUICursor] = _evaluator->getStringVar("pix_cursor_image"); + + _imageHandles = imageHandlesTable; + + for (int i = 0; i < kImageHandlesMax; ++i) { + ImageMan.registerSurface(_imageHandles[i], 0); + _images[i] = ImageMan.getSurface(_imageHandles[i]); + } + + // load the gradient factors from the config file + _gradientFactors[kMainDialogFactor] = _evaluator->getVar("gradient_dialog_main", 1); + _gradientFactors[kDialogFactor] = _evaluator->getVar("gradient_dialog", 1); + _gradientFactors[kDialogSpecialFactor] = _evaluator->getVar("gradient_dialog_special", 1); + + _gradientFactors[kWidgetSmallFactor] = _evaluator->getVar("gradient_widget_small", 1); + _gradientFactors[kWidgetFactor] = _evaluator->getVar("gradient_widget", 1); + + _gradientFactors[kButtonFactor] = _evaluator->getVar("gradient_button", 1); + + _gradientFactors[kSliderFactor] = _evaluator->getVar("gradient_slider", 1); + _gradientFactors[kSliderBackground] = _evaluator->getVar("gradient_silder_bkgd", 1); + + _gradientFactors[kTabFactor] = _evaluator->getVar("gradient_tab", 1); + + _gradientFactors[kScrollbarFactor] = _evaluator->getVar("gradient_scrollbar", 1); + _gradientFactors[kScrollbarBkgdFactor] = _evaluator->getVar("gradient_scrollbar_background", 1); + + _gradientFactors[kPopUpWidgetFactor] = _evaluator->getVar("gradient_popupwidget", 1); + + _gradientFactors[kEditTextFactor] = _evaluator->getVar("gradient_edittext", 1); + + // load values with default values from the config file + _shadowLeftWidth = _evaluator->getVar("shadow_left_width", 2); + _shadowRightWidth = _evaluator->getVar("shadow_right_width", 4); + _shadowTopHeight = _evaluator->getVar("shadow_top_height", 2); + _shadowBottomHeight = _evaluator->getVar("shadow_bottom_height", 4); + + _cursorHotspotX = _evaluator->getVar("cursor_hotspot_x", 0); + _cursorHotspotY = _evaluator->getVar("cursor_hotspot_y", 0); + + _cursorTargetScale = _evaluator->getVar("cursor_targetScale", 1); + + // inactive dialog shading stuff + + ShadingStyle shading = (ShadingStyle)_evaluator->getVar("inactive_dialog_shading", kShadingNone); + + switch (shading) { + case kShadingNone: + _dialogShadingCallback = 0; + break; + + case kShadingLuminance: + _dialogShadingCallback = &ThemeModern::calcLuminance; + break; + + case kShadingDim: + _dimPercentValue = _evaluator->getVar("shading_dim_percent", -1); + + if (_dimPercentValue < 0) { + _dimPercentValue = 0; + } else if (_dimPercentValue > 100) { + _dimPercentValue = 100; + } + + if (_dimPercentValue != 0) { + _dimPercentValue = 256 * (100 - _dimPercentValue) / 100; + _dialogShadingCallback = &ThemeModern::calcDimColor; + } + break; + + default: + warning("no valid 'inactive_dialog_shading' specified"); + } + + setupFonts(); + + // load the colors from the config file + setupColors(); + + // creates cursor image + if (_system->hasFeature(OSystem::kFeatureCursorHasPalette)) { + createCursor(); + } +} + +#pragma mark - + +OverlayColor ThemeModern::calcLuminance(OverlayColor col) { + uint8 r, g, b; + _system->colorToRGB(col, r, g, b); + + uint lum = (r >> 2) + (g >> 1) + (b >> 3); + + return _system->RGBToColor(lum, lum, lum); +} + +OverlayColor ThemeModern::calcDimColor(OverlayColor col) { + uint8 r, g, b; + _system->colorToRGB(col, r, g, b); + + r = r * _dimPercentValue >> 8; + g = g * _dimPercentValue >> 8; + b = b * _dimPercentValue >> 8; + + return _system->RGBToColor(r, g, b); +} + +#pragma mark - + +void ThemeModern::setUpCursor() { + PaletteMan.pushCursorPalette(_cursorPal, 0, MAX_CURS_COLORS); + CursorMan.pushCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, _cursorTargetScale); + CursorMan.showMouse(true); +} + +void ThemeModern::createCursor() { + const Surface *cursor = _images[kGUICursor]; + + _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; + + _system->colorToRGB(_colors[kColorTransparency], r, g, b); + uint16 transparency = RGBToColor<ColorMasks<565> >(r, g, b); + + 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) + error("Cursor contains too much colors (%d, but only %d are allowed)", colorsFound, MAX_CURS_COLORS); + } + + if (col != transparency) { + uint index = table[col]; + _cursor[y * _cursorWidth + x] = index; + } + } + src += _cursorWidth; + } + + _useCursor = true; + delete [] table; +} + +#pragma mark - + +template<class T> +inline OverlayColor getColorAlphaImpl(OverlayColor col1, OverlayColor col2, int alpha) { + OverlayColor output = 0; + output |= ((alpha * ((col1 & T::kRedMask) - (col2 & T::kRedMask)) >> 8) + (col2 & T::kRedMask)) & T::kRedMask; + output |= ((alpha * ((col1 & T::kGreenMask) - (col2 & T::kGreenMask)) >> 8) + (col2 & T::kGreenMask)) & T::kGreenMask; + output |= ((alpha * ((col1 & T::kBlueMask) - (col2 & T::kBlueMask)) >> 8) + (col2 & T::kBlueMask)) & T::kBlueMask; + output |= ~(T::kRedMask | T::kGreenMask | T::kBlueMask); + return output; +} + +// broken implementation! +template<class T> +inline OverlayColor getColorAlphaImp2(OverlayColor col1, OverlayColor col2, int alpha) { + OverlayColor output = 0; + output |= ((alpha * ((~col1 & T::kRedMask) - (col2 & T::kRedMask)) >> 8) + (col2 & T::kRedMask)) & T::kRedMask; + output |= ((alpha * ((~col1 & T::kGreenMask) - (col2 & T::kGreenMask)) >> 8) + (col2 & T::kGreenMask)) & T::kGreenMask; + output |= ((alpha * ((~col1 & T::kBlueMask) - (col2 & T::kBlueMask)) >> 8) + (col2 & T::kBlueMask)) & T::kBlueMask; + output |= ~(T::kRedMask | T::kGreenMask | T::kBlueMask); + return output; +} + +OverlayColor getColorAlpha(OverlayColor col1, OverlayColor col2, int alpha) { + if (alpha >= 0) { + if (gBitFormat == 565) { + return getColorAlphaImpl<ColorMasks<565> >(col1, col2, alpha); + } else { + return getColorAlphaImpl<ColorMasks<555> >(col1, col2, alpha); + } + } else { + if (gBitFormat == 565) { + return getColorAlphaImp2<ColorMasks<565> >(col1, col2, -alpha - 256); + } else { + return getColorAlphaImp2<ColorMasks<555> >(col1, col2, -alpha - 256); + } + } +} + +template<class T> +inline OverlayColor calcGradient(OverlayColor start, OverlayColor end, int pos) { + OverlayColor output = 0; + output |= (start + ((((end & T::kRedMask) - (start & T::kRedMask))) * pos >> 12)) & T::kRedMask; + output |= (start + ((((end & T::kGreenMask) - (start & T::kGreenMask))) * pos >> 12)) & T::kGreenMask; + output |= (start + ((((end & T::kBlueMask) - (start & T::kBlueMask))) * pos >> 12)) & T::kBlueMask; + output |= ~(T::kRedMask | T::kGreenMask | T::kBlueMask); + return output; +} + +OverlayColor calcGradient(OverlayColor start, OverlayColor end, int pos, int max, uint factor = 1) { + max /= factor; + pos *= factor; + if (pos >= max) + return end; + + pos = (0x1000 * pos) / max; + + if (gBitFormat == 565) { + return calcGradient<ColorMasks<565> >(start, end, pos); + } else { + return calcGradient<ColorMasks<555> >(start, end, pos); + } +} +} // end of namespace GUI + +#endif |