/* 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 3 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, see . * */ #include "bagel/spacebar/boflib/gui/button.h" #include "bagel/spacebar/boflib/app.h" #include "bagel/spacebar/boflib/gfx/text.h" namespace Bagel { namespace SpaceBar { #define SELECTED_TEXT_OFFSET_DX 1 #define SELECTED_TEXT_OFFSET_DY 1 #define BUTTON_TEXT_SIZE 10 #define CHECK_BOX_SIZE 14 #define CHECK_BOX_OFFSET_DX 4 #define CHECK_BOX_OFFSET_DY 4 #define CHECK_BOX_TEXT_SIZE 10 #define RADIO_BOX_SIZE 13 #define RADIO_BOX_OFFSET_DX 3 #define RADIO_BOX_OFFSET_DY 3 #define RADIO_BOX_TEXT_SIZE 10 static ST_COLORSCHEME g_stDefaultColors = { RGB(171, 151, 127), RGB(207, 199, 183), RGB(131, 111, 91), RGB(0, 0, 0), RGB(0, 0, 0), RGB(0, 0, 0) }; CBofButton::CBofButton() { // Inits _nState = BUTTON_UP; // Load a default color scheme until another is loaded loadColorScheme(&g_stDefaultColors); } CBofButton::CBofButton(ST_COLORSCHEME *pColorScheme) { assert(pColorScheme != nullptr); // Inits _nState = BUTTON_UP; loadColorScheme(pColorScheme); } CBofButton::~CBofButton() { assert(isValidObject(this)); } ErrorCode CBofButton::paint(CBofRect *) { assert(isValidObject(this)); // Only continue if this button is visible if (isVisible() && (_parent != nullptr) && _parent->isVisible()) { CBofPalette *pPalette = CBofApp::getApp()->getPalette(); int nWidth = _cRect.width(); int nHeight = _cRect.height(); // Create our off-screen buffer CBofBitmap cBmp(nWidth, nHeight, pPalette); cBmp.fillRect(&_cRect, pPalette->getNearestIndex(_cFaceColor)); int left = _cRect.left; int right = _cRect.right; int top = _cRect.top; int bottom = _cRect.bottom; byte iShadow = pPalette->getNearestIndex(_cShadowColor); byte iHighlight = pPalette->getNearestIndex(_cHighlightColor); if (_nState == BUTTON_DOWN) { byte iTemp = iShadow; iShadow = iHighlight; iHighlight = iTemp; } byte c1 = iShadow; byte c2 = iHighlight; int i; for (i = 1; i <= 3; i++) { cBmp.line(left + i, bottom - i, right - i, bottom - i, c1); cBmp.line(right - i, bottom - i, right - i, top + i - 1, c1); } for (i = 1; i <= 3; i++) { cBmp.line(left + i, bottom - i, left + i, top + i - 1, c2); cBmp.line(left + i, top + i - 1, right - i, top + i - 1, c2); } cBmp.drawRect(&_cRect, pPalette->getNearestIndex(_cOutlineColor)); // Create a temporary text object CBofRect cTempRect(3, 3, _cRect.right - 3, _cRect.bottom - 3); if (_nState == BUTTON_DOWN) { cTempRect += CBofPoint(1, 1); } CBofText cText(&cTempRect); // Print text into button COLORREF cTextColor = _cTextColor; if (_nState == BUTTON_DISABLED) cTextColor = _cTextDisabledColor; cText.display(&cBmp, _szTitle, BUTTON_TEXT_SIZE, TEXT_NORMAL, cTextColor); if (_nState == BUTTON_FOCUS) { cBmp.drawRect(&cTempRect, pPalette->getNearestIndex(_cOutlineColor)); } // Now we can update the window cBmp.paint(this, 0, 0); } return _errCode; } void CBofButton::loadColorScheme(ST_COLORSCHEME *pColorScheme) { assert(isValidObject(this)); assert(pColorScheme != nullptr); // Save all of the color info we need to build a button _cFaceColor = pColorScheme->_cFace; _cHighlightColor = pColorScheme->_cHighlight; _cShadowColor = pColorScheme->_cShadow; _cTextColor = pColorScheme->_cText; _cTextDisabledColor = pColorScheme->_cTextDisabled; _cOutlineColor = pColorScheme->_cOutline; } void CBofButton::enable() { assert(isValidObject(this)); CBofWindow::enable(); setState(BUTTON_UP); } void CBofButton::disable() { assert(isValidObject(this)); setState(BUTTON_DISABLED); CBofWindow::disable(); } ErrorCode CBofButton::setState(int nNewState, bool bRepaintNow) { assert(isValidObject(this)); assert(nNewState >= BUTTON_UP && nNewState <= BUTTON_DISABLED); // Remember last button state int nOldState = _nState; _nState = nNewState; // Update the window if forced to or if button state has changed if (bRepaintNow || (nOldState != nNewState)) { paint(); } // I must have a valid parent assert(_parent != nullptr); // Tell parent the new state of this button if (_parent != nullptr) { _parent->onBofButton(this, _nState); } return _errCode; } void CBofButton::onPaint(CBofRect *pRect) { assert(isValidObject(this)); assert(pRect != nullptr); paint(pRect); } void CBofButton::onLButtonDown(uint32, CBofPoint *pPoint, void *) { assert(isValidObject(this)); assert(pPoint != nullptr); if (!_bCaptured && _nState != BUTTON_DISABLED) { setCapture(); setState(BUTTON_DOWN, true); } } void CBofButton::onLButtonUp(uint32, CBofPoint *pPoint, void *) { assert(isValidObject(this)); assert(pPoint != nullptr); if (_bCaptured) { releaseCapture(); setState(BUTTON_UP, true); if (_cRect.ptInRect(*pPoint) && (_parent != nullptr)) { _parent->onBofButton(this, BUTTON_CLICKED); } } } /*****************************************************************************/ /* CBofRadioButton */ /*****************************************************************************/ void CBofRadioButton::onLButtonDown(uint32, CBofPoint *pPoint, void *) { assert(isValidObject(this)); assert(pPoint != nullptr); if (_nState == BUTTON_UP) { setState(BUTTON_DOWN, true); } } void CBofRadioButton::onLButtonUp(uint32, CBofPoint *pPoint, void *) { assert(isValidObject(this)); assert(pPoint != nullptr); } ErrorCode CBofRadioButton::paint(CBofRect *) { assert(isValidObject(this)); // Only continue if this button is visible if (isVisible() && (_parent != nullptr) && _parent->isVisible()) { CBofPalette *pPalette = CBofApp::getApp()->getPalette(); int nWidth = _cRect.width(); int nHeight = _cRect.height(); // Create a temporary off-screen buffer CBofBitmap cBmp(nWidth, nHeight, pPalette); // Fill in the background color cBmp.fillRect(&_cRect, pPalette->getNearestIndex(_cFaceColor)); COLORREF cTextColor = _cTextColor; if (_nState == BUTTON_DISABLED) cTextColor = _cTextDisabledColor; byte iShadow = pPalette->getNearestIndex(cTextColor); byte iHighlight = pPalette->getNearestIndex(cTextColor); // Paint the radio button circle int nRadius = 7; int x = nRadius + RADIO_BOX_OFFSET_DX; int y = nHeight / 2; cBmp.circle(x, y, (uint16)nRadius, iShadow); nRadius--; cBmp.circle(x, y, (uint16)nRadius, iShadow); // Create a temporary text object CBofRect cTempRect(20, RADIO_BOX_OFFSET_DY, _cRect.right, _cRect.bottom - RADIO_BOX_OFFSET_DY); if (_nState == BUTTON_DOWN) { nRadius = 1; cBmp.circle(x, y, (uint16)nRadius, iHighlight); nRadius = 2; cBmp.circle(x, y, (uint16)nRadius, iHighlight); nRadius = 3; cBmp.circle(x, y, (uint16)nRadius, iHighlight); nRadius = 4; cBmp.circle(x, y, (uint16)nRadius, iHighlight); } CBofText cText(&cTempRect, JUSTIFY_LEFT); // Put a box around the whole button cBmp.drawRect(&_cRect, pPalette->getNearestIndex(_cOutlineColor)); // Show text disabled if button is disabled // Print text into button cText.display(&cBmp, _szTitle, RADIO_BOX_TEXT_SIZE, TEXT_NORMAL, cTextColor); // If button has focus, then put a box around the text if (_nState == BUTTON_FOCUS) { cBmp.drawRect(&cTempRect, pPalette->getNearestIndex(_cOutlineColor)); } // Now we can update the window cBmp.paint(this, 0, 0); } return _errCode; } /*****************************************************************************/ /* CBofCheckButton */ /*****************************************************************************/ void CBofCheckButton::onLButtonDown(uint32, CBofPoint *pPoint, void *) { assert(isValidObject(this)); assert(pPoint != nullptr); if (_nState == BUTTON_DISABLED) return; if (_nState == BUTTON_UP) { setState(BUTTON_DOWN, true); } else if (_nState == BUTTON_DOWN) { setState(BUTTON_UP, true); } } void CBofCheckButton::onLButtonUp(uint32, CBofPoint *pPoint, void *) { assert(isValidObject(this)); assert(pPoint != nullptr); // Do nothing, and don't call the base class version of this function } ErrorCode CBofCheckButton::paint(CBofRect *) { assert(isValidObject(this)); // Only continue if this button is visible if (isVisible() && (_parent != nullptr) && _parent->isVisible()) { CBofPalette *pPalette = CBofApp::getApp()->getPalette(); int nWidth = _cRect.width(); int nHeight = _cRect.height(); // Create a temporary off-screen buffer CBofBitmap cBmp(nWidth, nHeight, pPalette); // Fill in the background color cBmp.fillRect(&_cRect, pPalette->getNearestIndex(_cFaceColor)); // Show text disabled if button is disabled COLORREF cTextColor = _cTextColor; if (_nState == BUTTON_DISABLED) cTextColor = _cTextDisabledColor; byte iShadow = pPalette->getNearestIndex(cTextColor); // Draw the check box (centered vertically) int y = ((nHeight - CHECK_BOX_SIZE) / 2); CBofRect cTempRect(CHECK_BOX_OFFSET_DX, y, CHECK_BOX_SIZE + CHECK_BOX_OFFSET_DX - 1, y + CHECK_BOX_SIZE - 1); cBmp.drawRect(&cTempRect, iShadow); // if button is in DOWN/ON state, then put an X in the box // if (_nState == BUTTON_DOWN) { cBmp.line(cTempRect.left, cTempRect.top, cTempRect.right, cTempRect.bottom, iShadow); cBmp.line(cTempRect.left, cTempRect.bottom, cTempRect.right, cTempRect.top, iShadow); } // Create a temporary text object cTempRect.setRect(CHECK_BOX_SIZE + CHECK_BOX_OFFSET_DX, CHECK_BOX_OFFSET_DX, _cRect.right, _cRect.bottom - CHECK_BOX_OFFSET_DX); CBofText cText(&cTempRect, JUSTIFY_LEFT); // Put a box around the whole button cBmp.drawRect(&_cRect, pPalette->getNearestIndex(_cOutlineColor)); // Print text into button // cText.display(&cBmp, _szTitle, CHECK_BOX_TEXT_SIZE, TEXT_NORMAL, cTextColor); // If button has focus, then put a box around the text if (_nState == BUTTON_FOCUS) { cBmp.drawRect(&cTempRect, pPalette->getNearestIndex(_cOutlineColor)); } // Now we can update the window cBmp.paint(this, 0, 0); } return _errCode; } ErrorCode CBofCheckButton::SetCheck(bool bChecked) { assert(isValidObject(this)); setState(bChecked ? BUTTON_CHECKED : BUTTON_UNCHECKED, false); return _errCode; } /*****************************************************************************/ /* CBofBmpButton */ /*****************************************************************************/ CBofBmpButton::CBofBmpButton() { _pButtonUp = nullptr; _pButtonDown = nullptr; _pButtonFocus = nullptr; _pButtonDisabled = nullptr; _pBackground = nullptr; _nState = BUTTON_UP; _nMaskColor = NOT_TRANSPARENT; } CBofBmpButton::~CBofBmpButton() { delete _pButtonUp; _pButtonUp = nullptr; delete _pButtonDown; _pButtonDown = nullptr; delete _pButtonDisabled; _pButtonDisabled = nullptr; delete _pButtonFocus; _pButtonFocus = nullptr; delete _pBackground; _pBackground = nullptr; } ErrorCode CBofBmpButton::paint(CBofRect *) { assert(isValidObject(this)); // loadBitmaps must be called before the button can be painted assert(_pButtonUp != nullptr); assert(_pButtonDown != nullptr); assert(_pButtonFocus != nullptr); assert(_pButtonDisabled != nullptr); // Only continue if this button is visible if (isVisible() && (_parent != nullptr) && _parent->isVisible()) { CBofPalette *pPalette = _pButtonUp->getPalette(); int nWidth = _cRect.width(); int nHeight = _cRect.height(); // Do all painting off-screen CBofBitmap cOffScreen(nWidth, nHeight, pPalette); if (_pBackground == nullptr) { _pBackground = new CBofBitmap(nWidth, nHeight, pPalette); } else { _pBackground->paint(&cOffScreen, 0, 0); } // Assume UP state CBofBitmap *pBitmap = _pButtonUp; // Display the correct bitmap based on state if (_nState == BUTTON_DOWN) { pBitmap = _pButtonDown; } else if (_nState == BUTTON_FOCUS) { pBitmap = _pButtonFocus; } else if (_nState == BUTTON_DISABLED) { pBitmap = _pButtonDisabled; } // Paint button offscreen pBitmap->paint(&cOffScreen, 0, 0, nullptr, _nMaskColor); // Now we can update the window cOffScreen.paint(this, 0, 0); } return _errCode; } ErrorCode CBofBmpButton::loadBitmaps(CBofBitmap *pUp, CBofBitmap *pDown, CBofBitmap *pFocus, CBofBitmap *pDisabled, int nMaskColor) { assert(isValidObject(this)); assert(pUp != nullptr); assert(pDown != nullptr); // Use the bitmaps passed in _pButtonUp = pUp; _pButtonDown = pDown; _pButtonFocus = pFocus; _pButtonDisabled = pDisabled; // Remember the transparent color for these bitmaps _nMaskColor = nMaskColor; return _errCode; } ErrorCode CBofBmpButton::loadBitmaps(CBofPalette *pPalette, const char *pszUp, const char *pszDown, const char *pszFocus, const char *pszDisabled, int nMaskColor) { assert(isValidObject(this)); assert(pPalette != nullptr); assert(pszUp != nullptr); assert(pszDown != nullptr); assert(pszFocus != nullptr); assert(pszDisabled != nullptr); // Remember the button transparent color _nMaskColor = nMaskColor; // Load each of the bitmaps that represent the button state _pButtonUp = new CBofBitmap(pszUp, pPalette); _pButtonUp->setReadOnly(true); _pButtonDown = new CBofBitmap(pszDown, pPalette); _pButtonDown->setReadOnly(true); _pButtonFocus = new CBofBitmap(pszFocus, pPalette); _pButtonFocus->setReadOnly(true); _pButtonDisabled = new CBofBitmap(pszDisabled, pPalette); _pButtonDisabled->setReadOnly(true); return _errCode; } ErrorCode CBofBmpButton::setState(int nNewState, bool bRepaintNow) { assert(isValidObject(this)); assert(nNewState >= BUTTON_UP && nNewState <= BUTTON_DISABLED); // Remember last button state int nOldState = _nState; _nState = nNewState; // Update the window if forced to or if button state has changed if (bRepaintNow || (nOldState != nNewState)) { paint(); } // I MUST have a valid parent assert(_parent != nullptr); // Tell parent the new state of this button if (_parent != nullptr) { _parent->onBofButton(this, _nState); } return _errCode; } void CBofBmpButton::onPaint(CBofRect *pRect) { assert(isValidObject(this)); assert(pRect != nullptr); paint(pRect); } void CBofBmpButton::onLButtonDown(uint32, CBofPoint *pPoint, void *) { assert(isValidObject(this)); assert(pPoint != nullptr); if (!_bCaptured && _nState != BUTTON_DISABLED) { setCapture(); setState(BUTTON_DOWN, true); } } void CBofBmpButton::onLButtonUp(uint32, CBofPoint *pPoint, void *) { assert(isValidObject(this)); assert(pPoint != nullptr); if (_bCaptured) { releaseCapture(); setState(BUTTON_UP, true); if (_cRect.ptInRect(*pPoint) && (_parent != nullptr)) { _parent->onBofButton(this, BUTTON_CLICKED); } } } } // namespace SpaceBar } // namespace Bagel