diff options
author | Paul Gilbert | 2018-10-23 19:25:00 -0700 |
---|---|---|
committer | Paul Gilbert | 2018-12-08 19:05:59 -0800 |
commit | ba907c9aec333903acb013e9d3a25962c69b6ca6 (patch) | |
tree | fe2cb6f5dbf613505cb990b18065706dabd1469c | |
parent | cb2b7d439fb9cb89f2758d103ba2ea710e4f0a72 (diff) | |
download | scummvm-rg350-ba907c9aec333903acb013e9d3a25962c69b6ca6.tar.gz scummvm-rg350-ba907c9aec333903acb013e9d3a25962c69b6ca6.tar.bz2 scummvm-rg350-ba907c9aec333903acb013e9d3a25962c69b6ca6.zip |
GLK: Implementing text buffer window methods
-rw-r--r-- | engines/gargoyle/draw.cpp | 21 | ||||
-rw-r--r-- | engines/gargoyle/draw.h | 12 | ||||
-rw-r--r-- | engines/gargoyle/gargoyle.cpp | 9 | ||||
-rw-r--r-- | engines/gargoyle/gargoyle.h | 5 | ||||
-rw-r--r-- | engines/gargoyle/glk_types.h | 29 | ||||
-rw-r--r-- | engines/gargoyle/picture.cpp | 25 | ||||
-rw-r--r-- | engines/gargoyle/picture.h | 20 | ||||
-rw-r--r-- | engines/gargoyle/speech.h | 49 | ||||
-rw-r--r-- | engines/gargoyle/window_graphics.cpp | 5 | ||||
-rw-r--r-- | engines/gargoyle/window_graphics.h | 3 | ||||
-rw-r--r-- | engines/gargoyle/window_mask.cpp | 2 | ||||
-rw-r--r-- | engines/gargoyle/window_mask.h | 7 | ||||
-rw-r--r-- | engines/gargoyle/window_text_buffer.cpp | 1319 | ||||
-rw-r--r-- | engines/gargoyle/window_text_buffer.h | 72 | ||||
-rw-r--r-- | engines/gargoyle/window_text_grid.cpp | 9 | ||||
-rw-r--r-- | engines/gargoyle/window_text_grid.h | 11 | ||||
-rw-r--r-- | engines/gargoyle/windows.cpp | 27 | ||||
-rw-r--r-- | engines/gargoyle/windows.h | 31 |
18 files changed, 1462 insertions, 194 deletions
diff --git a/engines/gargoyle/draw.cpp b/engines/gargoyle/draw.cpp index 86d5fb0f92..f16d1a56ab 100644 --- a/engines/gargoyle/draw.cpp +++ b/engines/gargoyle/draw.cpp @@ -24,9 +24,28 @@ namespace Gargoyle { -int Draw::drawStringUni(int x, int y, int fidx, byte *rgb, glui32 *s, int n, int spw) { +int Draw::drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw) { // TODO return 0; } +int Draw::drawStringUni(int x, int y, int fidx, const byte *rgb, const glui32 *s, int n, int spw) { + // TODO + return 0; +} + +int Draw::stringWidth(int fidx, const char *s, int n, int spw) { + // TODO + return 0; +} + +int Draw::stringWidthUni(int fidx, const glui32 *s, int n, int spw) { + // TODO + return 0; +} + +void Draw::drawCaret(const Common::Point &pos) { + // TODO +} + } // End of namespace Gargoyle diff --git a/engines/gargoyle/draw.h b/engines/gargoyle/draw.h index 61dfe1891a..e0ec133642 100644 --- a/engines/gargoyle/draw.h +++ b/engines/gargoyle/draw.h @@ -23,14 +23,22 @@ #ifndef GARGOYLE_DRAW_H #define GARGOYLE_DRAW_H -#include "common/events.h" +#include "common/rect.h" #include "gargoyle/glk_types.h" namespace Gargoyle { class Draw { protected: - int drawStringUni(int x, int y, int fidx, byte *rgb, glui32 *s, int n, int spw); + int drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw); + + int drawStringUni(int x, int y, int fidx, const byte *rgb, const glui32 *s, int n, int spw); + + int stringWidth(int fidx, const char *s, int n, int spw); + + int stringWidthUni(int fidx, const glui32 *s, int n, int spw); + + void drawCaret(const Common::Point &pos); }; } // End of namespace Gargoyle diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp index 8620e886e0..37df6d1f3e 100644 --- a/engines/gargoyle/gargoyle.cpp +++ b/engines/gargoyle/gargoyle.cpp @@ -31,8 +31,10 @@ #include "gargoyle/gargoyle.h" #include "gargoyle/conf.h" #include "gargoyle/events.h" +#include "gargoyle/picture.h" #include "gargoyle/streams.h" #include "gargoyle/windows.h" +#include "gargoyle/window_mask.h" namespace Gargoyle { @@ -40,7 +42,8 @@ GargoyleEngine *g_vm; GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) : _gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _conf(nullptr), - _events(nullptr), _screen(nullptr), _windows(nullptr), + _events(nullptr), _picList(nullptr), _screen(nullptr), _windows(nullptr), + _windowMask(nullptr), _copySelect(false), gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) { g_vm = this; } @@ -48,9 +51,11 @@ GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gam GargoyleEngine::~GargoyleEngine() { delete _conf; delete _events; + delete _picList; delete _screen; delete _streams; delete _windows; + delete _windowMask; } void GargoyleEngine::initialize() { @@ -64,8 +69,10 @@ void GargoyleEngine::initialize() { _screen = new Graphics::Screen(); _conf = new Conf(); _events = new Events(); + _picList = new PicList(); _streams = new Streams(); _windows = new Windows(_screen); + _windowMask = new WindowMask(); } Common::Error GargoyleEngine::run() { diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h index 1d81cb92e7..b99b7b44f6 100644 --- a/engines/gargoyle/gargoyle.h +++ b/engines/gargoyle/gargoyle.h @@ -36,7 +36,9 @@ namespace Gargoyle { class Conf; class Events; +class PicList; class Windows; +class WindowMask; class Streams; enum InterpreterType { @@ -94,8 +96,11 @@ protected: public: Conf *_conf; Events *_events; + PicList *_picList; Streams *_streams; Windows *_windows; + WindowMask *_windowMask; + bool _copySelect; void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock); gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, const char *typecode); void (*gli_unregister_arr)(void *array, glui32 len, const char *typecode, gidispatch_rock_t objrock); diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h index eaebb6f929..a25a413221 100644 --- a/engines/gargoyle/glk_types.h +++ b/engines/gargoyle/glk_types.h @@ -53,6 +53,35 @@ class Window; typedef struct glk_fileref_struct *frefid_t; typedef struct glk_schannel_struct *schanid_t; +/** + * Usurp C1 space for ligatures and smart typography glyphs + */ +enum Enc { + ENC_LIG_FI = 128, + ENC_LIG_FL = 129, + ENC_LSQUO = 130, + ENC_RSQUO = 131, + ENC_LDQUO = 132, + ENC_RDQUO = 133, + ENC_NDASH = 134, + ENC_MDASH = 135, + ENC_FLOWBREAK = 136 +}; + +/** + * These are the Unicode versions + */ +enum UniChars { + UNI_LIG_FI = 0xFB01, + UNI_LIG_FL = 0xFB02, + UNI_LSQUO = 0x2018, + UNI_RSQUO = 0x2019, + UNI_LDQUO = 0x201c, + UNI_RDQUO = 0x201d, + UNI_NDASH = 0x2013, + UNI_MDASH = 0x2014 +}; + enum Gestalt { gestalt_Version = 0, gestalt_CharInput = 1, diff --git a/engines/gargoyle/picture.cpp b/engines/gargoyle/picture.cpp index 4b0b892322..fd33e718dd 100644 --- a/engines/gargoyle/picture.cpp +++ b/engines/gargoyle/picture.cpp @@ -24,6 +24,16 @@ namespace Gargoyle { +void PicList::increment() { + // TODO +} + +void PicList::decrement() { + // TODO +} + +/*--------------------------------------------------------------------------*/ + void Picture::increment() { ++_refCount; } @@ -35,4 +45,19 @@ void Picture::decrement() { } } +Picture *Picture::load(uint32 id) { + // TODO: gli_picture_load + return nullptr; +} + +Picture *Picture::scale(int sx, int sy) { + // TODO: gli_picture_scale + return nullptr; +} + +void Picture::drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1) { + // TODO: drawPicture +} + + } // End of namespace Gargoyle diff --git a/engines/gargoyle/picture.h b/engines/gargoyle/picture.h index 38f776c6bc..c9527c5663 100644 --- a/engines/gargoyle/picture.h +++ b/engines/gargoyle/picture.h @@ -27,7 +27,17 @@ namespace Gargoyle { +class PicList { +public: + void increment(); + + void decrement(); +}; + struct Picture : Graphics::Surface { +public: + static Picture *load(uint32 id); +public: int _refCount; uint32 _id; bool _scaled; @@ -46,6 +56,16 @@ struct Picture : Graphics::Surface { * Decrement reference counter */ void decrement(); + + /** + * Rescale the picture to a new picture of a given size + */ + Picture *scale(int sx, int sy); + + /** + * Draw the picture + */ + void drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1); }; } // End of namespace Gargoyle diff --git a/engines/gargoyle/speech.h b/engines/gargoyle/speech.h new file mode 100644 index 0000000000..1ff2cb178d --- /dev/null +++ b/engines/gargoyle/speech.h @@ -0,0 +1,49 @@ +/* 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. + * + */ + +#ifndef GARGOYLE_SPEECH_H +#define GARGOYLE_SPEECH_H + +#include "common/events.h" +#include "gargoyle/glk_types.h" + +namespace Gargoyle { + +/** + * Currently not implemented + */ +class Speech { +protected: + void gli_initialize_tts(void) {} + + void gli_tts_flush(void) {} + + void gli_tts_purge(void) {} + + void gli_tts_speak(const glui32 *buf, size_t len) {} + + void gli_free_tts(void) {} +}; + +} // End of namespace Gargoyle + +#endif diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp index cc9a5dd19e..732686d2da 100644 --- a/engines/gargoyle/window_graphics.cpp +++ b/engines/gargoyle/window_graphics.cpp @@ -86,4 +86,9 @@ void GraphicsWindow::redraw() { // TODO } +glui32 GraphicsWindow::imageDraw(glui32 image, glui32 align, bool scaled, glui32 width, glui32 height) { + // TODO: win_graphics_draw_picture + return 0; +} + } // End of namespace Gargoyle diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h index 77e49105b2..b0f53faf72 100644 --- a/engines/gargoyle/window_graphics.h +++ b/engines/gargoyle/window_graphics.h @@ -77,6 +77,9 @@ public: */ virtual void redraw() override; + virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0, + glui32 height = 0) override; + /** * Get the window dimensions */ diff --git a/engines/gargoyle/window_mask.cpp b/engines/gargoyle/window_mask.cpp index f0f6aa36bf..fe1d463eb7 100644 --- a/engines/gargoyle/window_mask.cpp +++ b/engines/gargoyle/window_mask.cpp @@ -202,7 +202,7 @@ int WindowMask::checkSelection(uint x0, uint y0, uint x1, uint y1) { return false; } -int WindowMask::getSelection(uint x0, uint y0, uint x1, uint y1, uint *rx0, uint *rx1) { +int WindowMask::getSelection(uint x0, uint y0, uint x1, uint y1, int *rx0, int *rx1) { uint row, upper, lower, above, below; int row_selected, found_left, found_right; int from_right, from_below, is_above, is_below; diff --git a/engines/gargoyle/window_mask.h b/engines/gargoyle/window_mask.h index db0d7e655d..9dc5826916 100644 --- a/engines/gargoyle/window_mask.h +++ b/engines/gargoyle/window_mask.h @@ -30,13 +30,14 @@ namespace Gargoyle { class Window; -struct WindowMask { +class WindowMask { +public: size_t _hor, _ver; glui32 **_links; Common::Rect _select; static int _lastX, _lastY; - +public: /** * Constructor */ @@ -59,7 +60,7 @@ struct WindowMask { int checkSelection(uint x0, uint y0, uint x1, uint y1); - int getSelection(uint x0, uint y0, uint x1, uint y1, uint *rx0, uint *rx1); + int getSelection(uint x0, uint y0, uint x1, uint y1, int *rx0, int *rx1); }; } // End of namespace Gargoyle diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp index b916fc4b5c..3df2e5cd18 100644 --- a/engines/gargoyle/window_text_buffer.cpp +++ b/engines/gargoyle/window_text_buffer.cpp @@ -23,9 +23,17 @@ #include "gargoyle/window_text_buffer.h" #include "gargoyle/conf.h" #include "gargoyle/gargoyle.h" +#include "gargoyle/string.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), @@ -35,7 +43,7 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windo _type = wintype_TextBuffer; Common::fill(&_history[0], &_history[HISTORYLEN], nullptr); - Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], styles); + Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], _styles); } TextBufferWindow::~TextBufferWindow() { @@ -49,10 +57,10 @@ TextBufferWindow::~TextBufferWindow() { 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(); + if (_lines[i]._lPic) + _lines[i]._lPic->decrement(); + if (_lines[i]._rPic) + _lines[i]._rPic->decrement(); } } @@ -109,7 +117,7 @@ void TextBufferWindow::reflow() { if (_height < 4 || _width < 20) return; - _lines[0].len = _numChars; + _lines[0]._len = _numChars; /* allocate temp buffers */ Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN]; @@ -142,32 +150,32 @@ void TextBufferWindow::reflow() { if (k == 0 && _lineRequest) inputbyte = p + _inFence; - if (_lines[k].lpic) { + if (_lines[k]._lPic) { offsetbuf[x] = p; alignbuf[x] = imagealign_MarginLeft; - pictbuf[x] = _lines[k].lpic; + pictbuf[x] = _lines[k]._lPic; if (pictbuf[x]) pictbuf[x]->increment(); - hyperbuf[x] = _lines[k].lhyper; + hyperbuf[x] = _lines[k]._lHyper; x++; } - if (_lines[k].rpic) { + if (_lines[k]._rPic) { offsetbuf[x] = p; alignbuf[x] = imagealign_MarginRight; - pictbuf[x] = _lines[k].rpic; + pictbuf[x] = _lines[k]._rPic; if (pictbuf[x]) pictbuf[x]->increment(); - hyperbuf[x] = _lines[k].rhyper; + 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]; + 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) { + if (_lines[k]._newLine) { attrbuf[p] = curattr; charbuf[p] = '\n'; p++; @@ -224,32 +232,32 @@ void TextBufferWindow::touchScroll() { _windows->repaint(_bbox); for (int i = 0; i < _scrollMax; i++) - _lines[i].dirty = true; + _lines[i]._dirty = true; } bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) { if (align == imagealign_MarginRight) { - if (_lines[0].rpic || _numChars) + 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; + _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) + 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; + _lines[0]._lPic = pic; + _lines[0]._lm = _ladjw; + _lines[0]._lHyper = linkval; if (align != imagealign_MarginLeft) flowBreak(); @@ -258,17 +266,110 @@ bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) { return true; } -void TextBufferWindow::flowBreak() { - // TODO +glui32 TextBufferWindow::imageDraw(glui32 image, glui32 align, bool 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; +} + +bool TextBufferWindow::flowBreak() { + while (_ladjn || _radjn) + putCharUni('\n'); + return true; +} + +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) { + int i; + for (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) { - // TODO + 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) { int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading; - _lines[line].dirty = 1; + _lines[line]._dirty = 1; _windows->clearSelection(); _windows->repaint(Common::Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2)); } @@ -278,7 +379,6 @@ glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const { } void TextBufferWindow::putCharUni(glui32 ch) { - /* glui32 bchars[TBLINELEN]; Attributes battrs[TBLINELEN]; int pw; @@ -286,29 +386,28 @@ void TextBufferWindow::putCharUni(glui32 ch) { int saved; int i; int linelen; - unsigned char *color; + byte *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; + pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) * GLI_SUBPIX; + pw = pw - 2 * SLOP - _radjw - _ladjw; - color = Windows::_overrideBgSet ? gli_window_color : bgcolor; + color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor; // oops ... overflow - if (numchars + 1 >= TBLINELEN) - scrolloneline(dwin, 0); + if (_numChars + 1 >= TBLINELEN) + scrollOneLine(0); if (ch == '\n') { - scrolloneline(dwin, 1); + scrollOneLine(1); return; } - if (gli_conf_quotes) { + if (g_conf->_quotes) { // fails for 'tis a wonderful day in the '80s - if (gli_conf_quotes > 1 && ch == '\'') - { - if (numchars == 0 || leftquote(_chars[numchars - 1])) + if (g_conf->_quotes > 1 && ch == '\'') { + if (_numChars == 0 || leftquote(_chars[_numChars - 1])) ch = UNI_LSQUO; } @@ -318,122 +417,112 @@ void TextBufferWindow::putCharUni(glui32 ch) { if (ch == '\'') ch = UNI_RSQUO; - if (ch == '"') - { - if (numchars == 0 || leftquote(_chars[numchars - 1])) + 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) + 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--; + if (_dashed == 3) { + _numChars--; ch = UNI_MDASH; - dashed = 0; + _dashed = 0; } + } else { + _dashed = 0; } - else - dashed = 0; } - if (gli_conf_spaces && attr.style != style_Preformatted - && styles[attr.style].bg == color - && !styles[attr.style].reverse) + 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 (gli_conf_spaces == 1) - { + if (g_conf->_spaces == 1) { if (ch == '.') - spaced = 1; - else if (ch == ' ' && spaced == 1) - spaced = 2; - else if (ch == ' ' && spaced == 2) - { - spaced = 0; + _spaced = 1; + else if (ch == ' ' && _spaced == 1) + _spaced = 2; + else if (ch == ' ' && _spaced == 2) { + _spaced = 0; return; + } else { + _spaced = 0; } - else - spaced = 0; } // Turn (per sp x) into (per sp sp x) - if (gli_conf_spaces == 2) - { + if (g_conf->_spaces == 2) { if (ch == '.') - spaced = 1; - else if (ch == ' ' && spaced == 1) - spaced = 2; - else if (ch != ' ' && spaced == 2) + _spaced = 1; + else if (ch == ' ' && _spaced == 1) + _spaced = 2; + else if (ch != ' ' && _spaced == 2) { - spaced = 0; - win_textbuffer_putchar_uni(win, ' '); + _spaced = 0; + putCharUni(' '); + } else { + _spaced = 0; } - else - spaced = 0; } } - _chars[numchars] = ch; - attrs[numchars] = attr; - numchars++; + _chars[_numChars] = ch; + _attrs[_numChars] = _attr; + _numChars++; // kill spaces at the end for line width calculation - linelen = numchars; + linelen = _numChars; while (linelen > 1 && _chars[linelen - 1] == ' ' - && styles[attrs[linelen - 1].style].bg == color - && !styles[attrs[linelen - 1].style].reverse) + && _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; + if (calcWidth(_chars, _attrs, 0, linelen, -1) >= pw) { + bpoint = _numChars; - for (i = numchars - 1; i > 0; i--) - if (_chars[i] == ' ') - { + for (i = _numChars - 1; i > 0; i--) + if (_chars[i] == ' ') { bpoint = i + 1; // skip space break; } - saved = numchars - bpoint; + saved = _numChars - bpoint; memcpy(bchars, _chars + bpoint, saved * 4); - memcpy(battrs, attrs + bpoint, saved * sizeof(attr_t)); - numchars = bpoint; + memcpy(battrs, _attrs + bpoint, saved * sizeof(Attributes)); + _numChars = bpoint; - scrolloneline(dwin, 0); + scrollOneLine(0); memcpy(_chars, bchars, saved * 4); - memcpy(attrs, battrs, saved * sizeof(attr_t)); - numchars = saved; + memcpy(_attrs, battrs, saved * sizeof(Attributes)); + _numChars = saved; } touch(0); - */ } bool TextBufferWindow::unputCharUni(uint32 ch) { - // TODO - return false; -} + if (_numChars > 0 && _chars[_numChars - 1] == ch) { + _numChars--; + touch(0); + return true; + } -void TextBufferWindow::moveCursor(const Common::Point &newPos) { - // TODO + return false; } void TextBufferWindow::clear() { @@ -454,20 +543,20 @@ void TextBufferWindow::clear() { _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; + _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; @@ -478,24 +567,146 @@ void TextBufferWindow::clear() { touch(i); } +void TextBufferWindow::click(const Common::Point &newPos) { + int gh = false; + int gs = false; + + if (_lineRequest || _charRequest + || _lineRequestUni || _charRequestUni + || _moreRequest || _scrollRequest) + _windows->setFocus(this); + + if (_hyperRequest) { + glui32 linkval = g_vm->_windowMask->getHyperlink(newPos); + if (linkval) { + g_vm->_events->eventStore(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->_windowMask->startSelection(newPos); + } +} + void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) { - if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) - { + if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) { warning("request_line_event: window already has keyboard request"); return; } - // TODO + 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); + } + + _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) - { + if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) { warning("request_line_event_uni: window already has keyboard request"); return; } - // TODO + 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); + } + + _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) { @@ -571,21 +782,857 @@ void TextBufferWindow::cancelLineEvent(Event *ev) { } void TextBufferWindow::redraw() { - // TODO + 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 selbuf, selrow, selchar, sx0, sx1, selleft, selright; + int tx, tsc, tsw, lsc, rsc; + + _lines[0]._len = _numChars; + + 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->_windowMask->checkSelection(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->_windowMask->getSelection(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(Common::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->_windowMask->putHyperlink(0, x0/GLI_SUBPIX, y, + x1/GLI_SUBPIX, y + g_conf->_leading); + + /* + * fill in background colors + */ + color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor; + _windows->drawRect(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 = stringWidthUni(font, ln->_chars + a, b - a, spw); + _windows->drawRect(x/GLI_SUBPIX, y, + w/GLI_SUBPIX, g_conf->_leading, + color); + if (link) { + _windows->drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1, + w/GLI_SUBPIX + 1, g_conf->_linkStyle, + g_conf->_linkColor); + g_vm->_windowMask->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 = stringWidthUni(font, ln->_chars + a, b - a, spw); + _windows->drawRect(x/GLI_SUBPIX, y, w/GLI_SUBPIX, + g_conf->_leading, color); + if (link) { + _windows->drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1, + w/GLI_SUBPIX + 1, g_conf->_linkStyle, + g_conf->_linkColor); + g_vm->_windowMask->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; + _windows->drawRect(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) + drawCaret(Common::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 = drawStringUni(x, y + g_conf->_baseLine, + font, color, 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); + drawStringUni(x, y + g_conf->_baseLine, + font, color, 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->_windowMask->putHyperlink(0, x0/GLI_SUBPIX, y, + x1/GLI_SUBPIX, y + g_conf->_leading); + + color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor; + _windows->drawRect(x/GLI_SUBPIX, y, + x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading, + color); + + w = stringWidth(g_conf->_moreFont, + g_conf->_morePrompt.c_str(), g_conf->_morePrompt.size(), -1); + + 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; + drawString(x, y + g_conf->_baseLine, + g_conf->_moreFont, color, + g_conf->_morePrompt.c_str(), g_conf->_morePrompt.size(), -1); + 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->_windowMask->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->_windowMask->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->_windowMask->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; + } + + _windows->drawRect(x0+1, y0, x1-x0-2, y1-y0, g_conf->_scrollBg); + _windows->drawRect(x0+1, t0, x1-x0-2, t1-t0, g_conf->_scrollFg); + + for (i = 0; i < g_conf->_scrollWidth / 2 + 1; i++) { + _windows->drawRect(x0+g_conf->_scrollWidth/2-i, + y0 - g_conf->_scrollWidth/2 + i, + i*2, 1, g_conf->_scrollFg); + _windows->drawRect(x0+g_conf->_scrollWidth/2-i, + y1 + g_conf->_scrollWidth/2 - i, + i*2, 1, g_conf->_scrollFg); + } + } + + /* send selected text to clipboard */ + if (selbuf && _copyPos) { + Windows::_claimSelect = true; + + copyTextToClipboard(_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; + + free(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; + } + + 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->eventStore(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; + } -TextBufferWindow::TextBufferRow::TextBufferRow() : len(0), newline(0), dirty(false), repaint(false), - lpic(nullptr), rpic(nullptr), lhyper(0), rhyper(0), lm(0), rm(0) { + touch(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, ' '); +/* Return or enter, during line input. Ends line input. */ +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->eventStore(evtype_LineInput, this, len, val2); + free(_lineTerminators); + _lineTerminators = nullptr; + } else { + g_vm->_events->eventStore(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 = 0; + _lines[i]._repaint = 0; + _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) { + int w = 0; + int a, b; + + a = startchar; + for (b = startchar; b < numChars; b++) { + if (attrs[a] == attrs[b]) { + w += stringWidthUni(attrs[a].attrFont(_styles), + chars + a, b - a, spw); + a = b; + } + } + + w += stringWidthUni(attrs[a].attrFont(_styles), + chars + a, b - a, spw); + + return w; +} + +void TextBufferWindow::copyTextToClipboard(const glui32 *text, size_t len) { + // TODO +} + +/*--------------------------------------------------------------------------*/ + +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 diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h index ebba2b4502..2a1c1f7748 100644 --- a/engines/gargoyle/window_text_buffer.h +++ b/engines/gargoyle/window_text_buffer.h @@ -25,47 +25,72 @@ #include "gargoyle/windows.h" #include "gargoyle/picture.h" +#include "gargoyle/speech.h" namespace Gargoyle { /** * Text Buffer window */ -class TextBufferWindow : public Window { +class TextBufferWindow : public Window, Speech { /** * Structure for a row within the window */ struct TextBufferRow { - Common::Array<uint32> chars; - Common::Array<Attributes> attr; - int len, newline; - bool dirty, repaint; - Picture *lpic, *rpic; - glui32 lhyper, rhyper; - int lm, rm; + glui32 _chars[TBLINELEN]; + Attributes _attrs[TBLINELEN]; + int _len, _newLine; + bool _dirty, _repaint; + Picture *_lPic, *_rPic; + glui32 _lHyper, _rHyper; + int _lm, _rm; /** * Constructor */ TextBufferRow(); - - /** - * Resize the row - */ - void resize(size_t newSize); }; typedef Common::Array<TextBufferRow> TextBufferRows; private: void reflow(); void touchScroll(); bool putPicture(Picture *pic, glui32 align, glui32 linkval); + + /** + * @remarks Only for input text + */ + void putText(const char *buf, int len, int pos, int oldlen); + + /** + * @remarks Only for input text + */ void putTextUni(const glui32 *buf, int len, int pos, int oldlen); - void flowBreak(); + + bool flowBreak(); + + void acceptLine(glui32 keycode); + + /** + * Return true if a following quotation mark should be an opening mark, + * false if it should be a closing mark. Opening quotation marks will + * appear following an open parenthesis, open square bracket, or + * whitespace. + */ + bool leftquote(glui32 c); /** * Mark a given text row as modified */ void touch(int line); + + void scrollOneLine(bool forced); + void scrollResize(); + int calcWidth(glui32 *chars, Attributes *attrs, int startchar, int numchars, int spw); + + /** + * Copy the passed text to the clipboard + */ + void copyTextToClipboard(const glui32 *text, size_t len); public: int _width, _height; int _spaced; @@ -106,7 +131,7 @@ public: glui32 *_lineTerminators; /* style hints and settings */ - WindowStyle styles[style_NUMSTYLES]; + WindowStyle _styles[style_NUMSTYLES]; /* for copy selection */ glui32 *_copyBuf; @@ -143,14 +168,14 @@ public: virtual bool unputCharUni(uint32 ch) override; /** - * Move the cursor + * Clear the window */ - virtual void moveCursor(const Common::Point &newPos) override; + virtual void clear() override; /** - * Clear the window + * Click the window */ - virtual void clear() override; + virtual void click(const Common::Point &newPos) override; /** * Prepare for inputing a line @@ -176,6 +201,15 @@ public: * Redraw the window */ virtual void redraw() override; + + virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0, + glui32 height = 0) override; + + virtual void acceptReadLine(glui32 arg) override; + + virtual void acceptReadChar(glui32 arg) override; + + int acceptScroll(glui32 arg); }; } // End of namespace Gargoyle diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp index 17bb2221c8..91331d7ff8 100644 --- a/engines/gargoyle/window_text_grid.cpp +++ b/engines/gargoyle/window_text_grid.cpp @@ -23,6 +23,7 @@ #include "gargoyle/window_text_grid.h" #include "gargoyle/conf.h" #include "gargoyle/gargoyle.h" +#include "gargoyle/window_mask.h" namespace Gargoyle { @@ -203,7 +204,7 @@ void TextGridWindow::click(const Common::Point &newPos) { } if (_hyperRequest) { - glui32 linkval = _windows->getHyperlink(newPos); + glui32 linkval = g_vm->_windowMask->getHyperlink(newPos); if (linkval) { g_vm->_events->eventStore(evtype_Hyperlink, this, linkval, 0); @@ -588,7 +589,7 @@ void TextGridWindow::redraw() { y = y0 + i * g_conf->_leading; /* clear any stored hyperlink coordinates */ - _windows->setHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading); + g_vm->_windowMask->putHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading); a = 0; for (b = 0; b < _width; b++) { @@ -610,7 +611,7 @@ void TextGridWindow::redraw() { if (link) { _windows->drawRect(x, y + g_conf->_baseLine + 1, w, g_conf->_linkStyle, g_conf->_linkColor); - _windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading); + g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading); } x += w; a = b; @@ -634,7 +635,7 @@ void TextGridWindow::redraw() { if (link) { _windows->drawRect(x, y + g_conf->_baseLine + 1, w, g_conf->_linkStyle, g_conf->_linkColor); - _windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading); + g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading); } } } diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h index 0cf6d5e774..2c5a0a847c 100644 --- a/engines/gargoyle/window_text_grid.h +++ b/engines/gargoyle/window_text_grid.h @@ -56,17 +56,10 @@ private: */ void touch(int line); - void acceptReadChar(glui32 arg); - /** * Return or enter, during line input. Ends line input. */ void acceptLine(glui32 keycode); - - /** - * Any regular key, during line input. - */ - void acceptReadLine(glui32 arg); public: int _width, _height; TextGridRows _lines; @@ -158,6 +151,10 @@ public: * Redraw the window */ virtual void redraw() override; + + virtual void acceptReadLine(glui32 arg) override; + + virtual void acceptReadChar(glui32 arg) override; }; } // End of namespace Gargoyle diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp index cf9b7de77d..c1c28e5123 100644 --- a/engines/gargoyle/windows.cpp +++ b/engines/gargoyle/windows.cpp @@ -37,6 +37,8 @@ bool Windows::_overrideReverse; bool Windows::_overrideFgSet; bool Windows::_overrideBgSet; bool Windows::_forceRedraw; +bool Windows::_claimSelect; +bool Windows::_moreFocus; int Windows::_overrideFgVal; int Windows::_overrideBgVal; int Windows::_zcolor_fg; @@ -48,14 +50,15 @@ byte Windows::_zcolor_Bright[3]; /*--------------------------------------------------------------------------*/ -Windows::Windows(Graphics::Screen *screen) : - _screen(screen), _moreFocus(false), _windowList(nullptr), - _rootWin(nullptr), _focusWin(nullptr), _mask(nullptr), _claimSelect(0) { +Windows::Windows(Graphics::Screen *screen) : _screen(screen), _windowList(nullptr), + _rootWin(nullptr), _focusWin(nullptr), _mask(nullptr) { _mask = new WindowMask(); _overrideReverse = false; _overrideFgSet = false; _overrideBgSet = false; _forceRedraw = true; + _claimSelect = false; + _moreFocus = false; _overrideFgVal = 0; _overrideBgVal = 0; _zcolor_fg = _zcolor_bg = 0; @@ -234,6 +237,10 @@ void Windows::redraw() { // TODO: gli_windows_redraw } +void Windows::redrawRect(const Common::Rect &r) { + // TODO: gli_redraw_rect +} + void Windows::repaint(const Common::Rect &box) { // TODO } @@ -291,7 +298,7 @@ Window::Window(Windows *windows, glui32 rock) : _windows(windows), _rock(rock), _type(0), _parent(nullptr), _next(nullptr), _prev(nullptr), _yAdj(0), _lineRequest(0), _lineRequestUni(0), _charRequest(0), _charRequestUni(0), _mouseRequest(0), _hyperRequest(0), _moreRequest(0), _scrollRequest(0), _imageLoaded(0), - _echoLineInput(true), _lineTerminatorsBase(nullptr), _termCt(0), _echoStream(nullptr) { + _echoLineInputBase(true), _lineTerminatorsBase(nullptr), _termCt(0), _echoStream(nullptr) { _attr.fgset = 0; _attr.bgset = 0; _attr.reverse = 0; @@ -337,6 +344,10 @@ void Window::cancelLineEvent(Event *ev) { g_vm->_events->clearEvent(ev); } +void Window::moveCursor(const Common::Point &newPos) { + warning("moveCursor: not a TextGrid window"); +} + void Window::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) { warning("requestLineEvent: window does not support keyboard input"); } @@ -353,6 +364,14 @@ void Window::redraw() { } } +void Window::acceptReadLine(glui32 arg) { + warning("acceptReadLine:: window does not support keyboard input"); +} + +void Window::acceptReadChar(glui32 arg) { + warning("acceptReadChar:: window does not support keyboard input"); +} + bool Window::checkTerminator(glui32 ch) { if (ch == keycode_Escape) return true; diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h index 5e6374c340..3f5c0bc274 100644 --- a/engines/gargoyle/windows.h +++ b/engines/gargoyle/windows.h @@ -85,8 +85,6 @@ private: Window * _windowList; ///< List of all windows Window *_rootWin; ///< The topmost window Window *_focusWin; ///< The window selected by the player - bool _moreFocus; - bool _claimSelect; WindowMask *_mask; private: /** @@ -108,6 +106,8 @@ public: static bool _overrideFgSet; static bool _overrideBgSet; static bool _forceRedraw; + static bool _claimSelect; + static bool _moreFocus; static int _overrideFgVal; static int _overrideBgVal; static int _zcolor_fg, _zcolor_bg; @@ -157,6 +157,8 @@ public: void redraw(); + void redrawRect(const Common::Rect &r); + /** * Repaint an area of the windows */ @@ -176,18 +178,6 @@ public: * Returns the end point of window iteration */ iterator end() { return iterator(nullptr); } - - /** - * Gets a hyperlink - */ - glui32 getHyperlink(const Common::Point &pos) { return _mask->getHyperlink(pos); } - - /** - * Sets a hyperlink - */ - void setHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1) { - return _mask->putHyperlink(linkval, x0, y0, x1, y1); - } }; /** @@ -278,7 +268,7 @@ public: int _scrollRequest; int _imageLoaded; - glui32 _echoLineInput; + glui32 _echoLineInputBase; glui32 *_lineTerminatorsBase; glui32 _termCt; @@ -323,7 +313,7 @@ public: /** * Move the cursor */ - virtual void moveCursor(const Common::Point &newPos) {} + virtual void moveCursor(const Common::Point &newPos); /** * Clear the window @@ -364,6 +354,15 @@ public: * Redraw the window */ virtual void redraw(); + + virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0, + glui32 height = 0) { return false; } + + virtual void acceptReadLine(glui32 arg); + + virtual void acceptReadChar(glui32 arg); + + int acceptScroll(glui32 arg); }; typedef Window *winid_t; |