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.
27 #include "agi/sprite.h"
28 #include "agi/graphics.h"
29 #include "agi/keyboard.h"
31 #include "common/list.h"
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 */
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
) {
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
;
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
) {
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
) {
134 bool Menu::mouseOverText(int line
, int col
, char *s
) {
135 if (g_mouse
.x
< col
* CHAR_COLS
)
138 if (g_mouse
.x
> (int)(col
+ strlen(s
)) * CHAR_COLS
)
141 if (g_mouse
.y
< line
* CHAR_LINES
)
144 if (g_mouse
.y
>= (line
+ 1) * CHAR_LINES
)
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
);
158 d
->index
= (v_max_menu
[0] += 1);
160 agi_menu
*m
= *menubar
.begin();
161 m
->down
.push_back(d
);
163 if (m
->width
< (int)strlen(text
))
164 m
->width
= strlen(text
);
172 Menu::Menu(AgiEngine
*vm
, GfxMgr
*gfx
, PictureMgr
*picture
) {
184 MenuList::iterator iterh
;
185 for (iterh
= _menubar
.reverse_begin(); iterh
!= _menubar
.end(); ) {
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
);
200 iterv
= m
->down
.reverse_erase(iterv
);
205 iterh
= _menubar
.reverse_erase(iterh
);
209 void Menu::add(const char *s
) {
210 AgiMenu
*m
= new AgiMenu
;
213 while (m
->text
[strlen(m
->text
) - 1] == ' ')
214 m
->text
[strlen(m
->text
) - 1] = 0;
218 m
->index
= _hIndex
++;
220 m
->wincol
= _hCol
- 1;
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
) {
233 AgiMenuOption
* d
= new AgiMenuOption
;
238 d
->index
= _vIndex
++;
240 // add to last menu in list
241 assert(_menubar
.reverse_begin() != _menubar
.end());
242 AgiMenu
*m
= *_menubar
.reverse_begin();
245 _vMaxMenu
[m
->index
] = d
->index
;
250 if (m
->wincol
+ l
> 38)
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(); ) {
270 if (m
->down
.empty()) {
276 iter
= _menubar
.reverse_erase(iter
);
283 bool Menu::keyhandler(int key
) {
285 static int menuActive
= false;
286 static int buttonUsed
= 0;
288 if (!_vm
->getflag(fMenusWork
) && !(_vm
->getFeatures() & GF_MENUS
))
292 clockVal
= _vm
->_game
.clockEnabled
;
293 _vm
->_game
.clockEnabled
= false;
299 if (g_mouse
.button
) {
302 buttonUsed
= 1; // Button has been used at least once
304 if (g_mouse
.y
<= CHAR_LINES
) {
308 MenuList::iterator iterh
;
310 for (iterh
= _menubar
.begin(); iterh
!= _menubar
.end(); ++iterh
) {
313 if (mouseOverText(0, m
->col
, m
->text
)) {
320 if (hmenu
<= _hMaxMenu
) {
321 if (_hCurMenu
!= hmenu
) {
323 newMenuSelected(hmenu
);
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
)) {
345 if (vmenu
<= _vMaxMenu
[_hCurMenu
]) {
346 if (_vCurMenu
!= vmenu
) {
347 drawMenuOption(_hCurMenu
);
348 drawMenuOptionHilite(_hCurMenu
, vmenu
);
353 } else if (buttonUsed
) {
357 debugC(6, kDebugLevelMenu
| kDebugLevelInput
, "button released!");
362 drawMenuOptionHilite(_hCurMenu
, _vCurMenu
);
364 if (g_mouse
.y
<= CHAR_LINES
) {
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
377 debugC(6, kDebugLevelMenu
| kDebugLevelInput
, "event %d registered", d
->event
);
378 _vm
->_game
.controllerOccured
[d
->event
] = true;
379 _vm
->_menuSelected
= true;
390 if (_hCurMenu
>= 0) {
391 drawMenuHilite(_hCurMenu
);
392 drawMenuOption(_hCurMenu
);
393 if (!buttonUsed
&& _vCurMenu
>= 0)
394 drawMenuOptionHilite(_hCurMenu
, _vCurMenu
);
401 debugC(6, kDebugLevelMenu
| kDebugLevelInput
, "KEY_ESCAPE");
405 debugC(6, kDebugLevelMenu
| kDebugLevelInput
, "KEY_ENTER");
406 AgiMenuOption
* d
= getMenuOption(_hCurMenu
, _vCurMenu
);
409 debugC(6, kDebugLevelMenu
| kDebugLevelInput
, "event %d registered", d
->event
);
410 _vm
->_game
.controllerOccured
[d
->event
] = true;
417 _vCurMenu
+= key
== KEY_DOWN
? 1 : -1;
420 _vCurMenu
= _vMaxMenu
[_hCurMenu
];
421 if (_vCurMenu
> _vMaxMenu
[_hCurMenu
])
424 drawMenuOption(_hCurMenu
);
425 drawMenuOptionHilite(_hCurMenu
, _vCurMenu
);
429 _hCurMenu
+= key
== KEY_RIGHT
? 1 : -1;
432 _hCurMenu
= _hMaxMenu
;
433 if (_hCurMenu
> _hMaxMenu
)
437 newMenuSelected(_hCurMenu
);
438 drawMenuOptionHilite(_hCurMenu
, _vCurMenu
);
449 _vm
->setvar(vKey
, 0);
450 _vm
->_game
.keypress
= 0;
451 _vm
->_game
.clockEnabled
= clockVal
;
454 debugC(3, kDebugLevelMenu
, "exit_menu: input mode reset to %d", _vm
->_game
.inputMode
);
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
) {
468 MenuOptionList::iterator iterv
;
470 for (iterv
= m
->down
.begin(); iterv
!= m
->down
.end(); ++iterv
) {
471 AgiMenuOption
*d
= *iterv
;
473 if (d
->event
== event
) {
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
) {
486 MenuOptionList::iterator iterv
;
488 for (iterv
= m
->down
.begin(); iterv
!= m
->down
.end(); ++iterv
) {
489 AgiMenuOption
*d
= *iterv
;
497 AgiTextColor
AgiButtonStyle::getColor(bool hasFocus
, bool pressed
, bool positive
) const {
500 if (pressed
) { // Positive pressed Amiga-style button
502 return AgiTextColor(amigaBlack
, amigaOrange
);
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();
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