/* ************************************************************************* ArmageTron -- Just another Tron Lightcycle Game in 3D. Copyright (C) 2000 Manuel Moos (manuel@moosnet.de) Copyright (C) 2004 Armagetron Advanced Team (http://sourceforge.net/projects/armagetronad/) ************************************************************************** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *************************************************************************** */ #include "tSysTime.h" #include "uMenu.h" #include "rSysdep.h" #include "rScreen.h" #include "rViewport.h" #include "tString.h" #include "math.h" #include "uInputQueue.h" #include "tConsole.h" #include "uInput.h" #include "tDirectories.h" #ifndef DEDICATED #include "rRender.h" #include "rSDL.h" #endif FUNCPTR uMenu::idle(NULL); bool uMenu::wrap=true; bool uMenu::quickexit=false; bool uMenu::exitToMain=false; // ***************************************************** #ifdef SLOPPYLOCALE uMenu::uMenu(const char *t="",bool exit_item) :exitFlag(0),spaceBelow(.4),title(t){ if (exit_item) new uMenuItemExit(this); center=0; menuTop=.7; menuBot=-.7; yOffset=0; selected = -1; } #endif uMenu::uMenu(const tOutput &t,bool exit_item) :exitFlag(0),spaceBelow(.4),title(t){ if (exit_item) new uMenuItemExit(this); center=0; menuTop=.7; menuBot=-.7; yOffset=0; selected = -1; } uMenu::~uMenu(){ for(int i=items.Len()-1;i>=0;i--) delete items[i]; } void uMenu::ReverseItems(){ tList dummy = items; items.SetLen(0); for (int i=dummy.Len()-1; i>=0; i--){ uMenuItem *x = dummy[i]; dummy.Remove(x, x->idnum); items.Add (x, x->idnum); } } //static REAL text_height=rCHEIGHT_NORMAL; //static REAL text_width=rCWIDTH_NORMAL; static REAL text_height=.11; #ifndef DEDICATED static REAL text_width=.05; #endif #ifndef DEDICATED static REAL titlefac=1.2; #endif int menuentries=0; REAL uMenu::YPos(int num){ return yOffset-text_height*(menuentries-num); } static inline void arrow(REAL x,REAL y,REAL dy,REAL size){ #ifndef DEDICATED if (sr_glOut){ BeginLineLoop(); Vertex(x,y+2*dy*size); Vertex(x+size,y); Vertex(x+.3*size,y); Vertex(x+.3*size,y-2*dy*size); Vertex(x-.3*size,y-2*dy*size); Vertex(x-.3*size,y); Vertex(x-size,y); RenderEnd(); } #endif } static bool repeat = false; #ifndef DEDICATED static SDL_Event tEvent; static bool disphelp=false; static REAL lastkey; #endif void uMenu::Enter(){ #ifndef DEDICATED float nextrepeat = 0.0f; static const float repeatdelay = 0.3f; static const float repeatrate = 0.05f; #else return; #endif uCallbackMenuEnter::MenuEnter(); if (items.Len()<=0) return; if (selected < 0 || selected >= items.Len()) selected = items.Len()-1; exitFlag=0; yOffset=menuTop; REAL lastt=0; REAL ts=0; #ifndef DEDICATED lastkey=tSysTimeFloat(); static const REAL timeout=3; #endif while (!exitFlag && !quickexit && !exitToMain){ ts=tSysTimeFloat()-lastt; lastt=tSysTimeFloat(); if (ts>.2) ts=.2; menuentries=items.Len(); #ifndef DEDICATED while(su_GetSDLInput(tEvent)) { REAL entertime = tSysTimeFloat(); switch (tEvent.type) { case SDL_KEYDOWN: repeat = true; nextrepeat = tSysTimeFloat() + repeatdelay; break; case SDL_KEYUP: repeat = false; break; } this->HandleEvent( tEvent ); if ( tSysTimeFloat() - entertime > 1 ) { repeat = false; } } if ( repeat && tSysTimeFloat() > nextrepeat ) { this->HandleEvent( tEvent ); nextrepeat = tSysTimeFloat() + repeatrate; } #endif menuBot=-1+spaceBelow; const REAL border=.3; const REAL smallborder=.1; menuentries=items.Len(); REAL ysel=YPos(selected); if (yselmenuTop-border) yOffset+=(menuTop-border-ysel)*6*ts; if (yselmenuTop-smallborder) yOffset+=(menuTop-smallborder-ysel); if (YPos(0)>menuBot+smallborder) yOffset+=menuBot+smallborder-YPos(0); if (YPos(menuentries-1)RenderBackground(); if (selected >= items.Len()) selected = items.Len()-1; if (items.Len() <= 0) return; items[selected]->Render(center,YPos(selected),1,true); for(int i=items.Len()-1;i>=0;i--) if(i!=selected){ REAL y=YPos(i); REAL alpha=1; const REAL b=.1; if(ymenuTop-b) alpha=(menuTop-y)/b; if (y>menuBot && yRender(center,y,alpha,false); } Color(.6,.6,1,1); ::DisplayText(0,menuTop+text_height*titlefac ,text_width*titlefac,text_height*titlefac, title,0); glDisable(GL_TEXTURE_2D); //glDisable(GL_TEXTURE); Color(1,.2,.2,.5); if (YPos(0)menuTop && (int(tSysTimeFloat())+1)%2) arrow(.9,menuTop,1,.05); if (tSysTimeFloat()-lastkey>timeout){ disphelp=true; if (sr_alphaBlend) glColor4f(1,.8,.8,tSysTimeFloat()-lastkey-timeout); else Color(tSysTimeFloat()-lastkey-timeout, .8*(tSysTimeFloat()-lastkey-timeout), .8*(tSysTimeFloat()-lastkey-timeout)); rTextField c(-.95f,menuBot-.04f); c.SetWidth(static_cast((1.9f-items[selected]->SpaceRight())/c.GetCWidth())); c << items[selected]->Help(); } else disphelp=false; sr_SwapGL(); } else #endif { usleep( 100000 ); } } repeat = false; } void uMenu::HandleEvent( SDL_Event event ) { #ifndef DEDICATED if (!items[selected]->Event(event)) // let the input subsystem handle events for later processing su_HandleEvent( tEvent, true ); switch (event.type){ case SDL_KEYDOWN: { if (!disphelp) lastkey=tSysTimeFloat(); switch (tEvent.key.keysym.sym){ case(SDLK_ESCAPE): repeat = false; lastkey=tSysTimeFloat(); Exit(); break; case(SDLK_UP): lastkey=tSysTimeFloat(); selected++; if (selected>=items.Len()) if (wrap) selected=0; else selected=items.Len()-1; break; case(SDLK_DOWN): lastkey=tSysTimeFloat(); selected--; if (selected<0) if(wrap) selected=items.Len()-1; else selected=0; break; case(SDLK_LEFT): items[selected]->LeftRight(-1); break; case(SDLK_RIGHT): items[selected]->LeftRight(1); break; case(SDLK_SPACE): case(SDLK_KP_ENTER): case(SDLK_RETURN): repeat = false; items[selected]->Enter(); repeat = false; lastkey=tSysTimeFloat(); break; default: break; } } } #endif } // paints a nice background void uMenu::GenericBackground(){ #ifndef DEDICATED if (idle) (*idle)(); else if (sr_glOut){ uCallbackMenuBackground::MenuBackground(); } else SDL_Delay(100000); #endif sr_ResetRenderState(true); } // marks the menu for exit void uMenu::Exit(){ exitFlag=1; } // ***************************************************** void uMenuItem::DisplayText(REAL x,REAL y,const char *text, bool selected,REAL alpha, int center,int c,int cp){ #ifndef DEDICATED if (sr_glOut){ REAL time=tSysTimeFloat()*10; if(selected) glColor4f(.8+.2*sin(time),.3-.1*sin(time),.3-.1*sin(time),alpha); else glColor4f(1,1,1,alpha); REAL tw = text_width; REAL th = text_height; REAL availw = 1.9f; if (center < 0) availw = (.9f-x); if (center > 0) availw = (x + .9f); int len = strlen(text); if (len * tw > availw) { th *= availw/(len * tw); tw = availw/len; } ::DisplayText(x,y,tw,th,text,center,c,cp); } #endif } void uMenuItem::DisplayTextSpecial(REAL x,REAL y,const char *text, bool selected, REAL alpha,int center){ /* if(selected) glColor3f(.9,.3,.3); else glColor3f(.7,.7,1); ::DisplayText(x,y,text_width,text_height,text,center); */ DisplayText(x,y,text,selected,alpha,center); } // ************************************* const tOutput& uMenuItemExit::ExitText() { static tOutput exitText("$menuitem_exit_text"); return exitText; } const tOutput& uMenuItemExit::ExitHelp() { static tOutput exitHelp("$menuitem_exit_help"); return exitHelp; } // ************************************* void uMenuItemToggle::NewChoice(uSelectItem *){}; void uMenuItemToggle::NewChoice(const char *,bool ){}; #ifdef SLOPPYLOCALE uMenuItemToggle::uMenuItemToggle(uMenu *m, const char *tit, const char *help, bool &targ) :uMenuItemSelection(m,tit,help,targ){ uMenuItemSelection::NewChoice("$menuitem_toggle_on","",true); uMenuItemSelection::NewChoice("$menuitem_toggle_off","",false); } #endif uMenuItemToggle::uMenuItemToggle(uMenu *m, const tOutput& tit, const tOutput& help, bool &targ) :uMenuItemSelection(m,tit,help,targ){ uMenuItemSelection::NewChoice("$menuitem_toggle_on","",true); uMenuItemSelection::NewChoice("$menuitem_toggle_off","",false); } uMenuItemToggle::~uMenuItemToggle(){} void uMenuItemToggle::LeftRight(int){ select=1-select; *target=!(*target); } void uMenuItemToggle::Enter(){ LeftRight(0); } // ***************************************** // Integer Choose // ***************************************** #ifdef SLOPPYLOCALE uMenuItemInt::uMenuItemInt (uMenu *m,const char *tit,const char *help,int &targ, int mi,int ma,int step) :uMenuItem(m,help),title(tit),target(targ),Min(mi),Max(ma), Step(step){ if (targetMax) target=Max; } #endif uMenuItemInt::uMenuItemInt (uMenu *m,const tOutput &tit,const tOutput &help,int &targ, int mi,int ma,int step) :uMenuItem(m,help),title(tit),target(targ),Min(mi),Max(ma), Step(step){ if (targetMax) target=Max; } void uMenuItemInt::LeftRight(int dir){ target+=dir*Step; if (targetMax) target=Max; } void uMenuItemInt::Render(REAL x,REAL y,REAL alpha, bool selected){ DisplayText(x-.02,y,title,selected,alpha,1); tString s; s << target; DisplayText(x+.02,y,s,selected,alpha,-1); } // ***************************************************** uMenuItemString::uMenuItemString(uMenu *M, const tOutput& de, const tOutput& help, tString &c, int maxLength ) :uMenuItem(M,help),description(de),content(&c),cursorPos(0), maxLength_( maxLength ){ int len=content->Len(); if (len==0 || (*content)(len-1)!=0) (*content)[len]=0; cursorPos=content->Len()-1; } void uMenuItemString::Render(REAL x,REAL y, REAL alpha,bool selected){ #ifndef DEDICATED static int counter=0; counter++; /* if (selected){ if (counter & 32) glColor4f(1,1,1,.5*alpha); else glColor4f(1,1,0,alpha); REAL c=x+.02+text_width*cursorPos; glDisable(GL_TEXTURE); glDisable(GL_TEXTURE_2D); glBegin(GL_LINES); glVertex2f(c,y+text_height*.5); glVertex2f(c,y-text_height*.5); //glVertex2f(c+.1,y-text_height); glEnd(); } */ int cmode=0; if (selected){ cmode=1; if (counter & 32) cmode=2; } DisplayText(x-.02,y,description,selected,alpha,1); DisplayText(x+.02,y,&((*content)[0]),selected,alpha,-1,cmode,cursorPos); #endif } bool uMenuItemString::Event(SDL_Event &e){ #ifndef DEDICATED if (e.type!=SDL_KEYDOWN) return false; bool ret=true; SDL_keysym &c=e.key.keysym; int len=content->Len(); /* if (c.sym < 0) c.sym="?"; */ if (//32 <= c.sym && c.sym < 127 && 32 <= c.unicode && c.unicode < 127 && len < maxLength_ ){ for(int i=len-1;i>=cursorPos;i--) (*content)[i+1]=(*content)[i]; (*content)[cursorPos]=c.unicode; cursorPos++; len++; } else switch(c.sym){ case(SDLK_LEFT): cursorPos--; break; case(SDLK_RIGHT): cursorPos++; break; case(SDLK_DELETE): if (cursorPos0){ for(int i=cursorPos;iSetLen(len-1); len--; } break; case(SDLK_KP_ENTER): case(SDLK_RETURN): ret = false; c.sym = SDLK_DOWN; break; default: ret=false; break; } if(cursorPos<0) cursorPos=0; if(cursorPos>=len) cursorPos=len-1; (*content)[len-1]='\0'; return ret; #else return false; #endif } // ***************************************************** // Submenu // ***************************************************** uMenuItemSubmenu::uMenuItemSubmenu(uMenu *M, uMenu *s, const tOutput& help) :uMenuItem(M,help),submenu(s){} void uMenuItemSubmenu::Render(REAL x,REAL y,REAL alpha,bool selected){ DisplayTextSpecial(x,y,submenu->title,selected,alpha,0); } void uMenuItemSubmenu::Enter(){ submenu->Enter(); } // ***************************************************** // action // ***************************************************** uMenuItemAction::uMenuItemAction(uMenu *M, const tOutput& n, const tOutput& help ) :uMenuItem(M,help),name_(n){} void uMenuItemAction::Render(REAL x,REAL y,REAL alpha,bool selected){ DisplayTextSpecial(x,y,name_,selected,alpha,0); } void uMenuItemAction::Enter() { tASSERT( 0 ) } // ***************************************************** // function // ***************************************************** uMenuItemFunction::uMenuItemFunction(uMenu *M, const tOutput& n, const tOutput& help, FUNCPTR f) :uMenuItemAction(M,n,help),func(f){} void uMenuItemFunction::Enter(){ (*func)(); } uMenuItemFunctionInt::uMenuItemFunctionInt(uMenu *M, const tOutput& n, const tOutput& help, INTFUNCPTR f,int a) :uMenuItemAction(M,n,help),func(f),arg(a){} void uMenuItemFunctionInt::Enter(){ (*func)(arg); } // ***************************************************** // File Selection (added by k) // ***************************************************** void uMenuItemFileSelection::NewChoice( uSelectItem * ) {}; void uMenuItemFileSelection::NewChoice( char *, bool ) {}; void uMenuItemFileSelection::Reload() { Clear(); if ( defaultFileName_.Len() > 1 && defaultFilePath_.Len() > 1 ) AddFile( defaultFileName_, defaultFilePath_, formatName_ ); LoadDirectory( dir_, fileSpec_, formatName_ ); } void uMenuItemFileSelection::LoadDirectory( const char *dir, const char *fileSpec, bool formatName /*= true*/ ) { tArray files; tString filePath = dir; tDirectories::GetFiles( dir, fileSpec, files, getFilesFlag_ ); for ( int i = 0; i < files.Len(); i++ ) { AddFile( files( i ), filePath + files( i ), formatName ); } } void uMenuItemFileSelection::AddFile( const char *fileName, const char *filePath, bool formatName /*= true*/ ) { tString menuName = fileName; if ( formatName ) tDirectories::FileNameToMenuName( fileName, menuName ); uMenuItemSelection::NewChoice( (const char *)menuName, "", filePath ); } // ***************************************************** // Menu Enter/Leave-Callback // ***************************************************** static tCallback *enter_anchor=NULL,*leave_anchor=NULL, *background_anchor=NULL; uCallbackMenuEnter::uCallbackMenuEnter(VOIDFUNC *f) :tCallback(enter_anchor,f){} void uCallbackMenuEnter::MenuEnter(){ Exec(enter_anchor); } uCallbackMenuLeave::uCallbackMenuLeave(VOIDFUNC *f) :tCallback(leave_anchor,f){} void uCallbackMenuLeave::MenuLeave(){ Exec(leave_anchor); } uCallbackMenuBackground::uCallbackMenuBackground(VOIDFUNC *f) :tCallback(background_anchor,f){} void uCallbackMenuBackground::MenuBackground(){ Exec(background_anchor); } void uMenu::Message(const tOutput& message, const tOutput& interpretation, REAL to){ #ifdef DEDICATED con << message << ":\n"; con << interpretation << '\n'; #else if (!sr_glOut) return; bool textOutBack = sr_textOut; sr_textOut = false; sr_ClearGL(); sr_SwapGL(); rFont::s_defaultFont.Select(); rFont::s_defaultFontSmall.Select(); sr_ClearGL(); sr_SwapGL(); REAL timeout = tSysTimeFloat() + to; SDL_Event tEvent; // catch some keyboard input while(su_GetSDLInput(tEvent)); while(sr_glOut && (!su_GetSDLInput(tEvent) || tEvent.type!=SDL_KEYDOWN) && (to < 0 || tSysTimeFloat() < timeout)){ sr_ResetRenderState(true); rViewport::s_viewportFullscreen.Select(); sr_ClearGL(); GenericBackground(); REAL w=16*3/640.0; REAL h=32*3/480.0; //REAL middle=-.6; tString m(message); int len = m.Len(); if (w * len > 1.8) { h = h * 1.8 / (w * len); w = 1.8 / len; } Color(1,1,1); DisplayText(0,.8,w,h, message); w = 16/640.0; h = 32/480.0; { rTextField c(-.8,.6, w, h); c << interpretation; } sr_SwapGL(); } // catch some keyboard input while (su_GetSDLInput(tEvent)); sr_textOut = textOutBack; #endif }