Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

ui_qmenu.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 /**********************************************************************
00024     UI_QMENU.C
00025 
00026     Quake's menu framework system.
00027 **********************************************************************/
00028 #include "ui_local.h"
00029 
00030 sfxHandle_t menu_in_sound;
00031 sfxHandle_t menu_move_sound;
00032 sfxHandle_t menu_out_sound;
00033 sfxHandle_t menu_buzz_sound;
00034 sfxHandle_t menu_null_sound;
00035 sfxHandle_t weaponChangeSound;
00036 
00037 static qhandle_t    sliderBar;
00038 static qhandle_t    sliderButton_0;
00039 static qhandle_t    sliderButton_1;
00040 
00041 vec4_t menu_text_color      = {1.0f, 1.0f, 1.0f, 1.0f};
00042 vec4_t menu_dim_color       = {0.0f, 0.0f, 0.0f, 0.75f};
00043 vec4_t color_black      = {0.00f, 0.00f, 0.00f, 1.00f};
00044 vec4_t color_white      = {1.00f, 1.00f, 1.00f, 1.00f};
00045 vec4_t color_yellow     = {1.00f, 1.00f, 0.00f, 1.00f};
00046 vec4_t color_blue       = {0.00f, 0.00f, 1.00f, 1.00f};
00047 vec4_t color_lightOrange    = {1.00f, 0.68f, 0.00f, 1.00f };
00048 vec4_t color_orange     = {1.00f, 0.43f, 0.00f, 1.00f};
00049 vec4_t color_red        = {1.00f, 0.00f, 0.00f, 1.00f};
00050 vec4_t color_dim        = {0.00f, 0.00f, 0.00f, 0.25f};
00051 
00052 // current color scheme
00053 vec4_t pulse_color          = {1.00f, 1.00f, 1.00f, 1.00f};
00054 vec4_t text_color_disabled  = {0.50f, 0.50f, 0.50f, 1.00f}; // light gray
00055 vec4_t text_color_normal    = {1.00f, 0.43f, 0.00f, 1.00f}; // light orange
00056 vec4_t text_color_highlight = {1.00f, 1.00f, 0.00f, 1.00f}; // bright yellow
00057 vec4_t listbar_color        = {1.00f, 0.43f, 0.00f, 0.30f}; // transluscent orange
00058 vec4_t text_color_status    = {1.00f, 1.00f, 1.00f, 1.00f}; // bright white 
00059 
00060 // action widget
00061 static void Action_Init( menuaction_s *a );
00062 static void Action_Draw( menuaction_s *a );
00063 
00064 // radio button widget
00065 static void RadioButton_Init( menuradiobutton_s *rb );
00066 static void RadioButton_Draw( menuradiobutton_s *rb );
00067 static sfxHandle_t RadioButton_Key( menuradiobutton_s *rb, int key );
00068 
00069 // slider widget
00070 static void Slider_Init( menuslider_s *s );
00071 static sfxHandle_t Slider_Key( menuslider_s *s, int key );
00072 static void Slider_Draw( menuslider_s *s );
00073 
00074 // spin control widget
00075 static void SpinControl_Init( menulist_s *s );
00076 static void SpinControl_Draw( menulist_s *s );
00077 static sfxHandle_t SpinControl_Key( menulist_s *l, int key );
00078 
00079 // text widget
00080 static void Text_Init( menutext_s *b );
00081 static void Text_Draw( menutext_s *b );
00082 
00083 // scrolllist widget
00084 static void ScrollList_Init( menulist_s *l );
00085 sfxHandle_t ScrollList_Key( menulist_s *l, int key );
00086 
00087 // proportional text widget
00088 static void PText_Init( menutext_s *b );
00089 static void PText_Draw( menutext_s *b );
00090 
00091 // proportional banner text widget
00092 static void BText_Init( menutext_s *b );
00093 static void BText_Draw( menutext_s *b );
00094 
00095 /*
00096 =================
00097 Text_Init
00098 =================
00099 */
00100 static void Text_Init( menutext_s *t )
00101 {
00102     t->generic.flags |= QMF_INACTIVE;
00103 }
00104 
00105 /*
00106 =================
00107 Text_Draw
00108 =================
00109 */
00110 static void Text_Draw( menutext_s *t )
00111 {
00112     int     x;
00113     int     y;
00114     char    buff[512];  
00115     float*  color;
00116 
00117     x = t->generic.x;
00118     y = t->generic.y;
00119 
00120     buff[0] = '\0';
00121 
00122     // possible label
00123     if (t->generic.name)
00124         strcpy(buff,t->generic.name);
00125 
00126     // possible value
00127     if (t->string)
00128         strcat(buff,t->string);
00129         
00130     if (t->generic.flags & QMF_GRAYED)
00131         color = text_color_disabled;
00132     else
00133         color = t->color;
00134 
00135     UI_DrawString( x, y, buff, t->style, color );
00136 }
00137 
00138 /*
00139 =================
00140 BText_Init
00141 =================
00142 */
00143 static void BText_Init( menutext_s *t )
00144 {
00145     t->generic.flags |= QMF_INACTIVE;
00146 }
00147 
00148 /*
00149 =================
00150 BText_Draw
00151 =================
00152 */
00153 static void BText_Draw( menutext_s *t )
00154 {
00155     int     x;
00156     int     y;
00157     float*  color;
00158 
00159     x = t->generic.x;
00160     y = t->generic.y;
00161 
00162     if (t->generic.flags & QMF_GRAYED)
00163         color = text_color_disabled;
00164     else
00165         color = t->color;
00166 
00167     UI_DrawBannerString( x, y, t->string, t->style, color );
00168 }
00169 
00170 /*
00171 =================
00172 PText_Init
00173 =================
00174 */
00175 static void PText_Init( menutext_s *t )
00176 {
00177     int x;
00178     int y;
00179     int w;
00180     int h;
00181     float   sizeScale;
00182 
00183     sizeScale = UI_ProportionalSizeScale( t->style );
00184 
00185     x = t->generic.x;
00186     y = t->generic.y;
00187     w = UI_ProportionalStringWidth( t->string ) * sizeScale;
00188     h = PROP_HEIGHT * sizeScale;
00189 
00190     if( t->generic.flags & QMF_RIGHT_JUSTIFY ) {
00191         x -= w;
00192     }
00193     else if( t->generic.flags & QMF_CENTER_JUSTIFY ) {
00194         x -= w / 2;
00195     }
00196 
00197     t->generic.left   = x - PROP_GAP_WIDTH * sizeScale;
00198     t->generic.right  = x + w + PROP_GAP_WIDTH * sizeScale;
00199     t->generic.top    = y;
00200     t->generic.bottom = y + h;
00201 }
00202 
00203 /*
00204 =================
00205 PText_Draw
00206 =================
00207 */
00208 static void PText_Draw( menutext_s *t )
00209 {
00210     int     x;
00211     int     y;
00212     float * color;
00213     int     style;
00214 
00215     x = t->generic.x;
00216     y = t->generic.y;
00217 
00218     if (t->generic.flags & QMF_GRAYED)
00219         color = text_color_disabled;
00220     else
00221         color = t->color;
00222 
00223     style = t->style;
00224     if( t->generic.flags & QMF_PULSEIFFOCUS ) {
00225         if( Menu_ItemAtCursor( t->generic.parent ) == t ) {
00226             style |= UI_PULSE;
00227         }
00228         else {
00229             style |= UI_INVERSE;
00230         }
00231     }
00232 
00233     UI_DrawProportionalString( x, y, t->string, style, color );
00234 }
00235 
00236 /*
00237 =================
00238 Bitmap_Init
00239 =================
00240 */
00241 void Bitmap_Init( menubitmap_s *b )
00242 {
00243     int x;
00244     int y;
00245     int w;
00246     int h;
00247 
00248     x = b->generic.x;
00249     y = b->generic.y;
00250     w = b->width;
00251     h = b->height;
00252     if( w < 0 ) {
00253         w = -w;
00254     }
00255     if( h < 0 ) {
00256         h = -h;
00257     }
00258 
00259     if (b->generic.flags & QMF_RIGHT_JUSTIFY)
00260     {
00261         x = x - w;
00262     }
00263     else if (b->generic.flags & QMF_CENTER_JUSTIFY)
00264     {
00265         x = x - w/2;
00266     }
00267 
00268     b->generic.left   = x;
00269     b->generic.right  = x + w;
00270     b->generic.top    = y;
00271     b->generic.bottom = y + h;
00272 
00273     b->shader      = 0;
00274     b->focusshader = 0;
00275 }
00276 
00277 /*
00278 =================
00279 Bitmap_Draw
00280 =================
00281 */
00282 void Bitmap_Draw( menubitmap_s *b )
00283 {
00284     float   x;
00285     float   y;
00286     float   w;
00287     float   h;
00288     vec4_t  tempcolor;
00289     float*  color;
00290 
00291     x = b->generic.x;
00292     y = b->generic.y;
00293     w = b->width;
00294     h = b->height;
00295 
00296     if (b->generic.flags & QMF_RIGHT_JUSTIFY)
00297     {
00298         x = x - w;
00299     }
00300     else if (b->generic.flags & QMF_CENTER_JUSTIFY)
00301     {
00302         x = x - w/2;
00303     }
00304 
00305     // used to refresh shader
00306     if (b->generic.name && !b->shader)
00307     {
00308         b->shader = trap_R_RegisterShaderNoMip( b->generic.name );
00309         if (!b->shader && b->errorpic)
00310             b->shader = trap_R_RegisterShaderNoMip( b->errorpic );
00311     }
00312 
00313     if (b->focuspic && !b->focusshader)
00314         b->focusshader = trap_R_RegisterShaderNoMip( b->focuspic );
00315 
00316     if (b->generic.flags & QMF_GRAYED)
00317     {
00318         if (b->shader)
00319         {
00320             trap_R_SetColor( colorMdGrey );
00321             UI_DrawHandlePic( x, y, w, h, b->shader );
00322             trap_R_SetColor( NULL );
00323         }
00324     }
00325     else
00326     {
00327         if (b->shader)
00328             UI_DrawHandlePic( x, y, w, h, b->shader );
00329 
00330         // bk001204 - parentheses
00331         if (  ( (b->generic.flags & QMF_PULSE) 
00332             || (b->generic.flags & QMF_PULSEIFFOCUS) )
00333               && (Menu_ItemAtCursor( b->generic.parent ) == b))
00334         {   
00335             if (b->focuscolor)          
00336             {
00337                 tempcolor[0] = b->focuscolor[0];
00338                 tempcolor[1] = b->focuscolor[1];
00339                 tempcolor[2] = b->focuscolor[2];
00340                 color        = tempcolor;   
00341             }
00342             else
00343                 color = pulse_color;
00344             color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR);
00345 
00346             trap_R_SetColor( color );
00347             UI_DrawHandlePic( x, y, w, h, b->focusshader );
00348             trap_R_SetColor( NULL );
00349         }
00350         else if ((b->generic.flags & QMF_HIGHLIGHT) || ((b->generic.flags & QMF_HIGHLIGHT_IF_FOCUS) && (Menu_ItemAtCursor( b->generic.parent ) == b)))
00351         {   
00352             if (b->focuscolor)
00353             {
00354                 trap_R_SetColor( b->focuscolor );
00355                 UI_DrawHandlePic( x, y, w, h, b->focusshader );
00356                 trap_R_SetColor( NULL );
00357             }
00358             else
00359                 UI_DrawHandlePic( x, y, w, h, b->focusshader );
00360         }
00361     }
00362 }
00363 
00364 /*
00365 =================
00366 Action_Init
00367 =================
00368 */
00369 static void Action_Init( menuaction_s *a )
00370 {
00371     int len;
00372 
00373     // calculate bounds
00374     if (a->generic.name)
00375         len = strlen(a->generic.name);
00376     else
00377         len = 0;
00378 
00379     // left justify text
00380     a->generic.left   = a->generic.x; 
00381     a->generic.right  = a->generic.x + len*BIGCHAR_WIDTH;
00382     a->generic.top    = a->generic.y;
00383     a->generic.bottom = a->generic.y + BIGCHAR_HEIGHT;
00384 }
00385 
00386 /*
00387 =================
00388 Action_Draw
00389 =================
00390 */
00391 static void Action_Draw( menuaction_s *a )
00392 {
00393     int     x, y;
00394     int     style;
00395     float*  color;
00396 
00397     style = 0;
00398     color = menu_text_color;
00399     if ( a->generic.flags & QMF_GRAYED )
00400     {
00401         color = text_color_disabled;
00402     }
00403     else if (( a->generic.flags & QMF_PULSEIFFOCUS ) && ( a->generic.parent->cursor == a->generic.menuPosition ))
00404     {
00405         color = text_color_highlight;
00406         style = UI_PULSE;
00407     }
00408     else if (( a->generic.flags & QMF_HIGHLIGHT_IF_FOCUS ) && ( a->generic.parent->cursor == a->generic.menuPosition ))
00409     {
00410         color = text_color_highlight;
00411     }
00412     else if ( a->generic.flags & QMF_BLINK )
00413     {
00414         style = UI_BLINK;
00415         color = text_color_highlight;
00416     }
00417 
00418     x = a->generic.x;
00419     y = a->generic.y;
00420 
00421     UI_DrawString( x, y, a->generic.name, UI_LEFT|style, color );
00422 
00423     if ( a->generic.parent->cursor == a->generic.menuPosition )
00424     {
00425         // draw cursor
00426         UI_DrawChar( x - BIGCHAR_WIDTH, y, 13, UI_LEFT|UI_BLINK, color);
00427     }
00428 }
00429 
00430 /*
00431 =================
00432 RadioButton_Init
00433 =================
00434 */
00435 static void RadioButton_Init( menuradiobutton_s *rb )
00436 {
00437     int len;
00438 
00439     // calculate bounds
00440     if (rb->generic.name)
00441         len = strlen(rb->generic.name);
00442     else
00443         len = 0;
00444 
00445     rb->generic.left   = rb->generic.x - (len+1)*SMALLCHAR_WIDTH;
00446     rb->generic.right  = rb->generic.x + 6*SMALLCHAR_WIDTH;
00447     rb->generic.top    = rb->generic.y;
00448     rb->generic.bottom = rb->generic.y + SMALLCHAR_HEIGHT;
00449 }
00450 
00451 /*
00452 =================
00453 RadioButton_Key
00454 =================
00455 */
00456 static sfxHandle_t RadioButton_Key( menuradiobutton_s *rb, int key )
00457 {
00458     switch (key)
00459     {
00460         case K_MOUSE1:
00461             if (!(rb->generic.flags & QMF_HASMOUSEFOCUS))
00462                 break;
00463 
00464         case K_JOY1:
00465         case K_JOY2:
00466         case K_JOY3:
00467         case K_JOY4:
00468         case K_ENTER:
00469         case K_KP_ENTER:
00470         case K_KP_LEFTARROW:
00471         case K_LEFTARROW:
00472         case K_KP_RIGHTARROW:
00473         case K_RIGHTARROW:
00474             rb->curvalue = !rb->curvalue;
00475             if ( rb->generic.callback )
00476                 rb->generic.callback( rb, QM_ACTIVATED );
00477 
00478             return (menu_move_sound);
00479     }
00480 
00481     // key not handled
00482     return 0;
00483 }
00484 
00485 /*
00486 =================
00487 RadioButton_Draw
00488 =================
00489 */
00490 static void RadioButton_Draw( menuradiobutton_s *rb )
00491 {
00492     int x;
00493     int y;
00494     float *color;
00495     int style;
00496     qboolean focus;
00497 
00498     x = rb->generic.x;
00499     y = rb->generic.y;
00500 
00501     focus = (rb->generic.parent->cursor == rb->generic.menuPosition);
00502 
00503     if ( rb->generic.flags & QMF_GRAYED )
00504     {
00505         color = text_color_disabled;
00506         style = UI_LEFT|UI_SMALLFONT;
00507     }
00508     else if ( focus )
00509     {
00510         color = text_color_highlight;
00511         style = UI_LEFT|UI_PULSE|UI_SMALLFONT;
00512     }
00513     else
00514     {
00515         color = text_color_normal;
00516         style = UI_LEFT|UI_SMALLFONT;
00517     }
00518 
00519     if ( focus )
00520     {
00521         // draw cursor
00522         UI_FillRect( rb->generic.left, rb->generic.top, rb->generic.right-rb->generic.left+1, rb->generic.bottom-rb->generic.top+1, listbar_color ); 
00523         UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
00524     }
00525 
00526     if ( rb->generic.name )
00527         UI_DrawString( x - SMALLCHAR_WIDTH, y, rb->generic.name, UI_RIGHT|UI_SMALLFONT, color );
00528 
00529     if ( !rb->curvalue )
00530     {
00531         UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y + 2, 16, 16, uis.rb_off);
00532         UI_DrawString( x + SMALLCHAR_WIDTH + 16, y, "off", style, color );
00533     }
00534     else
00535     {
00536         UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y + 2, 16, 16, uis.rb_on );
00537         UI_DrawString( x + SMALLCHAR_WIDTH + 16, y, "on", style, color );
00538     }
00539 }
00540 
00541 /*
00542 =================
00543 Slider_Init
00544 =================
00545 */
00546 static void Slider_Init( menuslider_s *s )
00547 {
00548     int len;
00549 
00550     // calculate bounds
00551     if (s->generic.name)
00552         len = strlen(s->generic.name);
00553     else
00554         len = 0;
00555 
00556     s->generic.left   = s->generic.x - (len+1)*SMALLCHAR_WIDTH; 
00557     s->generic.right  = s->generic.x + (SLIDER_RANGE+2+1)*SMALLCHAR_WIDTH;
00558     s->generic.top    = s->generic.y;
00559     s->generic.bottom = s->generic.y + SMALLCHAR_HEIGHT;
00560 }
00561 
00562 /*
00563 =================
00564 Slider_Key
00565 =================
00566 */
00567 static sfxHandle_t Slider_Key( menuslider_s *s, int key )
00568 {
00569     sfxHandle_t sound;
00570     int         x;
00571     int         oldvalue;
00572 
00573     switch (key)
00574     {
00575         case K_MOUSE1:
00576             x           = uis.cursorx - s->generic.x - 2*SMALLCHAR_WIDTH;
00577             oldvalue    = s->curvalue;
00578             s->curvalue = (x/(float)(SLIDER_RANGE*SMALLCHAR_WIDTH)) * (s->maxvalue-s->minvalue) + s->minvalue;
00579 
00580             if (s->curvalue < s->minvalue)
00581                 s->curvalue = s->minvalue;
00582             else if (s->curvalue > s->maxvalue)
00583                 s->curvalue = s->maxvalue;
00584             if (s->curvalue != oldvalue)
00585                 sound = menu_move_sound;
00586             else
00587                 sound = 0;
00588             break;
00589 
00590         case K_KP_LEFTARROW:
00591         case K_LEFTARROW:
00592             if (s->curvalue > s->minvalue)
00593             {
00594                 s->curvalue--;
00595                 sound = menu_move_sound;
00596             }
00597             else
00598                 sound = menu_buzz_sound;
00599             break;          
00600 
00601         case K_KP_RIGHTARROW:
00602         case K_RIGHTARROW:
00603             if (s->curvalue < s->maxvalue)
00604             {
00605                 s->curvalue++;
00606                 sound = menu_move_sound;
00607             }
00608             else
00609                 sound = menu_buzz_sound;
00610             break;          
00611 
00612         default:
00613             // key not handled
00614             sound = 0;
00615             break;
00616     }
00617 
00618     if ( sound && s->generic.callback )
00619         s->generic.callback( s, QM_ACTIVATED );
00620 
00621     return (sound);
00622 }
00623 
00624 #if 1
00625 /*
00626 =================
00627 Slider_Draw
00628 =================
00629 */
00630 static void Slider_Draw( menuslider_s *s ) {
00631     int         x;
00632     int         y;
00633     int         style;
00634     float       *color;
00635     int         button;
00636     qboolean    focus;
00637     
00638     x = s->generic.x;
00639     y = s->generic.y;
00640     focus = (s->generic.parent->cursor == s->generic.menuPosition);
00641 
00642     if( s->generic.flags & QMF_GRAYED ) {
00643         color = text_color_disabled;
00644         style = UI_SMALLFONT;
00645     }
00646     else if( focus ) {
00647         color  = text_color_highlight;
00648         style = UI_SMALLFONT | UI_PULSE;
00649     }
00650     else {
00651         color = text_color_normal;
00652         style = UI_SMALLFONT;
00653     }
00654 
00655     // draw label
00656     UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, UI_RIGHT|style, color );
00657 
00658     // draw slider
00659     UI_SetColor( color );
00660     UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y, 96, 16, sliderBar );
00661     UI_SetColor( NULL );
00662 
00663     // clamp thumb
00664     if( s->maxvalue > s->minvalue ) {
00665         s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
00666         if( s->range < 0 ) {
00667             s->range = 0;
00668         }
00669         else if( s->range > 1) {
00670             s->range = 1;
00671         }
00672     }
00673     else {
00674         s->range = 0;
00675     }
00676 
00677     // draw thumb
00678     if( style & UI_PULSE) {
00679         button = sliderButton_1;
00680     }
00681     else {
00682         button = sliderButton_0;
00683     }
00684 
00685     UI_DrawHandlePic( (int)( x + 2*SMALLCHAR_WIDTH + (SLIDER_RANGE-1)*SMALLCHAR_WIDTH* s->range ) - 2, y - 2, 12, 20, button );
00686 }
00687 #else
00688 /*
00689 =================
00690 Slider_Draw
00691 =================
00692 */
00693 static void Slider_Draw( menuslider_s *s )
00694 {
00695     float *color;
00696     int style;
00697     int i;
00698     int x;
00699     int y;
00700     qboolean focus;
00701     
00702     x = s->generic.x;
00703     y = s->generic.y;
00704     focus = (s->generic.parent->cursor == s->generic.menuPosition);
00705 
00706     style = UI_SMALLFONT;
00707     if ( s->generic.flags & QMF_GRAYED )
00708     {
00709         color = text_color_disabled;
00710     }
00711     else if (focus)
00712     {
00713         color  = text_color_highlight;
00714         style |= UI_PULSE;
00715     }
00716     else
00717     {
00718         color = text_color_normal;
00719     }
00720 
00721     if ( focus )
00722     {
00723         // draw cursor
00724         UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); 
00725         UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
00726     }
00727 
00728     // draw label
00729     UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, UI_RIGHT|style, color );
00730 
00731     // draw slider
00732     UI_DrawChar( x + SMALLCHAR_WIDTH, y, 128, UI_LEFT|style, color);
00733     for ( i = 0; i < SLIDER_RANGE; i++ )
00734         UI_DrawChar( x + (i+2)*SMALLCHAR_WIDTH, y, 129, UI_LEFT|style, color);
00735     UI_DrawChar( x + (i+2)*SMALLCHAR_WIDTH, y, 130, UI_LEFT|style, color);
00736 
00737     // clamp thumb
00738     if (s->maxvalue > s->minvalue)
00739     {
00740         s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
00741         if ( s->range < 0)
00742             s->range = 0;
00743         else if ( s->range > 1)
00744             s->range = 1;
00745     }
00746     else
00747         s->range = 0;
00748 
00749     // draw thumb
00750     if (style & UI_PULSE) {
00751         style &= ~UI_PULSE;
00752         style |= UI_BLINK;
00753     }
00754     UI_DrawChar( (int)( x + 2*SMALLCHAR_WIDTH + (SLIDER_RANGE-1)*SMALLCHAR_WIDTH* s->range ), y, 131, UI_LEFT|style, color);
00755 }
00756 #endif
00757 
00758 /*
00759 =================
00760 SpinControl_Init
00761 =================
00762 */
00763 static void SpinControl_Init( menulist_s *s ) {
00764     int len;
00765     int l;
00766     const char* str;
00767 
00768     if (s->generic.name)
00769         len = strlen(s->generic.name) * SMALLCHAR_WIDTH;
00770     else
00771         len = 0;
00772 
00773     s->generic.left = s->generic.x - SMALLCHAR_WIDTH - len;
00774 
00775     len = s->numitems = 0;
00776     while ( (str = s->itemnames[s->numitems]) != 0 )
00777     {
00778         l = strlen(str);
00779         if (l > len)
00780             len = l;
00781 
00782         s->numitems++;
00783     }       
00784 
00785     s->generic.top    = s->generic.y;
00786     s->generic.right  = s->generic.x + (len+1)*SMALLCHAR_WIDTH;
00787     s->generic.bottom = s->generic.y + SMALLCHAR_HEIGHT;
00788 }
00789 
00790 /*
00791 =================
00792 SpinControl_Key
00793 =================
00794 */
00795 static sfxHandle_t SpinControl_Key( menulist_s *s, int key )
00796 {
00797     sfxHandle_t sound;
00798 
00799     sound = 0;
00800     switch (key)
00801     {
00802         case K_MOUSE1:
00803             s->curvalue++;
00804             if (s->curvalue >= s->numitems)
00805                 s->curvalue = 0;
00806             sound = menu_move_sound;
00807             break;
00808         
00809         case K_KP_LEFTARROW:
00810         case K_LEFTARROW:
00811             if (s->curvalue > 0)
00812             {
00813                 s->curvalue--;
00814                 sound = menu_move_sound;
00815             }
00816             else
00817                 sound = menu_buzz_sound;
00818             break;
00819 
00820         case K_KP_RIGHTARROW:
00821         case K_RIGHTARROW:
00822             if (s->curvalue < s->numitems-1)
00823             {
00824                 s->curvalue++;
00825                 sound = menu_move_sound;
00826             }
00827             else
00828                 sound = menu_buzz_sound;
00829             break;
00830     }
00831 
00832     if ( sound && s->generic.callback )
00833         s->generic.callback( s, QM_ACTIVATED );
00834 
00835     return (sound);
00836 }
00837 
00838 /*
00839 =================
00840 SpinControl_Draw
00841 =================
00842 */
00843 static void SpinControl_Draw( menulist_s *s )
00844 {
00845     float *color;
00846     int x,y;
00847     int style;
00848     qboolean focus;
00849 
00850     x = s->generic.x;
00851     y = s->generic.y;
00852 
00853     style = UI_SMALLFONT;
00854     focus = (s->generic.parent->cursor == s->generic.menuPosition);
00855 
00856     if ( s->generic.flags & QMF_GRAYED )
00857         color = text_color_disabled;
00858     else if ( focus )
00859     {
00860         color = text_color_highlight;
00861         style |= UI_PULSE;
00862     }
00863     else if ( s->generic.flags & QMF_BLINK )
00864     {
00865         color = text_color_highlight;
00866         style |= UI_BLINK;
00867     }
00868     else
00869         color = text_color_normal;
00870 
00871     if ( focus )
00872     {
00873         // draw cursor
00874         UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); 
00875         UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
00876     }
00877 
00878     UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color );
00879     UI_DrawString( x + SMALLCHAR_WIDTH, y, s->itemnames[s->curvalue], style|UI_LEFT, color );
00880 }
00881 
00882 /*
00883 =================
00884 ScrollList_Init
00885 =================
00886 */
00887 static void ScrollList_Init( menulist_s *l )
00888 {
00889     int     w;
00890 
00891     l->oldvalue = 0;
00892     l->curvalue = 0;
00893     l->top      = 0;
00894 
00895     if( !l->columns ) {
00896         l->columns = 1;
00897         l->seperation = 0;
00898     }
00899     else if( !l->seperation ) {
00900         l->seperation = 3;
00901     }
00902 
00903     w = ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH;
00904 
00905     l->generic.left   = l->generic.x;
00906     l->generic.top    = l->generic.y;   
00907     l->generic.right  = l->generic.x + w;
00908     l->generic.bottom = l->generic.y + l->height * SMALLCHAR_HEIGHT;
00909 
00910     if( l->generic.flags & QMF_CENTER_JUSTIFY ) {
00911         l->generic.left -= w / 2;
00912         l->generic.right -= w / 2;
00913     }
00914 }
00915 
00916 /*
00917 =================
00918 ScrollList_Key
00919 =================
00920 */
00921 sfxHandle_t ScrollList_Key( menulist_s *l, int key )
00922 {
00923     int x;
00924     int y;
00925     int w;
00926     int i;
00927     int j;  
00928     int c;
00929     int cursorx;
00930     int cursory;
00931     int column;
00932     int index;
00933 
00934     switch (key)
00935     {
00936         case K_MOUSE1:
00937             if (l->generic.flags & QMF_HASMOUSEFOCUS)
00938             {
00939                 // check scroll region
00940                 x = l->generic.x;
00941                 y = l->generic.y;
00942                 w = ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH;
00943                 if( l->generic.flags & QMF_CENTER_JUSTIFY ) {
00944                     x -= w / 2;
00945                 }
00946                 if (UI_CursorInRect( x, y, w, l->height*SMALLCHAR_HEIGHT ))
00947                 {
00948                     cursorx = (uis.cursorx - x)/SMALLCHAR_WIDTH;
00949                     column = cursorx / (l->width + l->seperation);
00950                     cursory = (uis.cursory - y)/SMALLCHAR_HEIGHT;
00951                     index = column * l->height + cursory;
00952                     if (l->top + index < l->numitems)
00953                     {
00954                         l->oldvalue = l->curvalue;
00955                         l->curvalue = l->top + index;
00956 
00957                         if (l->oldvalue != l->curvalue && l->generic.callback)
00958                         {
00959                             l->generic.callback( l, QM_GOTFOCUS );
00960                             return (menu_move_sound);
00961                         }
00962                     }
00963                 }
00964             
00965                 // absorbed, silent sound effect
00966                 return (menu_null_sound);
00967             }
00968             break;
00969 
00970         case K_KP_HOME:
00971         case K_HOME:
00972             l->oldvalue = l->curvalue;
00973             l->curvalue = 0;
00974             l->top      = 0;
00975 
00976             if (l->oldvalue != l->curvalue && l->generic.callback)
00977             {
00978                 l->generic.callback( l, QM_GOTFOCUS );
00979                 return (menu_move_sound);
00980             }
00981             return (menu_buzz_sound);
00982 
00983         case K_KP_END:
00984         case K_END:
00985             l->oldvalue = l->curvalue;
00986             l->curvalue = l->numitems-1;
00987             if( l->columns > 1 ) {
00988                 c = (l->curvalue / l->height + 1) * l->height;
00989                 l->top = c - (l->columns * l->height);
00990             }
00991             else {
00992                 l->top = l->curvalue - (l->height - 1);
00993             }
00994             if (l->top < 0)
00995                 l->top = 0;         
00996 
00997             if (l->oldvalue != l->curvalue && l->generic.callback)
00998             {
00999                 l->generic.callback( l, QM_GOTFOCUS );
01000                 return (menu_move_sound);
01001             }
01002             return (menu_buzz_sound);
01003 
01004         case K_PGUP:
01005         case K_KP_PGUP:
01006             if( l->columns > 1 ) {
01007                 return menu_null_sound;
01008             }
01009 
01010             if (l->curvalue > 0)
01011             {
01012                 l->oldvalue = l->curvalue;
01013                 l->curvalue -= l->height-1;
01014                 if (l->curvalue < 0)
01015                     l->curvalue = 0;
01016                 l->top = l->curvalue;
01017                 if (l->top < 0)
01018                     l->top = 0;
01019 
01020                 if (l->generic.callback)
01021                     l->generic.callback( l, QM_GOTFOCUS );
01022 
01023                 return (menu_move_sound);
01024             }
01025             return (menu_buzz_sound);
01026 
01027         case K_PGDN:
01028         case K_KP_PGDN:
01029             if( l->columns > 1 ) {
01030                 return menu_null_sound;
01031             }
01032 
01033             if (l->curvalue < l->numitems-1)
01034             {
01035                 l->oldvalue = l->curvalue;
01036                 l->curvalue += l->height-1;
01037                 if (l->curvalue > l->numitems-1)
01038                     l->curvalue = l->numitems-1;
01039                 l->top = l->curvalue - (l->height-1);
01040                 if (l->top < 0)
01041                     l->top = 0;
01042 
01043                 if (l->generic.callback)
01044                     l->generic.callback( l, QM_GOTFOCUS );
01045 
01046                 return (menu_move_sound);
01047             }
01048             return (menu_buzz_sound);
01049 
01050         case K_KP_UPARROW:
01051         case K_UPARROW:
01052             if( l->curvalue == 0 ) {
01053                 return menu_buzz_sound;
01054             }
01055 
01056             l->oldvalue = l->curvalue;
01057             l->curvalue--;
01058 
01059             if( l->curvalue < l->top ) {
01060                 if( l->columns == 1 ) {
01061                     l->top--;
01062                 }
01063                 else {
01064                     l->top -= l->height;
01065                 }
01066             }
01067 
01068             if( l->generic.callback ) {
01069                 l->generic.callback( l, QM_GOTFOCUS );
01070             }
01071 
01072             return (menu_move_sound);
01073 
01074         case K_KP_DOWNARROW:
01075         case