0x47 stub
[scummvm-innocent.git] / engines / agi / menu.cpp
blob5d30eda81d64a2203d61267facae1081681f2b61
1 /* ScummVM - Graphic Adventure Engine
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.
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.
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.
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.
21 * $URL$
22 * $Id$
26 #include "agi/agi.h"
27 #include "agi/sprite.h"
28 #include "agi/graphics.h"
29 #include "agi/keyboard.h"
30 #include "agi/menu.h"
31 #include "common/list.h"
33 namespace Agi {
35 // TODO: add constructor/destructor for agi_menu, agi_menu_option
37 struct AgiMenuOption {
38 int enabled; /**< option is enabled or disabled */
39 int event; /**< menu event */
40 int index; /**< number of option in this menu */
41 char *text; /**< text of menu option */
44 struct AgiMenu {
45 MenuOptionList down; /**< list head for menu options */
46 int index; /**< number of menu in menubar */
47 int width; /**< width of menu in characters */
48 int height; /**< height of menu in characters */
49 int col; /**< column of menubar entry */
50 int wincol; /**< column of menu window */
51 char *text; /**< menu name */
54 AgiMenu *Menu::getMenu(int i) {
55 MenuList::iterator iter;
56 for (iter = _menubar.begin(); iter != _menubar.end(); ++iter) {
57 AgiMenu *m = *iter;
58 if (m->index == i)
59 return m;
61 return NULL;
64 AgiMenuOption *Menu::getMenuOption(int i, int j) {
65 AgiMenu *m = getMenu(i);
66 MenuOptionList::iterator iter;
68 for (iter = m->down.begin(); iter != m->down.end(); ++iter) {
69 AgiMenuOption* d = *iter;
70 if (d->index == j)
71 return d;
74 return NULL;
77 void Menu::drawMenuBar() {
78 _vm->clearLines(0, 0, MENU_BG);
79 _vm->flushLines(0, 0);
81 MenuList::iterator iter;
82 for (iter = _menubar.begin(); iter != _menubar.end(); ++iter) {
83 AgiMenu *m = *iter;
85 _vm->printText(m->text, 0, m->col, 0, 40, MENU_FG, MENU_BG);
90 void Menu::drawMenuHilite(int curMenu) {
91 AgiMenu *m = getMenu(curMenu);
93 debugC(6, kDebugLevelMenu, "[%s]", m->text);
95 _vm->printText(m->text, 0, m->col, 0, 40, MENU_BG, MENU_FG);
96 _vm->flushLines(0, 0);
99 // draw box and pulldowns.
100 void Menu::drawMenuOption(int hMenu) {
101 // find which vertical menu it is
102 AgiMenu *m = getMenu(hMenu);
104 _gfx->drawBox(m->wincol * CHAR_COLS, 1 * CHAR_LINES, (m->wincol + m->width + 2) * CHAR_COLS,
105 (1 + m->height + 2) * CHAR_LINES, MENU_BG, MENU_LINE, 0);
107 MenuOptionList::iterator iter;
109 for (iter = m->down.begin(); iter != m->down.end(); ++iter) {
110 AgiMenuOption* d = *iter;
112 _vm->printText(d->text, 0, m->wincol + 1, d->index + 2, m->width + 2,
113 MENU_FG, MENU_BG, !d->enabled);
117 void Menu::drawMenuOptionHilite(int hMenu, int vMenu) {
118 AgiMenu *m = getMenu(hMenu);
119 AgiMenuOption *d = getMenuOption(hMenu, vMenu);
121 // Disabled menu items are "greyed out" with a checkerboard effect,
122 // rather than having a different colour. -- dsymonds
123 _vm->printText(d->text, 0, m->wincol + 1, vMenu + 2, m->width + 2,
124 MENU_BG, MENU_FG, !d->enabled);
127 void Menu::newMenuSelected(int i) {
128 _picture->showPic();
129 drawMenuBar();
130 drawMenuHilite(i);
131 drawMenuOption(i);
134 bool Menu::mouseOverText(int line, int col, char *s) {
135 if (g_mouse.x < col * CHAR_COLS)
136 return false;
138 if (g_mouse.x > (int)(col + strlen(s)) * CHAR_COLS)
139 return false;
141 if (g_mouse.y < line * CHAR_LINES)
142 return false;
144 if (g_mouse.y >= (line + 1) * CHAR_LINES)
145 return false;
147 return true;
150 #if 0
151 static void add_about_option() {
152 const char *text = "About AGI engine";
154 agi_menu_option *d = new agi_menu_option;
155 d->text = strdup(text);
156 d->enabled = true;
157 d->event = 255;
158 d->index = (v_max_menu[0] += 1);
160 agi_menu *m = *menubar.begin();
161 m->down.push_back(d);
162 m->height++;
163 if (m->width < (int)strlen(text))
164 m->width = strlen(text);
166 #endif
169 * Public functions
172 Menu::Menu(AgiEngine *vm, GfxMgr *gfx, PictureMgr *picture) {
173 _vm = vm;
174 _gfx = gfx;
175 _picture = picture;
176 _hIndex = 0;
177 _hCol = 1;
178 _hMaxMenu = 0;
179 _hCurMenu = 0;
180 _vCurMenu = 0;
183 Menu::~Menu() {
184 MenuList::iterator iterh;
185 for (iterh = _menubar.reverse_begin(); iterh != _menubar.end(); ) {
186 AgiMenu *m = *iterh;
188 debugC(3, kDebugLevelMenu, "deiniting hmenu %s", m->text);
190 MenuOptionList::iterator iterv;
192 for (iterv = m->down.reverse_begin(); iterv != m->down.end(); ) {
193 AgiMenuOption *d = *iterv;
195 debugC(3, kDebugLevelMenu, " deiniting vmenu %s", d->text);
197 free(d->text);
198 delete d;
200 iterv = m->down.reverse_erase(iterv);
202 free(m->text);
203 delete m;
205 iterh = _menubar.reverse_erase(iterh);
209 void Menu::add(const char *s) {
210 AgiMenu *m = new AgiMenu;
211 m->text = strdup(s);
213 while (m->text[strlen(m->text) - 1] == ' ')
214 m->text[strlen(m->text) - 1] = 0;
216 m->width = 0;
217 m->height = 0;
218 m->index = _hIndex++;
219 m->col = _hCol;
220 m->wincol = _hCol - 1;
221 _vIndex = 0;
222 _vMaxMenu[m->index] = 0;
223 _hCol += strlen(m->text) + 1;
224 _hMaxMenu = m->index;
226 debugC(3, kDebugLevelMenu, "add menu: '%s' %02x", s, m->text[strlen(m->text)]);
227 _menubar.push_back(m);
230 void Menu::addItem(const char *s, int code) {
231 int l;
233 AgiMenuOption* d = new AgiMenuOption;
235 d->text = strdup(s);
236 d->enabled = true;
237 d->event = code;
238 d->index = _vIndex++;
240 // add to last menu in list
241 assert(_menubar.reverse_begin() != _menubar.end());
242 AgiMenu *m = *_menubar.reverse_begin();
243 m->height++;
245 _vMaxMenu[m->index] = d->index;
247 l = strlen(d->text);
248 if (l > 40)
249 l = 38;
250 if (m->wincol + l > 38)
251 m->wincol = 38 - l;
252 if (l > m->width)
253 m->width = l;
255 debugC(3, kDebugLevelMenu, "Adding menu item: %s (size = %d)", s, m->height);
257 m->down.push_back(d);
260 void Menu::submit() {
261 debugC(3, kDebugLevelMenu, "Submitting menu");
263 // add_about_option ();
265 // If a menu has no options, delete it
266 MenuList::iterator iter;
267 for (iter = _menubar.reverse_begin(); iter != _menubar.end(); ) {
268 AgiMenu *m = *iter;
270 if (m->down.empty()) {
271 free(m->text);
272 delete m;
274 _hMaxMenu--;
276 iter = _menubar.reverse_erase(iter);
277 } else {
278 --iter;
283 bool Menu::keyhandler(int key) {
284 static int clockVal;
285 static int menuActive = false;
286 static int buttonUsed = 0;
288 if (!_vm->getflag(fMenusWork) && !(_vm->getFeatures() & GF_MENUS))
289 return false;
291 if (!menuActive) {
292 clockVal = _vm->_game.clockEnabled;
293 _vm->_game.clockEnabled = false;
294 drawMenuBar();
297 // Mouse handling
299 if (g_mouse.button) {
300 int hmenu, vmenu;
302 buttonUsed = 1; // Button has been used at least once
304 if (g_mouse.y <= CHAR_LINES) {
305 // on the menubar
306 hmenu = 0;
308 MenuList::iterator iterh;
310 for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) {
311 AgiMenu *m = *iterh;
313 if (mouseOverText(0, m->col, m->text)) {
314 break;
315 } else {
316 hmenu++;
320 if (hmenu <= _hMaxMenu) {
321 if (_hCurMenu != hmenu) {
322 _vCurMenu = -1;
323 newMenuSelected(hmenu);
325 _hCurMenu = hmenu;
327 } else {
328 // not in menubar
329 vmenu = 0;
331 AgiMenu *m = getMenu(_hCurMenu);
333 MenuOptionList::iterator iterv;
335 for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) {
336 AgiMenuOption *do1 = *iterv;
338 if (mouseOverText(2 + do1->index, m->wincol + 1, do1->text)) {
339 break;
340 } else {
341 vmenu++;
345 if (vmenu <= _vMaxMenu[_hCurMenu]) {
346 if (_vCurMenu != vmenu) {
347 drawMenuOption(_hCurMenu);
348 drawMenuOptionHilite(_hCurMenu, vmenu);
350 _vCurMenu = vmenu;
353 } else if (buttonUsed) {
354 // Button released
355 buttonUsed = 0;
357 debugC(6, kDebugLevelMenu | kDebugLevelInput, "button released!");
359 if (_vCurMenu < 0)
360 _vCurMenu = 0;
362 drawMenuOptionHilite(_hCurMenu, _vCurMenu);
364 if (g_mouse.y <= CHAR_LINES) {
365 // on the menubar
366 } else {
367 // see which option we selected
368 AgiMenu *m = getMenu(_hCurMenu);
369 MenuOptionList::iterator iterv;
371 for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) {
372 AgiMenuOption *d = *iterv;
374 if (mouseOverText(2 + d->index, m->wincol + 1, d->text)) {
375 // activate that option
376 if (d->enabled) {
377 debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event);
378 _vm->_game.controllerOccured[d->event] = true;
379 _vm->_menuSelected = true;
381 goto exit_menu;
385 goto exit_menu;
389 if (!menuActive) {
390 if (_hCurMenu >= 0) {
391 drawMenuHilite(_hCurMenu);
392 drawMenuOption(_hCurMenu);
393 if (!buttonUsed && _vCurMenu >= 0)
394 drawMenuOptionHilite(_hCurMenu, _vCurMenu);
396 menuActive = true;
399 switch (key) {
400 case KEY_ESCAPE:
401 debugC(6, kDebugLevelMenu | kDebugLevelInput, "KEY_ESCAPE");
402 goto exit_menu;
403 case KEY_ENTER:
405 debugC(6, kDebugLevelMenu | kDebugLevelInput, "KEY_ENTER");
406 AgiMenuOption* d = getMenuOption(_hCurMenu, _vCurMenu);
408 if (d->enabled) {
409 debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event);
410 _vm->_game.controllerOccured[d->event] = true;
411 goto exit_menu;
413 break;
415 case KEY_DOWN:
416 case KEY_UP:
417 _vCurMenu += key == KEY_DOWN ? 1 : -1;
419 if (_vCurMenu < 0)
420 _vCurMenu = _vMaxMenu[_hCurMenu];
421 if (_vCurMenu > _vMaxMenu[_hCurMenu])
422 _vCurMenu = 0;
424 drawMenuOption(_hCurMenu);
425 drawMenuOptionHilite(_hCurMenu, _vCurMenu);
426 break;
427 case KEY_RIGHT:
428 case KEY_LEFT:
429 _hCurMenu += key == KEY_RIGHT ? 1 : -1;
431 if (_hCurMenu < 0)
432 _hCurMenu = _hMaxMenu;
433 if (_hCurMenu > _hMaxMenu)
434 _hCurMenu = 0;
436 _vCurMenu = 0;
437 newMenuSelected(_hCurMenu);
438 drawMenuOptionHilite(_hCurMenu, _vCurMenu);
439 break;
442 return true;
444 exit_menu:
445 buttonUsed = 0;
446 _picture->showPic();
447 _vm->writeStatus();
449 _vm->setvar(vKey, 0);
450 _vm->_game.keypress = 0;
451 _vm->_game.clockEnabled = clockVal;
452 _vm->oldInputMode();
454 debugC(3, kDebugLevelMenu, "exit_menu: input mode reset to %d", _vm->_game.inputMode);
455 menuActive = false;
457 return true;
460 void Menu::setItem(int event, int state) {
461 // scan all menus for event number #
463 debugC(6, kDebugLevelMenu, "event = %d, state = %d", event, state);
464 MenuList::iterator iterh;
466 for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) {
467 AgiMenu *m = *iterh;
468 MenuOptionList::iterator iterv;
470 for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) {
471 AgiMenuOption *d = *iterv;
473 if (d->event == event) {
474 d->enabled = state;
475 // keep going; we need to set the state of every menu item
476 // with this event code. -- dsymonds
482 void Menu::enableAll() {
483 MenuList::iterator iterh;
484 for (iterh = _menubar.begin(); iterh != _menubar.end(); ++iterh) {
485 AgiMenu *m = *iterh;
486 MenuOptionList::iterator iterv;
488 for (iterv = m->down.begin(); iterv != m->down.end(); ++iterv) {
489 AgiMenuOption *d = *iterv;
491 d->enabled = true;
497 AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, bool positive) const {
498 if (_amigaStyle) {
499 if (positive) {
500 if (pressed) { // Positive pressed Amiga-style button
501 if (_olderAgi) {
502 return AgiTextColor(amigaBlack, amigaOrange);
503 } else {
504 return AgiTextColor(amigaBlack, amigaPurple);
506 } else { // Positive unpressed Amiga-style button
507 return AgiTextColor(amigaWhite, amigaGreen);
509 } else { // _amigaStyle && !positive
510 if (pressed) { // Negative pressed Amiga-style button
511 return AgiTextColor(amigaBlack, amigaCyan);
512 } else { // Negative unpressed Amiga-style button
513 return AgiTextColor(amigaWhite, amigaRed);
516 } else { // PC-style button
517 if (hasFocus || pressed) { // A pressed or in focus PC-style button
518 return AgiTextColor(pcWhite, pcBlack);
519 } else { // An unpressed PC-style button without focus
520 return AgiTextColor(pcBlack, pcWhite);
525 AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, int baseFgColor, int baseBgColor) const {
526 return getColor(hasFocus, pressed, AgiTextColor(baseFgColor, baseBgColor));
529 AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, const AgiTextColor &baseColor) const {
530 if (hasFocus || pressed)
531 return baseColor.swap();
532 else
533 return baseColor;
536 int AgiButtonStyle::getTextOffset(bool hasFocus, bool pressed) const {
537 return (pressed && !_amigaStyle) ? 1 : 0;
540 bool AgiButtonStyle::getBorder(bool hasFocus, bool pressed) const {
541 return _amigaStyle && !_authenticAmiga && (hasFocus || pressed);
544 void AgiButtonStyle::setAmigaStyle(bool amigaStyle, bool olderAgi, bool authenticAmiga) {
545 _amigaStyle = amigaStyle;
546 _olderAgi = olderAgi;
547 _authenticAmiga = authenticAmiga;
550 void AgiButtonStyle::setPcStyle(bool pcStyle) {
551 setAmigaStyle(!pcStyle);
554 AgiButtonStyle::AgiButtonStyle(Common::RenderMode renderMode) {
555 setAmigaStyle(renderMode == Common::kRenderAmiga);
558 } // End of namespace Agi