File: client\qmenu.c

    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 
  675