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  * Additional copyright for this file:
8  * Copyright (C) 1994-1998 Revolution Software Ltd.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24 
25 
26 #include "common/rect.h"
27 #include "common/system.h"
28 
29 #include "sword2/sword2.h"
30 #include "sword2/defs.h"
31 #include "sword2/header.h"
32 #include "sword2/controls.h"
33 #include "sword2/mouse.h"
34 #include "sword2/resman.h"
35 #include "sword2/screen.h"
36 #include "sword2/sound.h"
37 
38 #define	MAX_STRING_LEN    64	// 20 was too low; better to be safe ;)
39 #define CHARACTER_OVERLAP 2	// overlap characters by 3 pixels
40 
41 // our fonts start on SPACE character (32)
42 #define SIZE_OF_CHAR_SET  (256 - 32)
43 
44 namespace Sword2 {
45 
46 static int baseSlot = 0;
47 
48 class Widget;
49 
50 /**
51  * Base class for all widgets.
52  */
53 
54 class Widget {
55 protected:
56 	Sword2Engine *_vm;
57 	Dialog *_parent;
58 
59 	SpriteInfo *_sprites;
60 
61 	struct WidgetSurface {
62 		byte *_surface;
63 		bool _original;
64 	};
65 
66 	WidgetSurface *_surfaces;
67 	int _numStates;
68 	int _state;
69 
70 	Common::Rect _hitRect;
71 
72 public:
73 	Widget(Dialog *parent, int states);
74 
75 	virtual ~Widget();
76 
77 	void createSurfaceImage(int state, uint32 res, int x, int y, uint32 pc);
78 	void linkSurfaceImage(Widget *from, int state, int x, int y);
79 
80 	void createSurfaceImages(uint32 res, int x, int y);
81 	void linkSurfaceImages(Widget *from, int x, int y);
82 
83 	void setHitRect(int x, int y, int width, int height);
84 	bool isHit(int16 x, int16 y);
85 
86 	void setState(int state);
87 	int getState();
88 
89 	virtual void paint(Common::Rect *clipRect = NULL);
90 
onMouseEnter()91 	virtual void onMouseEnter() {}
onMouseExit()92 	virtual void onMouseExit() {}
onMouseMove(int x,int y)93 	virtual void onMouseMove(int x, int y) {}
onMouseDown(int x,int y)94 	virtual void onMouseDown(int x, int y) {}
onMouseUp(int x,int y)95 	virtual void onMouseUp(int x, int y) {}
onWheelUp(int x,int y)96 	virtual void onWheelUp(int x, int y) {}
onWheelDown(int x,int y)97 	virtual void onWheelDown(int x, int y) {}
onKey(KeyboardEvent * ke)98 	virtual void onKey(KeyboardEvent *ke) {}
onTick()99 	virtual void onTick() {}
100 
releaseMouse(int x,int y)101 	virtual void releaseMouse(int x, int y) {}
102 };
103 
104 /**
105  * This class is used to draw text in dialogs, buttons, etc.
106  */
107 
108 class FontRendererGui {
109 private:
110 	Sword2Engine *_vm;
111 
112 	struct Glyph {
113 		byte *_data;
114 		int _width;
115 		int _height;
116 	};
117 
118 	Glyph _glyph[SIZE_OF_CHAR_SET];
119 
120 public:
121 	enum {
122 		kAlignLeft,
123 		kAlignRight,
124 		kAlignCenter
125 	};
126 
127 	FontRendererGui(Sword2Engine *vm, int fontId);
128 	~FontRendererGui();
129 
130 	void fetchText(uint32 textId, byte *buf);
131 
132 	int getCharWidth(byte c);
133 	int getCharHeight(byte c);
134 
135 	int getTextWidth(byte *text);
136 	int getTextWidth(uint32 textId);
137 
138 	void drawText(byte *text, int x, int y, int alignment = kAlignLeft);
139 	void drawText(uint32 textId, int x, int y, int alignment = kAlignLeft);
140 };
141 
FontRendererGui(Sword2Engine * vm,int fontId)142 FontRendererGui::FontRendererGui(Sword2Engine *vm, int fontId)
143 	: _vm(vm) {
144 	byte *font = _vm->_resman->openResource(fontId);
145 	SpriteInfo sprite;
146 
147 	sprite.type = RDSPR_NOCOMPRESSION | RDSPR_TRANS;
148 
149 	for (int i = 0; i < SIZE_OF_CHAR_SET; i++) {
150 		byte *frame = _vm->fetchFrameHeader(font, i);
151 
152 		FrameHeader frame_head;
153 
154 		frame_head.read(frame);
155 
156 		sprite.data = frame + FrameHeader::size();
157 		sprite.w = frame_head.width;
158 		sprite.h = frame_head.height;
159 		_vm->_screen->createSurface(&sprite, &_glyph[i]._data);
160 		_glyph[i]._width = frame_head.width;
161 		_glyph[i]._height = frame_head.height;
162 	}
163 
164 	_vm->_resman->closeResource(fontId);
165 }
166 
~FontRendererGui()167 FontRendererGui::~FontRendererGui() {
168 	for (int i = 0; i < SIZE_OF_CHAR_SET; i++)
169 		_vm->_screen->deleteSurface(_glyph[i]._data);
170 }
171 
fetchText(uint32 textId,byte * buf)172 void FontRendererGui::fetchText(uint32 textId, byte *buf) {
173 	byte *data = _vm->fetchTextLine(_vm->_resman->openResource(textId / SIZE), textId & 0xffff);
174 	int i;
175 
176 	if (buf) {
177 		for (i = 0; data[i + 2]; i++)
178 			buf[i] = data[i + 2];
179 		buf[i] = 0;
180 	}
181 
182 	_vm->_resman->closeResource(textId / SIZE);
183 }
184 
getCharWidth(byte c)185 int FontRendererGui::getCharWidth(byte c) {
186 	if (c < 32)
187 		return 0;
188 	return _glyph[c - 32]._width;
189 }
190 
getCharHeight(byte c)191 int FontRendererGui::getCharHeight(byte c) {
192 	if (c < 32)
193 		return 0;
194 	return _glyph[c - 32]._height;
195 }
196 
getTextWidth(byte * text)197 int FontRendererGui::getTextWidth(byte *text) {
198 	int textWidth = 0;
199 
200 	for (int i = 0; text[i]; i++)
201 		if (text[i] >= ' ')
202 			textWidth += (getCharWidth(text[i]) - CHARACTER_OVERLAP);
203 	return textWidth;
204 }
205 
getTextWidth(uint32 textId)206 int FontRendererGui::getTextWidth(uint32 textId) {
207 	byte text[MAX_STRING_LEN];
208 
209 	fetchText(textId, text);
210 	return getTextWidth(text);
211 }
212 
drawText(byte * text,int x,int y,int alignment)213 void FontRendererGui::drawText(byte *text, int x, int y, int alignment) {
214 	SpriteInfo sprite;
215 	int i;
216 
217 	if (alignment != kAlignLeft) {
218 		int textWidth = getTextWidth(text);
219 
220 		switch (alignment) {
221 		case kAlignRight:
222 			x -= textWidth;
223 			break;
224 		case kAlignCenter:
225 			x -= (textWidth / 2);
226 			break;
227 		}
228 	}
229 
230 	sprite.x = x;
231 	sprite.y = y;
232 
233 	for (i = 0; text[i]; i++) {
234 		if (text[i] >= ' ') {
235 			sprite.w = getCharWidth(text[i]);
236 			sprite.h = getCharHeight(text[i]);
237 
238 			_vm->_screen->drawSurface(&sprite, _glyph[text[i] - 32]._data);
239 
240 			sprite.x += (getCharWidth(text[i]) - CHARACTER_OVERLAP);
241 		}
242 	}
243 }
244 
drawText(uint32 textId,int x,int y,int alignment)245 void FontRendererGui::drawText(uint32 textId, int x, int y, int alignment) {
246 	byte text[MAX_STRING_LEN];
247 
248 	fetchText(textId, text);
249 	drawText(text, x, y, alignment);
250 }
251 
252 //
253 // Dialog class functions
254 //
255 
Dialog(Sword2Engine * vm)256 Dialog::Dialog(Sword2Engine *vm)
257 	: _numWidgets(0), _finish(false), _result(0), _vm(vm) {
258 	_vm->_screen->setFullPalette(CONTROL_PANEL_PALETTE);
259 	_vm->_screen->clearScene();
260 	_vm->_screen->updateDisplay();
261 
262 	// Usually the mouse pointer will already be "normal", but not always.
263 	_vm->_mouse->setMouse(NORMAL_MOUSE_ID);
264 
265 	// Force mouse mode as system menu: normally not needed,
266 	// but value is not correct in case of game start dialog
267 	// (when asking to restart or load a game).
268 	// This is forced to avoid GMM loading/saving being enabled
269 	// during initial dialog.
270 	_vm->_mouse->setMouseMode(MOUSE_system_menu);
271 }
272 
~Dialog()273 Dialog::~Dialog() {
274 	for (int i = 0; i < _numWidgets; i++)
275 		delete _widgets[i];
276 	_vm->_screen->clearScene();
277 	_vm->_screen->updateDisplay();
278 }
279 
registerWidget(Widget * widget)280 void Dialog::registerWidget(Widget *widget) {
281 	if (_numWidgets < MAX_WIDGETS)
282 		_widgets[_numWidgets++] = widget;
283 }
284 
paint()285 void Dialog::paint() {
286 	_vm->_screen->clearScene();
287 	for (int i = 0; i < _numWidgets; i++)
288 		_widgets[i]->paint();
289 }
290 
setResult(int result)291 void Dialog::setResult(int result) {
292 	_result = result;
293 	_finish = true;
294 }
295 
runModal()296 int Dialog::runModal() {
297 	uint32 oldFilter = _vm->setInputEventFilter(0);
298 
299 	int i;
300 
301 	paint();
302 
303 	int oldMouseX = -1;
304 	int oldMouseY = -1;
305 
306 	while (!_finish) {
307 		// So that the menu icons will reach their full size
308 		_vm->_mouse->processMenu();
309 		_vm->_screen->updateDisplay(false);
310 
311 		int newMouseX, newMouseY;
312 
313 		_vm->_mouse->getPos(newMouseX, newMouseY);
314 
315 		newMouseY += 40;
316 
317 		MouseEvent *me = _vm->mouseEvent();
318 		KeyboardEvent *ke = _vm->keyboardEvent();
319 
320 		if (ke) {
321 			if (ke->kbd.keycode == Common::KEYCODE_ESCAPE)
322 				setResult(0);
323 			else if (ke->kbd.keycode == Common::KEYCODE_RETURN || ke->kbd.keycode == Common::KEYCODE_KP_ENTER)
324 				setResult(1);
325 		}
326 
327 		int oldHit = -1;
328 		int newHit = -1;
329 
330 		// Find out which widget the mouse was over the last time, and
331 		// which it is currently over. This assumes the widgets do not
332 		// overlap.
333 
334 		for (i = 0; i < _numWidgets; i++) {
335 			if (_widgets[i]->isHit(oldMouseX, oldMouseY))
336 				oldHit = i;
337 			if (_widgets[i]->isHit(newMouseX, newMouseY))
338 				newHit = i;
339 		}
340 
341 		// Was the mouse inside a widget the last time?
342 
343 		if (oldHit >= 0) {
344 			if (newHit != oldHit)
345 				_widgets[oldHit]->onMouseExit();
346 		}
347 
348 		// Is the mouse currently in a widget?
349 
350 		if (newHit >= 0) {
351 			if (newHit != oldHit)
352 				_widgets[newHit]->onMouseEnter();
353 
354 			if (me) {
355 				switch (me->buttons) {
356 				case RD_LEFTBUTTONDOWN:
357 					_widgets[newHit]->onMouseDown(newMouseX, newMouseY);
358 					break;
359 				case RD_LEFTBUTTONUP:
360 					_widgets[newHit]->onMouseUp(newMouseX, newMouseY);
361 					break;
362 				case RD_WHEELUP:
363 					_widgets[newHit]->onWheelUp(newMouseX, newMouseY);
364 					break;
365 				case RD_WHEELDOWN:
366 					_widgets[newHit]->onWheelDown(newMouseX, newMouseY);
367 					break;
368 				}
369 			}
370 		}
371 
372 		// Some events are passed to the widgets regardless of where
373 		// the mouse cursor is.
374 
375 		for (i = 0; i < _numWidgets; i++) {
376 			if (me && me->buttons == RD_LEFTBUTTONUP) {
377 				// So that slider widgets will know when the
378 				// user releases the mouse button, even if the
379 				// cursor is outside of the slider's hit area.
380 				_widgets[i]->releaseMouse(newMouseX, newMouseY);
381 			}
382 
383 			// This is to make it easier to drag the slider widget
384 
385 			if (newMouseX != oldMouseX || newMouseY != oldMouseY)
386 				_widgets[i]->onMouseMove(newMouseX, newMouseY);
387 
388 			if (ke)
389 				_widgets[i]->onKey(ke);
390 
391 			_widgets[i]->onTick();
392 		}
393 
394 		oldMouseX = newMouseX;
395 		oldMouseY = newMouseY;
396 
397 		_vm->_system->delayMillis(20);
398 
399 		if (_vm->shouldQuit())
400 			setResult(0);
401 	}
402 
403 	_vm->setInputEventFilter(oldFilter);
404 	return _result;
405 }
406 
407 //
408 // Widget functions
409 //
410 
Widget(Dialog * parent,int states)411 Widget::Widget(Dialog *parent, int states)
412 	: _vm(parent->_vm), _parent(parent), _numStates(states), _state(0) {
413 	_sprites = (SpriteInfo *)calloc(states, sizeof(SpriteInfo));
414 	_surfaces = (WidgetSurface *)calloc(states, sizeof(WidgetSurface));
415 
416 	_hitRect.left = _hitRect.right = _hitRect.top = _hitRect.bottom = -1;
417 }
418 
~Widget()419 Widget::~Widget() {
420 	for (int i = 0; i < _numStates; i++) {
421 		if (_surfaces[i]._original)
422 			_vm->_screen->deleteSurface(_surfaces[i]._surface);
423 	}
424 	free(_sprites);
425 	free(_surfaces);
426 }
427 
createSurfaceImage(int state,uint32 res,int x,int y,uint32 pc)428 void Widget::createSurfaceImage(int state, uint32 res, int x, int y, uint32 pc) {
429 	byte *file, *colTablePtr = NULL;
430 	AnimHeader anim_head;
431 	FrameHeader frame_head;
432 	CdtEntry cdt_entry;
433 	uint32 spriteType = RDSPR_TRANS;
434 
435 	// open anim resource file, point to base
436 	file = _vm->_resman->openResource(res);
437 
438 	byte *frame = _vm->fetchFrameHeader(file, pc);
439 
440 	anim_head.read(_vm->fetchAnimHeader(file));
441 	cdt_entry.read(_vm->fetchCdtEntry(file, pc));
442 	frame_head.read(frame);
443 
444 	// If the frame is flipped. (Only really applicable to frames using
445 	// offsets.)
446 
447 	if (cdt_entry.frameType & FRAME_FLIPPED)
448 		spriteType |= RDSPR_FLIP;
449 
450 	// Which compression was used?
451 
452 	switch (anim_head.runTimeComp) {
453 	case NONE:
454 		spriteType |= RDSPR_NOCOMPRESSION;
455 		break;
456 	case RLE256:
457 		spriteType |= RDSPR_RLE256;
458 		break;
459 	case RLE16:
460 		spriteType |= RDSPR_RLE256;
461 		// Points to just after last cdt_entry, i.e. start of color
462 		// table
463 		colTablePtr = _vm->fetchAnimHeader(file) + AnimHeader::size()
464 			+ anim_head.noAnimFrames * CdtEntry::size();
465 		break;
466 	}
467 
468 	_sprites[state].x = x;
469 	_sprites[state].y = y;
470 	_sprites[state].w = frame_head.width;
471 	_sprites[state].h = frame_head.height;
472 	_sprites[state].scale = 0;
473 	_sprites[state].type = spriteType;
474 	_sprites[state].blend = anim_head.blend;
475 
476 	// Points to just after frame header, ie. start of sprite data
477 	_sprites[state].data = frame + FrameHeader::size();
478 	_sprites[state].colorTable = colTablePtr;
479 	_sprites[state].isText = false;
480 
481 	_vm->_screen->createSurface(&_sprites[state], &_surfaces[state]._surface);
482 	_surfaces[state]._original = true;
483 
484 	// Release the anim resource
485 	_vm->_resman->closeResource(res);
486 }
487 
linkSurfaceImage(Widget * from,int state,int x,int y)488 void Widget::linkSurfaceImage(Widget *from, int state, int x, int y) {
489 	_sprites[state].x = x;
490 	_sprites[state].y = y;
491 	_sprites[state].w = from->_sprites[state].w;
492 	_sprites[state].h = from->_sprites[state].h;
493 	_sprites[state].scale = from->_sprites[state].scale;
494 	_sprites[state].type = from->_sprites[state].type;
495 	_sprites[state].blend = from->_sprites[state].blend;
496 
497 	_surfaces[state]._surface = from->_surfaces[state]._surface;
498 	_surfaces[state]._original = false;
499 }
500 
createSurfaceImages(uint32 res,int x,int y)501 void Widget::createSurfaceImages(uint32 res, int x, int y) {
502 	for (int i = 0; i < _numStates; i++)
503 		createSurfaceImage(i, res, x, y, i);
504 }
505 
linkSurfaceImages(Widget * from,int x,int y)506 void Widget::linkSurfaceImages(Widget *from, int x, int y) {
507 	for (int i = 0; i < from->_numStates; i++)
508 		linkSurfaceImage(from, i, x, y);
509 }
510 
setHitRect(int x,int y,int width,int height)511 void Widget::setHitRect(int x, int y, int width, int height) {
512 	_hitRect.left = x;
513 	_hitRect.right = x + width;
514 	_hitRect.top = y;
515 	_hitRect.bottom = y + height;
516 }
517 
isHit(int16 x,int16 y)518 bool Widget::isHit(int16 x, int16 y) {
519 	return _hitRect.left >= 0 && _hitRect.contains(x, y);
520 }
521 
setState(int state)522 void Widget::setState(int state) {
523 	if (state != _state) {
524 		_state = state;
525 		paint();
526 	}
527 }
528 
getState()529 int Widget::getState() {
530 	return _state;
531 }
532 
paint(Common::Rect * clipRect)533 void Widget::paint(Common::Rect *clipRect) {
534 	_vm->_screen->drawSurface(&_sprites[_state], _surfaces[_state]._surface, clipRect);
535 }
536 
537 /**
538  * Standard button class.
539  */
540 
541 class Button : public Widget {
542 public:
Button(Dialog * parent,int x,int y,int w,int h)543 	Button(Dialog *parent, int x, int y, int w, int h)
544 		: Widget(parent, 2) {
545 		setHitRect(x, y, w, h);
546 	}
547 
onMouseExit()548 	virtual void onMouseExit() {
549 		setState(0);
550 	}
551 
onMouseDown(int x,int y)552 	virtual void onMouseDown(int x, int y) {
553 		setState(1);
554 	}
555 
onMouseUp(int x,int y)556 	virtual void onMouseUp(int x, int y) {
557 		if (getState() != 0) {
558 			setState(0);
559 			_parent->onAction(this);
560 		}
561 	}
562 };
563 
564 /**
565  * Scroll buttons are used to scroll the savegame list. The difference between
566  * this and a normal button is that we want this to repeat.
567  */
568 
569 class ScrollButton : public Widget {
570 private:
571 	uint32 _holdCounter;
572 
573 public:
ScrollButton(Dialog * parent,int x,int y,int w,int h)574 	ScrollButton(Dialog *parent, int x, int y, int w, int h)
575 		: Widget(parent, 2), _holdCounter(0) {
576 		setHitRect(x, y, w, h);
577 	}
578 
onMouseExit()579 	virtual void onMouseExit() {
580 		setState(0);
581 	}
582 
onMouseDown(int x,int y)583 	virtual void onMouseDown(int x, int y) {
584 		setState(1);
585 		_parent->onAction(this);
586 		_holdCounter = 0;
587 	}
588 
onMouseUp(int x,int y)589 	virtual void onMouseUp(int x, int y) {
590 		setState(0);
591 	}
592 
onTick()593 	virtual void onTick() {
594 		if (getState() != 0) {
595 			_holdCounter++;
596 			if (_holdCounter > 16 && (_holdCounter % 4) == 0)
597 				_parent->onAction(this);
598 		}
599 	}
600 };
601 
602 /**
603  * A switch is a button that changes state when clicked, and keeps that state
604  * until clicked again.
605  */
606 
607 class Switch : public Widget {
608 private:
609 	bool _holding, _value;
610 	int _upState, _downState;
611 
612 public:
Switch(Dialog * parent,int x,int y,int w,int h)613 	Switch(Dialog *parent, int x, int y, int w, int h)
614 		: Widget(parent, 2), _holding(false), _value(false),
615 		  _upState(0), _downState(1) {
616 		setHitRect(x, y, w, h);
617 	}
618 
619 	// The sound mute switches have 0 as their "down" state and 1 as
620 	// their "up" state, so this function is needed to get consistent
621 	// behavior.
622 
reverseStates()623 	void reverseStates() {
624 		_upState = 1;
625 		_downState = 0;
626 	}
627 
setValue(bool value)628 	void setValue(bool value) {
629 		_value = value;
630 		if (_value)
631 			setState(_downState);
632 		else
633 			setState(_upState);
634 	}
635 
getValue()636 	bool getValue() {
637 		return _value;
638 	}
639 
onMouseExit()640 	virtual void onMouseExit() {
641 		if (_holding && !_value)
642 			setState(_upState);
643 		_holding = false;
644 	}
645 
onMouseDown(int x,int y)646 	virtual void onMouseDown(int x, int y) {
647 		_holding = true;
648 		setState(_downState);
649 	}
650 
onMouseUp(int x,int y)651 	virtual void onMouseUp(int x, int y) {
652 		if (_holding) {
653 			_holding = false;
654 			_value = !_value;
655 			if (_value)
656 				setState(_downState);
657 			else
658 				setState(_upState);
659 			_parent->onAction(this, getState());
660 		}
661 	}
662 };
663 
664 /**
665  * A slider is used to specify a value within a pre-defined range.
666  */
667 
668 class Slider : public Widget {
669 private:
670 	Widget *_background;
671 	bool _dragging;
672 	int _value, _targetValue;
673 	int _maxValue;
674 	int _valueStep;
675 	int _dragOffset;
676 
posFromValue(int value)677 	int posFromValue(int value) {
678 		return _hitRect.left + (value * (_hitRect.width() - 38)) / _maxValue;
679 	}
680 
valueFromPos(int x)681 	int valueFromPos(int x) {
682 		return (int)((double)(_maxValue * (x - _hitRect.left)) / (double)(_hitRect.width() - 38) + 0.5);
683 	}
684 
685 public:
Slider(Dialog * parent,Widget * background,int max,int x,int y,int w,int h,int step,Widget * base=NULL)686 	Slider(Dialog *parent, Widget *background, int max,
687 		int x, int y, int w, int h, int step, Widget *base = NULL)
688 		: Widget(parent, 1), _background(background),
689 		  _dragging(false), _value(0), _targetValue(0),
690 		  _maxValue(max), _valueStep(step) {
691 		setHitRect(x, y, w, h);
692 
693 		if (_valueStep <= 0)
694 			_valueStep = 1;
695 
696 		if (base)
697 			linkSurfaceImages(base, x, y);
698 		else
699 			createSurfaceImages(3406, x, y);
700 	}
701 
paint(Common::Rect * clipRect=NULL)702 	virtual void paint(Common::Rect *clipRect = NULL) {
703 		// This will redraw a bit more than is strictly necessary,
704 		// but I doubt that will make any noticeable difference.
705 
706 		_background->paint(&_hitRect);
707 		Widget::paint(clipRect);
708 	}
709 
setValue(int value)710 	void setValue(int value) {
711 		_value = value;
712 		_targetValue = value;
713 		_sprites[0].x = posFromValue(_value);
714 		paint();
715 	}
716 
getValue()717 	int getValue() {
718 		return _value;
719 	}
720 
onMouseMove(int x,int y)721 	virtual void onMouseMove(int x, int y) {
722 		if (_dragging) {
723 			int newX = x - _dragOffset;
724 			int newValue;
725 
726 			if (newX < _hitRect.left)
727 				newX = _hitRect.left;
728 			else if (newX + 38 > _hitRect.right)
729 				newX = _hitRect.right - 38;
730 
731 			_sprites[0].x = newX;
732 
733 			newValue = valueFromPos(newX);
734 			if (newValue != _value) {
735 				_value = newValue;
736 				_targetValue = newValue;
737 				_parent->onAction(this, newValue);
738 			}
739 
740 			paint();
741 		}
742 	}
743 
onMouseDown(int x,int y)744 	virtual void onMouseDown(int x, int y) {
745 		if (x >= _sprites[0].x && x < _sprites[0].x + 38) {
746 			_dragging = true;
747 			_dragOffset = x - _sprites[0].x;
748 		} else if (x < _sprites[0].x) {
749 			if (_targetValue >= _valueStep)
750 				_targetValue -= _valueStep;
751 			else
752 				_targetValue = 0;
753 		} else {
754 			if (_targetValue < _maxValue - _valueStep)
755 				_targetValue += _valueStep;
756 			else
757 				_targetValue = _maxValue;
758 		}
759 	}
760 
releaseMouse(int x,int y)761 	virtual void releaseMouse(int x, int y) {
762 		if (_dragging)
763 			_dragging = false;
764 	}
765 
onTick()766 	virtual void onTick() {
767 		if (!_dragging) {
768 			int target = posFromValue(_targetValue);
769 
770 			if (target != _sprites[0].x) {
771 				if (target < _sprites[0].x) {
772 					_sprites[0].x -= 4;
773 					if (_sprites[0].x < target)
774 						_sprites[0].x = target;
775 				} else if (target > _sprites[0].x) {
776 					_sprites[0].x += 4;
777 					if (_sprites[0].x > target)
778 						_sprites[0].x = target;
779 				}
780 
781 				int newValue = valueFromPos(_sprites[0].x);
782 				if (newValue != _value) {
783 					_value = newValue;
784 					_parent->onAction(this, newValue);
785 				}
786 
787 				paint();
788 			}
789 		}
790 	}
791 };
792 
793 /**
794  * The "mini" dialog.
795  */
796 
MiniDialog(Sword2Engine * vm,uint32 headerTextId,uint32 okTextId,uint32 cancelTextId)797 MiniDialog::MiniDialog(Sword2Engine *vm, uint32 headerTextId, uint32 okTextId, uint32 cancelTextId) : Dialog(vm) {
798 	_headerTextId = headerTextId;
799 	_okTextId = okTextId;
800 	_cancelTextId = cancelTextId;
801 
802 	_fr = new FontRendererGui(_vm, _vm->_controlsFontId);
803 
804 	_panel = new Widget(this, 1);
805 	_panel->createSurfaceImages(1996, 203, 104);
806 
807 	_okButton = new Button(this, 243, 214, 24, 24);
808 	_okButton->createSurfaceImages(2002, 243, 214);
809 
810 	_cancelButton = new Button(this, 243, 276, 24, 24);
811 	_cancelButton->linkSurfaceImages(_okButton, 243, 276);
812 
813 	registerWidget(_panel);
814 	registerWidget(_okButton);
815 	registerWidget(_cancelButton);
816 }
817 
~MiniDialog()818 MiniDialog::~MiniDialog() {
819 	delete _fr;
820 }
821 
paint()822 void MiniDialog::paint() {
823 	Dialog::paint();
824 
825 	if (_headerTextId)
826 		_fr->drawText(_headerTextId, 310, 134, FontRendererGui::kAlignCenter);
827 	_fr->drawText(_okTextId, 270, 214);
828 	_fr->drawText(_cancelTextId, 270, 276);
829 }
830 
onAction(Widget * widget,int result)831 void MiniDialog::onAction(Widget *widget, int result) {
832 	if (widget == _okButton)
833 		setResult(1);
834 	else if (widget == _cancelButton)
835 		setResult(0);
836 }
837 
StartDialog(Sword2Engine * vm)838 StartDialog::StartDialog(Sword2Engine *vm) : MiniDialog(vm, 0) {}
839 
runModal()840 int StartDialog::runModal() {
841 	while (1) {
842 		MiniDialog startDialog(_vm, 0, TEXT_RESTART, TEXT_RESTORE);
843 
844 		if (startDialog.runModal())
845 			return 1;
846 
847 		if (_vm->shouldQuit())
848 			return 0;
849 
850 		RestoreDialog restoreDialog(_vm);
851 
852 		if (restoreDialog.runModal())
853 			return 0;
854 
855 		if (_vm->shouldQuit())
856 			return 0;
857 	}
858 
859 	return 1;
860 }
861 
862 /**
863  * The restart dialog.
864  */
865 
RestartDialog(Sword2Engine * vm)866 RestartDialog::RestartDialog(Sword2Engine *vm) : MiniDialog(vm, TEXT_RESTART) {}
867 
runModal()868 int RestartDialog::runModal() {
869 	int result = MiniDialog::runModal();
870 
871 	if (result)
872 		_vm->restartGame();
873 
874 	return result;
875 }
876 
877 /**
878  * The quit dialog.
879  */
880 
QuitDialog(Sword2Engine * vm)881 QuitDialog::QuitDialog(Sword2Engine *vm) : MiniDialog(vm, TEXT_QUIT) {}
882 
runModal()883 int QuitDialog::runModal() {
884 	int result = MiniDialog::runModal();
885 
886 	if (result)
887 		_vm->quitGame();
888 
889 	return result;
890 }
891 
892 /**
893  * The game settings dialog.
894  */
895 
OptionsDialog(Sword2Engine * vm)896 OptionsDialog::OptionsDialog(Sword2Engine *vm) : Dialog(vm) {
897 	_fr = new FontRendererGui(_vm, _vm->_controlsFontId);
898 
899 	_mixer = _vm->_mixer;
900 
901 	_panel = new Widget(this, 1);
902 	_panel->createSurfaceImages(3405, 0, 40);
903 
904 	_objectLabelsSwitch = new Switch(this, 304, 100, 53, 32);
905 	_objectLabelsSwitch->createSurfaceImages(3687, 304, 100);
906 
907 	_subtitlesSwitch = new Switch(this, 510, 100, 53, 32);
908 	_subtitlesSwitch->linkSurfaceImages(_objectLabelsSwitch, 510, 100);
909 
910 	_reverseStereoSwitch = new Switch(this, 304, 293, 53, 32);
911 	_reverseStereoSwitch->linkSurfaceImages(_objectLabelsSwitch, 304, 293);
912 
913 	_musicSwitch = new Switch(this, 516, 157, 40, 32);
914 	_musicSwitch->createSurfaceImages(3315, 516, 157);
915 	_musicSwitch->reverseStates();
916 
917 	_speechSwitch = new Switch(this, 516, 205, 40, 32);
918 	_speechSwitch->linkSurfaceImages(_musicSwitch, 516, 205);
919 	_speechSwitch->reverseStates();
920 
921 	_fxSwitch = new Switch(this, 516, 250, 40, 32);
922 	_fxSwitch->linkSurfaceImages(_musicSwitch, 516, 250);
923 	_fxSwitch->reverseStates();
924 
925 	int volStep = Audio::Mixer::kMaxMixerVolume / 10;
926 
927 	_musicSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 161, 170, 27, volStep);
928 	_speechSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 208, 170, 27, volStep, _musicSlider);
929 	_fxSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 254, 170, 27, volStep, _musicSlider);
930 	_gfxSlider = new Slider(this, _panel, 3, 309, 341, 170, 27, 1, _musicSlider);
931 
932 	_gfxPreview = new Widget(this, 4);
933 	_gfxPreview->createSurfaceImages(256, 495, 310);
934 
935 	_okButton = new Button(this, 203, 382, 53, 32);
936 	_okButton->createSurfaceImages(901, 203, 382);
937 
938 	_cancelButton = new Button(this, 395, 382, 53, 32);
939 	_cancelButton->linkSurfaceImages(_okButton, 395, 382);
940 
941 	registerWidget(_panel);
942 	registerWidget(_objectLabelsSwitch);
943 	registerWidget(_subtitlesSwitch);
944 	registerWidget(_reverseStereoSwitch);
945 	registerWidget(_musicSwitch);
946 	registerWidget(_speechSwitch);
947 	registerWidget(_fxSwitch);
948 	registerWidget(_musicSlider);
949 	registerWidget(_speechSlider);
950 	registerWidget(_fxSlider);
951 	registerWidget(_gfxSlider);
952 	registerWidget(_gfxPreview);
953 	registerWidget(_okButton);
954 	registerWidget(_cancelButton);
955 
956 	_objectLabelsSwitch->setValue(_vm->_mouse->getObjectLabels());
957 	_subtitlesSwitch->setValue(_vm->getSubtitles());
958 	_reverseStereoSwitch->setValue(_vm->_sound->isReverseStereo());
959 	_musicSwitch->setValue(!_vm->_sound->isMusicMute());
960 	_speechSwitch->setValue(!_vm->_sound->isSpeechMute());
961 	_fxSwitch->setValue(!_vm->_sound->isFxMute());
962 
963 	_musicSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType));
964 	_speechSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType));
965 	_fxSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType));
966 
967 	_gfxSlider->setValue(_vm->_screen->getRenderLevel());
968 	_gfxPreview->setState(_vm->_screen->getRenderLevel());
969 }
970 
~OptionsDialog()971 OptionsDialog::~OptionsDialog() {
972 	delete _fr;
973 }
974 
paint()975 void OptionsDialog::paint() {
976 	Dialog::paint();
977 
978 	int maxWidth = 0;
979 	int width;
980 
981 	uint32 alignTextIds[] = {
982 		TEXT_OBJECT_LABELS,
983 		TEXT_MUSIC_VOLUME,
984 		TEXT_SPEECH_VOLUME,
985 		TEXT_FX_VOLUME,
986 		TEXT_GFX_QUALITY,
987 		TEXT_REVERSE_STEREO
988 	};
989 
990 	for (int i = 0; i < ARRAYSIZE(alignTextIds); i++) {
991 		width = _fr->getTextWidth(alignTextIds[i]);
992 		if (width > maxWidth)
993 			maxWidth = width;
994 	}
995 
996 	_fr->drawText(TEXT_OPTIONS, 321, 55, FontRendererGui::kAlignCenter);
997 	_fr->drawText(TEXT_SUBTITLES, 500, 103, FontRendererGui::kAlignRight);
998 	_fr->drawText(TEXT_OBJECT_LABELS, 299 - maxWidth, 103);
999 	_fr->drawText(TEXT_MUSIC_VOLUME, 299 - maxWidth, 161);
1000 	_fr->drawText(TEXT_SPEECH_VOLUME, 299 - maxWidth, 208);
1001 	_fr->drawText(TEXT_FX_VOLUME, 299 - maxWidth, 254);
1002 	_fr->drawText(TEXT_REVERSE_STEREO, 299 - maxWidth, 296);
1003 	_fr->drawText(TEXT_GFX_QUALITY, 299 - maxWidth, 341);
1004 	_fr->drawText(TEXT_OK, 193, 382, FontRendererGui::kAlignRight);
1005 	_fr->drawText(TEXT_CANCEL, 385, 382, FontRendererGui::kAlignRight);
1006 }
1007 
onAction(Widget * widget,int result)1008 void OptionsDialog::onAction(Widget *widget, int result) {
1009 	// Since there is music playing while the dialog is displayed we need
1010 	// to update music volume immediately.
1011 
1012 	if (widget == _musicSwitch) {
1013 		_vm->_sound->muteMusic(result != 0);
1014 	} else if (widget == _musicSlider) {
1015 		_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, result);
1016 		_vm->_sound->muteMusic(result == 0);
1017 		_musicSwitch->setValue(result != 0);
1018 	} else if (widget == _speechSlider) {
1019 		_speechSwitch->setValue(result != 0);
1020 	} else if (widget == _fxSlider) {
1021 		_fxSwitch->setValue(result != 0);
1022 	} else if (widget == _gfxSlider) {
1023 		_gfxPreview->setState(result);
1024 		_vm->_screen->setRenderLevel(result);
1025 	} else if (widget == _okButton) {
1026 		// Apply the changes
1027 		_vm->setSubtitles(_subtitlesSwitch->getValue());
1028 		_vm->_mouse->setObjectLabels(_objectLabelsSwitch->getValue());
1029 		_vm->_sound->muteMusic(!_musicSwitch->getValue());
1030 		_vm->_sound->muteSpeech(!_speechSwitch->getValue());
1031 		_vm->_sound->muteFx(!_fxSwitch->getValue());
1032 		_vm->_sound->setReverseStereo(_reverseStereoSwitch->getValue());
1033 		_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _musicSlider->getValue());
1034 		_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _speechSlider->getValue());
1035 		_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _fxSlider->getValue());
1036 		_vm->_screen->setRenderLevel(_gfxSlider->getValue());
1037 
1038 		_vm->writeSettings();
1039 		setResult(1);
1040 	} else if (widget == _cancelButton) {
1041 		// Revert the changes
1042 		_vm->readSettings();
1043 		setResult(0);
1044 	}
1045 }
1046 
1047 // Slot button actions. Note that keyboard input generates positive actions
1048 
1049 enum {
1050 	kSelectSlot = -1,
1051 	kDeselectSlot = -2,
1052 	kWheelDown = -3,
1053 	kWheelUp = -4,
1054 	kStartEditing = -5,
1055 	kCursorTick = -6
1056 };
1057 
1058 class Slot : public Widget {
1059 private:
1060 	int _mode;
1061 	FontRendererGui *_fr;
1062 	byte _text[SAVE_DESCRIPTION_LEN];
1063 	bool _clickable;
1064 	bool _editable;
1065 
1066 public:
Slot(Dialog * parent,int x,int y,int w,int h)1067 	Slot(Dialog *parent, int x, int y, int w, int h)
1068 		: Widget(parent, 2), _clickable(false), _editable(false) {
1069 		setHitRect(x, y, w, h);
1070 		_text[0] = 0;
1071 	}
1072 
setMode(int mode)1073 	void setMode(int mode) {
1074 		_mode = mode;
1075 	}
1076 
setClickable(bool clickable)1077 	void setClickable(bool clickable) {
1078 		_clickable = clickable;
1079 	}
1080 
setEditable(bool editable)1081 	void setEditable(bool editable) {
1082 		_editable = editable;
1083 		_vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, editable);
1084 	}
1085 
isEditable()1086 	bool isEditable() {
1087 		return _editable;
1088 	}
1089 
setText(FontRendererGui * fr,int slot,byte * text)1090 	void setText(FontRendererGui *fr, int slot, byte *text) {
1091 		_fr = fr;
1092 		if (text)
1093 			sprintf((char *)_text, "%d.  %s", slot, text);
1094 		else
1095 			sprintf((char *)_text, "%d.  ", slot);
1096 	}
1097 
getText()1098 	byte *getText() {
1099 		return &_text[0];
1100 	}
1101 
paint(Common::Rect * clipRect=NULL)1102 	virtual void paint(Common::Rect *clipRect = NULL) {
1103 		Widget::paint();
1104 
1105 		// HACK: The main dialog is responsible for drawing the text
1106 		// when in editing mode.
1107 
1108 		if (!_editable)
1109 			_fr->drawText(_text, _sprites[0].x + 16, _sprites[0].y + 4 + 2 * getState());
1110 	}
1111 
onMouseDown(int x,int y)1112 	virtual void onMouseDown(int x, int y) {
1113 		if (_clickable) {
1114 			if (getState() == 0) {
1115 				setState(1);
1116 				_parent->onAction(this, kSelectSlot);
1117 				if (_mode == kSaveDialog)
1118 					_parent->onAction(this, kStartEditing);
1119 			} else if (_mode == kRestoreDialog) {
1120 				setState(0);
1121 				_parent->onAction(this, kDeselectSlot);
1122 			}
1123 		}
1124 	}
1125 
onWheelUp(int x,int y)1126 	virtual void onWheelUp(int x, int y) {
1127 		_parent->onAction(this, kWheelUp);
1128 	}
1129 
onWheelDown(int x,int y)1130 	virtual void onWheelDown(int x, int y) {
1131 		_parent->onAction(this, kWheelDown);
1132 	}
1133 
onKey(KeyboardEvent * ke)1134 	virtual void onKey(KeyboardEvent *ke) {
1135 		if (_editable) {
1136 			if (ke->kbd.keycode == Common::KEYCODE_BACKSPACE)
1137 				_parent->onAction(this, Common::KEYCODE_BACKSPACE);
1138 			else if (ke->kbd.ascii >= ' ' && ke->kbd.ascii <= 255) {
1139 				// Accept the character if the font renderer
1140 				// has what looks like a valid glyph for it.
1141 				if (_fr->getCharWidth(ke->kbd.ascii))
1142 					_parent->onAction(this, ke->kbd.ascii);
1143 			}
1144 		}
1145 	}
1146 
onTick()1147 	virtual void onTick() {
1148 		if (_editable)
1149 			_parent->onAction(this, kCursorTick);
1150 	}
1151 
setY(int y)1152 	void setY(int y) {
1153 		for (int i = 0; i < _numStates; i++)
1154 			_sprites[i].y = y;
1155 		setHitRect(_hitRect.left, y, _hitRect.width(), _hitRect.height());
1156 	}
1157 
getY()1158 	int getY() {
1159 		return _sprites[0].y;
1160 	}
1161 };
1162 
SaveRestoreDialog(Sword2Engine * vm,int mode)1163 SaveRestoreDialog::SaveRestoreDialog(Sword2Engine *vm, int mode) : Dialog(vm) {
1164 	int i;
1165 
1166 	_mode = mode;
1167 	_selectedSlot = -1;
1168 
1169 	// FIXME: The "control font" and the "red font" are currently always
1170 	// the same font, so one should be eliminated.
1171 
1172 	_fr1 = new FontRendererGui(_vm, _vm->_controlsFontId);
1173 	_fr2 = new FontRendererGui(_vm, _vm->_redFontId);
1174 
1175 	_panel = new Widget(this, 1);
1176 	_panel->createSurfaceImages(2016, 0, 40);
1177 
1178 	for (i = 0; i < 4; i++) {
1179 		_slotButton[i] = new Slot(this, 114, 0, 384, 36);
1180 		_slotButton[i]->createSurfaceImages(2006 + i, 114, 0);
1181 		_slotButton[i]->setMode(mode);
1182 		_slotButton[i + 4] = new Slot(this, 114, 0, 384, 36);
1183 		_slotButton[i + 4]->linkSurfaceImages(_slotButton[i], 114, 0);
1184 		_slotButton[i + 4]->setMode(mode);
1185 	}
1186 
1187 	updateSlots();
1188 
1189 	_zupButton = new ScrollButton(this, 516, 65, 17, 17);
1190 	_zupButton->createSurfaceImages(1982, 516, 65);
1191 
1192 	_upButton = new ScrollButton(this, 516, 85, 17, 17);
1193 	_upButton->createSurfaceImages(2067, 516, 85);
1194 
1195 	_downButton = new ScrollButton(this, 516, 329, 17, 17);
1196 	_downButton->createSurfaceImages(1986, 516, 329);
1197 
1198 	_zdownButton = new ScrollButton(this, 516, 350, 17, 17);
1199 	_zdownButton->createSurfaceImages(1988, 516, 350);
1200 
1201 	_okButton = new Button(this, 130, 377, 24, 24);
1202 	_okButton->createSurfaceImages(2002, 130, 377);
1203 
1204 	_cancelButton = new Button(this, 350, 377, 24, 24);
1205 	_cancelButton->linkSurfaceImages(_okButton, 350, 377);
1206 
1207 	registerWidget(_panel);
1208 
1209 	for (i = 0; i < 8; i++)
1210 		registerWidget(_slotButton[i]);
1211 
1212 	registerWidget(_zupButton);
1213 	registerWidget(_upButton);
1214 	registerWidget(_downButton);
1215 	registerWidget(_zdownButton);
1216 	registerWidget(_okButton);
1217 	registerWidget(_cancelButton);
1218 }
1219 
~SaveRestoreDialog()1220 SaveRestoreDialog::~SaveRestoreDialog() {
1221 	delete _fr1;
1222 	delete _fr2;
1223 }
1224 
1225 // There aren't really a hundred different button objects of course, there are
1226 // only eight. Re-arrange them to simulate scrolling.
1227 
updateSlots()1228 void SaveRestoreDialog::updateSlots() {
1229 	for (int i = 0; i < 8; i++) {
1230 		Slot *slot = _slotButton[(baseSlot + i) % 8];
1231 		FontRendererGui *fr;
1232 		byte description[SAVE_DESCRIPTION_LEN];
1233 
1234 		slot->setY(72 + i * 36);
1235 
1236 		if (baseSlot + i == _selectedSlot) {
1237 			slot->setEditable(_mode == kSaveDialog);
1238 			slot->setState(1);
1239 			fr = _fr2;
1240 		} else {
1241 			slot->setEditable(false);
1242 			slot->setState(0);
1243 			fr = _fr1;
1244 		}
1245 
1246 		if (_vm->getSaveDescription(baseSlot + i, description) == SR_OK) {
1247 			slot->setText(fr, baseSlot + i, description);
1248 			slot->setClickable(true);
1249 		} else {
1250 			slot->setText(fr, baseSlot + i, NULL);
1251 			slot->setClickable(_mode == kSaveDialog);
1252 		}
1253 
1254 		if (slot->isEditable())
1255 			drawEditBuffer(slot);
1256 		else
1257 			slot->paint();
1258 	}
1259 }
1260 
drawEditBuffer(Slot * slot)1261 void SaveRestoreDialog::drawEditBuffer(Slot *slot) {
1262 	if (_selectedSlot == -1)
1263 		return;
1264 
1265 	// This will redraw a bit more than is strictly necessary, but I doubt
1266 	// that will make any noticeable difference.
1267 
1268 	slot->paint();
1269 	_fr2->drawText(_editBuffer, 130, 78 + (_selectedSlot - baseSlot) * 36);
1270 }
1271 
onAction(Widget * widget,int result)1272 void SaveRestoreDialog::onAction(Widget *widget, int result) {
1273 	if (widget == _zupButton) {
1274 		if (baseSlot > 0) {
1275 			if (baseSlot >= 8)
1276 				baseSlot -= 8;
1277 			else
1278 				baseSlot = 0;
1279 			updateSlots();
1280 		}
1281 	} else if (widget == _upButton) {
1282 		if (baseSlot > 0) {
1283 			baseSlot--;
1284 			updateSlots();
1285 		}
1286 	} else if (widget == _downButton) {
1287 		if (baseSlot < 92) {
1288 			baseSlot++;
1289 			updateSlots();
1290 		}
1291 	} else if (widget == _zdownButton) {
1292 		if (baseSlot < 92) {
1293 			if (baseSlot <= 84)
1294 				baseSlot += 8;
1295 			else
1296 				baseSlot = 92;
1297 			updateSlots();
1298 		}
1299 	} else if (widget == _okButton) {
1300 		setResult(1);
1301 	} else if (widget == _cancelButton) {
1302 		setResult(0);
1303 	} else {
1304 		Slot *slot = (Slot *)widget;
1305 		int textWidth;
1306 		byte tmp;
1307 		int i;
1308 		int j;
1309 
1310 		switch (result) {
1311 		case kWheelUp:
1312 			onAction(_upButton);
1313 			break;
1314 		case kWheelDown:
1315 			onAction(_downButton);
1316 			break;
1317 		case kSelectSlot:
1318 		case kDeselectSlot:
1319 			if (result == kSelectSlot)
1320 				_selectedSlot = baseSlot + (slot->getY() - 72) / 35;
1321 			else if (result == kDeselectSlot)
1322 				_selectedSlot = -1;
1323 
1324 			for (i = 0; i < 8; i++)
1325 				if (widget == _slotButton[i])
1326 					break;
1327 
1328 			for (j = 0; j < 8; j++) {
1329 				if (j != i) {
1330 					_slotButton[j]->setEditable(false);
1331 					_slotButton[j]->setState(0);
1332 				}
1333 			}
1334 			break;
1335 		case kStartEditing:
1336 			if (_selectedSlot >= 10)
1337 				_firstPos = 5;
1338 			else
1339 				_firstPos = 4;
1340 
1341 			strcpy((char *)_editBuffer, (char *)slot->getText());
1342 			_editPos = strlen((char *)_editBuffer);
1343 			_cursorTick = 0;
1344 			_editBuffer[_editPos] = '_';
1345 			_editBuffer[_editPos + 1] = 0;
1346 			slot->setEditable(true);
1347 			drawEditBuffer(slot);
1348 			break;
1349 		case kCursorTick:
1350 			_cursorTick++;
1351 			if (_cursorTick == 7) {
1352 				_editBuffer[_editPos] = ' ';
1353 				drawEditBuffer(slot);
1354 			} else if (_cursorTick == 14) {
1355 				_cursorTick = 0;
1356 				_editBuffer[_editPos] = '_';
1357 				drawEditBuffer(slot);
1358 			}
1359 			break;
1360 		case Common::KEYCODE_BACKSPACE:
1361 			if (_editPos > _firstPos) {
1362 				_editBuffer[_editPos - 1] = _editBuffer[_editPos];
1363 				_editBuffer[_editPos--] = 0;
1364 				drawEditBuffer(slot);
1365 			}
1366 			break;
1367 		default:
1368 			tmp = _editBuffer[_editPos];
1369 			_editBuffer[_editPos] = 0;
1370 			textWidth = _fr2->getTextWidth(_editBuffer);
1371 			_editBuffer[_editPos] = tmp;
1372 
1373 			if (textWidth < 340 && _editPos < SAVE_DESCRIPTION_LEN - 2) {
1374 				_editBuffer[_editPos + 1] = _editBuffer[_editPos];
1375 				_editBuffer[_editPos + 2] = 0;
1376 				_editBuffer[_editPos++] = result;
1377 				drawEditBuffer(slot);
1378 			}
1379 			break;
1380 		}
1381 	}
1382 }
1383 
paint()1384 void SaveRestoreDialog::paint() {
1385 	Dialog::paint();
1386 
1387 	_fr1->drawText((_mode == kRestoreDialog) ? TEXT_RESTORE : TEXT_SAVE, 165, 377);
1388 	_fr1->drawText(TEXT_CANCEL, 382, 377);
1389 }
1390 
setResult(int result)1391 void SaveRestoreDialog::setResult(int result) {
1392 	if (result) {
1393 		if (_selectedSlot == -1)
1394 			return;
1395 
1396 		if (_mode == kSaveDialog) {
1397 			if (_editPos <= _firstPos)
1398 				return;
1399 		}
1400 	}
1401 
1402 	Dialog::setResult(result);
1403 }
1404 
runModal()1405 int SaveRestoreDialog::runModal() {
1406 	int result = Dialog::runModal();
1407 
1408 	if (result) {
1409 		switch (_mode) {
1410 		case kSaveDialog:
1411 			// Remove the cursor character from the savegame name
1412 			_editBuffer[_editPos] = 0;
1413 
1414 			if (_vm->saveGame(_selectedSlot, (byte *)&_editBuffer[_firstPos]) != SR_OK)
1415 				result = 0;
1416 			break;
1417 		case kRestoreDialog:
1418 			if (_vm->restoreGame(_selectedSlot) != SR_OK)
1419 				result = 0;
1420 			break;
1421 		}
1422 	}
1423 
1424 	return result;
1425 }
1426 
1427 } // End of namespace Sword2
1428