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; 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; 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; 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; 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 "bladerunner/ui/ui_scroll_box.h"
24 
25 #include "bladerunner/audio_player.h"
26 #include "bladerunner/bladerunner.h"
27 #include "bladerunner/font.h"
28 #include "bladerunner/game_info.h"
29 #include "bladerunner/shape.h"
30 #include "bladerunner/time.h"
31 #include "bladerunner/game_constants.h"
32 #include "bladerunner/ui/kia.h"
33 
34 namespace BladeRunner {
35 
36 const Color256 UIScrollBox::k3DFrameColors[] = {
37 	{ 32, 32, 24 },
38 	{ 40, 40, 40 },
39 	{ 40, 40, 48 },
40 	{ 72, 64, 64 },
41 	{ 160, 136, 128 },
42 	{ 160, 136, 128 },
43 	{ 0, 0, 0 },
44 	{ 0, 0, 0 }
45 };
46 const Color256 UIScrollBox::kTextBackgroundColors[] = {
47 	{ 40, 56, 80 },
48 	{ 48, 64, 96 },
49 	{ 56, 72, 112 },
50 	{ 72, 88, 128 },
51 	{ 152, 192, 248 },
52 	{ 0, 0, 0 }
53 };
54 const Color256 UIScrollBox::kTextColors1[] = {
55 	{ 72, 104, 152 },
56 	{ 96, 120, 184 },
57 	{ 112, 144, 216 },
58 	{ 136, 168, 248 },
59 	{ 152, 192, 248 }
60 };
61 const Color256 UIScrollBox::kTextColors2[] = {
62 	{ 200, 216, 248 },
63 	{ 216, 224, 248 },
64 	{ 224, 232, 248 },
65 	{ 232, 240, 248 },
66 	{ 248, 248, 248 }
67 };
68 const Color256 UIScrollBox::kTextColors3[] = {
69 	{ 240, 232, 192 },
70 	{ 240, 232, 208 },
71 	{ 240, 240, 216 },
72 	{ 248, 240, 232 },
73 	{ 248, 248, 248 }
74 };
75 const Color256 UIScrollBox::kTextColors4[] = {
76 	{ 152, 112, 56 },
77 	{ 184, 144, 88 },
78 	{ 216, 184, 112 },
79 	{ 232, 208, 136 },
80 	{ 248, 224, 144 }
81 };
82 
UIScrollBox(BladeRunnerEngine * vm,UIScrollBoxClickedCallback * lineSelectedCallback,void * callbackData,int maxLineCount,int style,bool center,Common::Rect rect,Common::Rect scrollBarRect)83 UIScrollBox::UIScrollBox(BladeRunnerEngine *vm,
84 	                     UIScrollBoxClickedCallback *lineSelectedCallback,
85 	                     void *callbackData,
86 	                     int maxLineCount,
87 	                     int style,
88 	                     bool center,
89 	                     Common::Rect rect,
90 	                     Common::Rect scrollBarRect) : UIComponent(vm) {
91 
92 	_selectedLineState     = 0;
93 	_scrollUpButtonState   = 0;
94 	_scrollDownButtonState = 0;
95 	_scrollAreaUpState     = 0;
96 	_scrollAreaDownState   = 0;
97 	_scrollBarState        = 0;
98 
99 	_scrollUpButtonHover   = false;
100 	_scrollDownButtonHover = false;
101 	_scrollAreaUpHover     = false;
102 	_scrollAreaDownHover   = false;
103 	_scrollBarHover        = false;
104 
105 	_hoveredLine          = -1;
106 	_selectedLineIndex    = -1;
107 
108 	_lineSelectedCallback = lineSelectedCallback;
109 	_callbackData         = callbackData;
110 
111 	_isVisible  = false;
112 	_style      = style; // 0, 1 or (new) 2. "2" is similar to "1" but with solid background for main area and scroll bar
113 	_center     = center;
114 	_timeLastScroll    = _vm->_time->currentSystem();
115 	_timeLastCheckbox  = _vm->_time->currentSystem();
116 	_timeLastHighlight = _vm->_time->currentSystem();
117 
118 	_highlightFrame = 0;
119 
120 	_rect          = rect;
121 	_scrollBarRect = scrollBarRect;
122 	_scrollBarRect.right += 15; // right side was not used, but it's useful for determining if the control is selected
123 
124 	_lineCount    = 0;
125 	_maxLineCount = maxLineCount;
126 
127 	_firstLineVisible = 0;
128 	_maxLinesVisible  = _rect.height() / kLineHeight;
129 
130 	_mouseButton = false;
131 
132 	_rect.bottom = _rect.top + kLineHeight * _maxLinesVisible - 1;
133 
134 	_lines.resize(_maxLineCount);
135 	for (int i = 0; i < _maxLineCount; ++i) {
136 		_lines[i] = new Line();
137 		_lines[i]->lineData = -1;
138 		_lines[i]->flags = 0x00;
139 		_lines[i]->checkboxFrame = 5u;
140 	}
141 
142 	_mouseOver = false;
143 }
144 
~UIScrollBox()145 UIScrollBox::~UIScrollBox() {
146 	for (int i = 0; i < _maxLineCount; ++i) {
147 		delete _lines[i];
148 	}
149 }
150 
show()151 void UIScrollBox::show() {
152 	_selectedLineState     = 0;
153 	_scrollUpButtonState   = 0;
154 	_scrollDownButtonState = 0;
155 	_scrollAreaUpState     = 0;
156 	_scrollAreaDownState   = 0;
157 	_scrollBarState        = 0;
158 
159 	_hoveredLine       = -1;
160 	_selectedLineIndex = -1;
161 
162 	_scrollUpButtonHover   = false;
163 	_scrollDownButtonHover = false;
164 	_scrollAreaUpHover     = false;
165 	_scrollAreaDownHover   = false;
166 	_scrollBarHover        = false;
167 
168 	_timeLastScroll    = _vm->_time->currentSystem();
169 	_timeLastCheckbox  = _vm->_time->currentSystem();
170 	_timeLastHighlight = _vm->_time->currentSystem();
171 
172 	_highlightFrame = 0;
173 	_isVisible = true;
174 
175 	_mouseOver = false;
176 }
177 
hide()178 void UIScrollBox::hide() {
179 	_isVisible = false;
180 }
181 
isVisible()182 bool UIScrollBox::isVisible() {
183 	return _isVisible;
184 }
185 
hasFocus()186 bool UIScrollBox::hasFocus() {
187 	return _mouseOver;
188 }
189 
setBoxTop(int top)190 void UIScrollBox::setBoxTop(int top) {
191 	_rect.moveTo(_rect.left, top);
192 
193 	_rect.bottom = _rect.top + kLineHeight * _maxLinesVisible - 1;
194 }
195 
setBoxLeft(int left)196 void UIScrollBox::setBoxLeft(int left) {
197 	_rect.moveTo(left, _rect.top);
198 }
199 
setBoxWidth(uint16 width)200 void UIScrollBox::setBoxWidth(uint16 width) {
201 	_rect.setWidth(width);
202 }
203 
getBoxLeft()204 int UIScrollBox::getBoxLeft() {
205 	return _rect.left;
206 }
207 
getBoxWidth()208 uint16 UIScrollBox::getBoxWidth() {
209 	return _rect.width();
210 }
211 
setScrollbarTop(int top)212 void UIScrollBox::setScrollbarTop(int top) {
213 	_scrollBarRect.moveTo(_scrollBarRect.left, top);
214 }
215 
setScrollbarLeft(int left)216 void UIScrollBox::setScrollbarLeft(int left) {
217 	_scrollBarRect.moveTo(left, _scrollBarRect.top);
218 }
219 
setScrollbarWidth(uint16 width)220 void UIScrollBox::setScrollbarWidth(uint16 width) {
221 	_scrollBarRect.setWidth(width);
222 	_scrollBarRect.right += 15; // right side was not used, but it's useful for determining if the control is selected
223 }
224 
clearLines()225 void UIScrollBox::clearLines() {
226 	_lineCount = 0;
227 	_firstLineVisible = 0;
228 }
229 
addLine(const Common::String & text,int lineData,int flags)230 void UIScrollBox::addLine(const Common::String &text, int lineData, int flags) {
231 	_lines[_lineCount]->text = text;
232 	_lines[_lineCount]->lineData = lineData;
233 	_lines[_lineCount]->flags = flags;
234 
235 	++_lineCount;
236 }
237 
addLine(const char * text,int lineData,int flags)238 void UIScrollBox::addLine(const char *text, int lineData, int flags) {
239 	_lines[_lineCount]->text = text;
240 	_lines[_lineCount]->lineData = lineData;
241 	_lines[_lineCount]->flags = flags;
242 
243 	++_lineCount;
244 }
245 
sortLines()246 void UIScrollBox::sortLines() {
247 	qsort(_lines.data(), _lineCount, sizeof(Line *), &sortFunction);
248 }
249 
handleMouseMove(int mouseX,int mouseY)250 void UIScrollBox::handleMouseMove(int mouseX, int mouseY) {
251 	if (!_isVisible) {
252 		return;
253 	}
254 
255 	_mouseOver = _rect.contains(mouseX, mouseY) || _scrollBarRect.contains(mouseX, mouseY);
256 
257 	if (_rect.contains(mouseX, mouseY)) {
258 		int newHoveredLine = (mouseY - _rect.top) / 10 + _firstLineVisible;
259 		if (newHoveredLine >= _lineCount) {
260 			newHoveredLine = -1;
261 		}
262 
263 		if (newHoveredLine != _hoveredLine && newHoveredLine >= 0 && newHoveredLine < _lineCount) {
264 			if (_lines[newHoveredLine]->lineData >= 0 && _selectedLineState == 0) {
265 				int soundId = kSfxTEXT1;
266 				if (_lines[newHoveredLine]->flags & 0x01 ) {
267 					soundId = kSfxTEXT3;
268 				}
269 				_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(soundId), 100, 0, 0, 50, 0);
270 			}
271 		}
272 		_hoveredLine = newHoveredLine;
273 	} else {
274 		_hoveredLine = -1;
275 	}
276 
277 	_scrollUpButtonHover =
278 		(mouseX >= _scrollBarRect.left)
279 		&& (mouseX < _scrollBarRect.left + 15)
280 		&& (mouseY >= _scrollBarRect.top)
281 		&& (mouseY < _scrollBarRect.top + 8);
282 
283 	_scrollDownButtonHover =
284 		(mouseX >= _scrollBarRect.left)
285 		&& (mouseX < _scrollBarRect.left + 15)
286 		&& (mouseY > _scrollBarRect.bottom - 8)
287 		&& (mouseY <= _scrollBarRect.bottom);
288 
289 	int scrollAreaHeight = _scrollBarRect.bottom - _scrollBarRect.top - 15;
290 
291 	int scrollBarHeight = scrollAreaHeight;
292 	if (_lineCount > _maxLinesVisible) {
293 		scrollBarHeight = _maxLinesVisible * scrollAreaHeight / _lineCount;
294 	}
295 	if (scrollBarHeight < 16) {
296 		scrollBarHeight = 16;
297 	}
298 
299 	int scrollAreaEmptySize = scrollAreaHeight - scrollBarHeight;
300 
301 	int scrollBarY = 0;
302 	if (_lineCount > _maxLinesVisible) {
303 		scrollBarY = scrollAreaEmptySize * _firstLineVisible / (_lineCount - _maxLinesVisible);
304 	}
305 
306 	if (_scrollBarState == 2) {
307 		int v12 = scrollBarHeight / 2 + 8;
308 		if (mouseY - _scrollBarRect.top > v12 && _lineCount > _maxLinesVisible && scrollAreaEmptySize > 0) {
309 			_firstLineVisible = (_lineCount - _maxLinesVisible) * (mouseY - _scrollBarRect.top - v12) / scrollAreaEmptySize;
310 			if (_firstLineVisible > _lineCount - _maxLinesVisible) {
311 				_firstLineVisible = _lineCount - _maxLinesVisible;
312 			}
313 		} else {
314 			_firstLineVisible = 0;
315 		}
316 
317 		if (_lineCount <= _maxLinesVisible) {
318 			scrollBarY = 0;
319 		} else {
320 			scrollBarY = scrollAreaEmptySize * _firstLineVisible/ (_lineCount - _maxLinesVisible);
321 		}
322 	}
323 	scrollBarY = scrollBarY + _scrollBarRect.top + 8;
324 
325 	_scrollBarHover =
326 		(mouseX >= _scrollBarRect.left)
327 		&& (mouseX < _scrollBarRect.left + 15)
328 		&& (mouseY >= scrollBarY)
329 		&& (mouseY < scrollBarY + scrollBarHeight);
330 
331 	_scrollAreaUpHover =
332 		(mouseX >= _scrollBarRect.left)
333 		&& (mouseX < _scrollBarRect.left + 15)
334 		&& (mouseY >= _scrollBarRect.top + 8)
335 		&& (mouseY < scrollBarY);
336 
337 	_scrollAreaDownHover =
338 		(mouseX >= _scrollBarRect.left)
339 		&& (mouseX < _scrollBarRect.left + 15)
340 		&& (mouseY >= scrollBarY + scrollBarHeight)
341 		&& (mouseY < _scrollBarRect.bottom - 8);
342 }
343 
handleMouseDown(bool alternateButton)344 void UIScrollBox::handleMouseDown(bool alternateButton) {
345 	if (!_isVisible) {
346 		return;
347 	}
348 
349 	_mouseButton = alternateButton;
350 	if (_hoveredLine == -1) {
351 		_selectedLineState = 1;
352 	} else if (_selectedLineIndex == -1) {
353 		_selectedLineIndex = _hoveredLine;
354 		_selectedLineState = 2;
355 		if (_hoveredLine < _lineCount) {
356 			if (_lineSelectedCallback) {
357 				_lineSelectedCallback(_callbackData, this, _lines[_selectedLineIndex]->lineData, _mouseButton);
358 			}
359 
360 			if (_lines[_selectedLineIndex]->flags & 0x01) {
361 				_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxBEEP10), 100, 0, 0, 50, 0);
362 			}
363 		}
364 	}
365 	if (!alternateButton) {
366 		if (_scrollUpButtonHover) {
367 			_scrollUpButtonState = 2;
368 			_timeLastScroll = _vm->_time->currentSystem() - 160u;
369 		} else {
370 			_scrollUpButtonState = 1;
371 		}
372 		if (_scrollDownButtonHover) {
373 			_scrollDownButtonState = 2;
374 		} else {
375 			_scrollDownButtonState = 1;
376 		}
377 		if (_scrollBarHover) {
378 			_scrollBarState = 2;
379 		} else {
380 			_scrollBarState = 1;
381 		}
382 		if (_scrollAreaUpHover) {
383 			_scrollAreaUpState = 2;
384 			_timeLastScroll = _vm->_time->currentSystem() - 160u;
385 		} else {
386 			_scrollAreaUpState = 1;
387 		}
388 		if (_scrollAreaDownHover) {
389 			_scrollAreaDownState = 2;
390 			_timeLastScroll = _vm->_time->currentSystem() - 160u;
391 		} else {
392 			_scrollAreaDownState = 1;
393 		}
394 	}
395 }
396 
handleMouseUp(bool alternateButton)397 void UIScrollBox::handleMouseUp(bool alternateButton) {
398 	if (_isVisible) {
399 		if ( alternateButton == _mouseButton) {
400 			_selectedLineState = 0;
401 			_selectedLineIndex = -1;
402 		}
403 
404 		if (!alternateButton) {
405 			_scrollUpButtonState = 0;
406 			_scrollDownButtonState = 0;
407 			_scrollAreaUpState = 0;
408 			_scrollAreaDownState = 0;
409 			_scrollBarState = 0;
410 		}
411 	}
412 }
413 
handleMouseScroll(int direction)414 void UIScrollBox::handleMouseScroll(int direction) {
415 	if (_mouseOver) {
416 		if (direction > 0) {
417 			scrollDown();
418 		} else if (direction < 0) {
419 			scrollUp();
420 		}
421 	}
422 }
423 
getSelectedLineData()424 int UIScrollBox::getSelectedLineData() {
425 	if (_hoveredLine >= 0 && _selectedLineState != 1 && _hoveredLine < _lineCount) {
426 		return _lines[_hoveredLine]->lineData;
427 	}
428 	return -1;
429 }
430 
getLineText(int lineData)431 Common::String UIScrollBox::getLineText(int lineData) {
432 	if (hasLine(lineData)) {
433 		return _lines[_hoveredLine]->text;
434 	}
435 	return "";
436 }
437 
getMaxLinesVisible()438 int UIScrollBox::getMaxLinesVisible() {
439 	return _maxLinesVisible;
440 }
441 
getLineCount()442 int UIScrollBox::getLineCount() {
443 	return _lineCount;
444 }
445 
draw(Graphics::Surface & surface)446 void UIScrollBox::draw(Graphics::Surface &surface) {
447 	if (!_isVisible) {
448 		return;
449 	}
450 
451 	uint32 timeNow = _vm->_time->currentSystem();
452 
453 	// update scrolling
454 	if (_scrollUpButtonState == 2 && _scrollUpButtonHover) {
455 		// unsigned difference is intentional
456 		if ((timeNow - _timeLastScroll) > 160u) {
457 			scrollUp();
458 			_timeLastScroll = timeNow;
459 		}
460 	} else if (_scrollDownButtonState == 2 && _scrollDownButtonHover) {
461 		// unsigned difference is intentional
462 		if ((timeNow - _timeLastScroll) > 160u) {
463 			scrollDown();
464 			_timeLastScroll = timeNow;
465 		}
466 	} else if (_scrollAreaUpState == 2 && _scrollAreaUpHover) {
467 		// unsigned difference is intentional
468 		if ((timeNow - _timeLastScroll) > 160u) {
469 			_firstLineVisible -= _maxLinesVisible - 1;
470 			_firstLineVisible = CLIP(_firstLineVisible, 0, _lineCount - _maxLinesVisible);
471 			_timeLastScroll = timeNow;
472 		}
473 	} else if (_scrollAreaDownState == 2 && _scrollAreaDownHover) {
474 		// unsigned difference is intentional
475 		if ((timeNow - _timeLastScroll) > 160u) {
476 			_firstLineVisible += _maxLinesVisible - 1;
477 			_firstLineVisible = CLIP(_firstLineVisible, 0, _lineCount - _maxLinesVisible);
478 			_timeLastScroll = timeNow;
479 		}
480 	}
481 
482 	// update checkboxes
483 	// unsigned difference is intentional
484 	uint32 timeDiffCheckBox = timeNow - _timeLastCheckbox;
485 	if (timeDiffCheckBox > 67u) {
486 		_timeLastCheckbox = timeNow;
487 		for (int i = 0; i < _lineCount; ++i) {
488 			if (_lines[i]->flags & 0x01) { // has checkbox
489 				if (_lines[i]->flags & 0x02) { // checkbox checked
490 					if (_lines[i]->checkboxFrame < 5u) {
491 						_lines[i]->checkboxFrame += timeDiffCheckBox / 67u;
492 					}
493 					if (_lines[i]->checkboxFrame > 5u) {
494 						_lines[i]->checkboxFrame = 5u;
495 					}
496 				} else { // checkbox not checked
497 					if (_lines[i]->checkboxFrame > 0u) {
498 						_lines[i]->checkboxFrame =  (_lines[i]->checkboxFrame < (timeDiffCheckBox / 67u)) ? 0u : _lines[i]->checkboxFrame - (timeDiffCheckBox / 67u);
499 					}
500 					if (_lines[i]->checkboxFrame == 0u) { // original was < 0, int
501 						_lines[i]->checkboxFrame = 0u;
502 					}
503 				}
504 			}
505 		}
506 	}
507 
508 
509 	// update highlight
510 	// unsigned difference is intentional
511 	if ((timeNow - _timeLastHighlight) > 67u) {
512 		_timeLastHighlight = timeNow;
513 		_highlightFrame = (_highlightFrame + 1) % 8;
514 	}
515 
516 	// draw text lines
517 	int linesVisible = 0;
518 	int lastLineVisible = 0;
519 
520 	if (_maxLinesVisible < _lineCount - _firstLineVisible) {
521 		linesVisible = _maxLinesVisible;
522 		lastLineVisible = _firstLineVisible + _maxLinesVisible;
523 	} else {
524 		linesVisible = _lineCount - _firstLineVisible;
525 		lastLineVisible = _lineCount;
526 	}
527 
528 	if (_firstLineVisible < lastLineVisible) {
529 		int y = _rect.top;
530 		int y1 = _rect.top + 8;
531 		int y2 = _rect.top + 2;
532 		int i = _firstLineVisible;
533 		do {
534 			int startingColorIndex = 3;
535 			if (i - _firstLineVisible < 3) {
536 				startingColorIndex = i - _firstLineVisible;
537 			}
538 
539 			int endingColorIndex = 3;
540 			if (i - _firstLineVisible >= linesVisible - 3) {
541 				endingColorIndex = linesVisible - (i - _firstLineVisible + 1);
542 			}
543 
544 			int colorIndex = endingColorIndex;
545 			if (startingColorIndex < endingColorIndex) {
546 				colorIndex = startingColorIndex;
547 			}
548 
549 			bool v35 = false;
550 			int color = 0;
551 
552 			if ((((_selectedLineState == 0 && i == _hoveredLine) || (_selectedLineState == 2 && i == _selectedLineIndex && _selectedLineIndex == _hoveredLine)) && _lines[i]->lineData != -1) || _lines[i]->flags & 0x04) {
553 				v35 = true;
554 				if (_style) {
555 					color = surface.format.RGBToColor(kTextColors2[colorIndex].r, kTextColors2[colorIndex].g, kTextColors2[colorIndex].b);
556 				} else {
557 					color = surface.format.RGBToColor(kTextColors3[colorIndex].r, kTextColors3[colorIndex].g, kTextColors3[colorIndex].b);
558 				}
559 			}
560 			else {
561 				if (_style) {
562 					color = surface.format.RGBToColor(kTextColors1[colorIndex].r, kTextColors1[colorIndex].g, kTextColors1[colorIndex].b);
563 				} else {
564 					color = surface.format.RGBToColor(kTextColors4[colorIndex].r, kTextColors4[colorIndex].g, kTextColors4[colorIndex].b);
565 				}
566 			}
567 
568 			int x = _rect.left;
569 
570 			if (_lines[i]->flags & 0x01) { // has checkbox
571 				int checkboxShapeId = 0;
572 				if (_style == 0) {
573 					if (_lines[i]->checkboxFrame || v35) {
574 						if (_lines[i]->checkboxFrame != 5u || v35) {
575 							checkboxShapeId = _lines[i]->checkboxFrame + 62u;
576 						} else {
577 							checkboxShapeId = 61;
578 						}
579 					} else {
580 						checkboxShapeId = 60;
581 					}
582 				} else if (_lines[i]->checkboxFrame || v35) {
583 					if (_lines[i]->checkboxFrame != 5u || v35) {
584 						checkboxShapeId = _lines[i]->checkboxFrame + 54u;
585 					} else {
586 						checkboxShapeId = 53;
587 					}
588 				} else {
589 					checkboxShapeId = 52;
590 				}
591 				_vm->_kia->_shapes->get(checkboxShapeId)->draw(surface, x - 1, y);
592 				x += 11;
593 			}
594 
595 			if (_lines[i]->flags & 0x10) { // highlighted line
596 				if (_lines[i]->flags & 0x20) {
597 					int highlightShapeId = _highlightFrame;
598 					if (highlightShapeId > 4) {
599 						highlightShapeId = 8 - highlightShapeId;
600 					}
601 					_vm->_kia->_shapes->get(highlightShapeId + 85)->draw(surface, x, y2);
602 				}
603 				x += 6;
604 			}
605 
606 			if (_lines[i]->flags & 0x08) { // has background rectangle
607 				int colorBackground = 0;
608 				if (_vm->_cutContent && (_lines[i]->flags & 0x40)) {
609 					// A KIA clue marked as hidden/private, but already shared with Mainframe
610 					// Note, proper hidden clues will not have this mark and will get colorBackground
611 					// from below (case _style > 0)
612 					colorBackground = surface.format.RGBToColor(80, 46, 22);
613 				} else {
614 					if (_style == 2) {
615 						colorBackground = surface.format.RGBToColor(kTextBackgroundColors[colorIndex].r / 8, kTextBackgroundColors[colorIndex].g / 8, kTextBackgroundColors[colorIndex].b / 8);
616 					} else if (_style > 0) {
617 						colorBackground = surface.format.RGBToColor(kTextBackgroundColors[colorIndex].r, kTextBackgroundColors[colorIndex].g, kTextBackgroundColors[colorIndex].b);
618 					} else {
619 						colorBackground = surface.format.RGBToColor(80, 56, 32);
620 					}
621 				}
622 
623 				if (_style == 2) {
624 					// New: style = 2 (original unused)
625 					// original behavior -- No padding between the colored background of lines, simulate solid background (gradient)
626 					surface.fillRect(Common::Rect(CLIP(x - 1, 0, 639), y, _rect.right + 1, y + kLineHeight), colorBackground);
627 				} else {
628 					// original behavior -- there is padding between the colored background of lines
629 					surface.fillRect(Common::Rect(x, y, _rect.right + 1, y1 + 1), colorBackground);
630 				}
631 			}
632 
633 			if (_center) {
634 				x = _rect.left + (_rect.width() - _vm->_mainFont->getStringWidth(_lines[i]->text)) / 2;
635 			}
636 
637 			_vm->_mainFont->drawString(&surface, _lines[i]->text, x, y, surface.w, color);
638 
639 			y1 += kLineHeight;
640 			y2 += kLineHeight;
641 			y += kLineHeight;
642 			++i;
643 		} while (i < lastLineVisible);
644 	}
645 
646 	if (_style == 2 && getLineCount() >= getMaxLinesVisible()) {
647 		// New: style = 2 (original unused)
648 		// Solid background color for scrollbar
649 		int scrollBarFillColor = surface.format.RGBToColor(k3DFrameColors[0].r / 2, k3DFrameColors[0].g / 2, k3DFrameColors[0].b / 2);
650 		surface.fillRect(Common::Rect(_scrollBarRect.left, _scrollBarRect.top, CLIP(_scrollBarRect.left + 15, 0, 639), _scrollBarRect.bottom), scrollBarFillColor);
651 	}
652 
653 	if (_style != 2
654 	    || (_style == 2 && getLineCount() >= getMaxLinesVisible())
655 	) {
656 		// draw scroll up button
657 		int scrollUpButtonShapeId = 0;
658 		if (_scrollUpButtonState) {
659 			if (_scrollUpButtonState == 2) {
660 				if (_scrollUpButtonHover) {
661 					scrollUpButtonShapeId = 72;
662 				} else {
663 					scrollUpButtonShapeId = 71;
664 				}
665 			} else {
666 				scrollUpButtonShapeId = 70;
667 			}
668 		} else if (_scrollUpButtonHover) {
669 			scrollUpButtonShapeId = 71;
670 		} else {
671 			scrollUpButtonShapeId = 70;
672 		}
673 		_vm->_kia->_shapes->get(scrollUpButtonShapeId)->draw(surface, _scrollBarRect.left, _scrollBarRect.top);
674 
675 		// draw scroll down button
676 		int scrollDownButtonShapeId = 0;
677 		if (_scrollDownButtonState) {
678 			if (_scrollDownButtonState == 2) {
679 				if (_scrollDownButtonHover) {
680 					scrollDownButtonShapeId = 75;
681 				} else {
682 					scrollDownButtonShapeId = 74;
683 				}
684 			} else {
685 				scrollDownButtonShapeId = 73;
686 			}
687 		} else if (_scrollDownButtonHover) {
688 			scrollDownButtonShapeId = 74;
689 		} else {
690 			scrollDownButtonShapeId = 73;
691 		}
692 		_vm->_kia->_shapes->get(scrollDownButtonShapeId)->draw(surface, _scrollBarRect.left, _scrollBarRect.bottom - 7);
693 
694 		int scrollAreaSize = _scrollBarRect.bottom - (_scrollBarRect.top + 15);
695 		int scrollBarHeight = 0;
696 		if (_lineCount <= _maxLinesVisible) {
697 			scrollBarHeight = _scrollBarRect.bottom - (_scrollBarRect.top + 15);
698 		} else {
699 			scrollBarHeight = _maxLinesVisible * scrollAreaSize / _lineCount;
700 		}
701 		scrollBarHeight = MAX(scrollBarHeight, 16);
702 
703 		int v56 = 0;
704 		if (_lineCount <= _maxLinesVisible) {
705 			v56 = 0;
706 		} else {
707 			v56 = _firstLineVisible * (scrollAreaSize - scrollBarHeight) / (_lineCount - _maxLinesVisible);
708 		}
709 
710 		int v58 = v56 + _scrollBarRect.top + 8;
711 
712 		if (_scrollBarState == 2) {
713 			draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v58, _scrollBarRect.left + 15, v58 + scrollBarHeight), 1, 1);
714 		} else if (!_scrollBarState && _scrollBarHover) {
715 			draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v56 + _scrollBarRect.top + 8, _scrollBarRect.left + 15, v58 + scrollBarHeight), 0, 1);
716 		} else {
717 			draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v58, _scrollBarRect.left + 15, v58 + scrollBarHeight), 0, 0);
718 		}
719 	}
720 }
721 
checkAll()722 void UIScrollBox::checkAll() {
723 	for (int i = 0; i < _lineCount; ++i) {
724 		if (_lines[i]->flags & 0x01) {
725 			_lines[i]->flags |= 0x02;
726 		}
727 	}
728 }
729 
uncheckAll()730 void UIScrollBox::uncheckAll() {
731 	for (int i = 0; i < _lineCount; ++i) {
732 		if (_lines[i]->flags & 0x01) {
733 			_lines[i]->flags &= ~0x02;
734 		}
735 	}
736 }
737 
toggleCheckBox(int lineData)738 void UIScrollBox::toggleCheckBox(int lineData) {
739 	int i = findLine(lineData);
740 	if (i != -1) {
741 		if (_lines[i]->flags & 0x02) {
742 			_lines[i]->flags &= ~0x02;
743 		} else {
744 			_lines[i]->flags |= 0x02;
745 		}
746 	}
747 }
748 
hasLine(int lineData)749 bool UIScrollBox::hasLine(int lineData) {
750 	return findLine(lineData) != -1;
751 }
752 
resetHighlight(int lineData)753 void UIScrollBox::resetHighlight(int lineData) {
754 	int i = findLine(lineData);
755 	if (i != -1) {
756 		_lines[i]->flags &= ~0x20;
757 	}
758 }
759 
setFlags(int lineData,int flags)760 void UIScrollBox::setFlags(int lineData, int flags) {
761 	int i = findLine(lineData);
762 	if (i != -1) {
763 		_lines[i]->flags |= flags;
764 	}
765 }
766 
resetFlags(int lineData,int flags)767 void UIScrollBox::resetFlags(int lineData, int flags) {
768 	int i = findLine(lineData);
769 	if (i != -1) {
770 		_lines[i]->flags &= ~flags;
771 	}
772 }
773 
sortFunction(const void * item1,const void * item2)774 int UIScrollBox::sortFunction(const void *item1, const void *item2) {
775 	Line *line1 = *(Line * const *)item1;
776 	Line *line2 = *(Line * const *)item2;
777 	return line1->text.compareToIgnoreCase(line2->text);
778 }
779 
draw3DFrame(Graphics::Surface & surface,Common::Rect rect,bool pressed,int style)780 void UIScrollBox::draw3DFrame(Graphics::Surface &surface, Common::Rect rect, bool pressed, int style) {
781 	int color1, color2;
782 
783 	if (pressed) {
784 		color1 = surface.format.RGBToColor(k3DFrameColors[style + 6].r, k3DFrameColors[style + 6].g, k3DFrameColors[style + 6].b);
785 		color2 = surface.format.RGBToColor(k3DFrameColors[style + 4].r, k3DFrameColors[style + 4].g, k3DFrameColors[style + 4].b);
786 	} else {
787 		color1 = surface.format.RGBToColor(k3DFrameColors[style + 4].r, k3DFrameColors[style + 4].g, k3DFrameColors[style + 4].b);
788 		color2 = surface.format.RGBToColor(k3DFrameColors[style + 6].r, k3DFrameColors[style + 6].g, k3DFrameColors[style + 6].b);
789 	}
790 
791 	int color3 = surface.format.RGBToColor(k3DFrameColors[style].r, k3DFrameColors[style].g, k3DFrameColors[style].b);
792 	int fillColor = surface.format.RGBToColor(k3DFrameColors[style + 2].r, k3DFrameColors[style + 2].g, k3DFrameColors[style + 2].b);
793 
794 	surface.fillRect(Common::Rect(rect.left + 1, rect.top + 1, rect.right - 1, rect.bottom - 1), fillColor);
795 
796 	surface.hLine(rect.left + 1,  rect.top,        rect.right - 2,  color1);
797 	surface.hLine(rect.left + 1,  rect.bottom - 1, rect.right - 2,  color2);
798 	surface.vLine(rect.left,      rect.top,        rect.bottom - 2, color1);
799 	surface.vLine(rect.right - 1, rect.top + 1,    rect.bottom - 1, color2);
800 	surface.hLine(rect.right - 1, rect.top,        rect.right - 1,  color3);
801 	surface.hLine(rect.left,      rect.bottom - 1, rect.left,       color3);
802 }
803 
scrollUp()804 void UIScrollBox::scrollUp() {
805 	if (_firstLineVisible > 0) {
806 		--_firstLineVisible;
807 	}
808 }
809 
scrollDown()810 void UIScrollBox::scrollDown() {
811 	if (_lineCount - _firstLineVisible > _maxLinesVisible) {
812 		++_firstLineVisible;
813 	}
814 }
815 
findLine(int lineData)816 int UIScrollBox::findLine(int lineData) {
817 	for (int i = 0; i < _lineCount; ++i) {
818 		if (_lines[i]->lineData == lineData) {
819 			return i;
820 		}
821 	}
822 	return -1;
823 }
824 
825 } // End of namespace BladeRunner
826