diff options
Diffstat (limited to 'engines/sherlock/tattoo/widget_base.cpp')
-rw-r--r-- | engines/sherlock/tattoo/widget_base.cpp | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/engines/sherlock/tattoo/widget_base.cpp b/engines/sherlock/tattoo/widget_base.cpp new file mode 100644 index 0000000000..9d95fed9bf --- /dev/null +++ b/engines/sherlock/tattoo/widget_base.cpp @@ -0,0 +1,299 @@ +/* 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. + * + */ + +#include "sherlock/tattoo/widget_base.h" +#include "sherlock/tattoo/tattoo.h" +#include "sherlock/tattoo/tattoo_talk.h" +#include "sherlock/tattoo/tattoo_user_interface.h" + +namespace Sherlock { + +namespace Tattoo { + +WidgetBase::WidgetBase(SherlockEngine *vm) : _vm(vm) { + _scroll = false; +} + +void WidgetBase::summonWindow() { + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + + // Double-check that the same widget isn't added twice + for (Common::List<WidgetBase *>::iterator i = ui._widgets.begin(); i != ui._widgets.end(); ++i) { + if ((*i) == this) + error("Tried to add a widget twice"); + } + + // Add widget to the screen + ui._widgets.push_back(this); + _outsideMenu = false; + + draw(); +} + +void WidgetBase::banishWindow() { + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + + erase(); + _surface.free(); + ui._widgets.remove(this); +} + +bool WidgetBase::active() const { + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + for (Common::List<WidgetBase *>::iterator i = ui._widgets.begin(); i != ui._widgets.end(); ++i) { + if ((*i) == this) + return true; + } + + return false; +} + + +void WidgetBase::erase() { + Screen &screen = *_vm->_screen; + + if (_oldBounds.width() > 0) { + // Restore the affected area from the secondary back buffer into the first one, and then copy to screen + screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds); + screen.slamRect(_oldBounds); + + // Reset the old bounds so it won't be erased again + _oldBounds = Common::Rect(0, 0, 0, 0); + } +} + +void WidgetBase::draw() { + Screen &screen = *_vm->_screen; + + // If there was a previously drawn frame in a different position that hasn't yet been erased, then erase it + if (_oldBounds.width() > 0 && _oldBounds != _bounds) + erase(); + + if (_bounds.width() > 0 && !_surface.empty()) { + // Get the area to draw, adjusted for scroll position + restrictToScreen(); + + // Draw the background for the widget + drawBackground(); + + // Draw the widget onto the back buffer and then slam it to the screen + screen._backBuffer1.transBlitFrom(_surface, Common::Point(_bounds.left, _bounds.top)); + screen.slamRect(_bounds); + + // Store a copy of the drawn area for later erasing + _oldBounds = _bounds; + } +} + +void WidgetBase::drawBackground() { + TattooEngine &vm = *(TattooEngine *)_vm; + Screen &screen = *_vm->_screen; + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + + Common::Rect bounds = _bounds; + + if (vm._transparentMenus) { + ui.makeBGArea(bounds); + } else { + bounds.grow(-3); + screen._backBuffer1.fillRect(bounds, MENU_BACKGROUND); + } +} + +Common::String WidgetBase::splitLines(const Common::String &str, Common::StringArray &lines, int maxWidth, uint maxLines) { + Talk &talk = *_vm->_talk; + const char *strP = str.c_str(); + + // Loop counting up lines + lines.clear(); + do { + int width = 0; + const char *spaceP = nullptr; + const char *lineStartP = strP; + + // Find how many characters will fit on the next line + while (width < maxWidth && *strP && ((byte)*strP < talk._opcodes[OP_SWITCH_SPEAKER] || + (byte)*strP == talk._opcodes[OP_NULL])) { + width += _surface.charWidth(*strP); + + // Keep track of the last space + if (*strP == ' ') + spaceP = strP; + ++strP; + } + + // If the line was too wide to fit on a single line, go back to the last space + // if there was one, or otherwise simply break the line at this point + if (width >= maxWidth && spaceP != nullptr) + strP = spaceP; + + // Add the line to the output array + lines.push_back(Common::String(lineStartP, strP)); + + // Move the string ahead to the next line + if (*strP == ' ' || *strP == 13) + ++strP; + } while (*strP && (lines.size() < maxLines) && ((byte)*strP < talk._opcodes[OP_SWITCH_SPEAKER] + || (byte)*strP == talk._opcodes[OP_NULL])); + + // Return any remaining text left over + return *strP ? Common::String(strP) : Common::String(); +} + +void WidgetBase::restrictToScreen() { + Screen &screen = *_vm->_screen; + + if (_bounds.left < screen._currentScroll.x) + _bounds.moveTo(screen._currentScroll.x, _bounds.top); + if (_bounds.top < 0) + _bounds.moveTo(_bounds.left, 0); + if (_bounds.right > screen._backBuffer1.w()) + _bounds.moveTo(screen._backBuffer1.w() - _bounds.width(), _bounds.top); + if (_bounds.bottom > screen._backBuffer1.h()) + _bounds.moveTo(_bounds.left, screen._backBuffer1.h() - _bounds.height()); +} + +void WidgetBase::makeInfoArea(Surface &s) { + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + ImageFile &images = *ui._interfaceImages; + + // Draw the four corners of the Info Box + s.transBlitFrom(images[0], Common::Point(0, 0)); + s.transBlitFrom(images[1], Common::Point(s.w() - images[1]._width, 0)); + s.transBlitFrom(images[2], Common::Point(0, s.h() - images[2]._height)); + s.transBlitFrom(images[3], Common::Point(s.w() - images[3]._width, s.h())); + + // Draw the top of the Info Box + s.hLine(images[0]._width, 0, s.w() - images[1]._width, INFO_TOP); + s.hLine(images[0]._width, 1, s.w() - images[1]._width, INFO_MIDDLE); + s.hLine(images[0]._width, 2, s.w() - images[1]._width, INFO_BOTTOM); + + // Draw the bottom of the Info Box + s.hLine(images[0]._width, s.h()- 3, s.w() - images[1]._width, INFO_TOP); + s.hLine(images[0]._width, s.h()- 2, s.w() - images[1]._width, INFO_MIDDLE); + s.hLine(images[0]._width, s.h()- 1, s.w() - images[1]._width, INFO_BOTTOM); + + // Draw the left Side of the Info Box + s.vLine(0, images[0]._height, s.h()- images[2]._height, INFO_TOP); + s.vLine(1, images[0]._height, s.h()- images[2]._height, INFO_MIDDLE); + s.vLine(2, images[0]._height, s.h()- images[2]._height, INFO_BOTTOM); + + // Draw the right Side of the Info Box + s.vLine(s.w() - 3, images[0]._height, s.h()- images[2]._height, INFO_TOP); + s.vLine(s.w() - 2, images[0]._height, s.h()- images[2]._height, INFO_MIDDLE); + s.vLine(s.w() - 1, images[0]._height, s.h()- images[2]._height, INFO_BOTTOM); +} + +void WidgetBase::makeInfoArea() { + makeInfoArea(_surface); +} + +void WidgetBase::checkTabbingKeys(int numOptions) { +} + +void WidgetBase::drawScrollBar(int index, int pageSize, int count) { + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + bool raised; + + // Fill the area with transparency + Common::Rect r(BUTTON_SIZE, _bounds.height() - 6); + r.moveTo(_bounds.width() - BUTTON_SIZE - 3, 3); + _surface.fillRect(r, TRANSPARENCY); + + raised = ui._scrollHighlight != 1; + _surface.fillRect(Common::Rect(r.left + 2, r.top + 2, r.right - 2, r.top + BUTTON_SIZE - 2), INFO_MIDDLE); + ui.drawDialogRect(_surface, Common::Rect(r.left, r.top, r.left + BUTTON_SIZE, r.top + BUTTON_SIZE), raised); + + raised = ui._scrollHighlight != 5; + _surface.fillRect(Common::Rect(r.left + 2, r.bottom - BUTTON_SIZE + 2, r.right - 2, r.bottom - 2), INFO_MIDDLE); + ui.drawDialogRect(_surface, Common::Rect(r.left, r.bottom - BUTTON_SIZE, r.right, r.bottom), raised); + + // Draw the arrows on the scroll buttons + byte color = index ? INFO_BOTTOM + 2 : INFO_BOTTOM; + _surface.hLine(r.right / 2, r.top - 2 + BUTTON_SIZE / 2, r.right / 2, color); + _surface.fillRect(Common::Rect(r.right / 2 - 1, r.top - 1 + BUTTON_SIZE / 2, + r.right / 2 + 1, r.top - 1 + BUTTON_SIZE / 2), color); + _surface.fillRect(Common::Rect(r.right / 2 - 2, r.top + BUTTON_SIZE / 2, + r.right / 2 + 2, r.top + BUTTON_SIZE / 2), color); + _surface.fillRect(Common::Rect(r.right / 2 - 3, r.top + 1 + BUTTON_SIZE / 2, + r.right / 2 + 3, r.top + 1 + BUTTON_SIZE / 2), color); + + color = (index + pageSize) < count ? INFO_BOTTOM + 2 : INFO_BOTTOM; + _surface.fillRect(Common::Rect(r.right / 2 - 3, r.bottom - 1 - BUTTON_SIZE + BUTTON_SIZE / 2, + r.right / 2 + 3, r.bottom - 1 - BUTTON_SIZE + BUTTON_SIZE / 2), color); + _surface.fillRect(Common::Rect(r.right / 2 - 2, r.bottom - 1 - BUTTON_SIZE + 1 + BUTTON_SIZE / 2, + r.right / 2 + 2, r.bottom - 1 - BUTTON_SIZE + 1 + BUTTON_SIZE / 2), color); + _surface.fillRect(Common::Rect(r.right / 2 - 1, r.bottom - 1 - BUTTON_SIZE + 2 + BUTTON_SIZE / 2, + r.right / 2 + 1, r.bottom - 1 - BUTTON_SIZE + 2 + BUTTON_SIZE / 2), color); + _surface.fillRect(Common::Rect(r.right / 2, r.bottom - 1 - BUTTON_SIZE + 3 + BUTTON_SIZE / 2, + r.right / 2, r.bottom - 1 - BUTTON_SIZE + 3 + BUTTON_SIZE / 2), color); + + // Draw the scroll position bar + int barHeight = (_bounds.height() - BUTTON_SIZE * 2) * pageSize / count; + barHeight = CLIP(barHeight, BUTTON_SIZE, _bounds.height() - BUTTON_SIZE * 2); + int barY = (r.height() - BUTTON_SIZE * 2 - barHeight) * index / pageSize + r.top + BUTTON_SIZE; + + _surface.fillRect(Common::Rect(r.left + 2, barY + 2, r.right - 2, barY + barHeight - 3), INFO_MIDDLE); + ui.drawDialogRect(_surface, Common::Rect(r.left, barY, r.right, barY + barHeight), true); +} + +void WidgetBase::handleScrollbarEvents(int index, int pageSize, int count) { + Events &events = *_vm->_events; + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + Common::Point mousePos = events.mousePos(); + + // If they have selected the sollbar, return with the Scroll Bar Still selected + if (ui._scrollHighlight == 3) + return; + + ui._scrollHighlight = SH_NONE; + + if ((!events._pressed && !events._rightReleased) || !_scroll) + return; + + Common::Rect r(_bounds.right - BUTTON_SIZE - 3, _bounds.top + 3, _bounds.right - 3, _bounds.bottom - 3); + + // Calculate the Scroll Position bar + int barHeight = (_bounds.height() - BUTTON_SIZE * 2) * pageSize / count; + barHeight = CLIP(barHeight, BUTTON_SIZE, _bounds.height() - BUTTON_SIZE * 2); + int barY = (r.height() - BUTTON_SIZE * 2 - barHeight) * index / pageSize + r.top + BUTTON_SIZE; + + if (Common::Rect(r.left, r.top, r.right, r.top + BUTTON_SIZE).contains(mousePos)) + // Mouse on scroll up button + ui._scrollHighlight = SH_SCROLL_UP; + else if (Common::Rect(r.left, r.top + BUTTON_SIZE, r.right, barY).contains(mousePos)) + // Mouse on paging up area (the area of the vertical bar above the thumbnail) + ui._scrollHighlight = SH_PAGE_UP; + else if (Common::Rect(r.left, barY, r.right, barY + barHeight).contains(mousePos)) + // Mouse on scrollbar thumb + ui._scrollHighlight = SH_THUMBNAIL; + else if (Common::Rect(r.left, barY + barHeight, r.right, r.bottom - BUTTON_SIZE).contains(mousePos)) + // Mouse on paging down area (the area of the vertical bar below the thumbnail) + ui._scrollHighlight = SH_PAGE_DOWN; + else if (Common::Rect(r.left, r.bottom - BUTTON_SIZE, r.right, r.bottom).contains(mousePos)) + // Mouse on scroll down button + ui._scrollHighlight = SH_SCROLL_DOWN; +} + +} // End of namespace Tattoo + +} // End of namespace Sherlock |