qmenu.c (15302B)
1 /* 2 Copyright (C) 1997-2001 Id Software, Inc. 3 4 This program is free software; you can redistribute it and/or 5 modify it under the terms of the GNU General Public License 6 as published by the Free Software Foundation; either version 2 7 of the License, or (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13 See the GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 */ 20 #include <string.h> 21 #include <ctype.h> 22 23 #include "client.h" 24 #include "qmenu.h" 25 26 static void Action_DoEnter( menuaction_s *a ); 27 static void Action_Draw( menuaction_s *a ); 28 static void Menu_DrawStatusBar( const char *string ); 29 static void Menulist_DoEnter( menulist_s *l ); 30 static void MenuList_Draw( menulist_s *l ); 31 static void Separator_Draw( menuseparator_s *s ); 32 static void Slider_DoSlide( menuslider_s *s, int dir ); 33 static void Slider_Draw( menuslider_s *s ); 34 static void SpinControl_DoEnter( menulist_s *s ); 35 static void SpinControl_Draw( menulist_s *s ); 36 static void SpinControl_DoSlide( menulist_s *s, int dir ); 37 38 #define RCOLUMN_OFFSET 16 39 #define LCOLUMN_OFFSET -16 40 41 extern refexport_t re; 42 extern viddef_t viddef; 43 44 #define VID_WIDTH viddef.width 45 #define VID_HEIGHT viddef.height 46 47 #define Draw_Char re.DrawChar 48 #define Draw_Fill re.DrawFill 49 50 void Action_DoEnter( menuaction_s *a ) 51 { 52 if ( a->generic.callback ) 53 a->generic.callback( a ); 54 } 55 56 void Action_Draw( menuaction_s *a ) 57 { 58 if ( a->generic.flags & QMF_LEFT_JUSTIFY ) 59 { 60 if ( a->generic.flags & QMF_GRAYED ) 61 Menu_DrawStringDark( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name ); 62 else 63 Menu_DrawString( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name ); 64 } 65 else 66 { 67 if ( a->generic.flags & QMF_GRAYED ) 68 Menu_DrawStringR2LDark( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name ); 69 else 70 Menu_DrawStringR2L( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name ); 71 } 72 if ( a->generic.ownerdraw ) 73 a->generic.ownerdraw( a ); 74 } 75 76 qboolean Field_DoEnter( menufield_s *f ) 77 { 78 if ( f->generic.callback ) 79 { 80 f->generic.callback( f ); 81 return true; 82 } 83 return false; 84 } 85 86 void Field_Draw( menufield_s *f ) 87 { 88 int i; 89 char tempbuffer[128]=""; 90 91 if ( f->generic.name ) 92 Menu_DrawStringR2LDark( f->generic.x + f->generic.parent->x + LCOLUMN_OFFSET, f->generic.y + f->generic.parent->y, f->generic.name ); 93 94 strncpy( tempbuffer, f->buffer + f->visible_offset, f->visible_length ); 95 96 Draw_Char( f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y - 4, 18 ); 97 Draw_Char( f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y + 4, 24 ); 98 99 Draw_Char( f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y - 4, 20 ); 100 Draw_Char( f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y + 4, 26 ); 101 102 for ( i = 0; i < f->visible_length; i++ ) 103 { 104 Draw_Char( f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y - 4, 19 ); 105 Draw_Char( f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y + 4, 25 ); 106 } 107 108 Menu_DrawString( f->generic.x + f->generic.parent->x + 24, f->generic.y + f->generic.parent->y, tempbuffer ); 109 110 if ( Menu_ItemAtCursor( f->generic.parent ) == f ) 111 { 112 int offset; 113 114 if ( f->visible_offset ) 115 offset = f->visible_length; 116 else 117 offset = f->cursor; 118 119 if ( ( ( int ) ( Sys_Milliseconds() / 250 ) ) & 1 ) 120 { 121 Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8, 122 f->generic.y + f->generic.parent->y, 123 11 ); 124 } 125 else 126 { 127 Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8, 128 f->generic.y + f->generic.parent->y, 129 ' ' ); 130 } 131 } 132 } 133 134 qboolean Field_Key( menufield_s *f, int key ) 135 { 136 extern int keydown[]; 137 138 switch ( key ) 139 { 140 case K_KP_SLASH: 141 key = '/'; 142 break; 143 case K_KP_MINUS: 144 key = '-'; 145 break; 146 case K_KP_PLUS: 147 key = '+'; 148 break; 149 case K_KP_HOME: 150 key = '7'; 151 break; 152 case K_KP_UPARROW: 153 key = '8'; 154 break; 155 case K_KP_PGUP: 156 key = '9'; 157 break; 158 case K_KP_LEFTARROW: 159 key = '4'; 160 break; 161 case K_KP_5: 162 key = '5'; 163 break; 164 case K_KP_RIGHTARROW: 165 key = '6'; 166 break; 167 case K_KP_END: 168 key = '1'; 169 break; 170 case K_KP_DOWNARROW: 171 key = '2'; 172 break; 173 case K_KP_PGDN: 174 key = '3'; 175 break; 176 case K_KP_INS: 177 key = '0'; 178 break; 179 case K_KP_DEL: 180 key = '.'; 181 break; 182 } 183 184 if ( key > 127 ) 185 { 186 switch ( key ) 187 { 188 case K_DEL: 189 default: 190 return false; 191 } 192 } 193 194 /* 195 ** support pasting from the clipboard 196 */ 197 if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) || 198 ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) ) 199 { 200 char *cbd; 201 202 if ( ( cbd = Sys_GetClipboardData() ) != 0 ) 203 { 204 strtok( cbd, "\n\r\b" ); 205 206 strncpy( f->buffer, cbd, f->length - 1 ); 207 f->cursor = strlen( f->buffer ); 208 f->visible_offset = f->cursor - f->visible_length; 209 if ( f->visible_offset < 0 ) 210 f->visible_offset = 0; 211 212 free( cbd ); 213 } 214 return true; 215 } 216 217 switch ( key ) 218 { 219 case K_KP_LEFTARROW: 220 case K_LEFTARROW: 221 case K_BACKSPACE: 222 if ( f->cursor > 0 ) 223 { 224 memmove( &f->buffer[f->cursor-1], &f->buffer[f->cursor], strlen( &f->buffer[f->cursor] ) + 1 ); 225 f->cursor--; 226 227 if ( f->visible_offset ) 228 { 229 f->visible_offset--; 230 } 231 } 232 break; 233 234 case K_KP_DEL: 235 case K_DEL: 236 memmove( &f->buffer[f->cursor], &f->buffer[f->cursor+1], strlen( &f->buffer[f->cursor+1] ) + 1 ); 237 break; 238 239 case K_KP_ENTER: 240 case K_ENTER: 241 case K_ESCAPE: 242 case K_TAB: 243 return false; 244 245 case K_SPACE: 246 default: 247 if ( !isdigit( key ) && ( f->generic.flags & QMF_NUMBERSONLY ) ) 248 return false; 249 250 if ( f->cursor < f->length ) 251 { 252 f->buffer[f->cursor++] = key; 253 f->buffer[f->cursor] = 0; 254 255 if ( f->cursor > f->visible_length ) 256 { 257 f->visible_offset++; 258 } 259 } 260 } 261 262 return true; 263 } 264 265 void Menu_AddItem( menuframework_s *menu, void *item ) 266 { 267 if ( menu->nitems == 0 ) 268 menu->nslots = 0; 269 270 if ( menu->nitems < MAXMENUITEMS ) 271 { 272 menu->items[menu->nitems] = item; 273 ( ( menucommon_s * ) menu->items[menu->nitems] )->parent = menu; 274 menu->nitems++; 275 } 276 277 menu->nslots = Menu_TallySlots( menu ); 278 } 279 280 /* 281 ** Menu_AdjustCursor 282 ** 283 ** This function takes the given menu, the direction, and attempts 284 ** to adjust the menu's cursor so that it's at the next available 285 ** slot. 286 */ 287 void Menu_AdjustCursor( menuframework_s *m, int dir ) 288 { 289 menucommon_s *citem; 290 291 /* 292 ** see if it's in a valid spot 293 */ 294 if ( m->cursor >= 0 && m->cursor < m->nitems ) 295 { 296 if ( ( citem = Menu_ItemAtCursor( m ) ) != 0 ) 297 { 298 if ( citem->type != MTYPE_SEPARATOR ) 299 return; 300 } 301 } 302 303 /* 304 ** it's not in a valid spot, so crawl in the direction indicated until we 305 ** find a valid spot 306 */ 307 if ( dir == 1 ) 308 { 309 while ( 1 ) 310 { 311 citem = Menu_ItemAtCursor( m ); 312 if ( citem ) 313 if ( citem->type != MTYPE_SEPARATOR ) 314 break; 315 m->cursor += dir; 316 if ( m->cursor >= m->nitems ) 317 m->cursor = 0; 318 } 319 } 320 else 321 { 322 while ( 1 ) 323 { 324 citem = Menu_ItemAtCursor( m ); 325 if ( citem ) 326 if ( citem->type != MTYPE_SEPARATOR ) 327 break; 328 m->cursor += dir; 329 if ( m->cursor < 0 ) 330 m->cursor = m->nitems - 1; 331 } 332 } 333 } 334 335 void Menu_Center( menuframework_s *menu ) 336 { 337 int height; 338 339 height = ( ( menucommon_s * ) menu->items[menu->nitems-1])->y; 340 height += 10; 341 342 menu->y = ( VID_HEIGHT - height ) / 2; 343 } 344 345 void Menu_Draw( menuframework_s *menu ) 346 { 347 int i; 348 menucommon_s *item; 349 350 /* 351 ** draw contents 352 */ 353 for ( i = 0; i < menu->nitems; i++ ) 354 { 355 switch ( ( ( menucommon_s * ) menu->items[i] )->type ) 356 { 357 case MTYPE_FIELD: 358 Field_Draw( ( menufield_s * ) menu->items[i] ); 359 break; 360 case MTYPE_SLIDER: 361 Slider_Draw( ( menuslider_s * ) menu->items[i] ); 362 break; 363 case MTYPE_LIST: 364 MenuList_Draw( ( menulist_s * ) menu->items[i] ); 365 break; 366 case MTYPE_SPINCONTROL: 367 SpinControl_Draw( ( menulist_s * ) menu->items[i] ); 368 break; 369 case MTYPE_ACTION: 370 Action_Draw( ( menuaction_s * ) menu->items[i] ); 371 break; 372 case MTYPE_SEPARATOR: 373 Separator_Draw( ( menuseparator_s * ) menu->items[i] ); 374 break; 375 } 376 } 377 378 item = Menu_ItemAtCursor( menu ); 379 380 if ( item && item->cursordraw ) 381 { 382 item->cursordraw( item ); 383 } 384 else if ( menu->cursordraw ) 385 { 386 menu->cursordraw( menu ); 387 } 388 else if ( item && item->type != MTYPE_FIELD ) 389 { 390 if ( item->flags & QMF_LEFT_JUSTIFY ) 391 { 392 Draw_Char( menu->x + item->x - 24 + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) ); 393 } 394 else 395 { 396 Draw_Char( menu->x + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) ); 397 } 398 } 399 400 if ( item ) 401 { 402 if ( item->statusbarfunc ) 403 item->statusbarfunc( ( void * ) item ); 404 else if ( item->statusbar ) 405 Menu_DrawStatusBar( item->statusbar ); 406 else 407 Menu_DrawStatusBar( menu->statusbar ); 408 409 } 410 else 411 { 412 Menu_DrawStatusBar( menu->statusbar ); 413 } 414 } 415 416 void Menu_DrawStatusBar( const char *string ) 417 { 418 if ( string ) 419 { 420 int l = strlen( string ); 421 int maxrow = VID_HEIGHT / 8; 422 int maxcol = VID_WIDTH / 8; 423 int col = maxcol / 2 - l / 2; 424 425 Draw_Fill( 0, VID_HEIGHT-8, VID_WIDTH, 8, 4 ); 426 Menu_DrawString( col*8, VID_HEIGHT - 8, string ); 427 } 428 else 429 { 430 Draw_Fill( 0, VID_HEIGHT-8, VID_WIDTH, 8, 0 ); 431 } 432 } 433 434 void Menu_DrawString( int x, int y, const char *string ) 435 { 436 unsigned i; 437 438 for ( i = 0; i < strlen( string ); i++ ) 439 { 440 Draw_Char( ( x + i*8 ), y, string[i] ); 441 } 442 } 443 444 void Menu_DrawStringDark( int x, int y, const char *string ) 445 { 446 unsigned i; 447 448 for ( i = 0; i < strlen( string ); i++ ) 449 { 450 Draw_Char( ( x + i*8 ), y, string[i] + 128 ); 451 } 452 } 453 454 void Menu_DrawStringR2L( int x, int y, const char *string ) 455 { 456 unsigned i; 457 458 for ( i = 0; i < strlen( string ); i++ ) 459 { 460 Draw_Char( ( x - i*8 ), y, string[strlen(string)-i-1] ); 461 } 462 } 463 464 void Menu_DrawStringR2LDark( int x, int y, const char *string ) 465 { 466 unsigned i; 467 468 for ( i = 0; i < strlen( string ); i++ ) 469 { 470 Draw_Char( ( x - i*8 ), y, string[strlen(string)-i-1]+128 ); 471 } 472 } 473 474 void *Menu_ItemAtCursor( menuframework_s *m ) 475 { 476 if ( m->cursor < 0 || m->cursor >= m->nitems ) 477 return 0; 478 479 return m->items[m->cursor]; 480 } 481 482 qboolean Menu_SelectItem( menuframework_s *s ) 483 { 484 menucommon_s *item = ( menucommon_s * ) Menu_ItemAtCursor( s ); 485 486 if ( item ) 487 { 488 switch ( item->type ) 489 { 490 case MTYPE_FIELD: 491 return Field_DoEnter( ( menufield_s * ) item ) ; 492 case MTYPE_ACTION: 493 Action_DoEnter( ( menuaction_s * ) item ); 494 return true; 495 case MTYPE_LIST: 496 // Menulist_DoEnter( ( menulist_s * ) item ); 497 return false; 498 case MTYPE_SPINCONTROL: 499 // SpinControl_DoEnter( ( menulist_s * ) item ); 500 return false; 501 } 502 } 503 return false; 504 } 505 506 void Menu_SetStatusBar( menuframework_s *m, const char *string ) 507 { 508 m->statusbar = string; 509 } 510 511 void Menu_SlideItem( menuframework_s *s, int dir ) 512 { 513 menucommon_s *item = ( menucommon_s * ) Menu_ItemAtCursor( s ); 514 515 if ( item ) 516 { 517 switch ( item->type ) 518 { 519 case MTYPE_SLIDER: 520 Slider_DoSlide( ( menuslider_s * ) item, dir ); 521 break; 522 case MTYPE_SPINCONTROL: 523 SpinControl_DoSlide( ( menulist_s * ) item, dir ); 524 break; 525 } 526 } 527 } 528 529 int Menu_TallySlots( menuframework_s *menu ) 530 { 531 int i; 532 int total = 0; 533 534 for ( i = 0; i < menu->nitems; i++ ) 535 { 536 if ( ( ( menucommon_s * ) menu->items[i] )->type == MTYPE_LIST ) 537 { 538 int nitems = 0; 539 const char **n = ( ( menulist_s * ) menu->items[i] )->itemnames; 540 541 while (*n) 542 nitems++, n++; 543 544 total += nitems; 545 } 546 else 547 { 548 total++; 549 } 550 } 551 552 return total; 553 } 554 555 void Menulist_DoEnter( menulist_s *l ) 556 { 557 int start; 558 559 start = l->generic.y / 10 + 1; 560 561 l->curvalue = l->generic.parent->cursor - start; 562 563 if ( l->generic.callback ) 564 l->generic.callback( l ); 565 } 566 567 void MenuList_Draw( menulist_s *l ) 568 { 569 const char **n; 570 int y = 0; 571 572 Menu_DrawStringR2LDark( l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y, l->generic.name ); 573 574 n = l->itemnames; 575 576 Draw_Fill( l->generic.x - 112 + l->generic.parent->x, l->generic.parent->y + l->generic.y + l->curvalue*10 + 10, 128, 10, 16 ); 577 while ( *n ) 578 { 579 Menu_DrawStringR2LDark( l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y + y + 10, *n ); 580 581 n++; 582 y += 10; 583 } 584 } 585 586 void Separator_Draw( menuseparator_s *s ) 587 { 588 if ( s->generic.name ) 589 Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->generic.name ); 590 } 591 592 void Slider_DoSlide( menuslider_s *s, int dir ) 593 { 594 s->curvalue += dir; 595 596 if ( s->curvalue > s->maxvalue ) 597 s->curvalue = s->maxvalue; 598 else if ( s->curvalue < s->minvalue ) 599 s->curvalue = s->minvalue; 600 601 if ( s->generic.callback ) 602 s->generic.callback( s ); 603 } 604 605 #define SLIDER_RANGE 10 606 607 void Slider_Draw( menuslider_s *s ) 608 { 609 int i; 610 611 Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET, 612 s->generic.y + s->generic.parent->y, 613 s->generic.name ); 614 615 s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue ); 616 617 if ( s->range < 0) 618 s->range = 0; 619 if ( s->range > 1) 620 s->range = 1; 621 Draw_Char( s->generic.x + s->generic.parent->x + RCOLUMN_OFFSET, s->generic.y + s->generic.parent->y, 128); 622 for ( i = 0; i < SLIDER_RANGE; i++ ) 623 Draw_Char( RCOLUMN_OFFSET + s->generic.x + i*8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 129); 624 Draw_Char( RCOLUMN_OFFSET + s->generic.x + i*8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 130); 625 Draw_Char( ( int ) ( 8 + RCOLUMN_OFFSET + s->generic.parent->x + s->generic.x + (SLIDER_RANGE-1)*8 * s->range ), s->generic.y + s->generic.parent->y, 131); 626 } 627 628 void SpinControl_DoEnter( menulist_s *s ) 629 { 630 s->curvalue++; 631 if ( s->itemnames[s->curvalue] == 0 ) 632 s->curvalue = 0; 633 634 if ( s->generic.callback ) 635 s->generic.callback( s ); 636 } 637 638 void SpinControl_DoSlide( menulist_s *s, int dir ) 639 { 640 s->curvalue += dir; 641 642 if ( s->curvalue < 0 ) 643 s->curvalue = 0; 644 else if ( s->itemnames[s->curvalue] == 0 ) 645 s->curvalue--; 646 647 if ( s->generic.callback ) 648 s->generic.callback( s ); 649 } 650 651 void SpinControl_Draw( menulist_s *s ) 652 { 653 char buffer[100]; 654 655 if ( s->generic.name ) 656 { 657 Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET, 658 s->generic.y + s->generic.parent->y, 659 s->generic.name ); 660 } 661 if ( !strchr( s->itemnames[s->curvalue], '\n' ) ) 662 { 663 Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->itemnames[s->curvalue] ); 664 } 665 else 666 { 667 strcpy( buffer, s->itemnames[s->curvalue] ); 668 *strchr( buffer, '\n' ) = 0; 669 Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, buffer ); 670 strcpy( buffer, strchr( s->itemnames[s->curvalue], '\n' ) + 1 ); 671 Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y + 10, buffer ); 672 } 673 } 674