1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software(0), you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation(0), either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY(0), without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program(0), if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "titanic/gfx/text_control.h"
24 #include "titanic/support/strings.h"
25 #include "titanic/titanic.h"
26 
27 namespace Titanic {
28 
CTextControl(uint count)29 CTextControl::CTextControl(uint count) :
30 		_stringsMerged(false), _maxCharsPerLine(-1), _lineCount(0),
31 		_displayEndCharIndex(-1), _unused1(0), _unused2(0), _unused3(0),
32 		_backR(0xff), _backG(0xff), _backB(0xff),
33 		_textR(0), _textG(0), _textB(200),
34 		_fontNumber(0), _npcFlag(0), _npcId(0), _hasBorder(true),
35 		_scrollTop(0), _textCursor(nullptr) {
36 	setupArrays(count);
37 }
38 
setupArrays(int count)39 void CTextControl::setupArrays(int count) {
40 	freeArrays();
41 	if (count < 10 || count > 60)
42 		count = 10;
43 	_array.resize(count);
44 }
45 
freeArrays()46 void CTextControl::freeArrays() {
47 	_array.clear();
48 }
49 
setup()50 void CTextControl::setup() {
51 	for (int idx = 0; idx < (int)_array.size(); ++idx) {
52 		_array[idx]._line.clear();
53 		setLineColor(idx, _textR, _textG, _textB);
54 		_array[idx]._string3.clear();
55 	}
56 
57 	_lineCount = 0;
58 	_stringsMerged = false;
59 }
60 
setLineColor(uint lineNum,uint col)61 void CTextControl::setLineColor(uint lineNum, uint col) {
62 	setLineColor(lineNum, col & 0xff, (col >> 8) & 0xff, (col >> 16) & 0xff);
63 }
64 
setLineColor(uint lineNum,byte r,byte g,byte b)65 void CTextControl::setLineColor(uint lineNum, byte r, byte g, byte b) {
66 	_array[lineNum]._rgb = getColorText(r, g, b);
67 	_stringsMerged = false;
68 }
69 
getColorText(byte r,byte g,byte b)70 CString CTextControl::getColorText(byte r, byte g, byte b) {
71 	char buffer[6];
72 	if (!r)
73 		r = 1;
74 	if (!g)
75 		g = 1;
76 	if (!b)
77 		b = 1;
78 
79 	buffer[0] = TEXTCMD_SET_COLOR;
80 	buffer[1] = r;
81 	buffer[2] = g;
82 	buffer[3] = b;
83 	buffer[4] = TEXTCMD_SET_COLOR;
84 	buffer[5] = '\0';
85 
86 	return CString(buffer);
87 }
88 
load(SimpleFile * file,int param)89 void CTextControl::load(SimpleFile *file, int param) {
90 	if (!param) {
91 		uint numLines = file->readNumber();
92 		int charsPerLine = file->readNumber();
93 		uint count = file->readNumber();
94 		_bounds = file->readRect();
95 		_unused1 = file->readNumber();
96 		_unused2 = file->readNumber();
97 		_unused3 = file->readNumber();
98 		_backR = file->readNumber();
99 		_backG = file->readNumber();
100 		_backB = file->readNumber();
101 		_textR = file->readNumber();
102 		_textG = file->readNumber();
103 		_textB = file->readNumber();
104 		_hasBorder = file->readNumber() != 0;
105 		_scrollTop = file->readNumber();
106 
107 		setMaxCharsPerLine(charsPerLine);
108 		resize(numLines);
109 		_lineCount = (count == 0) ? 0 : count - 1;
110 
111 		assert(_array.size() >= count);
112 		for (uint idx = 0; idx < count; ++idx) {
113 			_array[idx]._line = file->readString();
114 			_array[idx]._rgb = file->readString();
115 			_array[idx]._string3 = file->readString();
116 		}
117 	}
118 }
119 
save(SimpleFile * file,int indent)120 void CTextControl::save(SimpleFile *file, int indent) {
121 	int numLines = _lineCount + 1;
122 
123 	file->writeNumberLine(_array.size(), indent);
124 	file->writeNumberLine(_maxCharsPerLine, indent);
125 	file->writeNumberLine(numLines, indent);
126 
127 	file->writeRect(_bounds, indent);
128 	file->writeNumberLine(_unused1, indent);
129 	file->writeNumberLine(_unused2, indent);
130 	file->writeNumberLine(_unused3, indent);
131 	file->writeNumberLine(_backR, indent);
132 	file->writeNumberLine(_backG, indent);
133 	file->writeNumberLine(_backB, indent);
134 	file->writeNumberLine(_textR, indent);
135 	file->writeNumberLine(_textG, indent);
136 	file->writeNumberLine(_textB, indent);
137 	file->writeNumberLine(_hasBorder, indent);
138 	file->writeNumberLine(_scrollTop, indent);
139 
140 	for (int idx = 0; idx < numLines; ++idx) {
141 		file->writeQuotedLine(_array[idx]._line, indent);
142 		file->writeQuotedLine(_array[idx]._rgb, indent);
143 		file->writeQuotedLine(_array[idx]._string3, indent);
144 	}
145 }
146 
draw(CScreenManager * screenManager)147 void CTextControl::draw(CScreenManager *screenManager) {
148 	Rect tempRect = _bounds;
149 
150 	if (_hasBorder) {
151 		// Create border effect
152 		// Top edge
153 		tempRect.bottom = tempRect.top + 1;
154 		screenManager->fillRect(SURFACE_BACKBUFFER, &tempRect, _backR, _backG, _backB);
155 
156 		// Bottom edge
157 		tempRect.top = _bounds.bottom - 1;
158 		tempRect.bottom = _bounds.bottom;
159 		screenManager->fillRect(SURFACE_BACKBUFFER, &tempRect, _backR, _backG, _backB);
160 
161 		// Left edge
162 		tempRect = _bounds;
163 		tempRect.right = tempRect.left + 1;
164 		screenManager->fillRect(SURFACE_BACKBUFFER, &tempRect, _backR, _backG, _backB);
165 
166 		// Right edge
167 		tempRect = _bounds;
168 		tempRect.left = tempRect.right - 1;
169 		screenManager->fillRect(SURFACE_BACKBUFFER, &tempRect, _backR, _backG, _backB);
170 	}
171 
172 	getTextHeight(screenManager);
173 
174 	tempRect = _bounds;
175 	tempRect.grow(-2);
176 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
177 
178 	_displayEndCharIndex = screenManager->writeString(SURFACE_BACKBUFFER, tempRect, _scrollTop, _lines, _textCursor);
179 
180 	screenManager->setFontNumber(oldFontNumber);
181 }
182 
mergeStrings()183 void CTextControl::mergeStrings() {
184 	if (!_stringsMerged) {
185 		_lines.clear();
186 
187 		for (int idx = 0; idx <= _lineCount; ++idx) {
188 			CString line = _array[idx]._rgb + _array[idx]._string3 +
189 				_array[idx]._line + "\n";
190 			_lines += line;
191 		}
192 
193 		_stringsMerged = true;
194 	}
195 }
196 
resize(uint count)197 void CTextControl::resize(uint count) {
198 	if (!count || _array.size() == count)
199 		return;
200 	_array.clear();
201 	_array.resize(count);
202 }
203 
getText() const204 CString CTextControl::getText() const {
205 	CString result = "";
206 	for (int idx = 0; idx <= _lineCount; ++idx)
207 		result += _array[idx]._line;
208 
209 	return result;
210 }
211 
setText(const CString & str)212 void CTextControl::setText(const CString &str) {
213 	setup();
214 	appendText(str);
215 }
216 
setText(StringId stringId)217 void CTextControl::setText(StringId stringId) {
218 	setText(g_vm->_strings[stringId]);
219 }
220 
appendText(const CString & str)221 void CTextControl::appendText(const CString &str) {
222 	int lineSize = _array[_lineCount]._line.size();
223 	int strSize = str.size();
224 
225 	if (_maxCharsPerLine == -1) {
226 		// No limit on horizontal characters, so append string to current line
227 		_array[_lineCount]._line += str;
228 	} else if ((lineSize + strSize) <= _maxCharsPerLine) {
229 		// New string fits into line, so add it on
230 		_array[_lineCount]._line += str;
231 	} else {
232 		// Only add part of the str up to the maximum allowed limit for line
233 		_array[_lineCount]._line += str.left(_maxCharsPerLine - lineSize);
234 	}
235 
236 	updateStr3(_lineCount);
237 	_stringsMerged = false;
238 }
239 
setColor(uint col)240 void CTextControl::setColor(uint col) {
241 	_textR = col & 0xff;
242 	_textG = (col >> 8) & 0xff;
243 	_textB = (col >> 16) & 0xff;
244 }
245 
setColor(byte r,byte g,byte b)246 void CTextControl::setColor(byte r, byte g, byte b) {
247 	_textR = r;
248 	_textG = g;
249 	_textB = b;
250 }
251 
remapColors(uint count,uint * srcColors,uint * destColors)252 void CTextControl::remapColors(uint count, uint *srcColors, uint *destColors) {
253 	for (int lineNum = 0; lineNum <= _lineCount; ++lineNum) {
254 		if (_array[lineNum]._rgb.empty())
255 			continue;
256 
257 		// Get the rgb values
258 		uint r = _array[lineNum]._rgb[1];
259 		uint g = _array[lineNum]._rgb[2];
260 		uint b = _array[lineNum]._rgb[3];
261 		uint color = r | (g << 8) | (b << 16);
262 
263 		for (uint index = 0; index < count; ++index) {
264 			if (color == srcColors[index]) {
265 				// Found a match, so replace the color
266 				setLineColor(lineNum, destColors[index]);
267 				break;
268 			}
269 		}
270 	}
271 
272 	_stringsMerged = false;
273 }
274 
setMaxCharsPerLine(int maxChars)275 void CTextControl::setMaxCharsPerLine(int maxChars) {
276 	if (maxChars >= -1 && maxChars < 257)
277 		_maxCharsPerLine = maxChars;
278 }
279 
updateStr3(int lineNum)280 void CTextControl::updateStr3(int lineNum) {
281 	if (_npcFlag > 0 && _npcId > 0) {
282 		char line[5];
283 		line[0] = line[3] = TEXTCMD_NPC;
284 		line[1] = _npcFlag;
285 		line[2] = _npcId;
286 		line[4] = '\0';
287 		_array[lineNum]._string3 = CString(line);
288 
289 		_stringsMerged = false;
290 		_npcFlag = _npcId = 0;
291 	}
292 }
293 
getTextWidth(CScreenManager * screenManager)294 int CTextControl::getTextWidth(CScreenManager *screenManager) {
295 	mergeStrings();
296 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
297 	int textWidth = screenManager->stringWidth(_lines);
298 	screenManager->setFontNumber(oldFontNumber);
299 
300 	return textWidth;
301 }
302 
getTextHeight(CScreenManager * screenManager)303 int CTextControl::getTextHeight(CScreenManager *screenManager) {
304 	mergeStrings();
305 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
306 	int textHeight = screenManager->getTextBounds(_lines, _bounds.width() - 4);
307 	screenManager->setFontNumber(oldFontNumber);
308 
309 	return textHeight;
310 }
311 
deleteLastChar()312 void CTextControl::deleteLastChar() {
313 	if (!_array[_lineCount]._line.empty()) {
314 		_array[_lineCount]._line.deleteLastChar();
315 		_stringsMerged = false;
316 	}
317 }
318 
setNPC(int npcFlag,int npcId)319 void CTextControl::setNPC(int npcFlag, int npcId) {
320 	_npcFlag = npcFlag;
321 	_npcId = npcId;
322 }
323 
scrollUp(CScreenManager * screenManager)324 void CTextControl::scrollUp(CScreenManager *screenManager) {
325 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
326 	_scrollTop -= screenManager->getFontHeight();
327 	constrainScrollUp(screenManager);
328 	screenManager->setFontNumber(oldFontNumber);
329 }
330 
scrollDown(CScreenManager * screenManager)331 void CTextControl::scrollDown(CScreenManager *screenManager) {
332 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
333 	_scrollTop += screenManager->getFontHeight();
334 	constrainScrollDown(screenManager);
335 	screenManager->setFontNumber(oldFontNumber);
336 }
337 
scrollUpPage(CScreenManager * screenManager)338 void CTextControl::scrollUpPage(CScreenManager *screenManager) {
339 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
340 	_scrollTop -= getPageHeight(screenManager);
341 	constrainScrollUp(screenManager);
342 	screenManager->setFontNumber(oldFontNumber);
343 }
344 
scrollDownPage(CScreenManager * screenManager)345 void CTextControl::scrollDownPage(CScreenManager *screenManager) {
346 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
347 	_scrollTop += getPageHeight(screenManager);
348 	constrainScrollDown(screenManager);
349 	screenManager->setFontNumber(oldFontNumber);
350 }
351 
scrollToTop(CScreenManager * screenManager)352 void CTextControl::scrollToTop(CScreenManager *screenManager) {
353 	_scrollTop = 0;
354 }
355 
scrollToBottom(CScreenManager * screenManager)356 void CTextControl::scrollToBottom(CScreenManager *screenManager) {
357 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
358 	_scrollTop = getTextHeight(screenManager);
359 	constrainScrollDown(screenManager);
360 	screenManager->setFontNumber(oldFontNumber);
361 }
362 
constrainScrollUp(CScreenManager * screenManager)363 void CTextControl::constrainScrollUp(CScreenManager *screenManager) {
364 	if (_scrollTop < 0)
365 		_scrollTop = 0;
366 }
367 
constrainScrollDown(CScreenManager * screenManager)368 void CTextControl::constrainScrollDown(CScreenManager *screenManager) {
369 	// Figure out the maximum scroll amount allowed
370 	int maxScroll = getTextHeight(screenManager) - _bounds.height() - 4;
371 	if (maxScroll < 0)
372 		maxScroll = 0;
373 
374 	if (_scrollTop > maxScroll)
375 		_scrollTop = maxScroll;
376 }
377 
getPageHeight(CScreenManager * screenManager)378 int CTextControl::getPageHeight(CScreenManager *screenManager) {
379 	int textHeight = _bounds.height();
380 	int oldFontNumber = screenManager->setFontNumber(_fontNumber);
381 	int fontHeight = screenManager->getFontHeight();
382 	screenManager->setFontNumber(oldFontNumber);
383 
384 	if (fontHeight) {
385 		int lines = textHeight / fontHeight;
386 		if (lines > 1)
387 			--lines;
388 		return lines * fontHeight;
389 	} else {
390 		return 0;
391 	}
392 }
393 
addLine(const CString & str)394 void CTextControl::addLine(const CString &str) {
395 	addLine(str, _textR, _textG, _textB);
396 }
397 
addLine(const CString & str,uint color)398 void CTextControl::addLine(const CString &str, uint color) {
399 	addLine(str, color & 0xff, (color >> 8) & 0xff,
400 		(color >> 16) & 0xff);
401 }
402 
addLine(const CString & str,byte r,byte g,byte b)403 void CTextControl::addLine(const CString &str, byte r, byte g, byte b) {
404 	if (_lineCount == ((int)_array.size() - 1)) {
405 		// Lines array is full
406 		if (_array.size() > 1) {
407 			// Delete the oldest line, and add a new entry at the end
408 			_array.remove_at(0);
409 			_array.resize(_array.size() + 1);
410 		}
411 
412 		--_lineCount;
413 	}
414 
415 	setLineColor(_lineCount, r, g, b);
416 	appendText(str);
417 	++_lineCount;
418 }
419 
handleKey(char c)420 bool CTextControl::handleKey(char c) {
421 	switch (c) {
422 	case (char)Common::KEYCODE_BACKSPACE:
423 		deleteLastChar();
424 		break;
425 
426 	case (char)Common::KEYCODE_RETURN:
427 		return true;
428 
429 	default:
430 		if ((byte)c >= 32 && (byte)c <= 127)
431 			appendText(CString(c, 1));
432 		break;
433 	}
434 
435 	return false;
436 }
437 
showCursor(int mode)438 void CTextControl::showCursor(int mode) {
439 	CScreenManager *screenManager = CScreenManager::setCurrent();
440 	_textCursor = screenManager->_textCursor;
441 	if (_textCursor) {
442 		_textCursor->setPos(Point(0, 0));
443 		_textCursor->setSize(Point(2, 10));
444 		_textCursor->setColor(0, 0, 0);
445 		_textCursor->setBlinkRate(300);
446 		_textCursor->setMode(mode);
447 		_textCursor->setBounds(_bounds);
448 		_textCursor->show();
449 	}
450 }
451 
hideCursor()452 void CTextControl::hideCursor() {
453 	if (_textCursor) {
454 		_textCursor->setMode(-1);
455 		_textCursor->hide();
456 		_textCursor = nullptr;
457 	}
458 }
459 
getNPCNum(uint ident,uint startIndex)460 int CTextControl::getNPCNum(uint ident, uint startIndex) {
461 	if (!_stringsMerged) {
462 		mergeStrings();
463 		if (!_stringsMerged)
464 			return -1;
465 	}
466 
467 	uint size = _lines.size();
468 	if (startIndex < 5 || startIndex >= size)
469 		return -1;
470 
471 	// Loop backwards from the starting index to find an NPC ident sequence
472 	for (const char *strP = _lines.c_str() + startIndex;
473 			strP >= (_lines.c_str() + 5); --strP) {
474 		if (*strP == 26) {
475 			byte id = *(strP - 2);
476 			if (id == ident)
477 				return *(strP - 1);
478 			strP -= 3;
479 		} else if (*strP == 27) {
480 			strP -= 4;
481 		}
482 	}
483 
484 	return -1;
485 }
486 
setFontNumber(int fontNumber)487 void CTextControl::setFontNumber(int fontNumber) {
488 	if (fontNumber >= 0 && fontNumber <= 2)
489 		_fontNumber = fontNumber;
490 }
491 
492 } // End of namespace Titanic
493