src/ui/uMenu.cpp

Go to the documentation of this file.
00001 /*
00002  
00003 *************************************************************************
00004  
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 Copyright (C) 2004  Armagetron Advanced Team (http://sourceforge.net/projects/armagetronad/)
00008  
00009 **************************************************************************
00010  
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015  
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 GNU General Public License for more details.
00020  
00021 You should have received a copy of the GNU General Public License
00022 along with this program; if not, write to the Free Software
00023 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024 
00025 ***************************************************************************
00026  
00027 */
00028 
00029 #include "tSysTime.h"
00030 #include "uMenu.h"
00031 #include "rSysdep.h"
00032 #include "rScreen.h"
00033 #include "rViewport.h"
00034 #include "rTexture.h"
00035 #include "tRecorder.h"
00036 #include "tString.h"
00037 #include "math.h"
00038 #include "uInputQueue.h"
00039 #include "rConsole.h"
00040 #include "uInput.h"
00041 #include "tDirectories.h"
00042 //#include "tRecording.h"
00043 #include "tToDo.h"
00044 #include "tException.h"
00045 #include <iterator>
00046 
00047 #ifndef DEDICATED
00048 #include "rRender.h"
00049 #include "rSDL.h"
00050 #endif
00051 
00052 #include <vector>
00053 
00054 FUNCPTR  uMenu::idle(NULL);
00055 
00056 bool uMenu::wrap=true;
00057 bool uMenu::quickexit=false;
00058 bool uMenu::exitToMain=false;
00059 
00060 // *****************************************************
00061 
00062 #ifdef SLOPPYLOCALE
00063 uMenu::uMenu(const char *t="",bool exit_item)
00064         :exitFlag(0),spaceBelow(.4),title(t){
00065     if (exit_item) new uMenuItemExit(this);
00066     center=0;
00067     menuTop=.7;
00068     menuBot=-.7;
00069     yOffset=0;
00070     selected = 10000000;
00071 }
00072 #endif
00073 
00074 uMenu::uMenu(const tOutput &t,bool exit_item)
00075         :exitFlag(0),spaceBelow(.4),title(t){
00076     if (exit_item) new uMenuItemExit(this);
00077     center=0;
00078     menuTop=.7;
00079     menuBot=-.7;
00080     yOffset=0;
00081     selected = 100000000;
00082 }
00083 
00084 uMenu::~uMenu(){
00085     for (int i=items.Len()-1;i>=0;i--)
00086         delete items[i];
00087 }
00088 
00089 void uMenu::ReverseItems(){
00090     tList<uMenuItem> dummy = items;
00091     items.SetLen(0);
00092 
00093     for (int i=dummy.Len()-1; i>=0; i--){
00094         uMenuItem *x = dummy[i];
00095         dummy.Remove(x, x->idnum);
00096         items.Add  (x, x->idnum);
00097     }
00098 }
00099 
00100 //static REAL text_height=rCHEIGHT_NORMAL;
00101 //static REAL text_width=rCWIDTH_NORMAL;
00102 
00103 static REAL text_height=.11;
00104 
00105 #ifndef DEDICATED
00106 static REAL titlefac=1.2;
00107 #endif
00108 int menuentries=0;
00109 
00110 REAL uMenu::YPos(int num){
00111     return yOffset-text_height*(menuentries-num);
00112 }
00113 
00114 
00115 static inline void arrow(REAL x,REAL y,REAL dy,REAL size){
00116 #ifndef DEDICATED
00117     if (sr_glOut){
00118         BeginLineLoop();
00119         Vertex(x,y+2*dy*size);
00120         Vertex(x+size,y);
00121         Vertex(x+.3*size,y);
00122         Vertex(x+.3*size,y-2*dy*size);
00123         Vertex(x-.3*size,y-2*dy*size);
00124         Vertex(x-.3*size,y);
00125         Vertex(x-size,y);
00126         RenderEnd();
00127     }
00128 #endif
00129 }
00130 
00131 static bool repeat = false;
00132 
00133 #ifndef DEDICATED
00134 static bool disphelp=false;
00135 static REAL lastkey;
00136 #endif
00137 
00138 // inhibit console newline display while in a menu, it causes flickering
00139 static bool su_inMenu = false;
00140 bool uMenu::MenuActive()
00141 {
00142     return su_inMenu;
00143 }
00144 static rNoAutoDisplayAtNewlineCallback su_noNewline( uMenu::MenuActive );
00145 // static rSmallConsoleCallback su_smallConsole( su_InMenu );
00146 
00147 void uMenu::OnEnter(){
00148 #ifndef DEDICATED
00149     float nextrepeat = 0.0f;
00150     static const float repeatdelay = 0.3f;
00151     static const float repeatrate  = 0.05f;
00152     SDL_Event tEventRepeat;
00153 #else
00154     return;
00155 #endif
00156 
00157     // delete stuck keys, maybe a menu item catches key release events.
00158     su_ClearKeys();
00159 
00160     uCallbackMenuEnter::MenuEnter();
00161     su_inMenu = true;
00162 
00163     if (items.Len()<=0)
00164         return;
00165 
00166     exitFlag=0;
00167     yOffset=menuTop;
00168     REAL lastt=0;
00169     REAL ts=0;
00170 
00171 #ifndef DEDICATED
00172     lastkey=tSysTimeFloat();
00173     static const REAL timeout=3;
00174 #endif
00175     // inverted logic (0 = last item! prev(0) = top most item)
00176     selected = GetPrevSelectable(0);
00177     while (!exitFlag && !quickexit && !exitToMain){
00178         st_DoToDo();
00179         tAdvanceFrame();
00180 
00181         ts=tSysTimeFloat()-lastt;
00182         lastt=tSysTimeFloat();
00183         if (ts>.2) ts=.2;
00184 
00185         menuentries=items.Len();
00186 
00187         // clamp cursor
00188         if (selected < 0 )
00189             selected = 0;
00190         if ( selected >= items.Len())
00191             selected = items.Len()-1;
00192 
00193 #ifndef DEDICATED
00194         {
00195             SDL_Event tEvent;
00196             uInputProcessGuard inputProcessGuard;
00197             while (su_GetSDLInput(tEvent))
00198             {
00199                 REAL entertime = tSysTimeFloat();
00200 
00201                 switch (tEvent.type)
00202                 {
00203                 case SDL_KEYDOWN:
00204                     repeat = true;
00205                     memcpy( &tEventRepeat, &tEvent, sizeof( SDL_Event ) );
00206                     nextrepeat = tSysTimeFloat() + repeatdelay;
00207                     break;
00208                 case SDL_KEYUP:
00209                     repeat = false;
00210                     break;
00211                 }
00212 
00213                 this->HandleEvent( tEvent );
00214 
00215                 // quit shortcut
00216                 if ( quickexit )
00217                     break;
00218 
00219                 if ( tSysTimeFloat() - entertime > 1 )
00220                 {
00221                     repeat = false;
00222                 }
00223             }
00224 
00225             if ( repeat && tSysTimeFloat() > nextrepeat )
00226             {
00227                 this->HandleEvent( tEventRepeat );
00228                 nextrepeat = tSysTimeFloat() + repeatrate;
00229             }
00230         }
00231 
00232         // we're about to render, last chance to make changes to the menu
00233         OnRender();
00234 
00235         // clamp cursor
00236         if (selected < 0 )
00237             selected = 0;
00238         if ( selected >= items.Len())
00239             selected = items.Len()-1;
00240 #endif
00241         // quit shortcut
00242         if ( quickexit )
00243             break;
00244 
00245 
00246         menuBot=-1+spaceBelow;
00247 
00248         const REAL border=.3;
00249         const REAL smallborder=.1;
00250 
00251         menuentries=items.Len();
00252 
00253         REAL ysel=YPos(selected);
00254 
00255         if (ysel<menuBot+border)
00256             yOffset+=(menuBot+border-ysel)*6*ts;
00257 
00258         if (ysel>menuTop-border)
00259             yOffset+=(menuTop-border-ysel)*6*ts;
00260 
00261         if (ysel<menuBot)
00262             yOffset+=(menuBot-ysel);
00263 
00264         if (ysel>menuTop-smallborder)
00265             yOffset+=(menuTop-smallborder-ysel);
00266 
00267         if (YPos(0)>menuBot+smallborder)
00268             yOffset+=menuBot+smallborder-YPos(0);
00269 
00270         if (YPos(menuentries-1)<menuTop-smallborder)
00271             yOffset+=menuTop-smallborder-YPos(menuentries-1);
00272 
00273 #ifndef DEDICATED
00274         sr_ResetRenderState(true);
00275         items[selected]->RenderBackground();
00276 
00277         if (selected >= items.Len()) selected = items.Len()-1;
00278         if (items.Len() <= 0)
00279             return;
00280 
00281         if (sr_glOut && !exitFlag && !quickexit){
00282             items[selected]->Render(center,YPos(selected),1,true);
00283 
00284             for (int i=items.Len()-1;i>=0;i--)
00285                 if (i!=selected){
00286                     REAL y=YPos(i);
00287                     REAL alpha=1;
00288                     const REAL b=.1;
00289                     if (y<menuBot+b)
00290                         alpha=(y-menuBot)/b;
00291                     if (y>menuTop-b)
00292                         alpha=(menuTop-y)/b;
00293                     if (y>menuBot && y<menuTop)
00294                     {
00295                         rTextField::SetDefaultColor( tColor(1,1,1,1) );
00296                         rTextField::SetBlendColor( tColor(1,1,1,1) );
00297                         items[i]->Render(center,y,alpha,false);
00298                     }
00299                 }
00300 
00301             rTextField::SetDefaultColor( tColor(1,1,1,1) );
00302             rTextField::SetBlendColor( tColor(1,1,1,1) );
00303 
00304             Color(.6,.6,1,1);
00305             ::DisplayText(0,menuTop+text_height*titlefac
00306                           ,text_height*titlefac,
00307                           title,sr_fontMenuTitle,0);
00308 
00309             glDisable(GL_TEXTURE_2D);
00310             //glDisable(GL_TEXTURE);
00311             Color(1,.2,.2,.5);
00312             if (YPos(0)<menuBot+smallborder && (int(tSysTimeFloat()))%2)
00313                 arrow(.9,menuBot+.1,-1,.05);
00314             if (YPos(menuentries-1)>menuTop && (int(tSysTimeFloat())+1)%2)
00315                 arrow(.9,menuTop,1,.05);
00316 
00317             if (tSysTimeFloat()-lastkey>timeout){
00318                 disphelp=true;
00319                 if (sr_alphaBlend)
00320                     glColor4f(1,.8,.8,tSysTimeFloat()-lastkey-timeout);
00321                 else
00322                     Color(tSysTimeFloat()-lastkey-timeout,
00323                           .8*(tSysTimeFloat()-lastkey-timeout),
00324                           .8*(tSysTimeFloat()-lastkey-timeout));
00325 
00326                 rTextField c(-.95f,menuBot-.04f,rCHEIGHT_NORMAL, sr_fontMenu);
00327                 c.SetWidth(1.9f-items[selected]->SpaceRight());
00328                 c.EnableLineWrap();
00329                 c << items[selected]->Help();
00330             }
00331             else disphelp=false;
00332         }
00333         else
00334 #endif
00335             if ( !sr_glOut )
00336             {
00337                 tDelay( 10000 );
00338             }
00339 
00340 #ifndef DEDICATED
00341         rSysDep::SwapGL();
00342         rSysDep::ClearGL();
00343 #endif
00344     }
00345 
00346     repeat = false;
00347 
00348     uCallbackMenuLeave::MenuLeave();
00349     su_inMenu = false;
00350 }
00351 
00352 void uMenu::HandleEvent( SDL_Event event )
00353 {
00354 #ifndef DEDICATED
00355     if (!items[selected]->Event(event))
00356     {
00357         // int newSelected = -1;
00358         switch (event.type){
00359         case SDL_KEYDOWN:
00360         {
00361             if (!disphelp)
00362                 lastkey=tSysTimeFloat();
00363             switch (event.key.keysym.sym){
00364 
00365             case(SDLK_ESCAPE):
00366                             repeat = false;
00367                 lastkey=tSysTimeFloat();
00368                 Exit();
00369                 break;
00370 
00371                 case(SDLK_UP):
00372                                 lastkey=tSysTimeFloat();
00373                     selected = GetNextSelectable(selected);
00374                     break;
00375                 case(SDLK_DOWN):
00376                                 lastkey=tSysTimeFloat();
00377                     selected = GetPrevSelectable(selected);
00378                     break;
00379 
00380             case(SDLK_LEFT):
00381                             items[selected]->LeftRight(-1);
00382                 break;
00383             case(SDLK_RIGHT):
00384                             items[selected]->LeftRight(1);
00385                 break;
00386 
00387             case(SDLK_SPACE):
00388                         case(SDLK_KP_ENTER):
00389                             case(SDLK_RETURN):
00390                                     repeat = false;
00391                 try
00392         {
00393                     su_inMenu = false;
00394                     items[selected]->Enter();
00395                 }
00396                 catch (tException const & e)
00397                 {
00398                     uMenu::SetIdle(NULL);
00399 
00400                     // inform user of generic errors
00401                     tConsole::Message( e.GetName(), e.GetDescription(), 20 );
00402                 }
00403 #ifdef _MSC_VER
00404 #pragma warning ( disable : 4286 )
00405                 // GRR. Visual C++ dones not handle generic exceptions with the above general statement.
00406                 // A specialized version is needed. The best part: it warns about the code below being redundant.
00407                 catch ( tGenericException const & e )
00408                 {
00409                     try
00410                     {
00411                         tConsole::Message( e.GetName(), e.GetDescription(), 20 );
00412                     }
00413                     catch (...)
00414                     {
00415                     }
00416                 }
00417 #endif
00418 
00419                 su_inMenu = true;
00420 
00421                 repeat = false;
00422                 lastkey=tSysTimeFloat();
00423                 break;
00424 
00425             default:
00426                 // let the input subsystem handle events for later processing
00427                 su_HandleEvent( event, true );
00428                 break;
00429             }
00430         }
00431         break;
00432         default:
00433             // let the input subsystem handle events for later processing
00434             su_HandleEvent( event, true );
00435             break;
00436         }
00437     }
00438 
00439     su_inMenu = true;
00440 #endif
00441 }
00442 
00443 // select the menu item below "start"
00444 int uMenu::GetPrevSelectable(int start)
00445 {
00446     int prev = start-1;
00447     while (prev!=start)
00448     {
00449         if (prev<0)
00450         {
00451             if (wrap)
00452                 prev = items.Len()-1;
00453             else
00454                 break;
00455         }
00456         if (items[prev]->IsSelectable())
00457         {
00458             return prev;
00459         }
00460         prev--;
00461     }
00462     return start;
00463 }
00464 
00465 // select the menu item above "start"
00466 int uMenu::GetNextSelectable(int start)
00467 {
00468     int next = start+1;
00469     while (next!=start)
00470     {
00471         if (next>=items.Len())
00472         {
00473             if (this->wrap)
00474                 next = 0;
00475             else
00476                 break;
00477         }
00478         if (items[next]->IsSelectable())
00479         {
00480             return next;
00481         }
00482         next++;
00483     }
00484     return start;
00485 }
00486 
00487 
00488 // paints a nice background
00489 void uMenu::GenericBackground(){
00490 #ifndef DEDICATED
00491     if (idle)
00492     {
00493         try
00494         {
00495             // throw tGenericException("test"); // (test exception throw to see if error handling works right)
00496             (*idle)();
00497         }
00498         catch ( ... )
00499         {
00500             // the idle background function is broken. Disable it and rethrow.
00501             idle = 0;
00502             throw;
00503         }
00504     }
00505     else if (sr_glOut){
00506         uCallbackMenuBackground::MenuBackground();
00507     }
00508     else
00509         tDelay(100000);
00510 #endif
00511     sr_ResetRenderState(true);
00512 }
00513 
00514 // marks the menu for exit
00515 void uMenu::OnExit(){
00516     exitFlag=1;
00517 }
00518 
00520 void uMenu::OnRender()
00521 {
00522 }
00523 
00524 // *****************************************************
00525 
00526 // *******************************************************************************************
00527 // *
00528 // *   SetColor
00529 // *
00530 // *******************************************************************************************
00535 // *******************************************************************************************
00536 
00537 void uMenuItem::SetColor( bool selected, REAL alpha )
00538 {
00539     //   rTextField::SetBlendColor( tColor(.8+.2*sin(time),.3-.1*sin(time),.3-.1*sin(time),alpha) );
00540     rTextField::SetDefaultColor( tColor(1,1,1,alpha) );
00541 
00542     if (selected)
00543     {
00544         REAL time=tSysTimeFloat()*10;
00545         REAL intensity = 1+.3*sin(time);
00546         rTextField::SetDefaultColor( tColor(.8,.3,.3,alpha) );
00547         rTextField::SetBlendColor( tColor(intensity,intensity,intensity,alpha) );
00548     }
00549 }
00550 
00551 void uMenuItem::DisplayText(REAL x,REAL y,const char *text,
00552                             bool selected,REAL alpha,
00553                             int center,int c,int cp, rTextField::ColorMode colorMode, float maxWidth ){
00554 #ifndef DEDICATED
00555     if (sr_glOut){
00556         SetColor( selected, alpha );
00557 
00558         REAL th = text_height;
00559 
00560         REAL availw = 1.9f;
00561         if (center < 0) availw = (.9f-x);
00562         if (center > 0) availw = (x + .9f);
00563         if (availw > maxWidth) availw = maxWidth;
00564 
00565         float usedwidth=rTextField::GetTextLength(tString(text), th, colorMode == rTextField::COLOR_USE);
00566         if (usedwidth > availw)
00567         {
00568             th *= availw/(usedwidth);
00569         }
00570 
00571         ::DisplayText(x,y,th,text,sr_fontMenu,center,c,cp, colorMode);
00572     }
00573 #endif
00574 }
00575 
00576 void uMenuItem::DisplayTextSpecial(REAL x,REAL y,const char *text,
00577                                    bool selected,
00578                                    REAL alpha,int center){
00579     /*
00580      if(selected)
00581        glColor3f(.9,.3,.3);
00582      else
00583        glColor3f(.7,.7,1);
00584 
00585      ::DisplayText(x,y,text_width,text_height,text,center);
00586      */
00587 
00588     DisplayText(x,y,text,selected,alpha,center);
00589 }
00590 
00591 // *************************************
00592 
00593 const tOutput& uMenuItemExit::ExitText()
00594 {
00595     static tOutput exitText("$menuitem_exit_text");
00596 
00597     return exitText;
00598 }
00599 
00600 const tOutput& uMenuItemExit::ExitHelp()
00601 {
00602     static tOutput exitHelp("$menuitem_exit_help");
00603 
00604     return exitHelp;
00605 }
00606 
00607 // *************************************
00608 
00609 void uMenuItemToggle::NewChoice(uSelectItem<bool> *){}
00610 void uMenuItemToggle::NewChoice(const char *,bool ){}
00611 
00612 #ifdef SLOPPYLOCALE
00613 uMenuItemToggle::uMenuItemToggle(uMenu *m,
00614                                  const char *tit,
00615                                  const char *help,
00616                                  bool &targ)
00617         :uMenuItemSelection<bool>(m,tit,help,targ){
00618     uMenuItemSelection<bool>::NewChoice("$menuitem_toggle_on","",true);
00619     uMenuItemSelection<bool>::NewChoice("$menuitem_toggle_off","",false);
00620 }
00621 #endif
00622 
00623 uMenuItemToggle::uMenuItemToggle(uMenu *m,
00624                                  const tOutput& tit,
00625                                  const tOutput& help,
00626                                  bool &targ)
00627         :uMenuItemSelection<bool>(m,tit,help,targ){
00628     uMenuItemSelection<bool>::NewChoice("$menuitem_toggle_on","",true);
00629     uMenuItemSelection<bool>::NewChoice("$menuitem_toggle_off","",false);
00630 }
00631 
00632 uMenuItemToggle::~uMenuItemToggle(){}
00633 
00634 void uMenuItemToggle::LeftRight(int){
00635     select=1-select;
00636     *target=!(*target);
00637 }
00638 
00639 void uMenuItemToggle::Enter(){
00640     LeftRight(0);
00641 }
00642 // *****************************************
00643 //               Integer Choose
00644 // *****************************************
00645 
00646 #ifdef SLOPPYLOCALE
00647 uMenuItemInt::uMenuItemInt
00648 (uMenu *m,const char *tit,const char *help,int &targ,
00649  int mi,int ma,int step)
00650         :uMenuItem(m,help),title(tit),target(targ),Min(mi),Max(ma),
00651         Step(step){
00652     if (target<Min) target=Min;
00653     if (target>Max) target=Max;
00654 }
00655 #endif
00656 
00657 uMenuItemInt::uMenuItemInt
00658 (uMenu *m,const tOutput &tit,const tOutput &help,int &targ,
00659  int mi,int ma,int step)
00660         :uMenuItem(m,help),title(tit),target(targ),Min(mi),Max(ma),
00661         Step(step){
00662     if (target<Min) target=Min;
00663     if (target>Max) target=Max;
00664 }
00665 
00666 
00667 void uMenuItemInt::LeftRight(int dir){
00668     target+=dir*Step;
00669     if (target<Min) target=Min;
00670     if (target>Max) target=Max;
00671 }
00672 
00673 void uMenuItemInt::Render(REAL x,REAL y,REAL alpha,
00674                           bool selected){
00675     DisplayText(x-.02,y,title,selected,alpha,1);
00676 
00677     tString s;
00678     s << target;
00679     DisplayText(x+.02,y,s,selected,alpha,-1);
00680 }
00681 
00682 
00683 // *****************************************************
00684 
00685 uMenuItemString::uMenuItemString(uMenu *M,
00686                                  const tOutput& de,
00687                                  const tOutput& help,
00688                                  tString &c,
00689                                  int maxLength )
00690         :uMenuItem(M,help),description(de),content(&c),cursorPos(0), maxLength_( maxLength ){
00691     // int len=content->Len();
00692     // if (len==0 || (*content)(len-1)!=0)
00693     //    (*content)[len]=0;
00694     cursorPos=content->Len()-1;
00695     colorMode_ = rTextField::COLOR_SHOW;
00696 }
00697 
00698 void uMenuItemString::Render(REAL x,REAL y,
00699                              REAL alpha,bool selected){
00700 #ifndef DEDICATED
00701     static int counter=0;
00702     counter++;
00703 
00704     int cmode=0;
00705     if (selected){
00706         cmode=1;
00707         if (counter & 32) cmode=2;
00708     }
00709 
00710     // unslected items with COLOR_SHOW should be rendered with COLOR_USE
00711     rTextField::ColorMode colorMode = colorMode_;
00712     if ( colorMode == rTextField::COLOR_SHOW && !selected )
00713         colorMode = rTextField::COLOR_USE;
00714 
00715     DisplayText(x-.02,y,description,selected,alpha,1);
00716     DisplayText(x+.02,y,*content,selected,alpha,-1,cmode,cursorPos,colorMode);
00717 #endif
00718 }
00719 
00720 bool uMenuItemString::Event(SDL_Event &e){
00721 #ifndef DEDICATED
00722     if (e.type!=SDL_KEYDOWN)
00723         return false;
00724     bool ret=true;
00725     SDL_keysym &c=e.key.keysym;
00726     SDLMod mod = c.mod;
00727     bool moveWordLeft, moveWordRight, deleteWordLeft, deleteWordRight, moveBeginning, moveEnd, killForwards;
00728     moveWordLeft = moveWordRight = deleteWordLeft = deleteWordRight = moveBeginning = moveEnd = killForwards = false;
00729 
00730 #if defined (MACOSX)
00731     // For moving over/deleting words
00732     if (mod & KMOD_ALT) {
00733         if (c.sym == SDLK_LEFT) {
00734             moveWordLeft = true;
00735         }
00736         else if (c.sym == SDLK_RIGHT) {
00737             moveWordRight = true;
00738         }
00739         else if (c.sym == SDLK_DELETE) {
00740             deleteWordRight = true;
00741         }
00742         else if (c.sym == SDLK_BACKSPACE) {
00743             deleteWordLeft = true;
00744         }
00745     }
00746     // For moving to extremes of the line
00747     else if (mod & KMOD_META) {
00748         if (c.sym == SDLK_LEFT) {
00749             moveBeginning = true;
00750         }
00751         else if (c.sym == SDLK_RIGHT) {
00752             moveEnd = true;
00753         }
00754     }
00755     // Linux and Windows
00756 #else
00757     // Word operations
00758     if (mod & KMOD_CTRL) {
00759         if (c.sym == SDLK_LEFT) {
00760             moveWordLeft = true;
00761         }
00762         else if (c.sym == SDLK_RIGHT) {
00763             moveWordRight = true;
00764         }
00765         else if (c.sym == SDLK_DELETE) {
00766             deleteWordRight = true;
00767         }
00768         else if (c.sym == SDLK_BACKSPACE) {
00769             deleteWordLeft = true;
00770         }
00771     }
00772     else if (c.sym == SDLK_HOME) {
00773         moveBeginning = true;
00774     }
00775     else if (c.sym == SDLK_END) {
00776         moveEnd = true;
00777     }
00778 #endif
00779     // "bash" keys
00780     if (mod & KMOD_CTRL) {
00781         if (c.sym == SDLK_a) {
00782             moveBeginning = true;
00783         }
00784         else if (c.sym == SDLK_e) {
00785             moveEnd = true;
00786         }
00787         else if (c.sym == SDLK_k) {
00788             killForwards = true;
00789         }
00790     }
00791     // moveWordLeft = moveWordRight = deleteWordLeft = deleteWordRight = moveBeginning = moveEnd = killForwards
00792 
00793     if (moveWordLeft) {
00794         cursorPos += content->PosWordLeft(cursorPos);
00795     }
00796     else if (moveWordRight) {
00797         cursorPos += content->PosWordRight(cursorPos);
00798     }
00799     else if (deleteWordLeft) {
00800         cursorPos += content->RemoveWordLeft(cursorPos);
00801     }
00802     else if (deleteWordRight) {
00803         content->RemoveWordRight(cursorPos);
00804     }
00805     else if (moveBeginning) {
00806         cursorPos = 0;
00807     }
00808     else if (moveEnd) {
00809         cursorPos = content->Len()-1;
00810     }
00811     else if (killForwards) {
00812         content->RemoveSubStr(cursorPos,content->Len()-1-cursorPos);
00813     }
00814     else if (c.sym == SDLK_LEFT) {
00815         if (cursorPos > 0) {
00816             cursorPos--;
00817         }
00818     }
00819     else if (c.sym == SDLK_RIGHT) {
00820         if (cursorPos < content->Len()-1) {
00821             cursorPos++;
00822         }
00823     }
00824     else if (c.sym == SDLK_DELETE) {
00825         if (cursorPos < content->Len()-1) {
00826             content->RemoveSubStr(cursorPos,1);
00827         }
00828     }
00829     else if (c.sym == SDLK_BACKSPACE) {
00830         if (cursorPos > 0) {
00831             content->RemoveSubStr(cursorPos,-1);
00832             cursorPos--;
00833         }
00834     }
00835     else if (c.sym == SDLK_KP_ENTER || c.sym == SDLK_RETURN) {
00836         ret = false;
00837         //        c.sym = SDLK_DOWN;
00838     }
00839     else {
00840         if (32 <= c.unicode  && c.unicode < 256)
00841         {
00842             ret=true;
00843 
00844             // insert character if there is room
00845             if (content->Len() < maxLength_)
00846             {
00847                 tString beg = content->SubStr(0,cursorPos);
00848                 tString end = content->SubStr(cursorPos);
00849                 *content = beg;
00850                 *content += tString::CHAR(c.unicode);
00851                 *content += end;
00852                 cursorPos++;
00853             }
00854         }
00855         else {
00856             ret=false;
00857         }
00858     }
00859 
00860     if (cursorPos<0)    cursorPos=0;
00861     if (cursorPos > content->Len()-1) cursorPos=content->Len()-1;
00862 
00863     return ret;
00864 #else
00865     return false;
00866 #endif
00867 }
00868 
00870 uAutoCompleter::uAutoCompleter(std::deque<tString> &words) :
00871         m_PossibleWords(words),
00872         m_LastCompletion(-1),
00873         m_ignorecase(true)
00874 {}
00875 
00879 int uAutoCompleter::FindLengthOfLastWord(tString &string, unsigned pos) {
00880     int charright = ' ', charleft  = ' ';
00881     if(string.size() >= 1) {
00882         if (pos == string.size()) {
00883             charleft=string.at(pos-1);
00884         }
00885         else if (pos < string.size()) {
00886             charright=string.at(pos);
00887             if(pos>1) {
00888                 charleft=string.at(pos-1);
00889             }
00890         }
00891     }
00892     if(charright != ' ' && charleft != ' ')
00893         return -1; //no completion possible
00894     if(charleft == ' ')
00895         return 0;
00896     else
00897         return pos - string.find_last_of(' ', pos - 1) - 1;
00898 }
00899 
00902 void uAutoCompleter::FindPossibleWords(tString word, std::deque<tString> &results) {
00903     if(m_ignorecase)
00904         word = Simplify(word);
00905     for(std::deque<tString>::iterator i=m_PossibleWords.begin(); i!=m_PossibleWords.end(); ++i) {
00906         size_t pos;
00907         if((pos = (m_ignorecase ? Simplify(*i) : *i).find(word)) != tString::npos) {
00908             if(isalpha((*i)[pos]) && pos != 0 && isalpha((*i)[pos-1]))
00909                 continue; //both the char we're at and the one before is alphanumeric; we're in the middle of a word
00910             results.push_back(*i);
00911         }
00912     }
00913 }
00914 
00918 tString uAutoCompleter::FindClosestMatch(tString &word, std::deque<tString> &results) {
00919     tString ret(m_ignorecase?Simplify(word):word);
00920     unsigned int len = ret.size();
00921     while(true) { // complete right side
00922         std::deque<tString>::iterator i;
00923         i = results.begin();
00924         bool found=true;
00925         tString::size_type pos=(m_ignorecase?Simplify(*i):*i).find(ret);
00926         if(pos!=tString::npos && pos+len < i->size()) {
00927             found=false;
00928             ret+=(m_ignorecase ? tolower(i->at(pos+len)) : i->at(pos+len));
00929             ++i;
00930             for(; i!=results.end(); ++i) {
00931                 if((m_ignorecase?Simplify(*i):*i).find(ret) == tString::npos) {
00932                     found=true;
00933                     ret.erase(ret.length()-1);
00934                     break;
00935                 }
00936             }
00937         }
00938         if (found) //we found a mismatch
00939             break;
00940         else
00941             len++;
00942     }
00943     while(true) { // something similar, but now for the left side of the word
00944         std::deque<tString>::iterator i;
00945         i = results.begin();
00946         bool found=true;
00947         tString::size_type pos=(m_ignorecase?Simplify(*i):*i).find(ret);
00948         if(pos!=tString::npos && pos > 0) {
00949             found=false;
00950             ret.insert(ret.begin(),(m_ignorecase ? tolower(i->at(pos-1)) : i->at(pos-1)));
00951             ++i;
00952             for(; i!=results.end(); ++i) {
00953                 if((m_ignorecase?Simplify(*i):*i).find(ret) == tString::npos) {
00954                     found=true;
00955                     ret.erase(0,1);
00956                     break;
00957                 }
00958             }
00959         }
00960         if (found)  //we found a mismatch
00961             break;
00962         else
00963             len++;
00964     }
00965     return ret;
00966 }
00967 
00970 void uAutoCompleter::ShowPossibilities(std::deque<tString> &results, tString &word) {
00971     if(results.size() > 10) {
00972         con << tOutput("$tab_completion_toomanyresults");
00973     }
00974     else {
00975         con << tOutput("$tab_completion_results");
00976         tString::size_type len=word.length();
00977         for(std::deque<tString>::iterator i=results.begin(); i!=results.end(); ++i) {
00978             tString::size_type pos=(m_ignorecase?Simplify(*i):*i).find(word);
00979             con << i->SubStr(0,pos)
00980             << "0xff8888"
00981             << i->SubStr(pos, len)
00982             << "0xffffff"
00983             << i->SubStr(pos+len)
00984             << "\n";
00985         }
00986     }
00987 }
00988 
00994 int uAutoCompleter::DoCompletion(tString &string, int pos, int len, tString &match) {
00995     string.erase(pos-len, len);
00996     string.insert(pos-len, match);
00997     return pos - len + match.size();
00998 }
00999 
01005 int uAutoCompleter::DoFullCompletion(tString &string, int pos, int len, tString &match) {
01006     tString actualString = match + " ";
01007     return DoCompletion(string, pos, len, actualString);
01008 }
01009 
01014 int uAutoCompleter::TryCompletion(tString &string, unsigned pos, unsigned len) {
01015     tString word(string.SubStr(pos-len, len));
01016     std::deque<tString> results;
01017     FindPossibleWords(word, results);
01018     if(results.size() > 1){
01019         tString match(FindClosestMatch(word, results));
01020         if(match == word) { //no completion took place
01021             ShowPossibilities(results, word);
01022             return pos;
01023         }
01024         else { //do the completion
01025             return DoCompletion(string, pos, len, match);
01026         }
01027     }
01028     else if(!results.empty()){
01029         return DoFullCompletion(string, pos, len, results.front());
01030     }
01031     return -1;
01032 }
01033 
01037 int uAutoCompleter::Complete(tString &string, unsigned pos) {
01038     if(m_LastCompletion != -1 && (unsigned)m_LastCompletion < pos) {
01039         int res = TryCompletion(string, pos, pos - m_LastCompletion);
01040         if(res != -1) return res;
01041     }
01042     int len = FindLengthOfLastWord(string, pos);
01043     if(len == -1) return pos; //in the middle of a word...
01044     int res = TryCompletion(string, pos, len);
01045     if(res != -1) {
01046         m_LastCompletion = pos - len;
01047         return res;
01048     }
01049     return pos;
01050 }
01051 
01053 void uAutoCompleter::SetIgnorecase(bool ignorecase) {
01054     m_ignorecase = ignorecase;
01055 }
01056 
01059 tString uAutoCompleter::Simplify(tString const &str) {
01060     return str.ToLower();
01061 }
01062 
01071 uMenuItemStringWithHistory::uMenuItemStringWithHistory(uMenu *M,const tOutput& desc, const tOutput& help,tString &c, int maxLength, history_t &history, int limit, uAutoCompleter *completer ):
01072         uMenuItemString(M, desc,help,c, maxLength ),
01073         m_History(history),
01074         m_HistoryPos(0),
01075         m_HistoryLimit(limit),
01076         m_Completer(completer),
01077 m_Searchmode(false) {
01078     m_History.push_front(tString());
01079 }
01080 
01082 uMenuItemStringWithHistory::~uMenuItemStringWithHistory() {
01083     if(content->Len() > 1){
01084         for(history_t::iterator i=m_History.begin(); i!=m_History.end(); ++i) {
01085             if(*i == *content) {
01086                 m_History.erase(i);
01087                 break;
01088             }
01089         }
01090         m_History.front() = *content;
01091     } else {
01092         m_History.pop_front();
01093     }
01094     if(m_History.size() > m_HistoryLimit)
01095         m_History.pop_back();
01096 }
01097 
01100 bool uMenuItemStringWithHistory::Event(SDL_Event &e){
01101 #ifndef DEDICATED
01102     if (e.type==SDL_KEYDOWN &&
01103             (e.key.keysym.sym==SDLK_UP && !m_Searchmode )){
01104         if (m_History.size() - 1 > m_HistoryPos)
01105         {
01106             if(m_HistoryPos == 0)  //the new entry... save it before overwriting it
01107                 m_History.front() = *content;
01108             m_HistoryPos++;
01109             *content = m_History[m_HistoryPos];
01110             cursorPos = content->Len() - 1;
01111         }
01112 
01113         return true;
01114     }
01115     else if (e.type==SDL_KEYDOWN &&
01116              (e.key.keysym.sym==SDLK_DOWN && !m_Searchmode)){
01117         if (m_HistoryPos > 0)
01118         {
01119             m_HistoryPos--;
01120             *content = m_History[m_HistoryPos];
01121             cursorPos = content->Len() - 1;
01122         }
01123 
01124         return true;
01125     }
01126     else if (e.type==SDL_KEYDOWN && e.key.keysym.mod & KMOD_CTRL && e.key.keysym.sym == SDLK_r ) {
01127         m_Searchmode = true;
01128         if(m_HistoryPos == 0 && !content->empty()) {  //the new entry... save it before overwriting it
01129             m_History.front() = *content;
01130             m_History.push_front(tString());
01131         }
01132         content->erase();
01133         m_HistoryPos++;
01134         cursorPos=0;
01135         m_SearchFailing=false;
01136         return true;
01137     }
01138     else if (e.type==SDL_KEYDOWN &&
01139              (e.key.keysym.sym==SDLK_TAB && !m_Searchmode)){
01140         if(m_Completer != 0) {
01141             cursorPos = m_Completer->Complete(*content, cursorPos);
01142         }
01143 
01144         return true;
01145     }
01146     else if (e.type==SDL_KEYDOWN && m_Searchmode) {
01147         if(e.key.keysym.sym==SDLK_LEFT || e.key.keysym.sym==SDLK_RIGHT) {
01148             *content = m_History[m_HistoryPos];
01149             m_Searchmode = false;
01150             cursorPos=0;
01151             return true;
01152         }
01153         bool ret = uMenuItemString::Event(e);
01154         tString searchstring = content->ToLower();
01155         unsigned int pos = 0;
01156         for(history_t::iterator iter = m_History.begin(); iter != m_History.end(); ++iter, ++pos) {
01157             if (iter->ToLower().find(searchstring) != tString::npos) {
01158                 m_HistoryPos = pos;
01159                 m_SearchFailing=false;
01160                 return ret;
01161             }
01162         }
01163         m_SearchFailing=true;
01164         return ret;
01165     }
01166 
01167     else
01168         return uMenuItemString::Event(e);
01169 #endif
01170     return false;
01171 }
01172 
01173 void uMenuItemStringWithHistory::Render(REAL x,REAL y,REAL alpha,bool selected) {
01174     if(m_Searchmode) {
01175         DisplayText(-.88, y-.1, ((m_SearchFailing ? "Failing reverse search: " : "Reverse search: ") + m_History[m_HistoryPos]).c_str(), selected, alpha, -1);
01176     }
01177     uMenuItemString::Render(x, y, alpha, selected);
01178 }
01179 
01181 uMenuItemStringWithHistory::history_t::history_t(char const *filename) : std::deque<tString>(), m_filename(filename) {
01182     tTextFileRecorder the_file ( tDirectories::Var(), m_filename );
01183     while(!the_file.EndOfFile()) {
01184         push_front(the_file.GetLine());
01185     }
01186 }
01187 
01188 uMenuItemStringWithHistory::history_t::~history_t() {
01189     std::ofstream the_file;
01190     if(tDirectories::Var().Open( the_file, m_filename)) {
01191         for(reverse_iterator i = rbegin(); i != rend(); ++i) {
01192             the_file << *i << "\n";
01193         }
01194     }
01195 }
01196 
01197 
01198 // *****************************************************
01199 //  Submenu
01200 // *****************************************************
01201 
01202 
01203 uMenuItemSubmenu::uMenuItemSubmenu(uMenu *M,
01204                                    uMenu *s,
01205                                    const tOutput& help)
01206         :uMenuItem(M,help),submenu(s){}
01207 
01208 
01209 void uMenuItemSubmenu::Render(REAL x,REAL y,REAL alpha,bool selected){
01210     DisplayTextSpecial(x,y,submenu->title,selected,alpha,0);
01211 }
01212 
01213 void uMenuItemSubmenu::Enter(){
01214     submenu->Enter();
01215 }
01216 
01217 // *****************************************************
01218 //  action
01219 // *****************************************************
01220 
01221 
01222 uMenuItemAction::uMenuItemAction(uMenu *M,
01223                                  const tOutput& n, const tOutput& help )
01224         :uMenuItem(M,help),name_(n){}
01225 
01226 
01227 void uMenuItemAction::Render(REAL x,REAL y,REAL alpha,bool selected){
01228     DisplayTextSpecial(x,y,name_,selected,alpha,0);
01229 }
01230 
01231 
01232 void uMenuItemAction::Enter()
01233 {
01234     tASSERT( 0 )
01235 }
01236 
01237 
01238 // *****************************************************
01239 //  function
01240 // *****************************************************
01241 
01242 
01243 uMenuItemFunction::uMenuItemFunction(uMenu *M,
01244                                      const tOutput& n, const tOutput& help,
01245                                      FUNCPTR f)
01246         :uMenuItemAction(M,n,help),func(f){}
01247 
01248 void uMenuItemFunction::Enter(){
01249     (*func)();
01250 }
01251 
01252 
01253 
01254 uMenuItemFunctionInt::uMenuItemFunctionInt(uMenu *M,
01255         const tOutput& n,
01256         const tOutput& help,
01257         INTFUNCPTR f,int a)
01258         :uMenuItemAction(M,n,help),func(f),arg(a){}
01259 
01260 
01261 void uMenuItemFunctionInt::Enter(){
01262     (*func)(arg);
01263 }
01264 
01265 // *****************************************************
01266 //  File Selection (added by k)
01267 // *****************************************************
01268 
01269 void uMenuItemFileSelection::NewChoice( uSelectItem<bool> * ) {}
01270 void uMenuItemFileSelection::NewChoice( char *, bool ) {}
01271 
01272 void uMenuItemFileSelection::Reload()
01273 {
01274     Clear();
01275     if ( defaultFileName_.Len() > 1 && defaultFilePath_.Len() > 1 )
01276         AddFile( defaultFileName_, defaultFilePath_, formatName_ );
01277     LoadDirectory( dir_, fileSpec_, formatName_ );
01278 }
01279 
01280 void uMenuItemFileSelection::LoadDirectory( const char *dir, const char *fileSpec,
01281         bool formatName /*= true*/ )
01282 {
01283     tArray <tString> files;
01284     tString filePath ( dir );
01285     tDirectories::GetFiles( tString( dir ), tString( fileSpec ), files, getFilesFlag_ );
01286     for ( int i = 0; i < files.Len(); i++ )
01287     {
01288         AddFile( files( i ), filePath + files( i ), formatName );
01289     }
01290 }
01291 
01292 void uMenuItemFileSelection::AddFile( const char *fileName, const char *filePath,
01293                                       bool formatName /*= true*/ )
01294 {
01295     tString menuName ( fileName );
01296     if ( formatName )
01297         tDirectories::FileNameToMenuName( fileName, menuName );
01298     uMenuItemSelection<tString>::NewChoice( menuName, "", tString( filePath ) );
01299 }
01300 
01301 // *****************************************************
01302 // Menu Enter/Leave-Callback
01303 // *****************************************************
01304 
01305 static tCallback *enter_anchor=NULL,*leave_anchor=NULL, *background_anchor=NULL;
01306 
01307 uCallbackMenuEnter::uCallbackMenuEnter(AA_VOIDFUNC *f)
01308         :tCallback(enter_anchor,f){}
01309 
01310 void uCallbackMenuEnter::MenuEnter(){
01311     Exec(enter_anchor);
01312 }
01313 
01314 uCallbackMenuLeave::uCallbackMenuLeave(AA_VOIDFUNC *f)
01315         :tCallback(leave_anchor,f){}
01316 
01317 void uCallbackMenuLeave::MenuLeave(){
01318     Exec(leave_anchor);
01319 }
01320 
01321 uCallbackMenuBackground::uCallbackMenuBackground(AA_VOIDFUNC *f)
01322         :tCallback(background_anchor,f){}
01323 
01324 void uCallbackMenuBackground::MenuBackground(){
01325     Exec(background_anchor);
01326 }
01327 
01328 // poll input, return true if ESC was pressed
01329 bool uMenu::IdleInput()
01330 {
01331 #ifndef DEDICATED
01332     SDL_Event event;
01333     uInputProcessGuard inputProcessGuard;
01334     while (su_GetSDLInput(event))
01335     {   
01336         switch (event.type)
01337         {
01338         case SDL_KEYDOWN:
01339             switch (event.key.keysym.sym)
01340             {
01341             case(SDLK_ESCAPE):
01342                 repeat = false;
01343                 lastkey=tSysTimeFloat();
01344                 return true;
01345                 break;
01346             default:
01347                 break;
01348             }   
01349         default:
01350             break;
01351         }
01352     }   
01353 #endif
01354 
01355     return false;
01356 }
01357 
01358 // return value: false only if the user pressed ESC
01359 bool uMenu::Message(const tOutput& message, const tOutput& interpretation, REAL to){
01360     bool ret = true;
01361 #ifdef DEDICATED
01362     con << message << ":\n";
01363     con << interpretation << '\n';
01364 #else
01365     // reload textures (just in case)
01366     rITexture::UnloadAll();
01367 
01368     tAdvanceFrame();
01369 
01370     bool textOutBack = sr_textOut;
01371     sr_textOut = false;
01372 
01373     FUNCPTR idle_back = idle;
01374     uMenu::SetIdle(NULL);
01375 
01376     rTextField::SetDefaultColor( tColor(1,1,1,1) );
01377     rTextField::SetBlendColor( tColor(1,1,1,1) );
01378 
01379     rSysDep::ClearGL();
01380     rSysDep::SwapGL();
01381     //    if (sr_glOut)
01382     //    {
01383     //        rFont::s_defaultFont.Select();
01384     //        rFont::s_defaultFontSmall.Select();
01385     //    }
01386     rSysDep::ClearGL();
01387     rSysDep::SwapGL();
01388 
01389     REAL timeout = tSysTimeFloat() + to;
01390     SDL_Event tEvent;
01391 
01392     // catch some keyboard input
01393     {
01394         uInputProcessGuard inputProcessGuard;
01395         while (su_GetSDLInput(tEvent));
01396     }
01397 
01398     {
01399         uInputProcessGuard inputProcessGuard;
01400 
01401         unsigned offset = 0; //amount of scrolling taking place
01402         //convert to an array for scrolling
01403         tString interpretationString;
01404         interpretationString << interpretation << "\n";
01405         std::vector<tString> lines;
01406         int lastNewline = 0;
01407         for (int i = 0; i < interpretationString.Len() - 1; ++i) {
01408             if (interpretationString[i] == '\n' && i != 0) {
01409                 lines.push_back(interpretationString.SubStr(lastNewline, i - lastNewline));
01410                 lastNewline = i + 1;
01411             }
01412         }
01413         while (  !quickexit &&
01414                  (to < 0 || tSysTimeFloat() < timeout)){
01415             //while(  !quickexit && ( !su_GetSDLInput(tEvent) || tEvent.type!=SDL_KEYDOWN) &&
01416             //        (to < 0 || tSysTimeFloat() < timeout)){
01417             if ( su_GetSDLInput(tEvent) && tEvent.type==SDL_KEYDOWN) {
01418                 switch (tEvent.key.keysym.sym) {
01419                 case SDLK_UP:
01420                     if (offset > 0)
01421                         offset -= 1;
01422                     continue;
01423                 case SDLK_DOWN:
01424                     offset += 1;
01425                     continue;
01426                 case SDLK_ESCAPE:
01427                     ret = false;
01428                     break;
01429                 default:
01430                     break;
01431                 }
01432                 break;
01433             }
01434             if ( sr_glOut )
01435             {
01436                 sr_ResetRenderState(true);
01437                 rViewport::s_viewportFullscreen.Select();
01438 
01439                 rSysDep::ClearGL();
01440 
01441                 GenericBackground();
01442 
01443                 REAL w=16*3/640.0;
01444                 REAL h=32*3/480.0;
01445 
01446 
01447                 //REAL middle=-.6;
01448 
01449                 tString m(message);
01450                 int len = m.Len();
01451                 if (w * len > 1.8)
01452                 {
01453                     h = h * 1.8 / (w * len);
01454                     w = 1.8 / len;
01455                 }
01456 
01457                 Color(1,1,1);
01458                 DisplayText(0,.8,w,message,sr_fontError);
01459 
01460                 w = 16/640.0;
01461                 h = 32/480.0;
01462 
01463                 if (offset >= lines.size()) offset = lines.size() - 1;
01464                 {
01465                     rTextField c(-.8,.6, h, sr_fontError);
01466                     c.EnableLineWrap();
01467 
01468                     for (unsigned i = offset; i < lines.size(); ++i)
01469                         c << lines[i] << "\n";
01470                 }
01471             }
01472             rSysDep::SwapGL();
01473             tAdvanceFrame();
01474         }
01475     }
01476 
01477     // catch some keyboard input
01478     {
01479         uInputProcessGuard inputProcessGuard;
01480         while (su_GetSDLInput(tEvent));
01481     }
01482 
01483     uMenu::SetIdle(idle_back);
01484 
01485     // reload textures (just in case)
01486     rITexture::UnloadAll();
01487 
01488     sr_textOut = textOutBack;
01489 #endif
01490 
01491     return ret;
01492 }
01493 

Generated on Sat Mar 15 22:56:14 2008 for Armagetron Advanced by  doxygen 1.5.4