scummvm/engines/sci/graphics/controls32.cpp
Colin Snover c8019487f9 SCI32: Remove unused titled text bitmap code
Titled text bitmaps do not appear to be used by any game scripts.
They seem to only be used by the error manager in SSCI, but
ScummVM has its own error handling, so we don’t need this
implementation.
2016-05-28 09:14:33 -05:00

353 lines
12 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/system.h"
#include "sci/sci.h"
#include "sci/console.h"
#include "sci/event.h"
#include "sci/engine/kernel.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/state.h"
#include "sci/graphics/cache.h"
#include "sci/graphics/compare.h"
#include "sci/graphics/controls32.h"
#include "sci/graphics/font.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/text32.h"
namespace Sci {
GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text) :
_segMan(segMan),
_gfxCache(cache),
_gfxText32(text),
_overwriteMode(false),
_nextCursorFlashTick(0) {}
reg_t GfxControls32::kernelEditText(const reg_t controlObject) {
SegManager *segMan = _segMan;
TextEditor editor;
reg_t textObject = readSelector(_segMan, controlObject, SELECTOR(text));
editor.text = _segMan->getString(textObject);
editor.foreColor = readSelectorValue(_segMan, controlObject, SELECTOR(fore));
editor.backColor = readSelectorValue(_segMan, controlObject, SELECTOR(back));
editor.skipColor = readSelectorValue(_segMan, controlObject, SELECTOR(skip));
editor.fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
editor.maxLength = readSelectorValue(_segMan, controlObject, SELECTOR(width));
editor.bitmap = readSelector(_segMan, controlObject, SELECTOR(bitmap));
editor.cursorCharPosition = 0;
editor.cursorIsDrawn = false;
editor.borderColor = readSelectorValue(_segMan, controlObject, SELECTOR(borderColor));
reg_t titleObject = readSelector(_segMan, controlObject, SELECTOR(title));
int16 titleHeight = 0;
GuiResourceId titleFontId = readSelectorValue(_segMan, controlObject, SELECTOR(titleFont));
if (!titleObject.isNull()) {
GfxFont *titleFont = _gfxCache->getFont(titleFontId);
titleHeight += _gfxText32->scaleUpHeight(titleFont->getHeight()) + 1;
if (editor.borderColor != -1) {
titleHeight += 2;
}
}
int16 width = 0;
int16 height = titleHeight;
GfxFont *editorFont = _gfxCache->getFont(editor.fontId);
height += _gfxText32->scaleUpHeight(editorFont->getHeight()) + 1;
_gfxText32->setFont(editor.fontId);
int16 emSize = _gfxText32->getCharWidth('M', true);
width += editor.maxLength * emSize + 1;
if (editor.borderColor != -1) {
width += 4;
height += 2;
}
Common::Rect editorPlaneRect(width, height);
editorPlaneRect.translate(readSelectorValue(_segMan, controlObject, SELECTOR(x)), readSelectorValue(_segMan, controlObject, SELECTOR(y)));
reg_t planeObj = readSelector(_segMan, controlObject, SELECTOR(plane));
Plane *sourcePlane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj);
if (sourcePlane == nullptr) {
error("Could not find plane %04x:%04x", PRINT_REG(planeObj));
}
editorPlaneRect.translate(sourcePlane->_gameRect.left, sourcePlane->_gameRect.top);
editor.textRect = Common::Rect(2, titleHeight + 2, width - 1, height - 1);
editor.width = width;
if (editor.bitmap.isNull()) {
TextAlign alignment = (TextAlign)readSelectorValue(_segMan, controlObject, SELECTOR(mode));
if (titleObject.isNull()) {
bool dimmed = readSelectorValue(_segMan, controlObject, SELECTOR(dimmed));
editor.bitmap = _gfxText32->createFontBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, dimmed, true);
} else {
error("Titled bitmaps are not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!");
}
}
drawCursor(editor);
Plane *plane = new Plane(editorPlaneRect, kPlanePicTransparent);
plane->changePic();
g_sci->_gfxFrameout->addPlane(*plane);
CelInfo32 celInfo;
celInfo.type = kCelTypeMem;
celInfo.bitmap = editor.bitmap;
ScreenItem *screenItem = new ScreenItem(plane->_object, celInfo, Common::Point(), ScaleInfo());
plane->_screenItemList.add(screenItem);
// frameOut must be called after the screen item is
// created, and before it is updated at the end of the
// event loop, otherwise it has both created and updated
// flags set which crashes the engine (it runs updates
// before creations)
g_sci->_gfxFrameout->frameOut(true);
EventManager *eventManager = g_sci->getEventManager();
bool clearTextOnInput = true;
bool textChanged = false;
for (;;) {
// We peek here because the last event needs to be allowed to
// dispatch a second time to the normal event handling system.
// In the actual engine, the event is always consumed and then
// the last event just gets posted back to the event manager for
// reprocessing, but instead, we only remove the event from the
// queue *after* we have determined it is not a defocusing event
const SciEvent event = eventManager->getSciEvent(SCI_EVENT_ANY | SCI_EVENT_PEEK);
bool focused = true;
// Original engine did not have a QUIT event but we have to handle it
if (event.type == SCI_EVENT_QUIT) {
focused = false;
break;
} else if (event.type == SCI_EVENT_MOUSE_PRESS && !editorPlaneRect.contains(event.mousePosSci)) {
focused = false;
} else if (event.type == SCI_EVENT_KEYBOARD) {
switch (event.character) {
case SCI_KEY_ESC:
case SCI_KEY_UP:
case SCI_KEY_DOWN:
case SCI_KEY_TAB:
case SCI_KEY_SHIFT_TAB:
case SCI_KEY_ENTER:
focused = false;
break;
}
}
if (!focused) {
break;
}
// Consume the event now that we know it is not one of the
// defocusing events above
eventManager->getSciEvent(SCI_EVENT_ANY);
// NOTE: In the original engine, the font and bitmap were
// reset here on each iteration through the loop, but it
// doesn't seem like this should be necessary since
// control is not yielded back to the VM until input is
// received, which means there is nothing that could modify
// the GfxText32's state with a different font in the
// meantime
bool shouldDeleteChar = false;
bool shouldRedrawText = false;
uint16 lastCursorPosition = editor.cursorCharPosition;
if (event.type == SCI_EVENT_KEYBOARD) {
switch (event.character) {
case SCI_KEY_LEFT:
clearTextOnInput = false;
if (editor.cursorCharPosition > 0) {
--editor.cursorCharPosition;
}
break;
case SCI_KEY_RIGHT:
clearTextOnInput = false;
if (editor.cursorCharPosition < editor.text.size()) {
++editor.cursorCharPosition;
}
break;
case SCI_KEY_HOME:
clearTextOnInput = false;
editor.cursorCharPosition = 0;
break;
case SCI_KEY_END:
clearTextOnInput = false;
editor.cursorCharPosition = editor.text.size();
break;
case SCI_KEY_INSERT:
clearTextOnInput = false;
// Redrawing also changes the cursor rect to
// reflect the new insertion mode
shouldRedrawText = true;
_overwriteMode = !_overwriteMode;
break;
case SCI_KEY_DELETE:
clearTextOnInput = false;
if (editor.cursorCharPosition < editor.text.size()) {
shouldDeleteChar = true;
}
break;
case SCI_KEY_BACKSPACE:
clearTextOnInput = false;
shouldDeleteChar = true;
if (editor.cursorCharPosition > 0) {
--editor.cursorCharPosition;
}
break;
case SCI_KEY_ETX:
editor.text.clear();
editor.cursorCharPosition = 0;
shouldRedrawText = true;
break;
default: {
if (event.character >= 20 && event.character < 257) {
if (clearTextOnInput) {
clearTextOnInput = false;
editor.text.clear();
}
if (
(_overwriteMode && editor.cursorCharPosition < editor.maxLength) ||
(editor.text.size() < editor.maxLength && _gfxText32->getCharWidth(event.character, true) + _gfxText32->getStringWidth(editor.text) < editor.textRect.width())
) {
if (_overwriteMode && editor.cursorCharPosition < editor.text.size()) {
editor.text.setChar(event.character, editor.cursorCharPosition);
} else {
editor.text.insertChar(event.character, editor.cursorCharPosition);
}
++editor.cursorCharPosition;
shouldRedrawText = true;
}
}
}
}
}
if (shouldDeleteChar) {
shouldRedrawText = true;
if (editor.cursorCharPosition < editor.text.size()) {
editor.text.deleteChar(editor.cursorCharPosition);
}
}
if (shouldRedrawText) {
eraseCursor(editor);
_gfxText32->erase(editor.textRect, true);
_gfxText32->drawTextBox(editor.text);
drawCursor(editor);
textChanged = true;
screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
} else if (editor.cursorCharPosition != lastCursorPosition) {
eraseCursor(editor);
drawCursor(editor);
screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
} else {
flashCursor(editor);
screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
}
g_sci->_gfxFrameout->frameOut(true);
g_sci->getSciDebugger()->onFrame();
g_sci->getEngineState()->speedThrottler(16);
g_sci->getEngineState()->_throttleTrigger = true;
}
g_sci->_gfxFrameout->deletePlane(*plane);
if (readSelectorValue(segMan, controlObject, SELECTOR(frameOut))) {
g_sci->_gfxFrameout->frameOut(true);
}
_segMan->freeHunkEntry(editor.bitmap);
if (textChanged) {
editor.text.trim();
SciString *string = _segMan->lookupString(textObject);
string->fromString(editor.text);
}
return make_reg(0, textChanged);
}
void GfxControls32::drawCursor(TextEditor &editor) {
if (!editor.cursorIsDrawn) {
editor.cursorRect.left = editor.textRect.left + _gfxText32->getTextWidth(editor.text, 0, editor.cursorCharPosition);
const int16 scaledFontHeight = _gfxText32->scaleUpHeight(_gfxText32->_font->getHeight());
// NOTE: The original code branched on borderColor here but
// the two branches appeared to be identical, differing only
// because the compiler decided to be differently clever
// when optimising multiplication in each branch
if (_overwriteMode) {
editor.cursorRect.top = editor.textRect.top;
editor.cursorRect.setHeight(scaledFontHeight);
} else {
editor.cursorRect.top = editor.textRect.top + scaledFontHeight - 1;
editor.cursorRect.setHeight(1);
}
const char currentChar = editor.cursorCharPosition < editor.text.size() ? editor.text[editor.cursorCharPosition] : ' ';
editor.cursorRect.setWidth(_gfxText32->getCharWidth(currentChar, true));
_gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
editor.cursorIsDrawn = true;
}
_nextCursorFlashTick = g_sci->getTickCount() + 30;
}
void GfxControls32::eraseCursor(TextEditor &editor) {
if (editor.cursorIsDrawn) {
_gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
editor.cursorIsDrawn = false;
}
_nextCursorFlashTick = g_sci->getTickCount() + 30;
}
void GfxControls32::flashCursor(TextEditor &editor) {
if (g_sci->getTickCount() > _nextCursorFlashTick) {
_gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
editor.cursorIsDrawn = !editor.cursorIsDrawn;
_nextCursorFlashTick = g_sci->getTickCount() + 30;
}
}
} // End of namespace Sci