aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/controls32.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/graphics/controls32.cpp')
-rw-r--r--engines/sci/graphics/controls32.cpp435
1 files changed, 293 insertions, 142 deletions
diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp
index df518888a9..fc028f7584 100644
--- a/engines/sci/graphics/controls32.cpp
+++ b/engines/sci/graphics/controls32.cpp
@@ -23,9 +23,11 @@
#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"
@@ -34,174 +36,323 @@
#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) {}
-GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text)
- : _segMan(segMan), _cache(cache), _text(text) {
-}
+GfxControls32::~GfxControls32() {}
-GfxControls32::~GfxControls32() {
-}
+reg_t GfxControls32::kernelEditText(const reg_t controlObject) {
+ SegManager *segMan = _segMan;
-void GfxControls32::kernelTexteditChange(reg_t controlObject) {
- SciEvent curEvent;
- uint16 maxChars = 40; //readSelectorValue(_segMan, controlObject, SELECTOR(max)); // TODO
- reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
- GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font)));
- Common::String text;
- uint16 textSize;
- bool textChanged = false;
- bool textAddChar = false;
- Common::Rect rect;
+ 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));
- if (textReference.isNull())
- error("kEditControl called on object that doesn't have a text reference");
- text = _segMan->getString(textReference);
+ 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;
+ }
+ }
- // TODO: Finish this
- warning("kEditText ('%s')", text.c_str());
- return;
+ int16 width = 0;
+ int16 height = titleHeight;
- uint16 cursorPos = 0;
- //uint16 oldCursorPos = cursorPos;
- bool captureEvents = true;
- EventManager* eventMan = g_sci->getEventManager();
+ 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;
+ }
- while (captureEvents) {
- curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
+ Common::Rect editorPlaneRect(width, height);
+ editorPlaneRect.translate(readSelectorValue(_segMan, controlObject, SELECTOR(x)), readSelectorValue(_segMan, controlObject, SELECTOR(y)));
- if (curEvent.type == SCI_EVENT_NONE) {
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
+ 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()) {
+ reg_t out;
+ 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, &out);
} else {
- textSize = text.size();
+ Common::String title = _segMan->getString(titleObject);
+ int16 titleBackColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleBack));
+ int16 titleForeColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleFore));
+ editor.bitmap = _gfxText32->createTitledBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, title, titleForeColor, titleBackColor, titleFontId, true, &out);
+ }
+ }
- switch (curEvent.type) {
- case SCI_EVENT_MOUSE_PRESS:
- // TODO: Implement mouse support for cursor change
+ 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) {
+ return NULL_REG;
+ } 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;
- case SCI_EVENT_KEYBOARD:
- switch (curEvent.character) {
- case SCI_KEY_BACKSPACE:
- if (cursorPos > 0) {
- cursorPos--; text.deleteChar(cursorPos);
- textChanged = true;
- }
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_DELETE:
- if (cursorPos < textSize) {
- text.deleteChar(cursorPos);
- textChanged = true;
- }
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_HOME: // HOME
- cursorPos = 0; textChanged = true;
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_END: // END
- cursorPos = textSize; textChanged = true;
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_LEFT: // LEFT
- if (cursorPos > 0) {
- cursorPos--; textChanged = true;
- }
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_RIGHT: // RIGHT
- if (cursorPos + 1 <= textSize) {
- cursorPos++; textChanged = true;
- }
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case 3: // returned in SCI1 late and newer when Control - C is pressed
- if (curEvent.modifiers & SCI_KEYMOD_CTRL) {
- // Control-C erases the whole line
- cursorPos = 0; text.clear();
- textChanged = true;
+ }
+ }
+
+ 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();
}
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_UP:
- case SCI_KEY_DOWN:
- case SCI_KEY_ENTER:
- case SCI_KEY_ESC:
- case SCI_KEY_TAB:
- case SCI_KEY_SHIFT_TAB:
- captureEvents = false;
- break;
- default:
- if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.character == 'c') {
- // Control-C in earlier SCI games (SCI0 - SCI1 middle)
- // Control-C erases the whole line
- cursorPos = 0; text.clear();
- textChanged = true;
- } else if (curEvent.character > 31 && curEvent.character < 256 && textSize < maxChars) {
- // insert pressed character
- textAddChar = true;
- textChanged = true;
+
+ 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;
}
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
}
- break;
+ }
}
}
- if (textChanged) {
- rect = g_sci->_gfxCompare->getNSRect(controlObject);
+ if (shouldDeleteChar) {
+ shouldRedrawText = true;
+ if (editor.cursorCharPosition < editor.text.size()) {
+ editor.text.deleteChar(editor.cursorCharPosition);
+ }
+ }
- if (textAddChar) {
- const char *textPtr = text.c_str();
+ 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();
+ }
- // We check if we are really able to add the new char
- uint16 textWidth = 0;
- while (*textPtr)
- textWidth += font->getCharWidth((byte)*textPtr++);
- textWidth += font->getCharWidth(curEvent.character);
+ g_sci->_gfxFrameout->frameOut(true);
+ g_sci->getSciDebugger()->onFrame();
+ g_sci->getEngineState()->speedThrottler(16);
+ g_sci->getEngineState()->_throttleTrigger = true;
+ }
- // Does it fit?
- if (textWidth >= rect.width()) {
- return;
- }
+ g_sci->_gfxFrameout->deletePlane(*plane);
+ if (readSelectorValue(segMan, controlObject, SELECTOR(frameOut))) {
+ g_sci->_gfxFrameout->frameOut(true);
+ }
- text.insertChar(curEvent.character, cursorPos++);
+ _segMan->freeHunkEntry(editor.bitmap);
- // Note: the following checkAltInput call might make the text
- // too wide to fit, but SSCI fails to check that too.
- }
+ if (textChanged) {
+ editor.text.trim();
+ SciString *string = _segMan->lookupString(textObject);
+ string->fromString(editor.text);
+ }
- reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap));
- Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject);
- //texteditCursorErase(); // TODO: Cursor
-
- // Write back string
- _segMan->strcpy(textReference, text.c_str());
- // Modify the buffer and show it
- warning("kernelTexteditChange");
-#if 0
- _text->createTextBitmap(controlObject, 0, 0, hunkId);
-
- _text->drawTextBitmap(0, 0, nsRect, controlObject);
- //texteditCursorDraw(rect, text.c_str(), cursorPos); // TODO: Cursor
- g_system->updateScreen();
-#endif
+ 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 {
- // TODO: Cursor
- /*
- if (g_system->getMillis() >= _texteditBlinkTime) {
- _paint16->invertRect(_texteditCursorRect);
- _paint16->bitsShow(_texteditCursorRect);
- _texteditCursorVisible = !_texteditCursorVisible;
- texteditSetBlinkTime();
- }
- */
+ editor.cursorRect.top = editor.textRect.top + scaledFontHeight - 1;
+ editor.cursorRect.setHeight(1);
}
- textAddChar = false;
- textChanged = false;
- g_sci->sleep(10);
- } // while
+ 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