592 lines
13 KiB
C++
592 lines
13 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 "gargoyle/window_text_buffer.h"
|
||
|
#include "gargoyle/conf.h"
|
||
|
#include "gargoyle/gargoyle.h"
|
||
|
|
||
|
namespace Gargoyle {
|
||
|
|
||
|
TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windows, rock),
|
||
|
_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;
|
||
|
Common::fill(&_history[0], &_history[HISTORYLEN], nullptr);
|
||
|
|
||
|
Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], styles);
|
||
|
}
|
||
|
|
||
|
TextBufferWindow::~TextBufferWindow() {
|
||
|
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 Common::Rect &box) {
|
||
|
Window::rearrange(box);
|
||
|
int newwid, newhgt;
|
||
|
int rnd;
|
||
|
|
||
|
newwid = (box.width() - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) / g_conf->_cellW;
|
||
|
newhgt = (box.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
|
||
|
|
||
|
/* align text with bottom */
|
||
|
rnd = newhgt * g_conf->_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 glui32[_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];
|
||
|
glui32 *charbuf = new glui32[SCROLLBACK * TBLINELEN];
|
||
|
int *alignbuf = new int[SCROLLBACK];
|
||
|
Picture **pictbuf = new Picture *[SCROLLBACK];
|
||
|
glui32 *hyperbuf = new glui32[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].attr[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() {
|
||
|
_windows->clearSelection();
|
||
|
_windows->repaint(_bbox);
|
||
|
|
||
|
for (int i = 0; i < _scrollMax; i++)
|
||
|
_lines[i].dirty = true;
|
||
|
}
|
||
|
|
||
|
bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
|
||
|
if (align == imagealign_MarginRight)
|
||
|
{
|
||
|
if (_lines[0].rpic || _numChars)
|
||
|
return false;
|
||
|
|
||
|
_radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
|
||
|
_radjn = (pic->h + g_conf->_cellH - 1) / g_conf->_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 + g_conf->_cellH - 1) / g_conf->_cellH;
|
||
|
_lines[0].lpic = pic;
|
||
|
_lines[0].lm = _ladjw;
|
||
|
_lines[0].lhyper = linkval;
|
||
|
|
||
|
if (align != imagealign_MarginLeft)
|
||
|
flowBreak();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void TextBufferWindow::flowBreak() {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldlen) {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void TextBufferWindow::touch(int line) {
|
||
|
int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
|
||
|
_lines[line].dirty = 1;
|
||
|
_windows->clearSelection();
|
||
|
_windows->repaint(Common::Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
|
||
|
}
|
||
|
|
||
|
glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
|
||
|
return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
|
||
|
}
|
||
|
|
||
|
void TextBufferWindow::putCharUni(glui32 ch) {
|
||
|
/*
|
||
|
glui32 bchars[TBLINELEN];
|
||
|
Attributes battrs[TBLINELEN];
|
||
|
int pw;
|
||
|
int bpoint;
|
||
|
int saved;
|
||
|
int i;
|
||
|
int linelen;
|
||
|
unsigned char *color;
|
||
|
|
||
|
gli_tts_speak(&ch, 1);
|
||
|
|
||
|
pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - gli_scroll_width) * GLI_SUBPIX;
|
||
|
pw = pw - 2 * SLOP - radjw - ladjw;
|
||
|
|
||
|
color = Windows::_overrideBgSet ? gli_window_color : bgcolor;
|
||
|
|
||
|
// oops ... overflow
|
||
|
if (numchars + 1 >= TBLINELEN)
|
||
|
scrolloneline(dwin, 0);
|
||
|
|
||
|
if (ch == '\n') {
|
||
|
scrolloneline(dwin, 1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (gli_conf_quotes) {
|
||
|
// fails for 'tis a wonderful day in the '80s
|
||
|
if (gli_conf_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 (gli_conf_dashes && attr.style != style_Preformatted)
|
||
|
{
|
||
|
if (ch == '-')
|
||
|
{
|
||
|
dashed++;
|
||
|
if (dashed == 2)
|
||
|
{
|
||
|
numchars--;
|
||
|
if (gli_conf_dashes == 2)
|
||
|
ch = UNI_NDASH;
|
||
|
else
|
||
|
ch = UNI_MDASH;
|
||
|
}
|
||
|
if (dashed == 3)
|
||
|
{
|
||
|
numchars--;
|
||
|
ch = UNI_MDASH;
|
||
|
dashed = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
dashed = 0;
|
||
|
}
|
||
|
|
||
|
if (gli_conf_spaces && attr.style != style_Preformatted
|
||
|
&& styles[attr.style].bg == color
|
||
|
&& !styles[attr.style].reverse)
|
||
|
{
|
||
|
// turn (period space space) into (period space)
|
||
|
if (gli_conf_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 (gli_conf_spaces == 2)
|
||
|
{
|
||
|
if (ch == '.')
|
||
|
spaced = 1;
|
||
|
else if (ch == ' ' && spaced == 1)
|
||
|
spaced = 2;
|
||
|
else if (ch != ' ' && spaced == 2)
|
||
|
{
|
||
|
spaced = 0;
|
||
|
win_textbuffer_putchar_uni(win, ' ');
|
||
|
}
|
||
|
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(dwin, _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(attr_t));
|
||
|
numchars = bpoint;
|
||
|
|
||
|
scrolloneline(dwin, 0);
|
||
|
|
||
|
memcpy(_chars, bchars, saved * 4);
|
||
|
memcpy(attrs, battrs, saved * sizeof(attr_t));
|
||
|
numchars = saved;
|
||
|
}
|
||
|
|
||
|
touch(0);
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
bool TextBufferWindow::unputCharUni(uint32 ch) {
|
||
|
// TODO
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void TextBufferWindow::moveCursor(const Common::Point &newPos) {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
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::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
|
||
|
if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
|
||
|
{
|
||
|
warning("request_line_event: window already has keyboard request");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
|
||
|
if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
|
||
|
{
|
||
|
warning("request_line_event_uni: window already has keyboard request");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void TextBufferWindow::cancelLineEvent(Event *ev) {
|
||
|
gidispatch_rock_t inarrayrock;
|
||
|
int ix;
|
||
|
int len;
|
||
|
void *inbuf;
|
||
|
int inmax;
|
||
|
int unicode = _lineRequestUni;
|
||
|
Event dummyEv;
|
||
|
|
||
|
if (!ev)
|
||
|
ev = &dummyEv;
|
||
|
|
||
|
g_vm->_events->clearEvent(ev);
|
||
|
|
||
|
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++) {
|
||
|
glui32 ch = _chars[_inFence + ix];
|
||
|
if (ch > 0xff)
|
||
|
ch = '?';
|
||
|
((char *)inbuf)[ix] = (char)ch;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for (ix = 0; ix<len; ix++)
|
||
|
((glui32 *)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) {
|
||
|
free(_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() {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
|
||
|
TextBufferWindow::TextBufferRow::TextBufferRow() : len(0), newline(0), dirty(false), repaint(false),
|
||
|
lpic(nullptr), rpic(nullptr), lhyper(0), rhyper(0), lm(0), rm(0) {
|
||
|
}
|
||
|
|
||
|
void TextBufferWindow::TextBufferRow::resize(size_t newSize) {
|
||
|
chars.clear();
|
||
|
attr.clear();
|
||
|
chars.resize(newSize);
|
||
|
attr.resize(newSize);
|
||
|
Common::fill(&chars[0], &chars[0] + newSize, ' ');
|
||
|
}
|
||
|
|
||
|
} // End of namespace Gargoyle
|