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