diff options
Diffstat (limited to 'engines/glk/window_text_grid.cpp')
-rw-r--r-- | engines/glk/window_text_grid.cpp | 654 |
1 files changed, 654 insertions, 0 deletions
diff --git a/engines/glk/window_text_grid.cpp b/engines/glk/window_text_grid.cpp new file mode 100644 index 0000000000..963083659f --- /dev/null +++ b/engines/glk/window_text_grid.cpp @@ -0,0 +1,654 @@ +/* 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 "glk/window_text_grid.h" +#include "glk/conf.h" +#include "glk/gargoyle.h" +#include "glk/selection.h" +#include "glk/screen.h" + +namespace Gargoyle { + +TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows, rock) { + _type = wintype_TextGrid; + _width = _height = 0; + _curX = _curY = 0; + _inBuf = nullptr; + _inOrgX = _inOrgY = 0; + _inMax = 0; + _inCurs = _inLen = 0; + _inArrayRock.num = 0; + _lineTerminators = nullptr; + + Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], _styles); +} + +TextGridWindow::~TextGridWindow() { + if (_inBuf) { + if (g_vm->gli_unregister_arr) + (*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock); + _inBuf = nullptr; + } + + delete[] _lineTerminators; +} + +void TextGridWindow::rearrange(const Rect &box) { + Window::rearrange(box); + int newwid, newhgt; + + newwid = box.width() / g_conf->_cellW; + newhgt = box.height() / g_conf->_cellH; + + if (newwid == _width && newhgt == _height) + return; + + _lines.resize(newhgt); + for (int y = 0; y < newhgt; ++y) { + _lines[y].resize(newwid); + touch(y); + } + + _attr.clear(); + _width = newwid; + _height = newhgt; +} + +void TextGridWindow::touch(int line) { + int y = _bbox.top + line * g_conf->_leading; + _lines[line].dirty = true; + _windows->repaint(Rect(_bbox.left, y, _bbox.right, y + g_conf->_leading)); +} + +glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const { + return vertical ? size * g_conf->_cellW : size * g_conf->_cellH; +} + +void TextGridWindow::putCharUni(uint32 ch) { + TextGridRow *ln; + + // Canonicalize the cursor position. That is, the cursor may have been + // left outside the window area; wrap it if necessary. + if (_curX < 0) { + _curX = 0; + } else if (_curX >= _width) { + _curX = 0; + _curY++; + } + if (_curY < 0) + _curY = 0; + else if (_curY >= _height) + return; // outside the window + + if (ch == '\n') { + // a newline just moves the cursor. + _curY++; + _curX = 0; + return; + } + + touch(_curY); + + ln = &(_lines[_curY]); + ln->_chars[_curX] = ch; + ln->_attrs[_curX] = _attr; + + _curX++; + // We can leave the cursor outside the window, since it will be + // canonicalized next time a character is printed. +} + +bool TextGridWindow::unputCharUni(uint32 ch) { + TextGridRow *ln; + int oldx = _curX, oldy = _curY; + + // Move the cursor back. + if (_curX >= _width) + _curX = _width - 1; + else + _curX--; + + // Canonicalize the cursor position. That is, the cursor may have been + // left outside the window area; wrap it if necessary. + if (_curX < 0) { + _curX = _width - 1; + _curY--; + } + if (_curY < 0) + _curY = 0; + else if (_curY >= _height) + return false; // outside the window + + if (ch == '\n') { + // a newline just moves the cursor. + if (_curX == _width - 1) + return 1; // deleted a newline + _curX = oldx; + _curY = oldy; + return 0; // it wasn't there + } + + ln = &(_lines[_curY]); + if (ln->_chars[_curX] == ch) { + ln->_chars[_curX] = ' '; + ln->_attrs[_curX].clear(); + touch(_curY); + return true; // deleted the char + } else { + _curX = oldx; + _curY = oldy; + return false; // it wasn't there + } +} + +void TextGridWindow::moveCursor(const Point &pos) { + // If the values are negative, they're really huge positive numbers -- + // remember that they were cast from glui32. So set them huge and + // let canonicalization take its course. + _curX = (pos.x < 0) ? 32767 : pos.x; + _curY = (pos.y < 0) ? 32767 : pos.y; +} + +void TextGridWindow::clear() { + _attr.fgset = Windows::_overrideFgSet; + _attr.bgset = Windows::_overrideBgSet; + _attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0; + _attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0; + _attr.reverse = false; + + for (int k = 0; k < _height; k++) { + TextGridRow &ln = _lines[k]; + touch(k); + for (uint j = 0; j < ln._attrs.size(); ++j) { + ln._chars[j] = ' '; + ln._attrs[j].clear(); + } + } + + _curX = 0; + _curY = 0; +} + +void TextGridWindow::click(const Point &newPos) { + int x = newPos.x - _bbox.left; + int y = newPos.y - _bbox.top; + + if (_lineRequest || _charRequest || _lineRequestUni || _charRequestUni + || _moreRequest || _scrollRequest) + _windows->setFocus(this); + + if (_mouseRequest) { + g_vm->_events->store(evtype_MouseInput, this, x / g_conf->_cellW, y / g_conf->_leading); + _mouseRequest = false; + if (g_conf->_safeClicks) + g_vm->_events->_forceClick = true; + } + + if (_hyperRequest) { + glui32 linkval = g_vm->_selection->getHyperlink(newPos); + if (linkval) { + g_vm->_events->store(evtype_Hyperlink, this, linkval, 0); + _hyperRequest = false; + if (g_conf->_safeClicks) + g_vm->_events->_forceClick = true; + } + } +} + +void TextGridWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) { + if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) { + warning("request_line_event: window already has keyboard request"); + return; + } + + _lineRequest = true; + + if ((int)maxlen > (_width - _curX)) + maxlen = (_width - _curX); + + _inBuf = buf; + _inMax = maxlen; + _inLen = 0; + _inCurs = 0; + _inOrgX = _curX; + _inOrgY = _curY; + _origAttr = _attr; + _attr.set(style_Input); + + if (initlen > maxlen) + initlen = maxlen; + + if (initlen) { + TextGridRow *ln = &_lines[_inOrgY]; + + for (glui32 ix = 0; ix < initlen; ix++) { + ln->_attrs[_inOrgX + ix].set(style_Input); + ln->_chars[_inOrgX + ix] = buf[ix]; + } + + _inCurs += initlen; + _inLen += initlen; + _curX = _inOrgX + _inCurs; + _curY = _inOrgY; + + touch(_inOrgY); + } + + if (_lineTerminatorsBase && _termCt) { + _lineTerminators = new glui32[_termCt + 1]; + + if (_lineTerminators) { + memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32)); + _lineTerminators[_termCt] = 0; + } + } + + if (g_vm->gli_register_arr) + _inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Cn"); +} + +void TextGridWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) { + if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) { + warning("requestLineEventUni: window already has keyboard request"); + return; + } + + if ((int)maxlen > (_width - _curX)) + maxlen = (_width - _curX); + + _inBuf = buf; + _inMax = maxlen; + _inLen = 0; + _inCurs = 0; + _inOrgX = _curX; + _inOrgY = _curY; + _origAttr = _attr; + _attr.set(style_Input); + + if (initlen > maxlen) + initlen = maxlen; + + if (initlen) { + TextGridRow *ln = &(_lines[_inOrgY]); + + for (glui32 ix = 0; ix < initlen; ix++) { + ln->_attrs[_inOrgX + ix].set(style_Input); + ln->_chars[_inOrgX + ix] = buf[ix]; + } + + _inCurs += initlen; + _inLen += initlen; + _curX = _inOrgX + _inCurs; + _curY = _inOrgY; + + touch(_inOrgY); + } + + if (_lineTerminatorsBase && _termCt) { + _lineTerminators = new glui32[_termCt + 1]; + + if (_lineTerminators) { + memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32)); + _lineTerminators[_termCt] = 0; + } + } + + if (g_vm->gli_register_arr) + _inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Iu"); +} + +void TextGridWindow::cancelLineEvent(Event *ev) { + int ix; + void *inbuf; + int inmax; + int unicode = _lineRequestUni; + gidispatch_rock_t inarrayrock; + TextGridRow *ln = &_lines[_inOrgY]; + Event dummyEv; + + if (!ev) + ev = &dummyEv; + + ev->clear(); + + if (!_lineRequest && !_lineRequestUni) + return; + + + inbuf = _inBuf; + inmax = _inMax; + inarrayrock = _inArrayRock; + + if (!unicode) { + for (ix = 0; ix < _inLen; ix++) { + glui32 ch = ln->_chars[_inOrgX + ix]; + if (ch > 0xff) + ch = '?'; + ((char *)inbuf)[ix] = (char)ch; + } + if (_echoStream) + _echoStream->echoLine((char *)_inBuf, _inLen); + } else { + for (ix = 0; ix < _inLen; ix++) + ((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix]; + if (_echoStream) + _echoStream->echoLineUni((glui32 *)inbuf, _inLen); + } + + _curY = _inOrgY + 1; + _curX = 0; + _attr = _origAttr; + + ev->type = evtype_LineInput; + ev->window = this; + ev->val1 = _inLen; + ev->val2 = 0; + + _lineRequest = false; + _lineRequestUni = false; + + if (_lineTerminators) { + free(_lineTerminators); + _lineTerminators = nullptr; + } + + _inBuf = nullptr; + _inMax = 0; + _inOrgX = 0; + _inOrgY = 0; + + if (g_vm->gli_unregister_arr) + (*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock); +} + +void TextGridWindow::acceptReadChar(glui32 arg) { + glui32 key; + + switch (arg) { + case keycode_Erase: + key = keycode_Delete; + break; + case keycode_MouseWheelUp: + case keycode_MouseWheelDown: + return; + default: + key = arg; + } + + if (key > 0xff && key < (0xffffffff - keycode_MAXVAL + 1)) { + if (!(_charRequestUni) || key > 0x10ffff) + key = keycode_Unknown; + } + + _charRequest = false; + _charRequestUni = false; + g_vm->_events->store(evtype_CharInput, this, key, 0); +} + +void TextGridWindow::acceptLine(glui32 keycode) { + int ix; + void *inbuf; + int inmax; + gidispatch_rock_t inarrayrock; + TextGridRow *ln = &(_lines[_inOrgY]); + int unicode = _lineRequestUni; + + if (!_inBuf) + return; + + inbuf = _inBuf; + inmax = _inMax; + inarrayrock = _inArrayRock; + + if (!unicode) { + for (ix = 0; ix < _inLen; ix++) + ((char *)inbuf)[ix] = (char)ln->_chars[_inOrgX + ix]; + if (_echoStream) + _echoStream->echoLine((char *)inbuf, _inLen); + } else { + for (ix = 0; ix < _inLen; ix++) + ((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix]; + if (_echoStream) + _echoStream->echoLineUni((glui32 *)inbuf, _inLen); + } + + _curY = _inOrgY + 1; + _curX = 0; + _attr = _origAttr; + + if (_lineTerminators) { + glui32 val2 = keycode; + if (val2 == keycode_Return) + val2 = 0; + g_vm->_events->store(evtype_LineInput, this, _inLen, val2); + free(_lineTerminators); + _lineTerminators = nullptr; + } else { + g_vm->_events->store(evtype_LineInput, this, _inLen, 0); + } + _lineRequest = false; + _lineRequestUni = false; + _inBuf = nullptr; + _inMax = 0; + _inOrgX = 0; + _inOrgY = 0; + + if (g_vm->gli_unregister_arr) + (*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock); +} + +void TextGridWindow::acceptReadLine(glui32 arg) { + int ix; + TextGridRow *ln = &(_lines[_inOrgY]); + + if (!_inBuf) + return; + + if (_lineTerminators && checkTerminator(arg)) { + glui32 *cx; + for (cx = _lineTerminators; *cx; cx++) { + if (*cx == arg) { + acceptLine(arg); + return; + } + } + } + + switch (arg) { + + // Delete keys, during line input. + case keycode_Delete: + if (_inLen <= 0) + return; + if (_inCurs <= 0) + return; + for (ix = _inCurs; ix < _inLen; ix++) + ln->_chars[_inOrgX + ix - 1] = ln->_chars[_inOrgX + ix]; + ln->_chars[_inOrgX + _inLen - 1] = ' '; + _inCurs--; + _inLen--; + break; + + case keycode_Erase: + if (_inLen <= 0) + return; + if (_inCurs >= _inLen) + return; + for (ix = _inCurs; ix < _inLen - 1; ix++) + ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix + 1]; + ln->_chars[_inOrgX + _inLen - 1] = ' '; + _inLen--; + break; + + case keycode_Escape: + if (_inLen <= 0) + return; + for (ix = 0; ix < _inLen; ix++) + ln->_chars[_inOrgX + ix] = ' '; + _inLen = 0; + _inCurs = 0; + break; + + // Cursor movement keys, during line input. + case keycode_Left: + if (_inCurs <= 0) + return; + _inCurs--; + break; + + case keycode_Right: + if (_inCurs >= _inLen) + return; + _inCurs++; + break; + + case keycode_Home: + if (_inCurs <= 0) + return; + _inCurs = 0; + break; + + case keycode_End: + if (_inCurs >= _inLen) + return; + _inCurs = _inLen; + break; + + case keycode_Return: + acceptLine(arg); + break; + + default: + if (_inLen >= _inMax) + return; + + if (arg < 32 || arg > 0xff) + return; + + if (g_conf->_caps && (arg > 0x60 && arg < 0x7b)) + arg -= 0x20; + + for (ix = _inLen; ix > _inCurs; ix--) + ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix - 1]; + ln->_attrs[_inOrgX + _inLen].set(style_Input); + ln->_chars[_inOrgX + _inCurs] = arg; + + _inCurs++; + _inLen++; + } + + _curX = _inOrgX + _inCurs; + _curY = _inOrgY; + + touch(_inOrgY); +} + +void TextGridWindow::redraw() { + TextGridRow *ln; + int x0, y0; + int x, y, w; + int i, a, b, k, o; + glui32 link; + int font; + byte *fgcolor, *bgcolor; + Screen &screen = *g_vm->_screen; + + Window::redraw(); + + x0 = _bbox.left; + y0 = _bbox.top; + + for (i = 0; i < _height; i++) { + ln = &_lines[i]; + if (ln->dirty || Windows::_forceRedraw) { + ln->dirty = false; + + x = x0; + y = y0 + i * g_conf->_leading; + + // clear any stored hyperlink coordinates + g_vm->_selection->putHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading); + + a = 0; + for (b = 0; b < _width; b++) { + if (ln->_attrs[a] != ln->_attrs[b]) { + link = ln->_attrs[a].hyper; + font = ln->_attrs[a].attrFont(_styles); + fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles); + bgcolor = ln->_attrs[a].attrBg(_styles); + w = (b - a) * g_conf->_cellW; + screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor); + o = x; + + for (k = a, o = x; k < b; k++, o += g_conf->_cellW) { + screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine), font, + fgcolor, Common::U32String(&ln->_chars[k], 1), -1); + } + if (link) { + screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w, + g_conf->_linkStyle), g_conf->_linkColor); + g_vm->_selection->putHyperlink(link, x, y, x + w, y + g_conf->_leading); + } + + x += w; + a = b; + } + } + link = ln->_attrs[a].hyper; + font = ln->_attrs[a].attrFont(_styles); + fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles); + bgcolor = ln->_attrs[a].attrBg(_styles); + w = (b - a) * g_conf->_cellW; + w += _bbox.right - (x + w); + screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor); + + for (k = a, o = x; k < b; k++, o += g_conf->_cellW) { + screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine), font, + fgcolor, Common::U32String(&ln->_chars[k], 1)); + } + if (link) { + screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w, g_conf->_linkStyle), + g_conf->_linkColor); + g_vm->_selection->putHyperlink(link, x, y, x + w, y + g_conf->_leading); + } + } + } +} + +void TextGridWindow::getSize(glui32 *width, glui32 *height) const { + if (width) + *width = _bbox.width() / g_conf->_cellW; + if (height) + *height = _bbox.height() / g_conf->_cellH; +} + +/*--------------------------------------------------------------------------*/ + +void TextGridWindow::TextGridRow::resize(size_t newSize) { + _chars.clear(); + _attrs.clear(); + _chars.resize(newSize); + _attrs.resize(newSize); + Common::fill(&_chars[0], &_chars[0] + newSize, ' '); +} + +} // End of namespace Gargoyle |