From 1fb931fbd950324754536ee0b33ed0b91f68c9a2 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Tue, 13 Nov 2018 19:47:07 -0800 Subject: GLK: Changing gargoyle folder to glk --- engines/glk/window_text_buffer.cpp | 1636 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1636 insertions(+) create mode 100644 engines/glk/window_text_buffer.cpp (limited to 'engines/glk/window_text_buffer.cpp') diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp new file mode 100644 index 0000000000..1506f4e163 --- /dev/null +++ b/engines/glk/window_text_buffer.cpp @@ -0,0 +1,1636 @@ +/* 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/gargoyle.h" +#include "glk/screen.h" +#include "glk/selection.h" +#include "glk/unicode.h" + +namespace Gargoyle { + +/** + * + * How many pixels we add to left/right margins + */ +#define SLOP (2 * GLI_SUBPIX) + + +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], (glui32 *)nullptr); + + _lines.resize(SCROLLBACK); + _chars = _lines[0]._chars; + _attrs = _lines[0]._attrs; + + 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 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]._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, 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; +} + +glui32 TextBufferWindow::drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height) { + Picture *pic; + glui32 hyperlink; + int error; + + pic = Picture::load(image); + + if (!pic) + return false; + + if (!_imageLoaded) { + g_vm->_picList->increment(); + _imageLoaded = true; + } + + if (scaled) { + Picture *tmp; + tmp = pic->scale(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 glui32 *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) * g_conf->_leading; + _windows->repaint(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; + byte *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 (g_conf->_quotes) { + // fails for 'tis a wonderful day in the '80s + if (g_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 (g_conf->_dashes && _attr.style != style_Preformatted) { + if (ch == '-') { + _dashed++; + if (_dashed == 2) { + _numChars--; + if (g_conf->_dashes == 2) + ch = UNI_NDASH; + else + ch = UNI_MDASH; + } + if (_dashed == 3) { + _numChars--; + ch = UNI_MDASH; + _dashed = 0; + } + } else { + _dashed = 0; + } + } + + if (g_conf->_spaces && _attr.style != style_Preformatted + && _styles[_attr.style].bg == color + && !_styles[_attr.style].reverse) { + // turn (period space space) into (period space) + if (g_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 (g_conf->_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) { + 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 = 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, glui32 maxlen, glui32 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 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 TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) { + if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) { + warning("request_line_event_uni: window already has keyboard request"); + return; + } + + 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'); + + //_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); + } + + // WORKAROUND: Mark bottom line as dirty so caret will be drawn + _lines[0]._dirty = true; + + _echoLineInput = _echoLineInputBase; + + 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 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; + + 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++) { + 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() { + TextBufferRow *ln; + int linelen; + int nsp, spw, pw; + int x0, y0, x1, y1; + int x, y, w; + int a, b; + glui32 link; + int font; + unsigned char *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; + + Window::redraw(); + + _lines[0]._len = _numChars; + sx0 = sx1 = selleft = selright = 0; + + ln = new TextBufferRow(); + if (!ln) + return; + + 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) * g_conf->_leading; + + // check if part of line is selected + if (selBuf) { + selrow = g_vm->_selection->getSelection(Rect(x0 / GLI_SUBPIX, y, + x1 / GLI_SUBPIX, y + g_conf->_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; + + memcpy(ln, &_lines[i], sizeof(TextBufferRow)); + + // 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 + g_conf->_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 (g_conf->_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 + g_conf->_leading); + + /* + * fill in background colors + */ + color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor; + screen.fillRect(Rect::fromXYWH(x0 / GLI_SUBPIX, y, (x1 - x0) / GLI_SUBPIX, g_conf->_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, g_conf->_leading), + color); + if (link) { + screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + g_conf->_baseLine + 1, + w / GLI_SUBPIX + 1, g_conf->_linkStyle), g_conf->_linkColor); + g_vm->_selection->putHyperlink(link, x / GLI_SUBPIX, y, + x / GLI_SUBPIX + w / GLI_SUBPIX, + y + g_conf->_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, g_conf->_leading), color); + if (link) { + screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + g_conf->_baseLine + 1, + w / GLI_SUBPIX + 1, g_conf->_linkStyle), g_conf->_linkColor); + g_vm->_selection->putHyperlink(link, x / GLI_SUBPIX, y, + x / GLI_SUBPIX + w / GLI_SUBPIX, + y + g_conf->_leading); + } + x += w; + + color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor; + screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, x1 / GLI_SUBPIX - x / GLI_SUBPIX, g_conf->_leading), color); + + /* + * draw caret + */ + + if (_windows->getFocusWindow() == this && i == 0 && (_lineRequest || _lineRequestUni)) { + w = calcWidth(_chars, _attrs, 0, _inCurs, spw); + if (w < pw - g_conf->_caretShape * 2 * GLI_SUBPIX) + screen.drawCaret(Point(x0 + SLOP + ln->_lm + w, y + g_conf->_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 ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles); + x = screen.drawStringUni(Point(x, y + g_conf->_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 ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles); + screen.drawStringUni(Point(x, y + g_conf->_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) * g_conf->_leading; + + g_vm->_selection->putHyperlink(0, x0 / GLI_SUBPIX, y, + x1 / GLI_SUBPIX, y + g_conf->_leading); + + color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor; + screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, x1 / GLI_SUBPIX - x / GLI_SUBPIX, g_conf->_leading), color); + + w = screen.stringWidth(g_conf->_moreFont, g_conf->_morePrompt); + + if (g_conf->_moreAlign == 1) + // center + x = x0 + SLOP + (x1 - x0 - w - SLOP * 2) / 2; + if (g_conf->_moreAlign == 2) + // right + x = x1 - SLOP - w; + + color = Windows::_overrideFgSet ? g_conf->_moreColor : _fgColor; + screen.drawString(Point(x, y + g_conf->_baseLine), + g_conf->_moreFont, color, g_conf->_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 * g_conf->_leading; + } + + /* + * draw the images + */ + for (i = 0; i < _scrollBack; i++) { + memcpy(ln, &_lines[i], sizeof(TextBufferRow)); + + y = y0 + (_height - (i - _scrollPos) - 1) * g_conf->_leading; + + if (ln->_lPic) { + if (y < y1 && y + ln->_lPic->h > y0) { + ln->_lPic->drawPicture(x0 / GLI_SUBPIX, y, 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(x1 / GLI_SUBPIX - ln->_rPic->w, y, + 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; + + delete ln; +} + +int TextBufferWindow::acceptScroll(glui32 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; + } + + if (_scrollPos > _scrollMax - _height + 1) + _scrollPos = _scrollMax - _height + 1; + if (_scrollPos < 0) + _scrollPos = 0; + touchScroll(); + + return (startpos || _scrollPos); +} + +void TextBufferWindow::acceptReadChar(glui32 arg) { + glui32 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(glui32 arg) { + glui32 *cx; + int len; + + if (_height < 2) + _scrollPos = 0; + + if (_scrollPos || arg == keycode_PageUp || arg == keycode_MouseWheelUp) { + acceptScroll(arg); + return; + } + + if (!_inBuf) + return; + + if (_lineTerminators && checkTerminator(arg)) { + for (cx = _lineTerminators; *cx; cx++) { + if (*cx == arg) { + acceptLine(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) { + cx = new glui32[len + 1]; + memcpy(cx, &(_chars[_inFence]), len * 4); + cx[len] = 0; + } else { + cx = nullptr; + } + if (_history[_historyPos]) + free(_history[_historyPos]); + _history[_historyPos] = cx; + } + _historyPos--; + if (_historyPos < 0) + _historyPos += HISTORYLEN; + cx = _history[_historyPos]; + putTextUni(cx, cx ? strlen_uni(cx) : 0, _inFence, + _numChars - _inFence); + break; + + case keycode_Down: + if (_historyPos == _historyPresent) + return; + _historyPos++; + if (_historyPos >= HISTORYLEN) + _historyPos -= HISTORYLEN; + cx = _history[_historyPos]; + putTextUni(cx, cx ? strlen_uni(cx) : 0, _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 (g_conf->_caps && (arg > 0x60 && arg < 0x7b)) + arg -= 0x20; + putTextUni(&arg, 1, _inCurs, 0); + } + break; + } + + touch(0); +} + +void TextBufferWindow::acceptLine(glui32 keycode) { + int ix; + int len, olen; + void *inbuf; + glui32 *s, *o; + int inmax; + gidispatch_rock_t inarrayrock; + int 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 glui32 *)&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 = new glui32[len + 1]; + memcpy(s, _chars + _inFence, len * sizeof(glui32)); + s[len] = 0; + + free(_history[_historyPresent]); + _history[_historyPresent] = nullptr; + + o = _history[(_historyPresent == 0 ? HISTORYLEN : _historyPresent) - 1]; + olen = o ? strlen_uni(o) : 0; + + if (len != olen || memcmp(s, o, olen * sizeof(glui32))) { + _history[_historyPresent] = s; + + _historyPresent++; + if (_historyPresent == HISTORYLEN) + _historyPresent = 0; + + if (_historyPresent == _historyFirst) { + _historyFirst++; + if (_historyFirst == HISTORYLEN) + _historyFirst = 0; + } + } else { + free(s); + } + } + + // Store in event buffer. + 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; + + if (_lineTerminators) { + glui32 val2 = keycode; + if (val2 == keycode_Return) + val2 = 0; + g_vm->_events->store(evtype_LineInput, this, len, val2); + free(_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(glui32 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; + memset(_chars, ' ', TBLINELEN * 4); + memset(_attrs, 0, TBLINELEN * sizeof(Attributes)); + + _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; + memset(_lines[i]._chars, ' ', sizeof _lines[i]._chars); + memset(_lines[i]._attrs, 0, sizeof _lines[i]._attrs); + } + + _scrollBack += SCROLLBACK; +} + +int TextBufferWindow::calcWidth(glui32 *chars, 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(glui32 *width, glui32 *height) const { + if (width) + *width = (_bbox.width() - g_conf->_tMarginX * 2) / g_conf->_cellW; + if (height) + *height = (_bbox.height() - g_conf->_tMarginY * 2) / g_conf->_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 Gargoyle -- cgit v1.2.3