mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 21:59:17 +00:00
1656 lines
38 KiB
C++
1656 lines
38 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 "glk/window_text_buffer.h"
|
|
#include "glk/conf.h"
|
|
#include "glk/glk.h"
|
|
#include "glk/screen.h"
|
|
#include "glk/selection.h"
|
|
#include "glk/unicode.h"
|
|
|
|
namespace Glk {
|
|
|
|
/**
|
|
*
|
|
* How many pixels we add to left/right margins
|
|
*/
|
|
#define SLOP (2 * GLI_SUBPIX)
|
|
|
|
|
|
TextBufferWindow::TextBufferWindow(Windows *windows, uint rock) : TextWindow(windows, rock),
|
|
_font(g_conf->_propInfo), _historyPos(0), _historyFirst(0), _historyPresent(0),
|
|
_lastSeen(0), _scrollPos(0), _scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1),
|
|
_inBuf(nullptr), _lineTerminators(nullptr), _echoLineInput(true), _ladjw(0), _radjw(0),
|
|
_ladjn(0), _radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr), _spaced(0), _dashed(0),
|
|
_copyBuf(0), _copyPos(0) {
|
|
_type = wintype_TextBuffer;
|
|
_history.resize(HISTORYLEN);
|
|
|
|
_lines.resize(SCROLLBACK);
|
|
_chars = _lines[0]._chars;
|
|
_attrs = _lines[0]._attrs;
|
|
|
|
Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], _styles);
|
|
|
|
if (g_conf->_speak)
|
|
gli_initialize_tts();
|
|
}
|
|
|
|
TextBufferWindow::~TextBufferWindow() {
|
|
if (g_conf->_speak)
|
|
gli_free_tts();
|
|
|
|
if (_inBuf) {
|
|
if (g_vm->gli_unregister_arr)
|
|
(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
|
|
_inBuf = nullptr;
|
|
}
|
|
|
|
delete[] _copyBuf;
|
|
delete[] _lineTerminators;
|
|
|
|
for (int i = 0; i < _scrollBack; i++) {
|
|
if (_lines[i]._lPic)
|
|
_lines[i]._lPic->decrement();
|
|
if (_lines[i]._rPic)
|
|
_lines[i]._rPic->decrement();
|
|
}
|
|
}
|
|
|
|
void TextBufferWindow::rearrange(const Rect &box) {
|
|
Window::rearrange(box);
|
|
int newwid, newhgt;
|
|
int rnd;
|
|
|
|
newwid = MAX((box.width() - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) / _font._cellW, 0);
|
|
newhgt = MAX((box.height() - g_conf->_tMarginY * 2) / _font._cellH, 0);
|
|
|
|
// align text with bottom
|
|
rnd = newhgt * _font._cellH + g_conf->_tMarginY * 2;
|
|
_yAdj = (box.height() - rnd);
|
|
_bbox.top += (box.height() - rnd);
|
|
|
|
if (newwid != _width) {
|
|
_width = newwid;
|
|
reflow();
|
|
}
|
|
|
|
if (newhgt != _height) {
|
|
// scroll up if we obscure new lines
|
|
if (_lastSeen >= newhgt - 1)
|
|
_scrollPos += (_height - newhgt);
|
|
|
|
_height = newhgt;
|
|
|
|
// keep window within 'valid' lines
|
|
if (_scrollPos > _scrollMax - _height + 1)
|
|
_scrollPos = _scrollMax - _height + 1;
|
|
if (_scrollPos < 0)
|
|
_scrollPos = 0;
|
|
touchScroll();
|
|
|
|
// allocate copy buffer
|
|
if (_copyBuf)
|
|
delete[] _copyBuf;
|
|
_copyBuf = new uint32[_height * TBLINELEN];
|
|
|
|
for (int i = 0; i < (_height * TBLINELEN); i++)
|
|
_copyBuf[i] = 0;
|
|
|
|
_copyPos = 0;
|
|
}
|
|
}
|
|
|
|
void TextBufferWindow::reflow() {
|
|
int inputbyte = -1;
|
|
Attributes curattr, oldattr;
|
|
int i, k, p, s;
|
|
int x;
|
|
|
|
if (_height < 4 || _width < 20)
|
|
return;
|
|
|
|
_lines[0]._len = _numChars;
|
|
|
|
// allocate temp buffers
|
|
Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN];
|
|
uint32 *charbuf = new uint32[SCROLLBACK * TBLINELEN];
|
|
int *alignbuf = new int[SCROLLBACK];
|
|
Picture **pictbuf = new Picture *[SCROLLBACK];
|
|
uint *hyperbuf = new uint[SCROLLBACK];
|
|
int *offsetbuf = new int[SCROLLBACK];
|
|
|
|
if (!attrbuf || !charbuf || !alignbuf || !pictbuf || !hyperbuf || !offsetbuf) {
|
|
delete[] attrbuf;
|
|
delete[] charbuf;
|
|
delete[] alignbuf;
|
|
delete[] pictbuf;
|
|
delete[] hyperbuf;
|
|
delete[] offsetbuf;
|
|
return;
|
|
}
|
|
|
|
// copy text to temp buffers
|
|
|
|
oldattr = _attr;
|
|
curattr.clear();
|
|
|
|
x = 0;
|
|
p = 0;
|
|
s = _scrollMax < SCROLLBACK ? _scrollMax : SCROLLBACK - 1;
|
|
|
|
for (k = s; k >= 0; k--) {
|
|
if (k == 0 && _lineRequest)
|
|
inputbyte = p + _inFence;
|
|
|
|
if (_lines[k]._lPic) {
|
|
offsetbuf[x] = p;
|
|
alignbuf[x] = imagealign_MarginLeft;
|
|
pictbuf[x] = _lines[k]._lPic;
|
|
|
|
if (pictbuf[x]) pictbuf[x]->increment();
|
|
hyperbuf[x] = _lines[k]._lHyper;
|
|
x++;
|
|
}
|
|
|
|
if (_lines[k]._rPic) {
|
|
offsetbuf[x] = p;
|
|
alignbuf[x] = imagealign_MarginRight;
|
|
pictbuf[x] = _lines[k]._rPic;
|
|
if (pictbuf[x]) pictbuf[x]->increment();
|
|
hyperbuf[x] = _lines[k]._rHyper;
|
|
x++;
|
|
}
|
|
|
|
for (i = 0; i < _lines[k]._len; i++) {
|
|
attrbuf[p] = curattr = _lines[k]._attrs[i];
|
|
charbuf[p] = _lines[k]._chars[i];
|
|
p++;
|
|
}
|
|
|
|
if (_lines[k]._newLine) {
|
|
attrbuf[p] = curattr;
|
|
charbuf[p] = '\n';
|
|
p++;
|
|
}
|
|
}
|
|
|
|
offsetbuf[x] = -1;
|
|
|
|
// clear window
|
|
clear();
|
|
|
|
// and dump text back
|
|
x = 0;
|
|
for (i = 0; i < p; i++) {
|
|
if (i == inputbyte)
|
|
break;
|
|
_attr = attrbuf[i];
|
|
|
|
if (offsetbuf[x] == i) {
|
|
putPicture(pictbuf[x], alignbuf[x], hyperbuf[x]);
|
|
x++;
|
|
}
|
|
|
|
putCharUni(charbuf[i]);
|
|
}
|
|
|
|
// terribly sorry about this...
|
|
_lastSeen = 0;
|
|
_scrollPos = 0;
|
|
|
|
if (inputbyte != -1) {
|
|
_inFence = _numChars;
|
|
putTextUni(charbuf + inputbyte, p - inputbyte, _numChars, 0);
|
|
_inCurs = _numChars;
|
|
}
|
|
|
|
// free temp buffers
|
|
delete[] attrbuf;
|
|
delete[] charbuf;
|
|
delete[] alignbuf;
|
|
delete[] pictbuf;
|
|
delete[] hyperbuf;
|
|
delete[] offsetbuf;
|
|
|
|
_attr = oldattr;
|
|
|
|
touchScroll();
|
|
}
|
|
|
|
void TextBufferWindow::touchScroll() {
|
|
g_vm->_selection->clearSelection();
|
|
_windows->repaint(_bbox);
|
|
|
|
for (int i = 0; i < _scrollMax; i++)
|
|
_lines[i]._dirty = true;
|
|
}
|
|
|
|
bool TextBufferWindow::putPicture(Picture *pic, uint align, uint linkval) {
|
|
if (align == imagealign_MarginRight) {
|
|
if (_lines[0]._rPic || _numChars)
|
|
return false;
|
|
|
|
_radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
|
|
_radjn = (pic->h + _font._cellH - 1) / _font._cellH;
|
|
_lines[0]._rPic = pic;
|
|
_lines[0]._rm = _radjw;
|
|
_lines[0]._rHyper = linkval;
|
|
} else {
|
|
if (align != imagealign_MarginLeft && _numChars)
|
|
putCharUni('\n');
|
|
|
|
if (_lines[0]._lPic || _numChars)
|
|
return false;
|
|
|
|
_ladjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
|
|
_ladjn = (pic->h + _font._cellH - 1) / _font._cellH;
|
|
_lines[0]._lPic = pic;
|
|
_lines[0]._lm = _ladjw;
|
|
_lines[0]._lHyper = linkval;
|
|
|
|
if (align != imagealign_MarginLeft)
|
|
flowBreak();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint TextBufferWindow::drawPicture(const Common::String &name, uint align, uint scaled, uint width, uint height) {
|
|
Picture *pic;
|
|
uint hyperlink;
|
|
int error;
|
|
|
|
pic = g_vm->_pictures->load(name);
|
|
|
|
if (!pic)
|
|
return false;
|
|
|
|
if (!_imageLoaded) {
|
|
g_vm->_pictures->increment();
|
|
_imageLoaded = true;
|
|
}
|
|
|
|
if (scaled) {
|
|
Picture *tmp;
|
|
tmp = g_vm->_pictures->scale(pic, width, height);
|
|
pic = tmp;
|
|
}
|
|
|
|
hyperlink = _attr.hyper;
|
|
|
|
pic->increment();
|
|
error = putPicture(pic, align, hyperlink);
|
|
|
|
return error;
|
|
}
|
|
|
|
void TextBufferWindow::putText(const char *buf, int len, int pos, int oldlen) {
|
|
int diff = len - oldlen;
|
|
|
|
if (_numChars + diff >= TBLINELEN)
|
|
return;
|
|
|
|
if (diff != 0 && pos + oldlen < _numChars) {
|
|
memmove(_chars + pos + len,
|
|
_chars + pos + oldlen,
|
|
(_numChars - (pos + oldlen)) * 4);
|
|
memmove(_attrs + pos + len,
|
|
_attrs + pos + oldlen,
|
|
(_numChars - (pos + oldlen)) * sizeof(Attributes));
|
|
}
|
|
if (len > 0) {
|
|
for (int i = 0; i < len; i++) {
|
|
_chars[pos + i] = buf[i];
|
|
_attrs[pos + i].set(style_Input);
|
|
}
|
|
}
|
|
_numChars += diff;
|
|
|
|
if (_inBuf) {
|
|
if (_inCurs >= pos + oldlen)
|
|
_inCurs += diff;
|
|
else if (_inCurs >= pos)
|
|
_inCurs = pos + len;
|
|
}
|
|
|
|
touch(0);
|
|
}
|
|
|
|
void TextBufferWindow::putTextUni(const uint32 *buf, int len, int pos, int oldlen) {
|
|
int diff = len - oldlen;
|
|
|
|
if (_numChars + diff >= TBLINELEN)
|
|
return;
|
|
|
|
if (diff != 0 && pos + oldlen < _numChars) {
|
|
memmove(_chars + pos + len,
|
|
_chars + pos + oldlen,
|
|
(_numChars - (pos + oldlen)) * 4);
|
|
memmove(_attrs + pos + len,
|
|
_attrs + pos + oldlen,
|
|
(_numChars - (pos + oldlen)) * sizeof(Attributes));
|
|
}
|
|
if (len > 0) {
|
|
int i;
|
|
memmove(_chars + pos, buf, len * 4);
|
|
for (i = 0; i < len; i++)
|
|
_attrs[pos + i].set(style_Input);
|
|
}
|
|
_numChars += diff;
|
|
|
|
if (_inBuf) {
|
|
if (_inCurs >= pos + oldlen)
|
|
_inCurs += diff;
|
|
else if (_inCurs >= pos)
|
|
_inCurs = pos + len;
|
|
}
|
|
|
|
touch(0);
|
|
}
|
|
|
|
void TextBufferWindow::touch(int line) {
|
|
_lines[line]._dirty = true;
|
|
g_vm->_selection->clearSelection();
|
|
|
|
int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * _font._leading;
|
|
_windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + _font._leading + 2));
|
|
}
|
|
|
|
uint TextBufferWindow::getSplit(uint size, bool vertical) const {
|
|
return (vertical) ? size * _font._cellW : size * _font._cellH;
|
|
}
|
|
|
|
void TextBufferWindow::putCharUni(uint32 ch) {
|
|
uint bchars[TBLINELEN];
|
|
Attributes battrs[TBLINELEN];
|
|
int pw;
|
|
int bpoint;
|
|
int saved;
|
|
int i;
|
|
int linelen;
|
|
uint color;
|
|
|
|
gli_tts_speak(&ch, 1);
|
|
|
|
pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) * GLI_SUBPIX;
|
|
pw = pw - 2 * SLOP - _radjw - _ladjw;
|
|
|
|
color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
|
|
|
|
// oops ... overflow
|
|
if (_numChars + 1 >= TBLINELEN)
|
|
scrollOneLine(0);
|
|
|
|
if (ch == '\n') {
|
|
scrollOneLine(1);
|
|
return;
|
|
}
|
|
|
|
if (_font._quotes) {
|
|
// fails for 'tis a wonderful day in the '80s
|
|
if (_font._quotes > 1 && ch == '\'') {
|
|
if (_numChars == 0 || leftquote(_chars[_numChars - 1]))
|
|
ch = UNI_LSQUO;
|
|
}
|
|
|
|
if (ch == '`')
|
|
ch = UNI_LSQUO;
|
|
|
|
if (ch == '\'')
|
|
ch = UNI_RSQUO;
|
|
|
|
if (ch == '"') {
|
|
if (_numChars == 0 || leftquote(_chars[_numChars - 1]))
|
|
ch = UNI_LDQUO;
|
|
else
|
|
ch = UNI_RDQUO;
|
|
}
|
|
}
|
|
|
|
if (_font._dashes && _attr.style != style_Preformatted) {
|
|
if (ch == '-') {
|
|
_dashed++;
|
|
if (_dashed == 2) {
|
|
_numChars--;
|
|
if (_font._dashes == 2)
|
|
ch = UNI_NDASH;
|
|
else
|
|
ch = UNI_MDASH;
|
|
}
|
|
if (_dashed == 3) {
|
|
_numChars--;
|
|
ch = UNI_MDASH;
|
|
_dashed = 0;
|
|
}
|
|
} else {
|
|
_dashed = 0;
|
|
}
|
|
}
|
|
|
|
if (_font._spaces && _attr.style != style_Preformatted
|
|
&& _styles[_attr.style].bg == color
|
|
&& !_styles[_attr.style].reverse) {
|
|
// turn (period space space) into (period space)
|
|
if (_font._spaces == 1) {
|
|
if (ch == '.')
|
|
_spaced = 1;
|
|
else if (ch == ' ' && _spaced == 1)
|
|
_spaced = 2;
|
|
else if (ch == ' ' && _spaced == 2) {
|
|
_spaced = 0;
|
|
return;
|
|
} else {
|
|
_spaced = 0;
|
|
}
|
|
}
|
|
|
|
// Turn (per sp x) into (per sp sp x)
|
|
if (_font._spaces == 2) {
|
|
if (ch == '.')
|
|
_spaced = 1;
|
|
else if (ch == ' ' && _spaced == 1)
|
|
_spaced = 2;
|
|
else if (ch != ' ' && _spaced == 2) {
|
|
_spaced = 0;
|
|
putCharUni(' ');
|
|
} else {
|
|
_spaced = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
_chars[_numChars] = ch;
|
|
_attrs[_numChars] = _attr;
|
|
_numChars++;
|
|
|
|
// kill spaces at the end for line width calculation
|
|
linelen = _numChars;
|
|
while (linelen > 1 && _chars[linelen - 1] == ' '
|
|
&& _styles[_attrs[linelen - 1].style].bg == color
|
|
&& !_styles[_attrs[linelen - 1].style].reverse)
|
|
linelen--;
|
|
|
|
if (calcWidth(_chars, _attrs, 0, linelen, -1) >= pw) {
|
|
bpoint = _numChars;
|
|
|
|
for (i = _numChars - 1; i > 0; i--) {
|
|
if (_chars[i] == ' ') {
|
|
bpoint = i + 1; // skip space
|
|
break;
|
|
}
|
|
}
|
|
|
|
saved = _numChars - bpoint;
|
|
|
|
memcpy(bchars, _chars + bpoint, saved * 4);
|
|
memcpy(battrs, _attrs + bpoint, saved * sizeof(Attributes));
|
|
_numChars = bpoint;
|
|
|
|
scrollOneLine(0);
|
|
|
|
memcpy(_chars, bchars, saved * 4);
|
|
memcpy(_attrs, battrs, saved * sizeof(Attributes));
|
|
_numChars = saved;
|
|
}
|
|
|
|
touch(0);
|
|
}
|
|
|
|
bool TextBufferWindow::unputCharUni(uint32 ch) {
|
|
if (_numChars > 0 && _chars[_numChars - 1] == ch) {
|
|
_numChars--;
|
|
touch(0);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void TextBufferWindow::clear() {
|
|
int i;
|
|
|
|
_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;
|
|
|
|
_ladjw = _radjw = 0;
|
|
_ladjn = _radjn = 0;
|
|
|
|
_spaced = 0;
|
|
_dashed = 0;
|
|
|
|
_numChars = 0;
|
|
|
|
for (i = 0; i < _scrollBack; i++) {
|
|
_lines[i]._len = 0;
|
|
|
|
if (_lines[i]._lPic) _lines[i]._lPic->decrement();
|
|
_lines[i]._lPic = nullptr;
|
|
if (_lines[i]._rPic) _lines[i]._rPic->decrement();
|
|
_lines[i]._rPic = nullptr;
|
|
|
|
_lines[i]._lHyper = 0;
|
|
_lines[i]._rHyper = 0;
|
|
_lines[i]._lm = 0;
|
|
_lines[i]._rm = 0;
|
|
_lines[i]._newLine = 0;
|
|
_lines[i]._dirty = true;
|
|
_lines[i]._repaint = false;
|
|
}
|
|
|
|
_lastSeen = 0;
|
|
_scrollPos = 0;
|
|
_scrollMax = 0;
|
|
|
|
for (i = 0; i < _height; i++)
|
|
touch(i);
|
|
}
|
|
|
|
void TextBufferWindow::click(const Point &newPos) {
|
|
int gh = false;
|
|
int gs = false;
|
|
|
|
if (_lineRequest || _charRequest
|
|
|| _lineRequestUni || _charRequestUni
|
|
|| _moreRequest || _scrollRequest)
|
|
_windows->setFocus(this);
|
|
|
|
if (_hyperRequest) {
|
|
uint 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 = 1;
|
|
gh = true;
|
|
}
|
|
}
|
|
|
|
if (newPos.x > _bbox.right - g_conf->_scrollWidth) {
|
|
if (newPos.y < _bbox.top + g_conf->_tMarginY + g_conf->_scrollWidth)
|
|
acceptScroll(keycode_Up);
|
|
else if (newPos.y > _bbox.bottom - g_conf->_tMarginY - g_conf->_scrollWidth)
|
|
acceptScroll(keycode_Down);
|
|
else if (newPos.y < (_bbox.top + _bbox.bottom) / 2)
|
|
acceptScroll(keycode_PageUp);
|
|
else
|
|
acceptScroll(keycode_PageDown);
|
|
gs = true;
|
|
}
|
|
|
|
if (!gh && !gs) {
|
|
g_vm->_copySelect = true;
|
|
g_vm->_selection->startSelection(newPos);
|
|
}
|
|
}
|
|
|
|
void TextBufferWindow::requestLineEvent(char *buf, uint maxlen, uint initlen) {
|
|
if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
|
|
warning("request_line_event: window already has keyboard request");
|
|
return;
|
|
}
|
|
|
|
_lineRequest = true;
|
|
int pw;
|
|
|
|
gli_tts_flush();
|
|
|
|
// because '>' prompt is ugly without extra space
|
|
if (_numChars && _chars[_numChars - 1] == '>')
|
|
putCharUni(' ');
|
|
if (_numChars && _chars[_numChars - 1] == '?')
|
|
putCharUni(' ');
|
|
|
|
// make sure we have some space left for typing...
|
|
pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2) * GLI_SUBPIX;
|
|
pw = pw - 2 * SLOP - _radjw + _ladjw;
|
|
if (calcWidth(_chars, _attrs, 0, _numChars, -1) >= pw * 3 / 4)
|
|
putCharUni('\n');
|
|
|
|
_inBuf = buf;
|
|
_inMax = maxlen;
|
|
_inFence = _numChars;
|
|
_inCurs = _numChars;
|
|
_origAttr = _attr;
|
|
_attr.set(style_Input);
|
|
|
|
_historyPos = _historyPresent;
|
|
|
|
if (initlen) {
|
|
touch(0);
|
|
putText(buf, initlen, _inCurs, 0);
|
|
}
|
|
|
|
// WORKAROUND: Mark bottom line as dirty so caret will be drawn
|
|
_lines[0]._dirty = true;
|
|
|
|
_echoLineInput = _echoLineInputBase;
|
|
|
|
if (_lineTerminatorsBase && _termCt) {
|
|
_lineTerminators = new uint[_termCt + 1];
|
|
|
|
if (_lineTerminators) {
|
|
memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(uint));
|
|
_lineTerminators[_termCt] = 0;
|
|
}
|
|
}
|
|
|
|
if (g_vm->gli_register_arr)
|
|
_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Cn");
|
|
|
|
// Switch focus to the new window
|
|
_windows->inputGuessFocus();
|
|
}
|
|
|
|
void TextBufferWindow::requestLineEventUni(uint32 *buf, uint maxlen, uint initlen) {
|
|
if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
|
|
warning("request_line_event_uni: window already has keyboard request");
|
|
return;
|
|
}
|
|
|
|
int pw;
|
|
|
|
_lineRequestUni = true;
|
|
gli_tts_flush();
|
|
|
|
// because '>' prompt is ugly without extra space
|
|
if (_numChars && _chars[_numChars - 1] == '>')
|
|
putCharUni(' ');
|
|
if (_numChars && _chars[_numChars - 1] == '?')
|
|
putCharUni(' ');
|
|
|
|
// make sure we have some space left for typing...
|
|
pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2) * GLI_SUBPIX;
|
|
pw = pw - 2 * SLOP - _radjw + _ladjw;
|
|
if (calcWidth(_chars, _attrs, 0, _numChars, -1) >= pw * 3 / 4)
|
|
putCharUni('\n');
|
|
|
|
//_lastSeen = 0;
|
|
|
|
_inBuf = buf;
|
|
_inMax = maxlen;
|
|
_inFence = _numChars;
|
|
_inCurs = _numChars;
|
|
_origAttr = _attr;
|
|
_attr.set(style_Input);
|
|
|
|
_historyPos = _historyPresent;
|
|
|
|
if (initlen) {
|
|
touch(0);
|
|
putTextUni(buf, initlen, _inCurs, 0);
|
|
}
|
|
|
|
_echoLineInput = _echoLineInputBase;
|
|
|
|
if (_lineTerminatorsBase && _termCt) {
|
|
_lineTerminators = new uint[_termCt + 1];
|
|
|
|
if (_lineTerminators) {
|
|
memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(uint));
|
|
_lineTerminators[_termCt] = 0;
|
|
}
|
|
}
|
|
|
|
if (g_vm->gli_register_arr)
|
|
_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Iu");
|
|
|
|
// Switch focus to the new window
|
|
_windows->inputGuessFocus();
|
|
}
|
|
|
|
void TextBufferWindow::requestCharEvent() {
|
|
_charRequest = true;
|
|
|
|
// Switch focus to the new window
|
|
_windows->inputGuessFocus();
|
|
}
|
|
|
|
void TextBufferWindow::requestCharEventUni() {
|
|
_charRequestUni = true;
|
|
|
|
// Switch focus to the new window
|
|
_windows->inputGuessFocus();
|
|
}
|
|
|
|
void TextBufferWindow::cancelLineEvent(Event *ev) {
|
|
gidispatch_rock_t inarrayrock;
|
|
int ix;
|
|
int len;
|
|
void *inbuf;
|
|
int inmax;
|
|
bool unicode = _lineRequestUni;
|
|
Event dummyEv;
|
|
|
|
if (!ev)
|
|
ev = &dummyEv;
|
|
|
|
ev->clear();
|
|
|
|
if (!_lineRequest && !_lineRequestUni)
|
|
return;
|
|
|
|
if (!_inBuf)
|
|
return;
|
|
|
|
inbuf = _inBuf;
|
|
inmax = _inMax;
|
|
inarrayrock = _inArrayRock;
|
|
|
|
len = _numChars - _inFence;
|
|
if (_echoStream)
|
|
_echoStream->echoLineUni(_chars + _inFence, len);
|
|
|
|
if (len > inmax)
|
|
len = inmax;
|
|
|
|
if (!unicode) {
|
|
for (ix = 0; ix < len; ix++) {
|
|
uint32 ch = _chars[_inFence + ix];
|
|
if (ch > 0xff)
|
|
ch = '?';
|
|
((char *)inbuf)[ix] = (char)ch;
|
|
}
|
|
} else {
|
|
for (ix = 0; ix < len; ix++)
|
|
((uint *)inbuf)[ix] = _chars[_inFence + ix];
|
|
}
|
|
|
|
_attr = _origAttr;
|
|
|
|
ev->type = evtype_LineInput;
|
|
ev->window = this;
|
|
ev->val1 = len;
|
|
ev->val2 = 0;
|
|
|
|
_lineRequest = false;
|
|
_lineRequestUni = false;
|
|
if (_lineTerminators) {
|
|
delete[] _lineTerminators;
|
|
_lineTerminators = nullptr;
|
|
}
|
|
|
|
_inBuf = nullptr;
|
|
_inMax = 0;
|
|
|
|
if (_echoLineInput) {
|
|
putCharUni('\n');
|
|
} else {
|
|
_numChars = _inFence;
|
|
touch(0);
|
|
}
|
|
|
|
if (g_vm->gli_unregister_arr)
|
|
(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
|
|
}
|
|
|
|
void TextBufferWindow::redraw() {
|
|
int linelen;
|
|
int nsp, spw, pw;
|
|
int x0, y0, x1, y1;
|
|
int x, y, w;
|
|
int a, b;
|
|
uint link;
|
|
int font;
|
|
uint color;
|
|
int i;
|
|
int hx0, hx1, hy0, hy1;
|
|
int selrow, selchar, sx0, sx1, selleft, selright;
|
|
bool selBuf;
|
|
int tx, tsc, tsw, lsc, rsc;
|
|
Screen &screen = *g_vm->_screen;
|
|
|
|
gli_tts_flush();
|
|
|
|
Window::redraw();
|
|
|
|
_lines[0]._len = _numChars;
|
|
sx0 = sx1 = selleft = selright = 0;
|
|
|
|
x0 = (_bbox.left + g_conf->_tMarginX) * GLI_SUBPIX;
|
|
x1 = (_bbox.right - g_conf->_tMarginX - g_conf->_scrollWidth) * GLI_SUBPIX;
|
|
y0 = _bbox.top + g_conf->_tMarginY;
|
|
y1 = _bbox.bottom - g_conf->_tMarginY;
|
|
|
|
pw = x1 - x0 - 2 * GLI_SUBPIX;
|
|
|
|
// check if any part of buffer is selected
|
|
selBuf = g_vm->_selection->checkSelection(Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
|
|
|
|
for (i = _scrollPos + _height - 1; i >= _scrollPos; i--) {
|
|
// top of line
|
|
y = y0 + (_height - (i - _scrollPos) - 1) * _font._leading;
|
|
|
|
// check if part of line is selected
|
|
if (selBuf) {
|
|
selrow = g_vm->_selection->getSelection(Rect(x0 / GLI_SUBPIX, y,
|
|
x1 / GLI_SUBPIX, y + _font._leading), &sx0, &sx1);
|
|
selleft = (sx0 == x0 / GLI_SUBPIX);
|
|
selright = (sx1 == x1 / GLI_SUBPIX);
|
|
} else {
|
|
selrow = false;
|
|
}
|
|
|
|
// mark selected line dirty
|
|
if (selrow)
|
|
_lines[i]._dirty = true;
|
|
|
|
TextBufferRow ln(_lines[i]);
|
|
|
|
// skip if we can
|
|
if (!ln._dirty && !ln._repaint && !Windows::_forceRedraw && _scrollPos == 0)
|
|
continue;
|
|
|
|
// repaint previously selected lines if needed
|
|
if (ln._repaint && !Windows::_forceRedraw)
|
|
_windows->redrawRect(Rect(x0 / GLI_SUBPIX, y,
|
|
x1 / GLI_SUBPIX, y + _font._leading));
|
|
|
|
// keep selected line dirty and flag for repaint
|
|
if (!selrow) {
|
|
_lines[i]._dirty = false;
|
|
_lines[i]._repaint = false;
|
|
} else {
|
|
_lines[i]._repaint = true;
|
|
}
|
|
|
|
// leave bottom line blank for [more] prompt
|
|
if (i == _scrollPos && i > 0)
|
|
continue;
|
|
|
|
linelen = ln._len;
|
|
|
|
// kill spaces at the end unless they're a different color
|
|
color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
|
|
while (i > 0 && linelen > 1 && ln._chars[linelen - 1] == ' '
|
|
&& _styles[ln._attrs[linelen - 1].style].bg == color
|
|
&& !_styles[ln._attrs[linelen - 1].style].reverse)
|
|
linelen --;
|
|
|
|
// kill characters that would overwrite the scroll bar
|
|
while (linelen > 1 && calcWidth(ln._chars, ln._attrs, 0, linelen, -1) >= pw)
|
|
linelen --;
|
|
|
|
/*
|
|
* count spaces and width for justification
|
|
*/
|
|
if (_font._justify && !ln._newLine && i > 0) {
|
|
for (a = 0, nsp = 0; a < linelen; a++)
|
|
if (ln._chars[a] == ' ')
|
|
nsp ++;
|
|
w = calcWidth(ln._chars, ln._attrs, 0, linelen, 0);
|
|
if (nsp)
|
|
spw = (x1 - x0 - ln._lm - ln._rm - 2 * SLOP - w) / nsp;
|
|
else
|
|
spw = 0;
|
|
} else {
|
|
spw = -1;
|
|
}
|
|
|
|
// find and highlight selected characters
|
|
if (selrow && !Windows::_claimSelect) {
|
|
lsc = 0;
|
|
rsc = 0;
|
|
selchar = false;
|
|
// optimized case for all chars selected
|
|
if (selleft && selright) {
|
|
rsc = linelen > 0 ? linelen - 1 : 0;
|
|
selchar = calcWidth(ln._chars, ln._attrs, lsc, rsc, spw) / GLI_SUBPIX;
|
|
} else {
|
|
// optimized case for leftmost char selected
|
|
if (selleft) {
|
|
tsc = linelen > 0 ? linelen - 1 : 0;
|
|
selchar = calcWidth(ln._chars, ln._attrs, lsc, tsc, spw) / GLI_SUBPIX;
|
|
} else {
|
|
// find the substring contained by the selection
|
|
tx = (x0 + SLOP + ln._lm) / GLI_SUBPIX;
|
|
// measure string widths until we find left char
|
|
for (tsc = 0; tsc < linelen; tsc++) {
|
|
tsw = calcWidth(ln._chars, ln._attrs, 0, tsc, spw) / GLI_SUBPIX;
|
|
if (tsw + tx >= sx0 ||
|
|
((tsw + tx + GLI_SUBPIX) >= sx0 && ln._chars[tsc] != ' ')) {
|
|
lsc = tsc;
|
|
selchar = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (selchar) {
|
|
// optimized case for rightmost char selected
|
|
if (selright) {
|
|
rsc = linelen > 0 ? linelen - 1 : 0;
|
|
} else {
|
|
// measure string widths until we find right char
|
|
for (tsc = lsc; tsc < linelen; tsc++) {
|
|
tsw = calcWidth(ln._chars, ln._attrs, lsc, tsc, spw) / GLI_SUBPIX;
|
|
if (tsw + sx0 < sx1)
|
|
rsc = tsc;
|
|
}
|
|
if (lsc && !rsc)
|
|
rsc = lsc;
|
|
}
|
|
}
|
|
}
|
|
// reverse colors for selected chars
|
|
if (selchar) {
|
|
for (tsc = lsc; tsc <= rsc; tsc++) {
|
|
ln._attrs[tsc].reverse = !ln._attrs[tsc].reverse;
|
|
_copyBuf[_copyPos] = ln._chars[tsc];
|
|
_copyPos++;
|
|
}
|
|
}
|
|
// add newline if we reach the end of the line
|
|
if (ln._len == 0 || ln._len == (rsc + 1)) {
|
|
_copyBuf[_copyPos] = '\n';
|
|
_copyPos++;
|
|
}
|
|
}
|
|
|
|
// clear any stored hyperlink coordinates
|
|
g_vm->_selection->putHyperlink(0, x0 / GLI_SUBPIX, y,
|
|
x1 / GLI_SUBPIX, y + _font._leading);
|
|
|
|
/*
|
|
* fill in background colors
|
|
*/
|
|
color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
|
|
screen.fillRect(Rect::fromXYWH(x0 / GLI_SUBPIX, y, (x1 - x0) / GLI_SUBPIX, _font._leading),
|
|
color);
|
|
|
|
x = x0 + SLOP + ln._lm;
|
|
a = 0;
|
|
for (b = 0; b < linelen; b++) {
|
|
if (ln._attrs[a] != ln._attrs[b]) {
|
|
link = ln._attrs[a].hyper;
|
|
font = ln._attrs[a].attrFont(_styles);
|
|
color = ln._attrs[a].attrBg(_styles);
|
|
w = screen.stringWidthUni(font, Common::U32String(ln._chars + a, b - a), spw);
|
|
screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, w / GLI_SUBPIX, _font._leading),
|
|
color);
|
|
if (link) {
|
|
screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + _font._baseLine + 1,
|
|
w / GLI_SUBPIX + 1, _font._linkStyle), _font._linkColor);
|
|
g_vm->_selection->putHyperlink(link, x / GLI_SUBPIX, y,
|
|
x / GLI_SUBPIX + w / GLI_SUBPIX,
|
|
y + _font._leading);
|
|
}
|
|
x += w;
|
|
a = b;
|
|
}
|
|
}
|
|
link = ln._attrs[a].hyper;
|
|
font = ln._attrs[a].attrFont(_styles);
|
|
color = ln._attrs[a].attrBg(_styles);
|
|
w = screen.stringWidthUni(font, Common::U32String(ln._chars + a, b - a), spw);
|
|
screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, w / GLI_SUBPIX, _font._leading), color);
|
|
if (link) {
|
|
screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + _font._baseLine + 1,
|
|
w / GLI_SUBPIX + 1, _font._linkStyle), _font._linkColor);
|
|
g_vm->_selection->putHyperlink(link, x / GLI_SUBPIX, y,
|
|
x / GLI_SUBPIX + w / GLI_SUBPIX,
|
|
y + _font._leading);
|
|
}
|
|
x += w;
|
|
|
|
color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
|
|
screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, x1 / GLI_SUBPIX - x / GLI_SUBPIX, _font._leading), color);
|
|
|
|
/*
|
|
* draw caret
|
|
*/
|
|
|
|
if (_windows->getFocusWindow() == this && i == 0 && (_lineRequest || _lineRequestUni)) {
|
|
w = calcWidth(_chars, _attrs, 0, _inCurs, spw);
|
|
if (w < pw - _font._caretShape * 2 * GLI_SUBPIX)
|
|
_font.drawCaret(Point(x0 + SLOP + ln._lm + w, y + _font._baseLine));
|
|
}
|
|
|
|
/*
|
|
* draw text
|
|
*/
|
|
|
|
x = x0 + SLOP + ln._lm;
|
|
a = 0;
|
|
for (b = 0; b < linelen; b++) {
|
|
if (ln._attrs[a] != ln._attrs[b]) {
|
|
link = ln._attrs[a].hyper;
|
|
font = ln._attrs[a].attrFont(_styles);
|
|
color = link ? _font._linkColor : ln._attrs[a].attrFg(_styles);
|
|
x = screen.drawStringUni(Point(x, y + _font._baseLine),
|
|
font, color, Common::U32String(ln._chars + a, b - a), spw);
|
|
a = b;
|
|
}
|
|
}
|
|
link = ln._attrs[a].hyper;
|
|
font = ln._attrs[a].attrFont(_styles);
|
|
color = link ? _font._linkColor : ln._attrs[a].attrFg(_styles);
|
|
screen.drawStringUni(Point(x, y + _font._baseLine), font, color, Common::U32String(ln._chars + a, linelen - a), spw);
|
|
}
|
|
|
|
/*
|
|
* draw more prompt
|
|
*/
|
|
if (_scrollPos && _height > 1) {
|
|
x = x0 + SLOP;
|
|
y = y0 + (_height - 1) * _font._leading;
|
|
|
|
g_vm->_selection->putHyperlink(0, x0 / GLI_SUBPIX, y,
|
|
x1 / GLI_SUBPIX, y + _font._leading);
|
|
|
|
color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
|
|
screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, x1 / GLI_SUBPIX - x / GLI_SUBPIX, _font._leading), color);
|
|
|
|
w = screen.stringWidth(_font._moreFont, _font._morePrompt);
|
|
|
|
if (_font._moreAlign == 1)
|
|
// center
|
|
x = x0 + SLOP + (x1 - x0 - w - SLOP * 2) / 2;
|
|
if (_font._moreAlign == 2)
|
|
// right
|
|
x = x1 - SLOP - w;
|
|
|
|
color = Windows::_overrideFgSet ? _font._moreColor : _fgColor;
|
|
screen.drawString(Point(x, y + _font._baseLine),
|
|
_font._moreFont, color, _font._morePrompt);
|
|
y1 = y; // don't want pictures overdrawing "[more]"
|
|
|
|
// try to claim the focus
|
|
_moreRequest = true;
|
|
Windows::_moreFocus = true;
|
|
} else {
|
|
_moreRequest = false;
|
|
y1 = y0 + _height * _font._leading;
|
|
}
|
|
|
|
/*
|
|
* draw the images
|
|
*/
|
|
for (i = 0; i < _scrollBack; i++) {
|
|
TextBufferRow ln(_lines[i]);
|
|
|
|
y = y0 + (_height - (i - _scrollPos) - 1) * _font._leading;
|
|
|
|
if (ln._lPic) {
|
|
if (y < y1 && y + ln._lPic->h > y0) {
|
|
ln._lPic->drawPicture(Point(x0 / GLI_SUBPIX, y),
|
|
Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
|
|
link = ln._lHyper;
|
|
hy0 = y > y0 ? y : y0;
|
|
hy1 = y + ln._lPic->h < y1 ? y + ln._lPic->h : y1;
|
|
hx0 = x0 / GLI_SUBPIX;
|
|
hx1 = x0 / GLI_SUBPIX + ln._lPic->w < x1 / GLI_SUBPIX
|
|
? x0 / GLI_SUBPIX + ln._lPic->w
|
|
: x1 / GLI_SUBPIX;
|
|
g_vm->_selection->putHyperlink(link, hx0, hy0, hx1, hy1);
|
|
}
|
|
}
|
|
|
|
if (ln._rPic) {
|
|
if (y < y1 && y + ln._rPic->h > y0) {
|
|
ln._rPic->drawPicture(Point(x1 / GLI_SUBPIX - ln._rPic->w, y),
|
|
Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
|
|
link = ln._rHyper;
|
|
hy0 = y > y0 ? y : y0;
|
|
hy1 = y + ln._rPic->h < y1 ? y + ln._rPic->h : y1;
|
|
hx0 = x1 / GLI_SUBPIX - ln._rPic->w > x0 / GLI_SUBPIX
|
|
? x1 / GLI_SUBPIX - ln._rPic->w
|
|
: x0 / GLI_SUBPIX;
|
|
hx1 = x1 / GLI_SUBPIX;
|
|
g_vm->_selection->putHyperlink(link, hx0, hy0, hx1, hy1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Draw the scrollbar
|
|
*/
|
|
|
|
// try to claim scroll keys
|
|
_scrollRequest = _scrollMax > _height;
|
|
|
|
if (_scrollRequest && g_conf->_scrollWidth) {
|
|
int t0, t1;
|
|
x0 = _bbox.right - g_conf->_scrollWidth;
|
|
x1 = _bbox.right;
|
|
y0 = _bbox.top + g_conf->_tMarginY;
|
|
y1 = _bbox.bottom - g_conf->_tMarginY;
|
|
|
|
g_vm->_selection->putHyperlink(0, x0, y0, x1, y1);
|
|
|
|
y0 += g_conf->_scrollWidth / 2;
|
|
y1 -= g_conf->_scrollWidth / 2;
|
|
|
|
// pos = thbot, pos - ht = thtop, max = wtop, 0 = wbot
|
|
t0 = (_scrollMax - _scrollPos) - (_height - 1);
|
|
t1 = (_scrollMax - _scrollPos);
|
|
if (_scrollMax > _height) {
|
|
t0 = t0 * (y1 - y0) / _scrollMax + y0;
|
|
t1 = t1 * (y1 - y0) / _scrollMax + y0;
|
|
} else {
|
|
t0 = t1 = y0;
|
|
}
|
|
|
|
screen.fillRect(Rect::fromXYWH(x0 + 1, y0, x1 - x0 - 2, y1 - y0), g_conf->_scrollBg);
|
|
screen.fillRect(Rect::fromXYWH(x0 + 1, t0, x1 - x0 - 2, t1 - t0), g_conf->_scrollFg);
|
|
|
|
for (i = 0; i < g_conf->_scrollWidth / 2 + 1; i++) {
|
|
screen.fillRect(Rect::fromXYWH(x0 + g_conf->_scrollWidth / 2 - i,
|
|
y0 - g_conf->_scrollWidth / 2 + i, i * 2, 1), g_conf->_scrollFg);
|
|
screen.fillRect(Rect::fromXYWH(x0 + g_conf->_scrollWidth / 2 - i,
|
|
y1 + g_conf->_scrollWidth / 2 - i, i * 2, 1), g_conf->_scrollFg);
|
|
}
|
|
}
|
|
|
|
// Keep track of selected text to be ready when user copies it to the clipboard
|
|
if (selBuf && _copyPos) {
|
|
Windows::_claimSelect = true;
|
|
|
|
g_vm->_clipboard->clipboardStore(Common::U32String(_copyBuf, _copyPos));
|
|
for (i = 0; i < _copyPos; i++)
|
|
_copyBuf[i] = 0;
|
|
_copyPos = 0;
|
|
}
|
|
|
|
// no more prompt means all text has been seen
|
|
if (!_moreRequest)
|
|
_lastSeen = 0;
|
|
}
|
|
|
|
int TextBufferWindow::acceptScroll(uint arg) {
|
|
int pageht = _height - 2; // 1 for prompt, 1 for overlap
|
|
int startpos = _scrollPos;
|
|
|
|
switch (arg) {
|
|
case keycode_PageUp:
|
|
_scrollPos += pageht;
|
|
break;
|
|
case keycode_End:
|
|
_scrollPos = 0;
|
|
break;
|
|
case keycode_Up:
|
|
_scrollPos++;
|
|
break;
|
|
case keycode_Down:
|
|
case keycode_Return:
|
|
_scrollPos--;
|
|
break;
|
|
case keycode_MouseWheelUp:
|
|
_scrollPos += 3;
|
|
startpos = true;
|
|
break;
|
|
case keycode_MouseWheelDown:
|
|
_scrollPos -= 3;
|
|
startpos = true;
|
|
break;
|
|
case ' ':
|
|
case keycode_PageDown:
|
|
//default:
|
|
if (pageht)
|
|
_scrollPos -= pageht;
|
|
else
|
|
_scrollPos = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (_scrollPos > _scrollMax - _height + 1)
|
|
_scrollPos = _scrollMax - _height + 1;
|
|
if (_scrollPos < 0)
|
|
_scrollPos = 0;
|
|
touchScroll();
|
|
|
|
return (startpos || _scrollPos);
|
|
}
|
|
|
|
void TextBufferWindow::acceptReadChar(uint arg) {
|
|
uint key;
|
|
|
|
if (_height < 2)
|
|
_scrollPos = 0;
|
|
|
|
if (_scrollPos
|
|
|| arg == keycode_PageUp
|
|
|| arg == keycode_MouseWheelUp) {
|
|
acceptScroll(arg);
|
|
return;
|
|
}
|
|
|
|
switch (arg) {
|
|
case keycode_Erase:
|
|
key = keycode_Delete;
|
|
break;
|
|
case keycode_MouseWheelUp:
|
|
case keycode_MouseWheelDown:
|
|
return;
|
|
default:
|
|
key = arg;
|
|
break;
|
|
}
|
|
|
|
gli_tts_purge();
|
|
|
|
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 TextBufferWindow::acceptReadLine(uint32 arg) {
|
|
uint *cx;
|
|
Common::U32String s;
|
|
int len;
|
|
|
|
if (_height < 2)
|
|
_scrollPos = 0;
|
|
|
|
if (!_inBuf)
|
|
return;
|
|
|
|
if (_lineTerminators && checkTerminators(arg)) {
|
|
for (cx = _lineTerminators; *cx; cx++) {
|
|
if (*cx == arg) {
|
|
acceptLine(arg);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_scrollPos || arg == keycode_PageUp || arg == keycode_MouseWheelUp) {
|
|
acceptScroll(arg);
|
|
return;
|
|
}
|
|
|
|
switch (arg) {
|
|
// History keys (up and down)
|
|
case keycode_Up:
|
|
if (_historyPos == _historyFirst)
|
|
return;
|
|
if (_historyPos == _historyPresent) {
|
|
len = _numChars - _inFence;
|
|
|
|
if (len > 0)
|
|
s = Common::U32String(&(_chars[_inFence]), len);
|
|
_history[_historyPos] = s;
|
|
}
|
|
|
|
_historyPos--;
|
|
if (_historyPos < 0)
|
|
_historyPos += HISTORYLEN;
|
|
s = _history[_historyPos];
|
|
putTextUni(s.u32_str(), s.size(), _inFence, _numChars - _inFence);
|
|
break;
|
|
|
|
case keycode_Down:
|
|
if (_historyPos == _historyPresent)
|
|
return;
|
|
_historyPos++;
|
|
if (_historyPos >= HISTORYLEN)
|
|
_historyPos -= HISTORYLEN;
|
|
s = _history[_historyPos];
|
|
putTextUni(s.u32_str(), s.size(), _inFence, _numChars - _inFence);
|
|
break;
|
|
|
|
// Cursor movement keys, during line input.
|
|
case keycode_Left:
|
|
if (_inCurs <= _inFence)
|
|
return;
|
|
_inCurs--;
|
|
break;
|
|
|
|
case keycode_Right:
|
|
if (_inCurs >= _numChars)
|
|
return;
|
|
_inCurs++;
|
|
break;
|
|
|
|
case keycode_Home:
|
|
if (_inCurs <= _inFence)
|
|
return;
|
|
_inCurs = _inFence;
|
|
break;
|
|
|
|
case keycode_End:
|
|
if (_inCurs >= _numChars)
|
|
return;
|
|
_inCurs = _numChars;
|
|
break;
|
|
|
|
case keycode_SkipWordLeft:
|
|
while (_inCurs > _inFence && _chars[_inCurs - 1] == ' ')
|
|
_inCurs--;
|
|
while (_inCurs > _inFence && _chars[_inCurs - 1] != ' ')
|
|
_inCurs--;
|
|
break;
|
|
|
|
case keycode_SkipWordRight:
|
|
while (_inCurs < _numChars && _chars[_inCurs] != ' ')
|
|
_inCurs++;
|
|
while (_inCurs < _numChars && _chars[_inCurs] == ' ')
|
|
_inCurs++;
|
|
break;
|
|
|
|
// Delete keys, during line input.
|
|
case keycode_Delete:
|
|
if (_inCurs <= _inFence)
|
|
return;
|
|
putTextUni(nullptr, 0, _inCurs - 1, 1);
|
|
break;
|
|
|
|
case keycode_Erase:
|
|
if (_inCurs >= _numChars)
|
|
return;
|
|
putTextUni(nullptr, 0, _inCurs, 1);
|
|
break;
|
|
|
|
case keycode_Escape:
|
|
if (_inFence >= _numChars)
|
|
return;
|
|
putTextUni(nullptr, 0, _inFence, _numChars - _inFence);
|
|
break;
|
|
|
|
// Regular keys
|
|
case keycode_Return:
|
|
acceptLine(arg);
|
|
break;
|
|
|
|
default:
|
|
if (arg >= 32 && arg <= 0x10FFFF) {
|
|
if (_font._caps && (arg > 0x60 && arg < 0x7b))
|
|
arg -= 0x20;
|
|
putTextUni(&arg, 1, _inCurs, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
touch(0);
|
|
}
|
|
|
|
void TextBufferWindow::acceptLine(uint32 keycode) {
|
|
int ix;
|
|
int len, olen;
|
|
void *inbuf;
|
|
Common::U32String s, o;
|
|
int inmax;
|
|
gidispatch_rock_t inarrayrock;
|
|
bool unicode = _lineRequestUni;
|
|
|
|
if (!_inBuf)
|
|
return;
|
|
|
|
inbuf = _inBuf;
|
|
inmax = _inMax;
|
|
inarrayrock = _inArrayRock;
|
|
|
|
len = _numChars - _inFence;
|
|
if (_echoStream)
|
|
_echoStream->echoLineUni(_chars + _inFence, len);
|
|
|
|
gli_tts_purge();
|
|
if (g_conf->_speakInput) {
|
|
const uint32 NEWLINE = '\n';
|
|
gli_tts_speak(_chars + _inFence, len);
|
|
gli_tts_speak((const uint32 *)&NEWLINE, 1);
|
|
}
|
|
|
|
/*
|
|
* Store in history.
|
|
* The history is a ring buffer, with historypresent being the index of the most recent
|
|
* element and historyfirst the index of the oldest element.
|
|
* A history entry should not repeat the string from the entry before it.
|
|
*/
|
|
if (len) {
|
|
s = Common::U32String(_chars + _inFence, len);
|
|
_history[_historyPresent].clear();
|
|
|
|
o = _history[(_historyPresent == 0 ? HISTORYLEN : _historyPresent) - 1];
|
|
olen = o.size();
|
|
|
|
if (len != olen || !s.equals(o)) {
|
|
_history[_historyPresent] = s;
|
|
|
|
_historyPresent++;
|
|
if (_historyPresent == HISTORYLEN)
|
|
_historyPresent = 0;
|
|
|
|
if (_historyPresent == _historyFirst) {
|
|
_historyFirst++;
|
|
if (_historyFirst == HISTORYLEN)
|
|
_historyFirst = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store in event buffer.
|
|
if (len > inmax)
|
|
len = inmax;
|
|
|
|
if (!unicode) {
|
|
for (ix = 0; ix < len; ix++) {
|
|
uint32 ch = _chars[_inFence + ix];
|
|
if (ch > 0xff)
|
|
ch = '?';
|
|
((char *)inbuf)[ix] = (char)ch;
|
|
}
|
|
} else {
|
|
for (ix = 0; ix < len; ix++)
|
|
((uint *)inbuf)[ix] = _chars[_inFence + ix];
|
|
}
|
|
|
|
_attr = _origAttr;
|
|
|
|
if (_lineTerminators) {
|
|
if (keycode == keycode_Return)
|
|
keycode = 0;
|
|
else
|
|
// TODO: Currently particularly for Beyond Zork, we don't echo a newline
|
|
// for line terminators, allowing description area scrolling to not keep adding
|
|
// blank lines in the command area. In the future I may need to make it configurable
|
|
// when I see if any other line terminators need to have a newline
|
|
_echoLineInput = false;
|
|
|
|
g_vm->_events->store(evtype_LineInput, this, len, keycode);
|
|
delete[] _lineTerminators;
|
|
_lineTerminators = nullptr;
|
|
} else {
|
|
g_vm->_events->store(evtype_LineInput, this, len, 0);
|
|
}
|
|
|
|
_lineRequest = false;
|
|
_lineRequestUni = false;
|
|
_inBuf = nullptr;
|
|
_inMax = 0;
|
|
|
|
if (_echoLineInput) {
|
|
putCharUni('\n');
|
|
} else {
|
|
_numChars = _inFence;
|
|
touch(0);
|
|
}
|
|
|
|
if (g_vm->gli_unregister_arr)
|
|
(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
|
|
}
|
|
|
|
bool TextBufferWindow::leftquote(uint32 c) {
|
|
switch (c) {
|
|
case '(':
|
|
case '[':
|
|
|
|
// The following are Unicode characters in the "Separator, Space" category.
|
|
case 0x0020:
|
|
case 0x00a0:
|
|
case 0x1680:
|
|
case 0x2000:
|
|
case 0x2001:
|
|
case 0x2002:
|
|
case 0x2003:
|
|
case 0x2004:
|
|
case 0x2005:
|
|
case 0x2006:
|
|
case 0x2007:
|
|
case 0x2008:
|
|
case 0x2009:
|
|
case 0x200a:
|
|
case 0x202f:
|
|
case 0x205f:
|
|
case 0x3000:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void TextBufferWindow::scrollOneLine(bool forced) {
|
|
_lastSeen++;
|
|
_scrollMax++;
|
|
|
|
if (_scrollMax > _scrollBack - 1
|
|
|| _lastSeen > _scrollBack - 1)
|
|
scrollResize();
|
|
|
|
if (_lastSeen >= _height)
|
|
_scrollPos++;
|
|
|
|
if (_scrollPos > _scrollMax - _height + 1)
|
|
_scrollPos = _scrollMax - _height + 1;
|
|
if (_scrollPos < 0)
|
|
_scrollPos = 0;
|
|
|
|
if (forced)
|
|
_dashed = 0;
|
|
_spaced = 0;
|
|
|
|
_lines[0]._len = _numChars;
|
|
_lines[0]._newLine = forced;
|
|
|
|
for (int i = _scrollBack - 1; i > 0; i--) {
|
|
memcpy(&_lines[i], &_lines[i - 1], sizeof(TextBufferRow));
|
|
if (i < _height)
|
|
touch(i);
|
|
}
|
|
|
|
if (_radjn)
|
|
_radjn--;
|
|
if (_radjn == 0)
|
|
_radjw = 0;
|
|
if (_ladjn)
|
|
_ladjn--;
|
|
if (_ladjn == 0)
|
|
_ladjw = 0;
|
|
|
|
touch(0);
|
|
_lines[0]._len = 0;
|
|
_lines[0]._newLine = 0;
|
|
_lines[0]._lm = _ladjw;
|
|
_lines[0]._rm = _radjw;
|
|
_lines[0]._lPic = nullptr;
|
|
_lines[0]._rPic = nullptr;
|
|
_lines[0]._lHyper = 0;
|
|
_lines[0]._rHyper = 0;
|
|
|
|
Common::fill(_chars, _chars + TBLINELEN, ' ');
|
|
Attributes *a = _attrs;
|
|
for (int i = 0; i < TBLINELEN; ++i, ++a)
|
|
a->clear();
|
|
|
|
_numChars = 0;
|
|
|
|
touchScroll();
|
|
|
|
}
|
|
|
|
void TextBufferWindow::scrollResize() {
|
|
int i;
|
|
|
|
_lines.clear();
|
|
_lines.resize(_scrollBack + SCROLLBACK);
|
|
|
|
_chars = _lines[0]._chars;
|
|
_attrs = _lines[0]._attrs;
|
|
|
|
for (i = _scrollBack; i < (_scrollBack + SCROLLBACK); i++) {
|
|
_lines[i]._dirty = false;
|
|
_lines[i]._repaint = false;
|
|
_lines[i]._lm = 0;
|
|
_lines[i]._rm = 0;
|
|
_lines[i]._lPic = 0;
|
|
_lines[i]._rPic = 0;
|
|
_lines[i]._lHyper = 0;
|
|
_lines[i]._rHyper = 0;
|
|
_lines[i]._len = 0;
|
|
_lines[i]._newLine = 0;
|
|
*(_lines[i]._chars) = 0;
|
|
_lines[i]._attrs->clear();
|
|
}
|
|
|
|
_scrollBack += SCROLLBACK;
|
|
}
|
|
|
|
int TextBufferWindow::calcWidth(const uint32 *chars, const Attributes *attrs, int startchar, int numChars, int spw) {
|
|
Screen &screen = *g_vm->_screen;
|
|
int w = 0;
|
|
int a, b;
|
|
|
|
a = startchar;
|
|
for (b = startchar; b < numChars; b++) {
|
|
if (attrs[a] != attrs[b]) {
|
|
w += screen.stringWidthUni(attrs[a].attrFont(_styles),
|
|
Common::U32String(chars + a, b - a), spw);
|
|
a = b;
|
|
}
|
|
}
|
|
|
|
w += screen.stringWidthUni(attrs[a].attrFont(_styles), Common::U32String(chars + a, b - a), spw);
|
|
|
|
return w;
|
|
}
|
|
|
|
void TextBufferWindow::getSize(uint *width, uint *height) const {
|
|
if (width)
|
|
*width = (_bbox.width() - g_conf->_tMarginX * 2) / _font._cellW;
|
|
if (height)
|
|
*height = (_bbox.height() - g_conf->_tMarginY * 2) / _font._cellH;
|
|
}
|
|
|
|
void TextBufferWindow::flowBreak() {
|
|
while (_ladjn || _radjn)
|
|
putCharUni('\n');
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
TextBufferWindow::TextBufferRow::TextBufferRow() : _len(0), _newLine(0), _dirty(false),
|
|
_repaint(false), _lPic(nullptr), _rPic(nullptr), _lHyper(0), _rHyper(0),
|
|
_lm(0), _rm(0) {
|
|
Common::fill(&_chars[0], &_chars[TBLINELEN], 0);
|
|
}
|
|
|
|
} // End of namespace Glk
|