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 "lure/menu.h"
24 #include "lure/luredefs.h"
25 #include "lure/decode.h"
26 #include "lure/surface.h"
27 #include "lure/res_struct.h"
28 #include "lure/res.h"
29 #include "lure/strings.h"
30 #include "lure/room.h"
31 #include "lure/events.h"
32 #include "lure/lure.h"
33 
34 #if defined(_WIN32_WCE) || defined(__SYMBIAN32__) || defined(WEBOS) || defined(__ANDROID__) || defined(__WII__)
35 #define LURE_CLICKABLE_MENUS
36 #endif
37 
38 namespace Lure {
39 
MenuRecord(const MenuRecordBounds * bounds,int numParams,...)40 MenuRecord::MenuRecord(const MenuRecordBounds *bounds, int numParams, ...) {
41 	// Store list of pointers to strings
42 	va_list params;
43 
44 	_numEntries = numParams;
45 	_entries = (const char **) malloc(sizeof(const char *) * _numEntries);
46 
47 	va_start(params, numParams);
48 	for (int index = 0; index < _numEntries; ++index)
49 		_entries[index] = va_arg(params, const char *);
50 	va_end(params);
51 
52 	// Store position data
53 	_hsxstart = bounds->left; _hsxend = bounds->right;
54 	_xstart = bounds->contentsX << 3;
55 	_width = (bounds->contentsWidth + 3) << 3;
56 }
57 
~MenuRecord()58 MenuRecord::~MenuRecord() {
59 	free(_entries);
60 	_entries = NULL;
61 }
62 
getEntry(uint8 index)63 const char *MenuRecord::getEntry(uint8 index) {
64 	if (index >= _numEntries) error("Invalid menuitem index specified: %d", index);
65 	return _entries[index];
66 }
67 
68 /*--------------------------------------------------------------------------*/
69 
70 static Menu *int_menu = NULL;
71 
72 const MenuRecordLanguage menuList[] = {
73 	{Common::EN_ANY, {{40, 87, 3, 7}, {127, 179, 13, 12}, {224, 281, 27, 10}}},
74 	{Common::IT_ITA, {{40, 98, 4, 6}, {120, 195, 14, 11}, {208, 281, 24, 13}}},
75 	{Common::FR_FRA, {{40, 90, 3, 7}, {120, 195, 13, 11}, {232, 273, 23, 13}}},
76 	{Common::DE_DEU, {{44, 95, 1, 11}, {135, 178, 8, 23}, {232, 273, 22, 15}}},
77 	{Common::ES_ESP, {{40, 90, 3, 8}, {120, 195, 11, 13}, {208, 281, 17, 18}}},
78 	{Common::RU_RUS, {{40, 87, 3, 7}, {127, 179, 13, 12}, {224, 281, 27, 10}}},
79 	{Common::UNK_LANG, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}}
80 };
81 
Menu()82 Menu::Menu() {
83 	int_menu = this;
84 	StringList &sl = Resources::getReference().stringList();
85 	Common::Language language = LureEngine::getReference().getLanguage();
86 
87 	MemoryBlock *data = Disk::getReference().getEntry(MENU_RESOURCE_ID);
88 	PictureDecoder decoder;
89 	_menu = decoder.decode(data, SCREEN_SIZE);
90 	delete data;
91 
92 	const MenuRecordLanguage *rec = &menuList[0];
93 	while ((rec->language != Common::UNK_LANG) && (rec->language != language))
94 		++rec;
95 	if (rec->language == Common::UNK_LANG)
96 		error("Unknown language encountered in top line handler");
97 
98 	_menus[0] = new MenuRecord(&rec->menus[0], 1, sl.getString(S_CREDITS));
99 	_menus[1] = new MenuRecord(&rec->menus[1], 3,
100 		sl.getString(S_RESTART_GAME), sl.getString(S_SAVE_GAME), sl.getString(S_RESTORE_GAME));
101 	_menus[2] = new MenuRecord(&rec->menus[2], 3,
102 		sl.getString(S_QUIT), sl.getString(S_SLOW_TEXT), sl.getString(S_SOUND_ON));
103 
104 	_selectedMenu = NULL;
105 }
106 
~Menu()107 Menu::~Menu() {
108 	for (int ctr=0; ctr<NUM_MENUS; ++ctr) delete _menus[ctr];
109 	delete _menu;
110 }
111 
getReference()112 Menu &Menu::getReference() {
113 	return *int_menu;
114 }
115 
execute()116 uint8 Menu::execute() {
117 	OSystem &system = *g_system;
118 	LureEngine &engine = LureEngine::getReference();
119 	Mouse &mouse = Mouse::getReference();
120 	Events &events = Events::getReference();
121 	Screen &screen = Screen::getReference();
122 
123 	mouse.setCursorNum(CURSOR_ARROW);
124 	system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0,
125 		FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
126 
127 	_selectedMenu = NULL;
128 	_surfaceMenu = NULL;
129 	_selectedIndex = 0;
130 
131 	while (mouse.lButton() || mouse.rButton()) {
132 		while (events.pollEvent()) {
133 			if (engine.shouldQuit()) return MENUITEM_NONE;
134 
135 			if (mouse.y() < MENUBAR_Y_SIZE) {
136 				MenuRecord *p = getMenuAt(mouse.x());
137 
138 				if (_selectedMenu != p) {
139 					// If necessary, remove prior menu
140 					if (_selectedMenu) {
141 						toggleHighlight(_selectedMenu);
142 						screen.updateArea(0, 0, FULL_SCREEN_WIDTH, _surfaceMenu->height() + 8);
143 						delete _surfaceMenu;
144 						_surfaceMenu = NULL;
145 						_selectedIndex = 0;
146 					}
147 
148 					_selectedMenu = p;
149 
150 					// If a new menu is selected, show it
151 					if (_selectedMenu) {
152 						toggleHighlight(_selectedMenu);
153 						_surfaceMenu = Surface::newDialog(
154 							_selectedMenu->width(), _selectedMenu->numEntries(),
155 							_selectedMenu->entries(), false, DEFAULT_TEXT_COLOR, false);
156 						_surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
157 					}
158 
159 					system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0,
160 						FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
161 				}
162 			}
163 
164 			// Check for changing selected index
165 			uint8 index = getIndexAt(mouse.x(), mouse.y());
166 			if (index != _selectedIndex) {
167 				if (_selectedIndex != 0) toggleHighlightItem(_selectedIndex);
168 				_selectedIndex = index;
169 				if (_selectedIndex != 0) toggleHighlightItem(_selectedIndex);
170 			}
171 		}
172 
173 		system.updateScreen();
174 		system.delayMillis(10);
175 	}
176 
177 	delete _surfaceMenu;
178 
179 	// Deselect the currently selected menu header
180 	if (_selectedMenu)
181 		toggleHighlight(_selectedMenu);
182 
183 	// Restore the previous screen
184 	screen.update();
185 
186 	if ((_selectedMenu == NULL) || (_selectedIndex == 0)) return MENUITEM_NONE;
187 	else if (_selectedMenu == _menus[0])
188 		return MENUITEM_CREDITS;
189 	else if (_selectedMenu == _menus[1]) {
190 		switch (_selectedIndex) {
191 		case 1:
192 			return MENUITEM_RESTART_GAME;
193 		case 2:
194 			return MENUITEM_SAVE_GAME;
195 		case 3:
196 			return MENUITEM_RESTORE_GAME;
197 		}
198 	} else {
199 		switch (_selectedIndex) {
200 		case 1:
201 			return MENUITEM_QUIT;
202 		case 2:
203 			return MENUITEM_TEXT_SPEED;
204 		case 3:
205 			return MENUITEM_SOUND;
206 		}
207 	}
208 	return MENUITEM_NONE;
209 }
210 
getMenuAt(int x)211 MenuRecord *Menu::getMenuAt(int x) {
212 	for (int ctr = 0; ctr < NUM_MENUS; ++ctr)
213 		if ((x >= _menus[ctr]->hsxstart()) && (x <= _menus[ctr]->hsxend()))
214 			return _menus[ctr];
215 
216 	return NULL;
217 }
218 
getIndexAt(uint16 x,uint16 y)219 uint8 Menu::getIndexAt(uint16 x, uint16 y) {
220 	if (!_selectedMenu) return 0;
221 
222 	int ys = MENUBAR_Y_SIZE + Surface::textY();
223 	int ye = MENUBAR_Y_SIZE + (_surfaceMenu->height() - Surface::textY());
224 	if ((y < ys) || (y > ye)) return 0;
225 
226 	uint16 yRelative = y - ys;
227 	uint8 index = (uint8) (yRelative / 8) + 1;
228 	if (index > _selectedMenu->numEntries()) index = _selectedMenu->numEntries();
229 	return index;
230 }
231 
232 #define MENUBAR_SELECTED_COLOR 0xf7
233 
toggleHighlight(MenuRecord * menuRec)234 void Menu::toggleHighlight(MenuRecord *menuRec) {
235 	const byte colorList[4] = {4, 2, 0, 0xf7};
236 	const byte *colors = LureEngine::getReference().isEGA() ? &colorList[0] : &colorList[2];
237 	byte *addr = _menu->data();
238 
239 	for (uint16 y=0; y<MENUBAR_Y_SIZE; ++y) {
240 		for (uint16 x=menuRec->hsxstart(); x<=menuRec->hsxend(); ++x) {
241 			if (addr[x] == colors[0]) addr[x] = colors[1];
242 			else if (addr[x] == colors[1]) addr[x] = colors[0];
243 		}
244 		addr += FULL_SCREEN_WIDTH;
245 	}
246 }
247 
toggleHighlightItem(uint8 index)248 void Menu::toggleHighlightItem(uint8 index) {
249 	const byte colorList[4] = {EGA_DIALOG_TEXT_COLOR, EGA_DIALOG_WHITE_COLOR,
250 		VGA_DIALOG_TEXT_COLOR, VGA_DIALOG_WHITE_COLOR};
251 	const byte *colors = LureEngine::getReference().isEGA() ? &colorList[0] : &colorList[2];
252 	byte *p = _surfaceMenu->data().data() + (Surface::textY() +
253 		((index - 1) * FONT_HEIGHT)) * _surfaceMenu->width() + Surface::textX();
254 	int numBytes =_surfaceMenu->width() - Surface::textX() * 2;
255 
256 	for (int y = 0; y < FONT_HEIGHT; ++y, p += _surfaceMenu->width()) {
257 		byte *pTemp = p;
258 
259 		for (int x = 0; x < numBytes; ++x, ++pTemp) {
260 			if (*pTemp == colors[0]) *pTemp = colors[1];
261 			else if (*pTemp == colors[1]) *pTemp = colors[0];
262 		}
263 	}
264 
265 	_surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
266 }
267 
268 /*--------------------------------------------------------------------------*/
269 
ShowInventory()270 uint16 PopupMenu::ShowInventory() {
271 	Resources &rsc = Resources::getReference();
272 	StringData &strings = StringData::getReference();
273 
274 	uint16 numItems = rsc.numInventoryItems();
275 	uint16 itemCtr = 0;
276 	char **itemNames = (char **) Memory::alloc(sizeof(char *) * numItems);
277 	uint16 *idList = (uint16 *) Memory::alloc(sizeof(uint16) * numItems);
278 
279 	HotspotDataList::iterator i;
280 	for (i = rsc.hotspotData().begin(); i != rsc.hotspotData().end(); ++i) {
281 		HotspotData const &hotspot = **i;
282 		if (hotspot.roomNumber == PLAYER_ID) {
283 			idList[itemCtr] = hotspot.hotspotId;
284 			char *hotspotName = itemNames[itemCtr++] = (char *) malloc(MAX_HOTSPOT_NAME_SIZE);
285 			strings.getString(hotspot.nameId, hotspotName);
286 		}
287 	}
288 
289 	uint16 result = Show(numItems, const_cast<const char **>(itemNames));
290 	if (result != 0xffff) result = idList[result];
291 
292 	for (itemCtr = 0; itemCtr < numItems; ++itemCtr)
293 		free(itemNames[itemCtr]);
294 
295 	Memory::dealloc(itemNames);
296 	Memory::dealloc(idList);
297 	return result;
298 }
299 
300 #define MAX_NUM_DISPLAY_ITEMS 20
301 
ShowItems(Action contextAction,uint16 roomNumber)302 uint16 PopupMenu::ShowItems(Action contextAction, uint16 roomNumber) {
303 	Resources &res = Resources::getReference();
304 	ValueTableData &fields = res.fieldList();
305 	RoomDataList &rooms = res.roomData();
306 	HotspotDataList &hotspots = res.hotspotData();
307 	StringData &strings = StringData::getReference();
308 	Room &room = Room::getReference();
309 	Screen &screen = Screen::getReference();
310 	Mouse &mouse = Mouse::getReference();
311 	RoomDataList::iterator ir;
312 	HotspotDataList::iterator ih;
313 	uint16 entryIds[MAX_NUM_DISPLAY_ITEMS];
314 	uint16 nameIds[MAX_NUM_DISPLAY_ITEMS];
315 	char *entryNames[MAX_NUM_DISPLAY_ITEMS];
316 	int numItems = 0;
317 	int itemCtr;
318 	uint32 contextBitflag = 1 << (contextAction - 1);
319 
320 	// Loop for rooms
321 	for (ir = rooms.begin(); ir != rooms.end(); ++ir) {
322 		RoomData const &roomData = **ir;
323 		// Pre-condition checks for whether to skip room
324 		if ((roomData.hdrFlags != 15) && ((roomData.hdrFlags & fields.hdrFlagMask()) == 0))
325 			continue;
326 		if (((roomData.flags & HOTSPOTFLAG_MENU_EXCLUSION) != 0) || ((roomData.flags & HOTSPOTFLAG_FOUND) == 0))
327 			continue;
328 		if ((roomData.actions & contextBitflag) == 0)
329 			continue;
330 
331 		// Add room to list of entries to display
332 		if (numItems == MAX_NUM_DISPLAY_ITEMS) error("Out of space in ask list");
333 		entryIds[numItems] = roomData.roomNumber;
334 		nameIds[numItems] = roomData.roomNumber;
335 		entryNames[numItems] = (char *) Memory::alloc(MAX_HOTSPOT_NAME_SIZE);
336 		strings.getString(roomData.roomNumber, entryNames[numItems]);
337 		++numItems;
338 	}
339 
340 	// Loop for hotspots
341 	for (ih = hotspots.begin(); ih != hotspots.end(); ++ih) {
342 		HotspotData const &hotspot = **ih;
343 
344 		if ((hotspot.headerFlags != 15) &&
345 			((hotspot.headerFlags & fields.hdrFlagMask()) == 0))
346 			continue;
347 
348 		if (((hotspot.flags & HOTSPOTFLAG_MENU_EXCLUSION) != 0) || ((hotspot.flags & HOTSPOTFLAG_FOUND) == 0))
349 			// Skip the current hotspot
350 			continue;
351 
352 		// If the hotspot is room specific, skip if the character will not be in the specified room
353 		if (((hotspot.flags & HOTSPOTFLAG_ROOM_SPECIFIC) != 0) &&
354 			(hotspot.roomNumber != roomNumber))
355 			continue;
356 
357 		// If hotspot does not allow action, then skip it
358 		if ((hotspot.actions & contextBitflag) == 0)
359 			continue;
360 
361 		// If a special hotspot Id, then skip displaying
362 		if ((hotspot.nameId == 0x17A) || (hotspot.nameId == 0x147))
363 			continue;
364 
365 		// Check if the hotspot's name is already used in an already set item
366 		itemCtr = 0;
367 		while ((itemCtr < numItems) && (nameIds[itemCtr] != hotspot.nameId))
368 			++itemCtr;
369 		if (itemCtr != numItems)
370 			// Item's name is already present - skip hotspot
371 			continue;
372 
373 		// Add hotspot to list of entries to display
374 		if (numItems == MAX_NUM_DISPLAY_ITEMS) error("Out of space in ask list");
375 		entryIds[numItems] = hotspot.hotspotId;
376 		nameIds[numItems] = hotspot.nameId;
377 		entryNames[numItems] = (char *) Memory::alloc(MAX_HOTSPOT_NAME_SIZE);
378 		strings.getString(hotspot.nameId, entryNames[numItems]);
379 		++numItems;
380 	}
381 
382 	if (numItems == 0) {
383 		// No items, so add a 'nothing' to the statusLine
384 		if (LureEngine::getReference().getLanguage() == Common::RU_RUS)
385 			strcat(room.statusLine(), "(ybxtuj ytn)");
386 		else
387 			strcat(room.statusLine(), "(nothing)");
388 	}
389 
390 	room.update();
391 	screen.update();
392 	mouse.waitForRelease();
393 
394 	if (numItems == 0)
395 		// Return flag for no items to ask for
396 		return 0xfffe;
397 
398 	// Display items
399 	uint16 result = Show(numItems, const_cast<const char **>(entryNames));
400 	if (result != 0xffff) result = entryIds[result];
401 
402 	// Deallocate display strings
403 	for (itemCtr = 0; itemCtr < numItems; ++itemCtr)
404 		Memory::dealloc(entryNames[itemCtr]);
405 
406 	return result;
407 }
408 
entryCompare(const char ** p1,const char ** p2)409 static int entryCompare(const char **p1, const char **p2) {
410 	return strcmp(*p1, *p2);
411 }
412 
413 typedef int (*CompareMethod)(const void*, const void*);
414 
Show(uint32 actionMask)415 Action PopupMenu::Show(uint32 actionMask) {
416 	StringList &stringList = Resources::getReference().stringList();
417 	int numEntries = 0;
418 	uint32 v = actionMask;
419 	int index;
420 	int currentAction;
421 	uint16 resultIndex;
422 	Action resultAction;
423 
424 	for (index = 1; index <= EXAMINE; ++index, v >>= 1) {
425 		if (v & 1) ++numEntries;
426 	}
427 
428 	const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
429 
430 	int strIndex = 0;
431 	for (currentAction = 0; currentAction < (int)EXAMINE; ++currentAction) {
432 		if ((actionMask & (1 << currentAction)) != 0) {
433 			strList[strIndex] = stringList.getString(currentAction);
434 			++strIndex;
435 		}
436 	}
437 
438 	// Sort the list
439 	qsort(strList, numEntries, sizeof(const char *), (CompareMethod) entryCompare);
440 
441 	// Show the entries
442 	resultIndex = Show(numEntries, strList);
443 
444 	resultAction = NONE;
445 	if (resultIndex != 0xffff) {
446 		// Scan through the list of actions to find the selected entry
447 		for (currentAction = 0; currentAction < (int)EXAMINE; ++currentAction) {
448 			if (strList[resultIndex] == stringList.getString(currentAction)) {
449 				resultAction = (Action) (currentAction + 1);
450 				break;
451 			}
452 		}
453 	}
454 
455 	Memory::dealloc(strList);
456 	return resultAction;
457 }
458 
Show(int numEntries,Action * actions)459 Action PopupMenu::Show(int numEntries, Action *actions) {
460 	StringList &stringList = Resources::getReference().stringList();
461 	const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
462 	Action *actionPtr = actions;
463 	for (int index = 0; index < numEntries; ++index)
464 		strList[index] = stringList.getString(*actionPtr++);
465 	uint16 result = Show(numEntries, strList);
466 
467 	Memory::dealloc(strList);
468 	if (result == 0xffff) return NONE;
469 	else return actions[result];
470 }
471 
Show(int numEntries,const char * actions[])472 uint16 PopupMenu::Show(int numEntries, const char *actions[]) {
473 	if (numEntries == 0) return 0xffff;
474 	LureEngine &engine = LureEngine::getReference();
475 	Events &e = Events::getReference();
476 	Mouse &mouse = Mouse::getReference();
477 	OSystem &system = *g_system;
478 	Screen &screen = Screen::getReference();
479 	Common::Rect r;
480 	bool isEGA = LureEngine::getReference().isEGA();
481 	byte bgColor = isEGA ? EGA_DIALOG_BG_COLOR : 0;
482 	byte textColor = isEGA ? EGA_DIALOG_TEXT_COLOR : VGA_DIALOG_TEXT_COLOR;
483 	byte whiteColor = isEGA ? EGA_DIALOG_WHITE_COLOR : VGA_DIALOG_WHITE_COLOR;
484 
485 
486 	const uint16 yMiddle = FULL_SCREEN_HEIGHT / 2;
487 #ifndef LURE_CLICKABLE_MENUS
488 	uint16 oldX = mouse.x();
489 	uint16 oldY = mouse.y();
490 	mouse.cursorOff();
491 	mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
492 
493 	// Round up number of lines in dialog to next odd number
494 	uint16 numLines = (numEntries / 2) * 2 + 1;
495 	if (numLines > 5) numLines = 5;
496 #else
497 	mouse.pushCursorNum(CURSOR_ARROW);
498 
499 	// In WinCE, the whole menu is shown and the items are click-selectable
500 	uint16 numLines = numEntries;
501 #endif
502 
503 	// Figure out the character width
504 	uint16 numCols = 0;
505 	for (int ctr = 0; ctr < numEntries; ++ctr) {
506 		int len = strlen(actions[ctr]);
507 		if (len > numCols)
508 			numCols = len;
509 	}
510 
511 	// Create the dialog surface
512 	Common::Point size;
513 	Surface::getDialogBounds(size, numCols, numLines, false);
514 	Surface *s = new Surface(size.x, size.y);
515 	s->createDialog(true);
516 
517 	int selectedIndex = 0;
518 	bool refreshFlag = true;
519 	r.left = Surface::textX();
520 	r.right = s->width() - Surface::textX() + 1;
521 	r.top = Surface::textY();
522 	r.bottom = s->height() - Surface::textY() + 1;
523 
524 	bool bailOut = false;
525 
526 	while (!bailOut) {
527 		if (refreshFlag) {
528 			// Set up the contents of the menu
529 			s->fillRect(r, bgColor);
530 
531 			for (int index = 0; index < numLines; ++index) {
532 #ifndef LURE_CLICKABLE_MENUS
533 				int actionIndex = selectedIndex - (numLines / 2) + index;
534 #else
535 				int actionIndex = index;
536 #endif
537 				if ((actionIndex >= 0) && (actionIndex < numEntries)) {
538 					s->writeString(Surface::textX(), Surface::textY() + index * FONT_HEIGHT,
539 						actions[actionIndex], true,
540 #ifndef LURE_CLICKABLE_MENUS
541 						(index == (numLines / 2)) ? whiteColor : textColor,
542 #else
543 						(index == selectedIndex) ? whiteColor : textColor,
544 #endif
545 						false);
546 				}
547 			}
548 
549 			s->copyToScreen(0, yMiddle-(s->height() / 2));
550 			system.updateScreen();
551 			refreshFlag = false;
552 		}
553 
554 		while (e.pollEvent()) {
555 			if (engine.shouldQuit()) {
556 				selectedIndex = 0xffff;
557 				bailOut = true;
558 				break;
559 			} else if (e.type() == Common::EVENT_WHEELUP) {
560 				// Scroll upwards
561 				if (selectedIndex > 0) {
562 					--selectedIndex;
563 					refreshFlag = true;
564 				}
565 			} else if (e.type() == Common::EVENT_WHEELDOWN) {
566 				// Scroll downwards
567 				if (selectedIndex < numEntries - 1) {
568 					++selectedIndex;
569 					refreshFlag = true;
570 				}
571 			} else if (e.type() == Common::EVENT_KEYDOWN) {
572 				uint16 keycode = e.event().kbd.keycode;
573 
574 				if (((keycode == Common::KEYCODE_KP8) || (keycode == Common::KEYCODE_UP)) && (selectedIndex > 0)) {
575 					--selectedIndex;
576 					refreshFlag = true;
577 				} else if (((keycode == Common::KEYCODE_KP2) || (keycode == Common::KEYCODE_DOWN)) &&
578 						(selectedIndex < numEntries-1)) {
579 					++selectedIndex;
580 					refreshFlag = true;
581 				} else if ((keycode == Common::KEYCODE_RETURN) || (keycode == Common::KEYCODE_KP_ENTER)) {
582 					bailOut = true;
583 					break;
584 				} else if (keycode == Common::KEYCODE_ESCAPE) {
585 					selectedIndex = 0xffff;
586 					bailOut = true;
587 					break;
588 				}
589 
590 #ifdef LURE_CLICKABLE_MENUS
591 			} else if (e.type() == Common::EVENT_LBUTTONDOWN || e.type() == Common::EVENT_MOUSEMOVE) {
592 				int16 x = mouse.x();
593 				int16 y = mouse.y() - yMiddle + (s->height() / 2);
594 				refreshFlag = true;
595 
596 				if (r.contains(x, y)) {
597 					selectedIndex = (y - r.top) / FONT_HEIGHT;
598 					if (e.type() == Common::EVENT_LBUTTONDOWN)
599 						bailOut = true;
600 						break;
601 				}
602 #else
603 			} else if ((e.type() == Common::EVENT_LBUTTONDOWN) ||
604 					(e.type() == Common::EVENT_MBUTTONDOWN)) {
605 				//mouse.waitForRelease();
606 				bailOut = true;
607 				break;
608 #endif
609 			} else if (e.type() == Common::EVENT_RBUTTONDOWN) {
610 				mouse.waitForRelease();
611 				selectedIndex = 0xffff;
612 				bailOut = true;
613 				break;
614 			}
615 		}
616 
617 		if (!bailOut) {
618 #ifndef LURE_CLICKABLE_MENUS
619 			// Warping the mouse to "neutral" even if the top/bottom menu
620 			// entry has been reached has both pros and cons. It makes the
621 			// menu behave a bit more sensibly, but it also makes it harder
622 			// to move the mouse pointer out of the ScummVM window.
623 
624 			if (mouse.y() < yMiddle - POPMENU_CHANGE_SENSITIVITY) {
625 				if (selectedIndex > 0) {
626 					--selectedIndex;
627 					refreshFlag = true;
628 				}
629 				mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
630 			} else if (mouse.y() > yMiddle + POPMENU_CHANGE_SENSITIVITY) {
631 				if (selectedIndex < numEntries - 1) {
632 					++selectedIndex;
633 					refreshFlag = true;
634 				}
635 				mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
636 			}
637 #endif
638 
639 			system.delayMillis(20);
640 		}
641 	}
642 
643 	// bailOut
644 	delete s;
645 
646 #ifndef LURE_CLICKABLE_MENUS
647 	mouse.setPosition(oldX, oldY);
648 	mouse.cursorOn();
649 #else
650 	mouse.popCursor();
651 #endif
652 	screen.update();
653 	return selectedIndex;
654 }
655 
656 } // End of namespace Lure
657