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 "common/util.h"
24 #include "common/stack.h"
25 #include "common/system.h"
26 #include "graphics/primitives.h"
27
28 #include "sci/sci.h"
29 #include "sci/event.h"
30 #include "sci/engine/kernel.h"
31 #include "sci/engine/state.h"
32 #include "sci/engine/selector.h"
33 #include "sci/graphics/compare.h"
34 #include "sci/graphics/ports.h"
35 #include "sci/graphics/paint16.h"
36 #include "sci/graphics/font.h"
37 #include "sci/graphics/screen.h"
38 #include "sci/graphics/text16.h"
39 #include "sci/graphics/controls16.h"
40
41 namespace Sci {
42
GfxControls16(SegManager * segMan,GfxPorts * ports,GfxPaint16 * paint16,GfxText16 * text16,GfxScreen * screen)43 GfxControls16::GfxControls16(SegManager *segMan, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen)
44 : _segMan(segMan), _ports(ports), _paint16(paint16), _text16(text16), _screen(screen) {
45 _texteditBlinkTime = 0;
46 _texteditCursorVisible = false;
47 }
48
~GfxControls16()49 GfxControls16::~GfxControls16() {
50 }
51
52 const char controlListUpArrow[2] = { 0x18, 0 };
53 const char controlListDownArrow[2] = { 0x19, 0 };
54
drawListControl(Common::Rect rect,reg_t obj,int16 maxChars,int16 count,const Common::String * entries,GuiResourceId fontId,int16 upperPos,int16 cursorPos,bool isAlias)55 void GfxControls16::drawListControl(Common::Rect rect, reg_t obj, int16 maxChars, int16 count, const Common::String *entries, GuiResourceId fontId, int16 upperPos, int16 cursorPos, bool isAlias) {
56 Common::Rect workerRect = rect;
57 GuiResourceId oldFontId = _text16->GetFontId();
58 int16 oldPenColor = _ports->_curPort->penClr;
59 uint16 fontSize = 0;
60 int16 i;
61 int16 lastYpos;
62
63 // draw basic window
64 _paint16->eraseRect(workerRect);
65 workerRect.grow(1);
66 _paint16->frameRect(workerRect);
67
68 // draw UP/DOWN arrows
69 // we draw UP arrow one pixel lower than sierra did, because it looks nicer. Also the DOWN arrow has one pixel
70 // line inbetween as well
71 // They "fixed" this in SQ4 by having the arrow character start one pixel line later, we don't adjust there
72 if (g_sci->getGameId() != GID_SQ4)
73 workerRect.top++;
74 _text16->Box(controlListUpArrow, false, workerRect, SCI_TEXT16_ALIGNMENT_CENTER, 0);
75 workerRect.top = workerRect.bottom - 10;
76 _text16->Box(controlListDownArrow, false, workerRect, SCI_TEXT16_ALIGNMENT_CENTER, 0);
77
78 // Draw inner lines
79 workerRect.top = rect.top + 9;
80 workerRect.bottom -= 10;
81 _paint16->frameRect(workerRect);
82 workerRect.grow(-1);
83
84 _text16->SetFont(fontId);
85 fontSize = _ports->_curPort->fontHeight;
86 _ports->penColor(_ports->_curPort->penClr); _ports->backColor(_ports->_curPort->backClr);
87 workerRect.bottom = workerRect.top + fontSize;
88 lastYpos = rect.bottom - fontSize;
89
90 // Write actual text
91 for (i = upperPos; i < count; i++) {
92 _paint16->eraseRect(workerRect);
93 const Common::String &listEntry = entries[i];
94 if (listEntry[0]) {
95 _ports->moveTo(workerRect.left, workerRect.top);
96 _text16->Draw(listEntry.c_str(), 0, MIN<int16>(maxChars, listEntry.size()), oldFontId, oldPenColor);
97 if ((!isAlias) && (i == cursorPos)) {
98 _paint16->invertRect(workerRect);
99 }
100 }
101 workerRect.translate(0, fontSize);
102 if (workerRect.bottom > lastYpos)
103 break;
104 }
105
106 _text16->SetFont(oldFontId);
107 }
108
texteditCursorDraw(Common::Rect rect,const char * text,uint16 curPos)109 void GfxControls16::texteditCursorDraw(Common::Rect rect, const char *text, uint16 curPos) {
110 int16 textWidth, i;
111 if (!_texteditCursorVisible) {
112 textWidth = 0;
113 for (i = 0; i < curPos; i++) {
114 textWidth += _text16->_font->getCharWidth((unsigned char)text[i]);
115 }
116 _texteditCursorRect.left = rect.left + textWidth;
117 _texteditCursorRect.top = rect.top;
118 _texteditCursorRect.bottom = _texteditCursorRect.top + _text16->_font->getHeight();
119 _texteditCursorRect.right = _texteditCursorRect.left + (text[curPos] == 0 ? 1 : _text16->_font->getCharWidth((unsigned char)text[curPos]));
120 _paint16->invertRect(_texteditCursorRect);
121 _paint16->bitsShow(_texteditCursorRect);
122 _texteditCursorVisible = true;
123 texteditSetBlinkTime();
124 }
125 }
126
texteditCursorErase()127 void GfxControls16::texteditCursorErase() {
128 if (_texteditCursorVisible) {
129 _paint16->invertRect(_texteditCursorRect);
130 _paint16->bitsShow(_texteditCursorRect);
131 _texteditCursorVisible = false;
132 }
133 texteditSetBlinkTime();
134 }
135
texteditSetBlinkTime()136 void GfxControls16::texteditSetBlinkTime() {
137 _texteditBlinkTime = g_system->getMillis() + (30 * 1000 / 60);
138 }
139
kernelTexteditChange(reg_t controlObject,reg_t eventObject)140 void GfxControls16::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
141 uint16 cursorPos = readSelectorValue(_segMan, controlObject, SELECTOR(cursor));
142 uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max));
143 reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
144 Common::String text;
145 uint16 textSize, eventType, eventKey = 0, modifiers = 0;
146 bool textChanged = false;
147 bool textAddChar = false;
148 Common::Rect rect;
149
150 if (textReference.isNull())
151 error("kEditControl called on object that doesn't have a text reference");
152 text = _segMan->getString(textReference);
153
154 uint16 oldCursorPos = cursorPos;
155
156 if (!eventObject.isNull()) {
157 textSize = text.size();
158 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
159
160 switch (eventType) {
161 case kSciEventMousePress:
162 // TODO: Implement mouse support for cursor change
163 break;
164 case kSciEventKeyDown:
165 eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message));
166 modifiers = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers));
167 switch (eventKey) {
168 case kSciKeyBackspace:
169 if (cursorPos > 0) {
170 cursorPos--; text.deleteChar(cursorPos);
171 textChanged = true;
172 }
173 break;
174 case kSciKeyDelete:
175 if (cursorPos < textSize) {
176 text.deleteChar(cursorPos);
177 textChanged = true;
178 }
179 break;
180 case kSciKeyHome:
181 cursorPos = 0; textChanged = true;
182 break;
183 case kSciKeyEnd:
184 cursorPos = textSize; textChanged = true;
185 break;
186 case kSciKeyLeft:
187 if (cursorPos > 0) {
188 cursorPos--; textChanged = true;
189 }
190 break;
191 case kSciKeyRight:
192 if (cursorPos + 1 <= textSize) {
193 cursorPos++; textChanged = true;
194 }
195 break;
196 case kSciKeyEtx:
197 if (modifiers & kSciKeyModCtrl) {
198 // Control-C erases the whole line
199 cursorPos = 0; text.clear();
200 textChanged = true;
201 }
202 break;
203 default:
204 if ((modifiers & kSciKeyModCtrl) && eventKey == 99) {
205 // Control-C in earlier SCI games (SCI0 - SCI1 middle)
206 // Control-C erases the whole line
207 cursorPos = 0; text.clear();
208 textChanged = true;
209 } else if (eventKey > 31 && eventKey < 256 && textSize < maxChars) {
210 // insert pressed character
211 textAddChar = true;
212 textChanged = true;
213 }
214 break;
215 }
216 break;
217 }
218 }
219
220 if (g_sci->getVocabulary() && !textChanged && oldCursorPos != cursorPos) {
221 assert(!textAddChar);
222 textChanged = g_sci->getVocabulary()->checkAltInput(text, cursorPos);
223 }
224
225 if (textChanged) {
226 GuiResourceId oldFontId = _text16->GetFontId();
227 GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
228 rect = g_sci->_gfxCompare->getNSRect(controlObject);
229
230 _text16->SetFont(fontId);
231 if (textAddChar) {
232
233 const char *textPtr = text.c_str();
234
235 // We check if we are really able to add the new char
236 uint16 textWidth = 0;
237 while (*textPtr)
238 textWidth += _text16->_font->getCharWidth((byte)*textPtr++);
239 textWidth += _text16->_font->getCharWidth(eventKey);
240
241 // Does it fit?
242 if (textWidth >= rect.width()) {
243 _text16->SetFont(oldFontId);
244 return;
245 }
246
247 text.insertChar(eventKey, cursorPos++);
248
249 // Note: the following checkAltInput call might make the text
250 // too wide to fit, but SSCI fails to check that too.
251 }
252 if (g_sci->getVocabulary())
253 g_sci->getVocabulary()->checkAltInput(text, cursorPos);
254 texteditCursorErase();
255 _paint16->eraseRect(rect);
256 _text16->Box(text.c_str(), false, rect, SCI_TEXT16_ALIGNMENT_LEFT, -1);
257 _paint16->bitsShow(rect);
258 texteditCursorDraw(rect, text.c_str(), cursorPos);
259 _text16->SetFont(oldFontId);
260 // Write back string
261 _segMan->strcpy(textReference, text.c_str());
262 } else {
263 if (g_system->getMillis() >= _texteditBlinkTime) {
264 _paint16->invertRect(_texteditCursorRect);
265 _paint16->bitsShow(_texteditCursorRect);
266 _texteditCursorVisible = !_texteditCursorVisible;
267 texteditSetBlinkTime();
268 }
269 }
270
271 writeSelectorValue(_segMan, controlObject, SELECTOR(cursor), cursorPos);
272 }
273
getPicNotValid()274 int GfxControls16::getPicNotValid() {
275 if (getSciVersion() >= SCI_VERSION_1_1)
276 return _screen->_picNotValidSci11;
277 return _screen->_picNotValid;
278 }
279
kernelDrawButton(Common::Rect rect,reg_t obj,const char * text,uint16 languageSplitter,int16 fontId,int16 style,bool hilite)280 void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 style, bool hilite) {
281 int16 sci0EarlyPen = 0, sci0EarlyBack = 0;
282 if (!hilite) {
283 if (getSciVersion() == SCI_VERSION_0_EARLY) {
284 // SCI0early actually used hardcoded green/black buttons instead of using the port colors
285 sci0EarlyPen = _ports->_curPort->penClr;
286 sci0EarlyBack = _ports->_curPort->backClr;
287 _ports->penColor(0);
288 _ports->backColor(2);
289 }
290 rect.grow(1);
291 _paint16->eraseRect(rect);
292 _paint16->frameRect(rect);
293 rect.grow(-2);
294 _ports->textGreyedOutput(!(style & SCI_CONTROLS_STYLE_ENABLED));
295 _text16->Box(text, languageSplitter, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId);
296 _ports->textGreyedOutput(false);
297 rect.grow(1);
298 if (style & SCI_CONTROLS_STYLE_SELECTED)
299 _paint16->frameRect(rect);
300 if (!getPicNotValid()) {
301 rect.grow(1);
302 _paint16->bitsShow(rect);
303 }
304 if (getSciVersion() == SCI_VERSION_0_EARLY) {
305 _ports->penColor(sci0EarlyPen);
306 _ports->backColor(sci0EarlyBack);
307 }
308 } else {
309 // SCI0early used xor to invert button rectangles resulting in pink/white buttons
310 if (getSciVersion() == SCI_VERSION_0_EARLY)
311 _paint16->invertRectViaXOR(rect);
312 else
313 _paint16->invertRect(rect);
314 _paint16->bitsShow(rect);
315 }
316 }
317
kernelDrawText(Common::Rect rect,reg_t obj,const char * text,uint16 languageSplitter,int16 fontId,TextAlignment alignment,int16 style,bool hilite)318 void GfxControls16::kernelDrawText(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, TextAlignment alignment, int16 style, bool hilite) {
319 if (!hilite) {
320 rect.grow(1);
321 _paint16->eraseRect(rect);
322 rect.grow(-1);
323 _text16->Box(text, languageSplitter, false, rect, alignment, fontId);
324 if (style & SCI_CONTROLS_STYLE_SELECTED) {
325 _paint16->frameRect(rect);
326 }
327 if (!getPicNotValid())
328 _paint16->bitsShow(rect);
329 } else {
330 _paint16->invertRect(rect);
331 _paint16->bitsShow(rect);
332 }
333 }
334
kernelDrawTextEdit(Common::Rect rect,reg_t obj,const char * text,uint16 languageSplitter,int16 fontId,int16 mode,int16 style,int16 cursorPos,int16 maxChars,bool hilite)335 void GfxControls16::kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite) {
336 Common::Rect textRect = rect;
337 uint16 oldFontId = _text16->GetFontId();
338
339 rect.grow(1);
340 _texteditCursorVisible = false;
341 texteditCursorErase();
342 _paint16->eraseRect(rect);
343 _text16->Box(text, languageSplitter, false, textRect, SCI_TEXT16_ALIGNMENT_LEFT, fontId);
344 _paint16->frameRect(rect);
345 if (style & SCI_CONTROLS_STYLE_SELECTED) {
346 _text16->SetFont(fontId);
347 rect.grow(-1);
348 texteditCursorDraw(rect, text, cursorPos);
349 _text16->SetFont(oldFontId);
350 rect.grow(1);
351 }
352 if (!getPicNotValid())
353 _paint16->bitsShow(rect);
354 }
355
kernelDrawIcon(Common::Rect rect,reg_t obj,GuiResourceId viewId,int16 loopNo,int16 celNo,int16 priority,int16 style,bool hilite)356 void GfxControls16::kernelDrawIcon(Common::Rect rect, reg_t obj, GuiResourceId viewId, int16 loopNo, int16 celNo, int16 priority, int16 style, bool hilite) {
357 if (!hilite) {
358 _paint16->drawCelAndShow(viewId, loopNo, celNo, rect.left, rect.top, priority, 0);
359 if (style & 0x20) {
360 _paint16->frameRect(rect);
361 }
362 if (!getPicNotValid())
363 _paint16->bitsShow(rect);
364 } else {
365 _paint16->invertRect(rect);
366 _paint16->bitsShow(rect);
367 }
368 }
369
kernelDrawList(Common::Rect rect,reg_t obj,int16 maxChars,int16 count,const Common::String * entries,GuiResourceId fontId,int16 style,int16 upperPos,int16 cursorPos,bool isAlias,bool hilite)370 void GfxControls16::kernelDrawList(Common::Rect rect, reg_t obj, int16 maxChars, int16 count, const Common::String *entries, GuiResourceId fontId, int16 style, int16 upperPos, int16 cursorPos, bool isAlias, bool hilite) {
371 if (!hilite) {
372 drawListControl(rect, obj, maxChars, count, entries, fontId, upperPos, cursorPos, isAlias);
373 rect.grow(1);
374 if (isAlias && (style & SCI_CONTROLS_STYLE_SELECTED)) {
375 _paint16->frameRect(rect);
376 }
377 if (!getPicNotValid())
378 _paint16->bitsShow(rect);
379 }
380 }
381
382 } // End of namespace Sci
383