Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | /* |
| 2 | * Portions of this file are copyright Rebirth contributors and licensed as |
||
| 3 | * described in COPYING.txt. |
||
| 4 | * Portions of this file are copyright Parallax Software and licensed |
||
| 5 | * according to the Parallax license below. |
||
| 6 | * See COPYING.txt for license details. |
||
| 7 | |||
| 8 | THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX |
||
| 9 | SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO |
||
| 10 | END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A |
||
| 11 | ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS |
||
| 12 | IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS |
||
| 13 | SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE |
||
| 14 | FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE |
||
| 15 | CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS |
||
| 16 | AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. |
||
| 17 | COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. |
||
| 18 | */ |
||
| 19 | |||
| 20 | #include <stdlib.h> |
||
| 21 | #include <string.h> |
||
| 22 | #include <ctype.h> |
||
| 23 | #include "physfsx.h" |
||
| 24 | #include "u_mem.h" |
||
| 25 | #include "maths.h" |
||
| 26 | #include "pstypes.h" |
||
| 27 | #include "gr.h" |
||
| 28 | #include "strutil.h" |
||
| 29 | #include "event.h" |
||
| 30 | #include "window.h" |
||
| 31 | #include "ui.h" |
||
| 32 | #include "mouse.h" |
||
| 33 | #include "key.h" |
||
| 34 | #include "func.h" |
||
| 35 | #include "dxxerror.h" |
||
| 36 | |||
| 37 | #include "compiler-range_for.h" |
||
| 38 | #include "partial_range.h" |
||
| 39 | #include <utility> |
||
| 40 | |||
| 41 | namespace dcx { |
||
| 42 | |||
| 43 | #define MAXMENUS 30 |
||
| 44 | #define MAXITEMS 32 |
||
| 45 | |||
| 46 | namespace { |
||
| 47 | |||
| 48 | struct ITEM { |
||
| 49 | short x, y, w, h; |
||
| 50 | RAIIdmem<char[]> Text; |
||
| 51 | RAIIdmem<char[]> InactiveText; |
||
| 52 | short Hotkey; |
||
| 53 | int (*user_function)(void); |
||
| 54 | }; |
||
| 55 | |||
| 56 | struct MENU : embed_window_pointer_t { |
||
| 57 | short x, y, w, h; |
||
| 58 | short ShowBar; |
||
| 59 | short CurrentItem; |
||
| 60 | uint16_t NumItems; |
||
| 61 | short Displayed; |
||
| 62 | short Active; |
||
| 63 | std::array<ITEM, MAXITEMS> Item; |
||
| 64 | }; |
||
| 65 | |||
| 66 | } |
||
| 67 | |||
| 68 | static std::array<MENU, MAXMENUS> Menu; |
||
| 69 | |||
| 70 | static unsigned num_menus; |
||
| 71 | static int state; |
||
| 72 | |||
| 73 | #define CMENU (Menu[0].CurrentItem+1) |
||
| 74 | |||
| 75 | static window_event_result menubar_handler(window *wind,const d_event &event, MENU *menu); |
||
| 76 | static window_event_result menu_handler(window *wind,const d_event &event, MENU *menu); |
||
| 77 | |||
| 78 | //------------------------- Show a menu item ------------------- |
||
| 79 | |||
| 80 | static void item_show( MENU * menu, int n ) |
||
| 81 | { |
||
| 82 | ITEM * item = &menu->Item[n]; |
||
| 83 | |||
| 84 | gr_set_default_canvas(); |
||
| 85 | auto &canvas = *grd_curcanv; |
||
| 86 | // If this is a seperator, then draw it. |
||
| 87 | if ( item->Text[0] == '-' ) |
||
| 88 | { |
||
| 89 | const auto color = CBLACK; |
||
| 90 | gr_urect(canvas, item->x, item->y+item->h/2, item->x+item->w-1, item->y+item->h/2, color); |
||
| 91 | return; |
||
| 92 | } |
||
| 93 | |||
| 94 | if ( menu->CurrentItem==n && menu->ShowBar ) |
||
| 95 | { |
||
| 96 | if ( menu != &Menu[0] ) |
||
| 97 | { |
||
| 98 | const auto color = CBLACK; |
||
| 99 | gr_urect(canvas, item->x+1, item->y+1, item->x+menu->w-2, item->y+item->h-2, color); |
||
| 100 | } |
||
| 101 | gr_set_fontcolor(canvas, CWHITE, CBLACK); |
||
| 102 | }else { |
||
| 103 | if ( menu != &Menu[0] ) |
||
| 104 | { |
||
| 105 | const auto color = CGREY; |
||
| 106 | gr_urect(canvas, item->x+1, item->y+1, item->x+menu->w-2, item->y+item->h-2, color); |
||
| 107 | } |
||
| 108 | gr_set_fontcolor(canvas, CBLACK, CGREY); |
||
| 109 | } |
||
| 110 | |||
| 111 | if ( menu != &Menu[0] ) |
||
| 112 | { |
||
| 113 | if ( menu->Active) |
||
| 114 | gr_ustring(canvas, *canvas.cv_font, item->x + 1, item->y + 1, item->Text.get()); |
||
| 115 | else |
||
| 116 | gr_ustring(canvas, *canvas.cv_font, item->x + 1, item->y + 1, item->InactiveText.get()); |
||
| 117 | } else { |
||
| 118 | if ( menu->Active) |
||
| 119 | gr_ustring(canvas, *canvas.cv_font, item->x, item->y, item->Text.get()); |
||
| 120 | else |
||
| 121 | gr_ustring(canvas, *canvas.cv_font, item->x, item->y, item->InactiveText.get()); |
||
| 122 | } |
||
| 123 | } |
||
| 124 | |||
| 125 | static void menu_draw(MENU *menu) |
||
| 126 | { |
||
| 127 | int i; |
||
| 128 | |||
| 129 | gr_set_default_canvas(); |
||
| 130 | auto &canvas = *grd_curcanv; |
||
| 131 | |||
| 132 | // Draw the menu background |
||
| 133 | gr_urect(canvas, menu->x, menu->y, menu->x + menu->w - 1, menu->y + menu->h - 1, CGREY); |
||
| 134 | if ( menu != &Menu[0] ) |
||
| 135 | { |
||
| 136 | gr_ubox(canvas, menu->x, menu->y, menu->x + menu->w - 1, menu->y + menu->h - 1, CBLACK); |
||
| 137 | } |
||
| 138 | |||
| 139 | // Draw the items |
||
| 140 | |||
| 141 | for (i=0; i< menu->NumItems; i++ ) |
||
| 142 | item_show( menu, i ); |
||
| 143 | } |
||
| 144 | |||
| 145 | //---------------------------- Show a menu --------------------- |
||
| 146 | |||
| 147 | static void menu_show( MENU * menu ) |
||
| 148 | { |
||
| 149 | if (!menu->wind) |
||
| 150 | { |
||
| 151 | menu->wind = window_create(grd_curscreen->sc_canvas, menu->x, menu->y, menu->w, menu->h, |
||
| 152 | ((menu == &Menu[0]) ? menubar_handler : menu_handler), menu); |
||
| 153 | if (!menu->wind) |
||
| 154 | return; |
||
| 155 | |||
| 156 | if (menu == &Menu[0]) |
||
| 157 | window_set_modal(Menu[0].wind, 0); // allow windows behind the menubar to accept events (e.g. the keypad dialogs) |
||
| 158 | } |
||
| 159 | |||
| 160 | window_set_visible(menu->wind, 1); |
||
| 161 | |||
| 162 | // Mark as displayed. |
||
| 163 | menu->Displayed = 1; |
||
| 164 | } |
||
| 165 | |||
| 166 | //-------------------------- Hide a menu ----------------------- |
||
| 167 | |||
| 168 | static void menu_hide( MENU * menu ) |
||
| 169 | { |
||
| 170 | |||
| 171 | // Can't hide if it's not already drawn |
||
| 172 | if (!menu->Displayed) return; |
||
| 173 | |||
| 174 | if ((menu != &Menu[0]) && menu->wind) |
||
| 175 | window_set_visible(menu->wind, 0); // don't draw or receive events |
||
| 176 | |||
| 177 | menu->Active = 0; |
||
| 178 | if (Menu[0].wind && menu == &Menu[0]) |
||
| 179 | window_set_modal(Menu[0].wind, 0); |
||
| 180 | |||
| 181 | // Mark as hidden. |
||
| 182 | menu->Displayed = 0; |
||
| 183 | } |
||
| 184 | |||
| 185 | |||
| 186 | //------------------------- Move the menu bar ------------------ |
||
| 187 | |||
| 188 | static void menu_move_bar_to( MENU * menu, int number ) |
||
| 189 | { |
||
| 190 | int old_item; |
||
| 191 | |||
| 192 | old_item = menu->CurrentItem; |
||
| 193 | menu->CurrentItem = number; |
||
| 194 | |||
| 195 | if (menu->Displayed && (number != old_item)) |
||
| 196 | { |
||
| 197 | item_show( menu, old_item ); |
||
| 198 | item_show( menu, number ); |
||
| 199 | } |
||
| 200 | } |
||
| 201 | |||
| 202 | //------------------------ Match keypress to item ------------------ |
||
| 203 | static int menu_match_keypress( MENU * menu, int keypress ) |
||
| 204 | { |
||
| 205 | int i; |
||
| 206 | char c; |
||
| 207 | if ((keypress & KEY_CTRLED) || (keypress & KEY_SHIFTED)) |
||
| 208 | return -1; |
||
| 209 | |||
| 210 | keypress &= 0xFF; |
||
| 211 | |||
| 212 | c = key_ascii(); |
||
| 213 | |||
| 214 | for (i=0; i< menu->NumItems; i++ ) |
||
| 215 | { |
||
| 216 | auto letter = strrchr(menu->Item[i].Text.get(), CC_UNDERLINE); |
||
| 217 | if (letter) |
||
| 218 | { |
||
| 219 | letter++; |
||
| 220 | if (c==tolower(*letter)) |
||
| 221 | return i; |
||
| 222 | } |
||
| 223 | } |
||
| 224 | return -1; |
||
| 225 | } |
||
| 226 | |||
| 227 | |||
| 228 | static int menu_is_mouse_on( ITEM * item ) |
||
| 229 | { |
||
| 230 | int x, y, z; |
||
| 231 | |||
| 232 | mouse_get_pos(&x, &y, &z); |
||
| 233 | |||
| 234 | if ((x >= item->x) && |
||
| 235 | (x < item->x + item->w ) && |
||
| 236 | (y >= item->y) && |
||
| 237 | (y <= item->y + item->h ) ) |
||
| 238 | return 1; |
||
| 239 | else |
||
| 240 | return 0; |
||
| 241 | } |
||
| 242 | |||
| 243 | static int menu_check_mouse_item( MENU * menu ) |
||
| 244 | { |
||
| 245 | int i; |
||
| 246 | |||
| 247 | for (i=0; i<menu->NumItems; i++ ) |
||
| 248 | { |
||
| 249 | if (menu_is_mouse_on( &menu->Item[i] )) |
||
| 250 | { |
||
| 251 | if (menu->Item[i].Text[0] == '-') |
||
| 252 | return -1; |
||
| 253 | else |
||
| 254 | return i; |
||
| 255 | } |
||
| 256 | } |
||
| 257 | return -1; |
||
| 258 | } |
||
| 259 | |||
| 260 | |||
| 261 | static void menu_hide_all() |
||
| 262 | { |
||
| 263 | range_for (auto &i, partial_range(Menu, 1u, num_menus)) |
||
| 264 | menu_hide(&i); |
||
| 265 | |||
| 266 | Menu[0].ShowBar = 0; |
||
| 267 | Menu[0].Active = 0; |
||
| 268 | if (Menu[0].wind) |
||
| 269 | window_set_modal(Menu[0].wind, 0); |
||
| 270 | menu_show( &Menu[0] ); |
||
| 271 | |||
| 272 | } |
||
| 273 | |||
| 274 | |||
| 275 | static int state2_alt_down; |
||
| 276 | |||
| 277 | static window_event_result do_state_0(const d_event &event) |
||
| 278 | { |
||
| 279 | int keypress = 0; |
||
| 280 | |||
| 281 | if (event.type == EVENT_KEY_COMMAND) |
||
| 282 | keypress = event_key_get(event); |
||
| 283 | |||
| 284 | Menu[0].Active = 0; |
||
| 285 | if (Menu[0].wind) |
||
| 286 | window_set_modal(Menu[0].wind, 0); |
||
| 287 | Menu[0].ShowBar = 0; |
||
| 288 | |||
| 289 | if ( keypress & KEY_ALTED ) { |
||
| 290 | const auto i = menu_match_keypress( &Menu[0], keypress ); |
||
| 291 | if (i > -1 ) |
||
| 292 | { |
||
| 293 | Menu[0].CurrentItem = i; |
||
| 294 | Menu[0].Active = 0; |
||
| 295 | if (Menu[0].wind) |
||
| 296 | window_set_modal(Menu[0].wind, 0); |
||
| 297 | |||
| 298 | state = 3; |
||
| 299 | Menu[ CMENU ].ShowBar = 1; |
||
| 300 | Menu[ CMENU ].Active = 1; |
||
| 301 | Menu[0].ShowBar = 1; |
||
| 302 | |||
| 303 | menu_show( &Menu[ CMENU ] ); |
||
| 304 | menu_show( &Menu[0] ); |
||
| 305 | return window_event_result::handled; |
||
| 306 | } |
||
| 307 | } |
||
| 308 | |||
| 309 | range_for (auto &i, partial_const_range(Menu, num_menus)) |
||
| 310 | range_for (auto &j, partial_const_range(i.Item, i.NumItems)) |
||
| 311 | { |
||
| 312 | if ( j.Hotkey == keypress ) |
||
| 313 | { |
||
| 314 | if (j.user_function) |
||
| 315 | j.user_function(); |
||
| 316 | return window_event_result::handled; |
||
| 317 | } |
||
| 318 | } |
||
| 319 | |||
| 320 | if (keypress & KEY_ALTED) |
||
| 321 | //if ( (keypress & 0xFF) == KEY_LALT ) |
||
| 322 | { |
||
| 323 | // Make sure the menubar receives events exclusively |
||
| 324 | state = 1; |
||
| 325 | Menu[0].Active = 1; |
||
| 326 | |||
| 327 | // Put the menubar in front - hope this doesn't mess anything up by leaving it there |
||
| 328 | // If it does, will need to remember the previous front window and restore it. |
||
| 329 | // (Personally, I just use either the mouse or 'hotkeys' for menus) |
||
| 330 | window_select(*Menu[0].wind); |
||
| 331 | |||
| 332 | window_set_modal(Menu[0].wind, 1); |
||
| 333 | menu_show( &Menu[0] ); |
||
| 334 | return window_event_result::handled; |
||
| 335 | } |
||
| 336 | |||
| 337 | const auto i = menu_check_mouse_item( &Menu[0] ); |
||
| 338 | |||
| 339 | if ( B1_JUST_PRESSED && (i > -1)) |
||
| 340 | { |
||
| 341 | Menu[0].CurrentItem = i; |
||
| 342 | state = 3; |
||
| 343 | Menu[ CMENU ].ShowBar = 1; |
||
| 344 | Menu[0].ShowBar = 1; |
||
| 345 | Menu[ CMENU ].Active = 1; |
||
| 346 | Menu[0].Active = 0; |
||
| 347 | window_set_modal(Menu[0].wind, 0); |
||
| 348 | menu_show( &Menu[ CMENU ] ); |
||
| 349 | menu_show( &Menu[0] ); |
||
| 350 | return window_event_result::handled; |
||
| 351 | } |
||
| 352 | return window_event_result::ignored; |
||
| 353 | } |
||
| 354 | |||
| 355 | static window_event_result do_state_1(const d_event &event) |
||
| 356 | { |
||
| 357 | int i; |
||
| 358 | int keypress = 0; |
||
| 359 | window_event_result rval = window_event_result::ignored; |
||
| 360 | |||
| 361 | if (event.type == EVENT_KEY_COMMAND) |
||
| 362 | keypress = event_key_get(event); |
||
| 363 | |||
| 364 | if ((event.type == EVENT_KEY_RELEASE) && !(event_key_get(event) & KEY_ALTED)) |
||
| 365 | { |
||
| 366 | state = 2; |
||
| 367 | state2_alt_down = 0; |
||
| 368 | Menu[0].ShowBar = 1; |
||
| 369 | Menu[0].Active = 1; |
||
| 370 | menu_show( &Menu[0] ); |
||
| 371 | rval = window_event_result::handled; |
||
| 372 | } |
||
| 373 | |||
| 374 | i = menu_match_keypress( &Menu[0], keypress ); |
||
| 375 | |||
| 376 | if (i > -1 ) |
||
| 377 | { |
||
| 378 | Menu[0].CurrentItem = i; |
||
| 379 | Menu[0].Active = 0; |
||
| 380 | window_set_modal(Menu[0].wind, 0); |
||
| 381 | state = 3; |
||
| 382 | Menu[ CMENU ].ShowBar = 1; |
||
| 383 | Menu[ CMENU ].Active = 1; |
||
| 384 | Menu[0].ShowBar = 1; |
||
| 385 | |||
| 386 | menu_show( &Menu[ CMENU ] ); |
||
| 387 | menu_show( &Menu[0] ); |
||
| 388 | rval = window_event_result::handled; |
||
| 389 | } |
||
| 390 | |||
| 391 | i = menu_check_mouse_item( &Menu[0] ); |
||
| 392 | |||
| 393 | if ( (i == -1) && B1_JUST_RELEASED ) |
||
| 394 | { |
||
| 395 | state = 0; |
||
| 396 | menu_hide_all(); |
||
| 397 | return window_event_result::handled; |
||
| 398 | } |
||
| 399 | |||
| 400 | if ( B1_JUST_PRESSED && (i > -1)) |
||
| 401 | { |
||
| 402 | Menu[0].CurrentItem = i; |
||
| 403 | state = 3; |
||
| 404 | Menu[ CMENU ].ShowBar = 1; |
||
| 405 | Menu[ CMENU ].Active = 1; |
||
| 406 | Menu[0].ShowBar = 1; |
||
| 407 | Menu[0].Active = 0; |
||
| 408 | window_set_modal(Menu[0].wind, 0); |
||
| 409 | menu_show( &Menu[ CMENU ] ); |
||
| 410 | menu_show( &Menu[0] ); |
||
| 411 | return window_event_result::handled; |
||
| 412 | } |
||
| 413 | |||
| 414 | return rval; |
||
| 415 | } |
||
| 416 | |||
| 417 | static window_event_result do_state_2(const d_event &event) |
||
| 418 | { |
||
| 419 | int i; |
||
| 420 | int keypress = 0; |
||
| 421 | window_event_result rval = window_event_result::ignored; |
||
| 422 | if (event.type == EVENT_KEY_COMMAND) |
||
| 423 | keypress = event_key_get(event); |
||
| 424 | |||
| 425 | if (keypress & KEY_ALTED) |
||
| 426 | { |
||
| 427 | state2_alt_down = 1; |
||
| 428 | rval = window_event_result::handled; |
||
| 429 | } |
||
| 430 | |||
| 431 | if ((event.type == EVENT_KEY_RELEASE) && !(event_key_get(event) & KEY_ALTED) && state2_alt_down) |
||
| 432 | { |
||
| 433 | state = 0; |
||
| 434 | menu_hide_all(); |
||
| 435 | rval = window_event_result::handled; |
||
| 436 | } |
||
| 437 | |||
| 438 | switch( keypress ) |
||
| 439 | { |
||
| 440 | case KEY_ESC: |
||
| 441 | state = 0; |
||
| 442 | menu_hide_all(); |
||
| 443 | return window_event_result::handled; |
||
| 444 | case KEY_LEFT: |
||
| 445 | case KEY_PAD4: |
||
| 446 | i = Menu[0].CurrentItem-1; |
||
| 447 | if (i < 0 ) i = Menu[0].NumItems-1; |
||
| 448 | menu_move_bar_to( &Menu[0], i ); |
||
| 449 | return window_event_result::handled; |
||
| 450 | case KEY_RIGHT: |
||
| 451 | case KEY_PAD6: |
||
| 452 | i = Menu[0].CurrentItem+1; |
||
| 453 | if (i >= Menu[0].NumItems ) i = 0; |
||
| 454 | menu_move_bar_to( &Menu[0], i ); |
||
| 455 | return window_event_result::handled; |
||
| 456 | case KEY_ENTER: |
||
| 457 | case KEY_PADENTER: |
||
| 458 | case KEY_DOWN: |
||
| 459 | case KEY_PAD2: |
||
| 460 | state = 3; |
||
| 461 | Menu[ CMENU ].ShowBar = 1; |
||
| 462 | Menu[ CMENU ].Active = 1; |
||
| 463 | Menu[0].Active = 0; |
||
| 464 | window_set_modal(Menu[0].wind, 0); |
||
| 465 | menu_show( &Menu[ 0 ] ); |
||
| 466 | menu_show( &Menu[ CMENU ] ); |
||
| 467 | return window_event_result::handled; |
||
| 468 | |||
| 469 | default: |
||
| 470 | i = menu_match_keypress( &Menu[0], keypress ); |
||
| 471 | |||
| 472 | if (i > -1 ) |
||
| 473 | { |
||
| 474 | Menu[0].CurrentItem = i; |
||
| 475 | Menu[0].Active = 0; |
||
| 476 | window_set_modal(Menu[0].wind, 0); |
||
| 477 | state = 3; |
||
| 478 | Menu[ CMENU ].ShowBar = 1; |
||
| 479 | Menu[ CMENU ].Active = 1; |
||
| 480 | Menu[0].ShowBar = 1; |
||
| 481 | menu_show( &Menu[ CMENU ] ); |
||
| 482 | menu_show( &Menu[0] ); |
||
| 483 | return window_event_result::handled; |
||
| 484 | } |
||
| 485 | |||
| 486 | i = menu_check_mouse_item( &Menu[0] ); |
||
| 487 | |||
| 488 | if ( (i == -1) && B1_JUST_RELEASED ) |
||
| 489 | { |
||
| 490 | state = 0; |
||
| 491 | menu_hide_all(); |
||
| 492 | return window_event_result::handled; |
||
| 493 | } |
||
| 494 | |||
| 495 | if (i > -1) |
||
| 496 | { |
||
| 497 | Menu[0].CurrentItem = i; |
||
| 498 | Menu[0].Active = 0; |
||
| 499 | window_set_modal(Menu[0].wind, 0); |
||
| 500 | state = 3; |
||
| 501 | Menu[ CMENU ].ShowBar = 1; |
||
| 502 | Menu[ CMENU ].Active = 1; |
||
| 503 | Menu[0].ShowBar = 1; |
||
| 504 | menu_show( &Menu[ CMENU ] ); |
||
| 505 | menu_show( &Menu[0] ); |
||
| 506 | return window_event_result::handled; |
||
| 507 | } |
||
| 508 | } |
||
| 509 | |||
| 510 | return rval; |
||
| 511 | } |
||
| 512 | |||
| 513 | |||
| 514 | |||
| 515 | static window_event_result menu_handler(window *, const d_event &event, MENU *menu) |
||
| 516 | { |
||
| 517 | int i; |
||
| 518 | int keypress = 0; |
||
| 519 | |||
| 520 | if (state != 3) |
||
| 521 | return window_event_result::ignored; |
||
| 522 | |||
| 523 | if (event.type == EVENT_KEY_COMMAND) |
||
| 524 | keypress = event_key_get(event); |
||
| 525 | else if (event.type == EVENT_WINDOW_CLOSE) // quitting |
||
| 526 | { |
||
| 527 | state = 0; |
||
| 528 | menu_hide_all(); |
||
| 529 | menu->wind = nullptr; |
||
| 530 | return window_event_result::ignored; |
||
| 531 | } |
||
| 532 | window_event_result rval = window_event_result::ignored; |
||
| 533 | switch( keypress ) |
||
| 534 | { |
||
| 535 | case 0: |
||
| 536 | break; |
||
| 537 | case KEY_ESC: |
||
| 538 | state = 0; |
||
| 539 | menu_hide_all(); |
||
| 540 | rval = window_event_result::handled; |
||
| 541 | break; |
||
| 542 | case KEY_DOWN: |
||
| 543 | case KEY_PAD2: |
||
| 544 | i = Menu[ CMENU ].CurrentItem; |
||
| 545 | do { |
||
| 546 | i++; |
||
| 547 | if ( i >= Menu[ CMENU ].NumItems ) |
||
| 548 | i = 0; |
||
| 549 | } while( Menu[CMENU].Item[i].Text[0] == '-'); |
||
| 550 | menu_move_bar_to( &Menu[ CMENU ], i ); |
||
| 551 | rval = window_event_result::handled; |
||
| 552 | break; |
||
| 553 | case KEY_UP: |
||
| 554 | case KEY_PAD8: |
||
| 555 | i = Menu[ CMENU ].CurrentItem; |
||
| 556 | do |
||
| 557 | { |
||
| 558 | i--; |
||
| 559 | if ( i < 0 ) |
||
| 560 | i = Menu[ CMENU ].NumItems-1; |
||
| 561 | } while( Menu[CMENU].Item[i].Text[0] == '-'); |
||
| 562 | menu_move_bar_to( &Menu[ CMENU ], i ); |
||
| 563 | rval = window_event_result::handled; |
||
| 564 | break; |
||
| 565 | case KEY_RIGHT: |
||
| 566 | case KEY_PAD6: |
||
| 567 | menu_hide( &Menu[ CMENU ] ); |
||
| 568 | i = Menu[0].CurrentItem+1; |
||
| 569 | if (i >= Menu[0].NumItems ) i = 0; |
||
| 570 | menu_move_bar_to( &Menu[0], i ); |
||
| 571 | Menu[CMENU].ShowBar = 1; |
||
| 572 | Menu[CMENU].Active = 1; |
||
| 573 | menu_show( &Menu[CMENU] ); |
||
| 574 | rval = window_event_result::handled; |
||
| 575 | break; |
||
| 576 | case KEY_LEFT: |
||
| 577 | case KEY_PAD4: |
||
| 578 | menu_hide( &Menu[ CMENU ] ); |
||
| 579 | i = Menu[0].CurrentItem-1; |
||
| 580 | if (i < 0 ) i = Menu[0].NumItems-1; |
||
| 581 | menu_move_bar_to( &Menu[0], i ); |
||
| 582 | Menu[ CMENU ].ShowBar = 1; |
||
| 583 | Menu[CMENU].Active = 1; |
||
| 584 | menu_show( &Menu[ CMENU ] ); |
||
| 585 | rval = window_event_result::handled; |
||
| 586 | break; |
||
| 587 | case KEY_ENTER: |
||
| 588 | case KEY_PADENTER: |
||
| 589 | state = 0; |
||
| 590 | menu_hide_all(); |
||
| 591 | |||
| 592 | if (Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function) |
||
| 593 | Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function(); |
||
| 594 | |||
| 595 | rval = window_event_result::handled; |
||
| 596 | break; |
||
| 597 | |||
| 598 | default: |
||
| 599 | i = menu_match_keypress( &Menu[ CMENU ], keypress ); |
||
| 600 | |||
| 601 | if (i > -1 ) |
||
| 602 | { |
||
| 603 | menu_move_bar_to( &Menu[ CMENU ], i ); |
||
| 604 | state = 0; |
||
| 605 | menu_hide_all(); |
||
| 606 | |||
| 607 | if (Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function) |
||
| 608 | Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function(); |
||
| 609 | rval = window_event_result::handled; |
||
| 610 | break; |
||
| 611 | } |
||
| 612 | } |
||
| 613 | |||
| 614 | if (event.type == EVENT_MOUSE_MOVED || B1_JUST_RELEASED) |
||
| 615 | { |
||
| 616 | i = menu_check_mouse_item( &Menu[CMENU] ); |
||
| 617 | |||
| 618 | if (i > -1 ) |
||
| 619 | { |
||
| 620 | if ( B1_JUST_RELEASED ) |
||
| 621 | { |
||
| 622 | menu_move_bar_to( &Menu[ CMENU ], i ); |
||
| 623 | state = 0; |
||
| 624 | menu_hide_all(); |
||
| 625 | |||
| 626 | if (Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function) |
||
| 627 | Menu[CMENU].Item[ Menu[CMENU].CurrentItem ].user_function(); |
||
| 628 | rval = window_event_result::handled; |
||
| 629 | } |
||
| 630 | else |
||
| 631 | { |
||
| 632 | menu_move_bar_to( &Menu[ CMENU ], i ); |
||
| 633 | rval = window_event_result::handled; |
||
| 634 | } |
||
| 635 | } else { |
||
| 636 | i = menu_check_mouse_item( &Menu[0] ); |
||
| 637 | |||
| 638 | if (i > -1) |
||
| 639 | { |
||
| 640 | if ( Menu[0].CurrentItem != i) { |
||
| 641 | menu_hide( &Menu[ CMENU ] ); |
||
| 642 | menu_move_bar_to( &Menu[0], i ); |
||
| 643 | Menu[ CMENU ].ShowBar = 1; |
||
| 644 | Menu[CMENU].Active = 1; |
||
| 645 | menu_show( &Menu[ CMENU ] ); |
||
| 646 | } |
||
| 647 | |||
| 648 | rval = window_event_result::handled; |
||
| 649 | } |
||
| 650 | |||
| 651 | if ( B1_JUST_RELEASED ) |
||
| 652 | { |
||
| 653 | state = 0; |
||
| 654 | menu_hide_all(); |
||
| 655 | rval = window_event_result::handled; |
||
| 656 | } |
||
| 657 | } |
||
| 658 | } |
||
| 659 | |||
| 660 | if (event.type == EVENT_WINDOW_DRAW) |
||
| 661 | { |
||
| 662 | menu_draw(&Menu[CMENU]); |
||
| 663 | return window_event_result::handled; |
||
| 664 | } |
||
| 665 | |||
| 666 | return rval; |
||
| 667 | } |
||
| 668 | |||
| 669 | static window_event_result menubar_handler(window *, const d_event &event, MENU *) |
||
| 670 | { |
||
| 671 | if (event.type == EVENT_WINDOW_DRAW) |
||
| 672 | { |
||
| 673 | menu_draw(&Menu[0]); |
||
| 674 | return window_event_result::handled; |
||
| 675 | } |
||
| 676 | else if (event.type == EVENT_WINDOW_CLOSE) |
||
| 677 | { |
||
| 678 | //menu_hide_all(); |
||
| 679 | //menu_hide( &Menu[0] ); |
||
| 680 | |||
| 681 | range_for (auto &i, partial_range(Menu, 1u, num_menus)) |
||
| 682 | { |
||
| 683 | if (i.wind) |
||
| 684 | { |
||
| 685 | window_close(std::exchange(i.wind, nullptr)); |
||
| 686 | } |
||
| 687 | } |
||
| 688 | |||
| 689 | Menu[0].wind = nullptr; |
||
| 690 | } |
||
| 691 | |||
| 692 | switch (state) |
||
| 693 | { |
||
| 694 | case 0: |
||
| 695 | return do_state_0(event); |
||
| 696 | case 1: |
||
| 697 | return do_state_1(event); |
||
| 698 | case 2: |
||
| 699 | return do_state_2(event); |
||
| 700 | case 3: |
||
| 701 | break; |
||
| 702 | default: |
||
| 703 | state = 0; |
||
| 704 | menu_hide_all(); |
||
| 705 | } |
||
| 706 | return window_event_result::ignored; |
||
| 707 | } |
||
| 708 | |||
| 709 | static void CommaParse(uint_fast32_t n, char * dest, const PHYSFSX_gets_line_t<200>::line_t &source) |
||
| 710 | { |
||
| 711 | int i = 0, j=0, cn = 0; |
||
| 712 | |||
| 713 | // Go to the n'th comma |
||
| 714 | while (cn < n ) |
||
| 715 | if (source[i++] == ',' ) |
||
| 716 | cn++; |
||
| 717 | // Read all the whitespace |
||
| 718 | while ( source[i]==' ' || source[i]=='\t' || source[i]==13 || source[i]==10 ) |
||
| 719 | i++; |
||
| 720 | |||
| 721 | // Read up until the next comma |
||
| 722 | while ( source[i] != ',' ) |
||
| 723 | { |
||
| 724 | dest[j] = source[i++]; |
||
| 725 | j++; |
||
| 726 | } |
||
| 727 | |||
| 728 | // Null-terminate |
||
| 729 | dest[j++] = 0; |
||
| 730 | } |
||
| 731 | |||
| 732 | //translate '&' characters to the underline character |
||
| 733 | static void ul_xlate(char *s) |
||
| 734 | { |
||
| 735 | while ((s=strchr(s,'&'))!=NULL) |
||
| 736 | *s = CC_UNDERLINE; |
||
| 737 | } |
||
| 738 | |||
| 739 | |||
| 740 | int menubar_init(grs_canvas &canvas, const char *const file) |
||
| 741 | { |
||
| 742 | int np; |
||
| 743 | char buf1[200]; |
||
| 744 | char buf2[204]; |
||
| 745 | |||
| 746 | num_menus = state = 0; |
||
| 747 | |||
| 748 | // This method should be faster than explicitly setting all the variables (I think) |
||
| 749 | Menu = {}; |
||
| 750 | |||
| 751 | range_for (auto &i, Menu) |
||
| 752 | range_for (auto &j, i.Item) |
||
| 753 | j.Hotkey = -1; |
||
| 754 | auto infile = PHYSFSX_openReadBuffered(file); |
||
| 755 | |||
| 756 | if (!infile) |
||
| 757 | { |
||
| 758 | Warning("Could not find %s\n", file); |
||
| 759 | return 0; |
||
| 760 | } |
||
| 761 | |||
| 762 | PHYSFSX_gets_line_t<200> buffer; |
||
| 763 | while ( PHYSFSX_fgets( buffer, infile) != NULL ) |
||
| 764 | { |
||
| 765 | if ( buffer[0] == ';' ) continue; |
||
| 766 | |||
| 767 | CommaParse( 0, buf1, buffer ); |
||
| 768 | char *p; |
||
| 769 | const auto mi = strtoul(buf1, &p, 10); |
||
| 770 | if (mi >= MAXMENUS) |
||
| 771 | { |
||
| 772 | con_printf(CON_URGENT, "%s:%u: too many menus defined: max=%u, requested=%lu in \"%s\"", __FILE__, __LINE__, MAXMENUS, mi, file); |
||
| 773 | break; |
||
| 774 | } |
||
| 775 | |||
| 776 | CommaParse( 1, buf1, buffer ); |
||
| 777 | const auto ii = strtoul(buf1, &p, 10); |
||
| 778 | if (ii >= MAXITEMS) |
||
| 779 | { |
||
| 780 | con_printf(CON_URGENT, "%s:%u: too many items defined: max=%u, requested=%lu in \"%s\"", __FILE__, __LINE__, MAXITEMS, ii, file); |
||
| 781 | break; |
||
| 782 | } |
||
| 783 | auto &menu = Menu[mi]; |
||
| 784 | auto &item = menu.Item[ii]; |
||
| 785 | |||
| 786 | CommaParse( 2, buf1, buffer ); |
||
| 787 | ul_xlate(buf1); |
||
| 788 | |||
| 789 | item.Text.reset(d_strdup(buf1[0] == '-' ? buf1 : (snprintf(buf2, sizeof(buf2), " %.197s ", buf1), buf2))); |
||
| 790 | |||
| 791 | item.InactiveText.reset(d_strdup(item.Text.get())); |
||
| 792 | |||
| 793 | for (int i = 0, j = 0;; i++ ) |
||
| 794 | { |
||
| 795 | np = item.Text[i]; |
||
| 796 | if (np != CC_UNDERLINE) |
||
| 797 | item.InactiveText[j++] = np; |
||
| 798 | if (!np) |
||
| 799 | break; |
||
| 800 | } |
||
| 801 | |||
| 802 | CommaParse( 3, buf1, buffer ); |
||
| 803 | if (buf1[0]=='{' && buf1[1] =='}') |
||
| 804 | item.Hotkey = -1; |
||
| 805 | else { |
||
| 806 | const auto i = DecodeKeyText(buf1); |
||
| 807 | if (i<1) { |
||
| 808 | UserError("Unknown key, %s, in %s\n", buf1, file ); |
||
| 809 | } else { |
||
| 810 | item.Hotkey = i; |
||
| 811 | } |
||
| 812 | } |
||
| 813 | CommaParse( 4, buf1, buffer ); |
||
| 814 | |||
| 815 | if (buf1[0]) |
||
| 816 | { |
||
| 817 | item.user_function = func_get(buf1, &np); |
||
| 818 | |||
| 819 | if (!item.user_function) |
||
| 820 | { |
||
| 821 | con_printf(CON_URGENT, "%s:%u: unknown function \"%s\" in \"%s\"", __FILE__, __LINE__, buf1, file); |
||
| 822 | } |
||
| 823 | } |
||
| 824 | |||
| 825 | item.x = menu.x; |
||
| 826 | item.y = menu.y; |
||
| 827 | |||
| 828 | int w, h; |
||
| 829 | if (item.Text[0] == '-') |
||
| 830 | { |
||
| 831 | w = 1; h = 3; |
||
| 832 | } else { |
||
| 833 | gr_get_string_size(*canvas.cv_font, item.Text.get(), &w, &h, nullptr); |
||
| 834 | w += 2; |
||
| 835 | h += 2; |
||
| 836 | } |
||
| 837 | |||
| 838 | if (mi == 0) |
||
| 839 | { |
||
| 840 | menu.h = h; |
||
| 841 | |||
| 842 | item.x = menu.x + menu.w; |
||
| 843 | |||
| 844 | item.y = menu.y; |
||
| 845 | |||
| 846 | Menu[ii+1].x = menu.x + menu.w; |
||
| 847 | Menu[ii+1].y = menu.h - 2; |
||
| 848 | |||
| 849 | item.w = w; |
||
| 850 | item.h = h; |
||
| 851 | |||
| 852 | menu.w += w; |
||
| 853 | |||
| 854 | }else { |
||
| 855 | if (w > menu.w) |
||
| 856 | { |
||
| 857 | menu.w = w; |
||
| 858 | range_for (auto &i, partial_range(menu.Item, menu.NumItems)) |
||
| 859 | i.w = w; |
||
| 860 | } |
||
| 861 | item.w = menu.w; |
||
| 862 | item.x = menu.x; |
||
| 863 | item.y = menu.y+menu.h; |
||
| 864 | item.h = h; |
||
| 865 | menu.h += h; |
||
| 866 | } |
||
| 867 | |||
| 868 | if (ii >= menu.NumItems) |
||
| 869 | { |
||
| 870 | menu.NumItems = ii + 1; |
||
| 871 | } |
||
| 872 | |||
| 873 | if (mi >= num_menus) |
||
| 874 | num_menus = mi + 1; |
||
| 875 | |||
| 876 | } |
||
| 877 | Menu[0].w = 700; |
||
| 878 | |||
| 879 | return 1; |
||
| 880 | } |
||
| 881 | |||
| 882 | void menubar_hide() |
||
| 883 | { |
||
| 884 | state = 0; |
||
| 885 | menu_hide_all(); |
||
| 886 | menu_hide( &Menu[0] ); |
||
| 887 | } |
||
| 888 | |||
| 889 | void menubar_show() |
||
| 890 | { |
||
| 891 | menu_show( &Menu[0] ); |
||
| 892 | } |
||
| 893 | |||
| 894 | void menubar_close() |
||
| 895 | { |
||
| 896 | if (!Menu[0].wind) |
||
| 897 | return; |
||
| 898 | window_close(std::exchange(Menu[0].wind, nullptr)); |
||
| 899 | } |
||
| 900 | |||
| 901 | } |