1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 Copyright (C) 2004  Armagetron Advanced Team (http://sourceforge.net/projects/armagetronad/)
8 
9 **************************************************************************
10 
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 GNU General Public License for more details.
20 
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24 
25 ***************************************************************************
26 
27 */
28 
29 #include "tSysTime.h"
30 #include "uMenu.h"
31 #include "rSysdep.h"
32 #include "rScreen.h"
33 #include "rViewport.h"
34 #include "tString.h"
35 #include "math.h"
36 #include "uInputQueue.h"
37 #include "rConsole.h"
38 #include "uInput.h"
39 #include "tDirectories.h"
40 //#include "tRecording.h"
41 #include "tToDo.h"
42 #include "tException.h"
43 
44 #ifndef DEDICATED
45 #include "rRender.h"
46 #include "rSDL.h"
47 #endif
48 
49 #include <vector>
50 
51 FUNCPTR  uMenu::idle(NULL);
52 
53 bool uMenu::wrap=true;
54 uMenu::QuickExit uMenu::quickexit=uMenu::QuickExit_Off;
55 bool uMenu::exitToMain=false;
56 
57 // *****************************************************
58 
59 #ifdef SLOPPYLOCALE
uMenu(const char * t="",bool exit_item)60 uMenu::uMenu(const char *t="",bool exit_item)
61         :exitFlag(0),spaceBelow(.4),title(t){
62     if (exit_item) new uMenuItemExit(this);
63     center=0;
64     menuTop=.7;
65     menuBot=-.7;
66     yOffset=0;
67     selected = 10000000;
68 }
69 #endif
70 
uMenu(const tOutput & t,bool exit_item)71 uMenu::uMenu(const tOutput &t,bool exit_item)
72         :exitFlag(0),spaceBelow(.4),title(t){
73     if (exit_item) new uMenuItemExit(this);
74     center=0;
75     menuTop=.7;
76     menuBot=-.7;
77     yOffset=0;
78     selected = 100000000;
79 }
80 
~uMenu()81 uMenu::~uMenu(){
82     for (int i=items.Len()-1;i>=0;i--)
83         delete items[i];
84 }
85 
ReverseItems()86 void uMenu::ReverseItems(){
87     tList<uMenuItem> dummy;
88     dummy.Swap( items );
89 
90     for (int i=dummy.Len()-1; i>=0; i--){
91         uMenuItem *x = dummy[i];
92         dummy.Remove(x, x->idnum);
93         items.Add  (x, x->idnum);
94     }
95 }
96 
97 //static REAL text_height=rCHEIGHT_NORMAL;
98 //static REAL text_width=rCWIDTH_NORMAL;
99 
100 static REAL text_height=.11;
101 #ifndef DEDICATED
102 static REAL text_width=.05;
103 #endif
104 
105 #ifndef DEDICATED
106 static REAL titlefac=1.2;
107 #endif
108 int menuentries=0;
109 
YPos(int num)110 REAL uMenu::YPos(int num){
111     return yOffset-text_height*(menuentries-num);
112 }
113 
114 
arrow(REAL x,REAL y,REAL dy,REAL size)115 static inline void arrow(REAL x,REAL y,REAL dy,REAL size){
116 #ifndef DEDICATED
117     if (sr_glOut){
118         BeginLineLoop();
119         Vertex(x,y+2*dy*size);
120         Vertex(x+size,y);
121         Vertex(x+.3*size,y);
122         Vertex(x+.3*size,y-2*dy*size);
123         Vertex(x-.3*size,y-2*dy*size);
124         Vertex(x-.3*size,y);
125         Vertex(x-size,y);
126         RenderEnd();
127     }
128 #endif
129 }
130 
131 static bool repeat = false;
132 
133 #ifndef DEDICATED
134 static bool disphelp=false;
135 static REAL lastkey;
136 #endif
137 
138 // inhibit console newline display while in a menu, it causes flickering
139 static bool su_inMenu = false;
MenuActive()140 bool uMenu::MenuActive()
141 {
142     return su_inMenu;
143 }
144 static rNoAutoDisplayAtNewlineCallback su_noNewline( uMenu::MenuActive );
145 // static rSmallConsoleCallback su_smallConsole( su_InMenu );
146 
OnEnter()147 void uMenu::OnEnter(){
148 #ifndef DEDICATED
149     float nextrepeat = 0.0f;
150     static const float repeatdelay = 0.3f;
151     static const float repeatrate  = 0.05f;
152     SDL_Event tEventRepeat;
153 #else
154     return;
155 #endif
156 
157     // delete stuck keys, maybe a menu item catches key release events.
158     su_ClearKeys();
159 
160     uCallbackMenuEnter::MenuEnter();
161     su_inMenu = true;
162 
163     if (items.Len()<=0)
164         return;
165 
166     exitFlag=0;
167     yOffset=menuTop;
168     REAL lastt=0;
169     REAL ts=0;
170 
171 #ifndef DEDICATED
172     lastkey=tSysTimeFloat();
173     static const REAL timeout=3;
174 #endif
175 
176     while (!exitFlag && !quickexit && !exitToMain){
177         st_DoToDo();
178         tAdvanceFrame();
179 
180         ts=tSysTimeFloat()-lastt;
181         lastt=tSysTimeFloat();
182         if (ts>.2) ts=.2;
183 
184         menuentries=items.Len();
185 
186         // clamp cursor
187         if (selected < 0 )
188             selected = 0;
189         if ( selected >= items.Len())
190             selected = items.Len()-1;
191 
192 #ifndef DEDICATED
193         {
194             SDL_Event tEvent;
195             uInputProcessGuard inputProcessGuard;
196             while (su_GetSDLInput(tEvent))
197             {
198                 REAL entertime = tSysTimeFloat();
199 
200                 switch (tEvent.type)
201                 {
202                 case SDL_KEYDOWN:
203                     if ( tEvent.key.keysym.sym == SDLK_UNKNOWN )
204                     {
205                         // don't repeat unknown syms. They come from multi-key compositions and
206                         // don't send keyup events when released.
207                         break;
208                     }
209                     repeat = true;
210                     memcpy( &tEventRepeat, &tEvent, sizeof( SDL_Event ) );
211                     nextrepeat = tSysTimeFloat() + repeatdelay;
212                     break;
213                 case SDL_KEYUP:
214                     repeat = false;
215                     break;
216                 }
217 
218                 this->HandleEvent( tEvent );
219 
220                 // quit shortcut
221                 if ( quickexit )
222                     break;
223 
224                 if ( tSysTimeFloat() - entertime > 1 )
225                 {
226                     repeat = false;
227                 }
228             }
229 
230             if ( repeat && tSysTimeFloat() > nextrepeat )
231             {
232                 this->HandleEvent( tEventRepeat );
233                 nextrepeat = tSysTimeFloat() + repeatrate;
234             }
235         }
236 
237         // we're about to render, last chance to make changes to the menu
238         OnRender();
239 
240         // clamp cursor
241         if (selected < 0 )
242             selected = 0;
243         if ( selected >= items.Len())
244             selected = items.Len()-1;
245 #endif
246         // quit shortcut
247         if ( quickexit )
248             break;
249 
250 
251         menuBot=-1+spaceBelow;
252 
253         const REAL border=.3;
254         const REAL smallborder=.1;
255 
256         menuentries=items.Len();
257 
258         REAL ysel=YPos(selected);
259 
260         if (ysel<menuBot+border)
261             yOffset+=(menuBot+border-ysel)*6*ts;
262 
263         if (ysel>menuTop-border)
264             yOffset+=(menuTop-border-ysel)*6*ts;
265 
266         if (ysel<menuBot)
267             yOffset+=(menuBot-ysel);
268 
269         if (ysel>menuTop-smallborder)
270             yOffset+=(menuTop-smallborder-ysel);
271 
272         if (YPos(0)>menuBot+smallborder)
273             yOffset+=menuBot+smallborder-YPos(0);
274 
275         if (YPos(menuentries-1)<menuTop-smallborder)
276             yOffset+=menuTop-smallborder-YPos(menuentries-1);
277 
278 #ifndef DEDICATED
279         sr_ResetRenderState(true);
280         items[selected]->RenderBackground();
281 
282         if (selected >= items.Len()) selected = items.Len()-1;
283         if (items.Len() <= 0)
284             return;
285 
286         if (sr_glOut && !exitFlag && !quickexit){
287             items[selected]->Render(center,YPos(selected),1,true);
288 
289             for (int i=items.Len()-1;i>=0;i--)
290                 if (i!=selected){
291                     REAL y=YPos(i);
292                     REAL alpha=1;
293                     const REAL b=.1;
294                     if (y<menuBot+b)
295                         alpha=(y-menuBot)/b;
296                     if (y>menuTop-b)
297                         alpha=(menuTop-y)/b;
298                     if (y>menuBot && y<menuTop)
299                     {
300                         rTextField::SetDefaultColor( tColor(1,1,1,1) );
301                         rTextField::SetBlendColor( tColor(1,1,1,1) );
302                         items[i]->Render(center,y,alpha,false);
303                     }
304                 }
305 
306             rTextField::SetDefaultColor( tColor(1,1,1,1) );
307             rTextField::SetBlendColor( tColor(1,1,1,1) );
308 
309             Color(.6,.6,1,1);
310             ::DisplayText(0,menuTop+text_height*titlefac
311                           ,text_width*titlefac,text_height*titlefac,
312                           title,0);
313 
314             glDisable(GL_TEXTURE_2D);
315             //glDisable(GL_TEXTURE);
316             Color(1,.2,.2,.5);
317             if (YPos(0)<menuBot+smallborder && (int(tSysTimeFloat()))%2)
318                 arrow(.9,menuBot+.1,-1,.05);
319             if (YPos(menuentries-1)>menuTop && (int(tSysTimeFloat())+1)%2)
320                 arrow(.9,menuTop,1,.05);
321 
322             if (tSysTimeFloat()-lastkey>timeout){
323                 disphelp=true;
324                 if (sr_alphaBlend)
325                     glColor4f(1,.8,.8,tSysTimeFloat()-lastkey-timeout);
326                 else
327                     Color(tSysTimeFloat()-lastkey-timeout,
328                           .8*(tSysTimeFloat()-lastkey-timeout),
329                           .8*(tSysTimeFloat()-lastkey-timeout));
330 
331                 rTextField c(-.95f,menuBot-.04f);
332                 c.SetWidth(static_cast<int>((1.9f-items[selected]->SpaceRight())/c.GetCWidth()));
333                 c << items[selected]->Help();
334             }
335             else disphelp=false;
336         }
337         else
338 #endif
339             if ( !sr_glOut )
340             {
341                 tDelay( 10000 );
342             }
343 
344 #ifndef DEDICATED
345         rSysDep::SwapGL();
346         rSysDep::ClearGL();
347 #endif
348     }
349 
350     repeat = false;
351 
352     uCallbackMenuLeave::MenuLeave();
353     su_inMenu = false;
354 }
355 
HandleEvent(SDL_Event event)356 void uMenu::HandleEvent( SDL_Event event )
357 {
358 #ifndef DEDICATED
359     if (!items[selected]->Event(event))
360     {
361         switch (event.type){
362         case SDL_KEYDOWN:
363         {
364             if (!disphelp)
365                 lastkey=tSysTimeFloat();
366             switch (event.key.keysym.sym){
367 
368             case(SDLK_ESCAPE):
369                             repeat = false;
370                 lastkey=tSysTimeFloat();
371                 Exit();
372                 break;
373 
374             case(SDLK_UP):
375                             lastkey=tSysTimeFloat();
376                 selected++;
377                 if (selected>=items.Len())
378                 {
379                     if (wrap)
380                         selected=0;
381                     else
382                         selected=items.Len()-1;
383                 }
384                 break;
385 
386             case(SDLK_DOWN):
387                             lastkey=tSysTimeFloat();
388                 selected--;
389                 if (selected<0)
390                 {
391                     if (wrap)
392                         selected=items.Len()-1;
393                     else
394                         selected=0;
395                 }
396 
397                 break;
398 
399             case(SDLK_LEFT):
400                             items[selected]->LeftRight(-1);
401                 break;
402             case(SDLK_RIGHT):
403                             items[selected]->LeftRight(1);
404                 break;
405 
406             case(SDLK_SPACE):
407                         case(SDLK_KP_ENTER):
408                             case(SDLK_RETURN):
409                                     repeat = false;
410                 try
411         {
412                     su_inMenu = false;
413                     items[selected]->Enter();
414                 }
415                 catch (tException const & e)
416                 {
417                     uMenu::SetIdle(NULL);
418 
419                     // inform user of generic errors
420                     tConsole::Message( e.GetName(), e.GetDescription(), 20 );
421                 }
422 #ifdef _MSC_VER
423 #pragma warning ( disable : 4286 )
424                 // GRR. Visual C++ dones not handle generic exceptions with the above general statement.
425                 // A specialized version is needed. The best part: it warns about the code below being redundant.
426                 catch ( tGenericException const & e )
427                 {
428                     try
429                     {
430                         tConsole::Message( e.GetName(), e.GetDescription(), 20 );
431                     }
432                     catch (...)
433                     {
434                     }
435                 }
436 #endif
437 
438                 su_inMenu = true;
439 
440                 repeat = false;
441                 lastkey=tSysTimeFloat();
442                 break;
443 
444             default:
445                 // let the input subsystem handle events for later processing
446                 su_HandleEvent( event, true );
447                 break;
448             }
449         }
450         break;
451         default:
452             // let the input subsystem handle events for later processing
453             su_HandleEvent( event, true );
454             break;
455         }
456     }
457 
458     su_inMenu = true;
459 #endif
460 }
461 
462 
463 // paints a nice background
GenericBackground()464 void uMenu::GenericBackground(){
465 #ifndef DEDICATED
466     if (idle)
467     {
468         try
469         {
470             // throw tGenericException("test"); // (test exception throw to see if error handling works right)
471             (*idle)();
472         }
473         catch ( ... )
474         {
475             // the idle background function is broken. Disable it and rethrow.
476             idle = 0;
477             throw;
478         }
479     }
480     else if (sr_glOut){
481         uCallbackMenuBackground::MenuBackground();
482     }
483     else
484         tDelay(100000);
485 #endif
486     sr_ResetRenderState(true);
487 }
488 
489 // marks the menu for exit
OnExit()490 void uMenu::OnExit(){
491     exitFlag=1;
492 }
493 
494 //! called every frame before the menu is rendered
OnRender()495 void uMenu::OnRender()
496 {
497 }
498 
499 // *****************************************************
500 
501 // *******************************************************************************************
502 // *
503 // *   SetColor
504 // *
505 // *******************************************************************************************
506 //!
507 //!        @param  selected    flag indicating whether the menu item is currently selected
508 //!        @param  alpha       transparency to use
509 //!
510 // *******************************************************************************************
511 
SetColor(bool selected,REAL alpha)512 void uMenuItem::SetColor( bool selected, REAL alpha )
513 {
514     //   rTextField::SetBlendColor( tColor(.8+.2*sin(time),.3-.1*sin(time),.3-.1*sin(time),alpha) );
515     rTextField::SetDefaultColor( tColor(1,1,1,alpha) );
516 
517     if (selected)
518     {
519         REAL time=tSysTimeFloat()*10;
520         REAL intensity = 1+.3*sin(time);
521         rTextField::SetDefaultColor( tColor(.8,.3,.3,alpha) );
522         rTextField::SetBlendColor( tColor(intensity,intensity,intensity,alpha) );
523     }
524 }
525 
DisplayText(REAL x,REAL y,const char * text,bool selected,REAL alpha,int center,int c,int cp,rTextField::ColorMode colorMode)526 void uMenuItem::DisplayText(REAL x,REAL y,const char *text,
527                             bool selected,REAL alpha,
528                             int center,int c,int cp, rTextField::ColorMode colorMode ){
529 #ifndef DEDICATED
530     if (sr_glOut){
531         SetColor( selected, alpha );
532 
533         REAL tw = text_width;
534         REAL th = text_height;
535 
536 
537 #if 0
538         // the function that is called takes care of that
539         REAL availw = 1.9f;
540         if (center < 0) availw = (.9f-x);
541         if (center > 0) availw = (x + .9f);
542 
543         int len = strlen(text);
544         if (len * tw > availw)
545         {
546             th *= availw/(len * tw);
547             tw  = availw/len;
548         }
549 #endif
550 
551         ::DisplayText(x,y,tw,th,text,center,c,cp, colorMode );
552     }
553 #endif
554 }
555 
DisplayTextSpecial(REAL x,REAL y,const char * text,bool selected,REAL alpha,int center)556 void uMenuItem::DisplayTextSpecial(REAL x,REAL y,const char *text,
557                                    bool selected,
558                                    REAL alpha,int center){
559     /*
560      if(selected)
561        glColor3f(.9,.3,.3);
562      else
563        glColor3f(.7,.7,1);
564 
565      ::DisplayText(x,y,text_width,text_height,text,center);
566      */
567 
568     DisplayText(x,y,text,selected,alpha,center);
569 }
570 
571 // *************************************
572 
ExitText()573 const tOutput& uMenuItemExit::ExitText()
574 {
575     static tOutput exitText("$menuitem_exit_text");
576 
577     return exitText;
578 }
579 
ExitHelp()580 const tOutput& uMenuItemExit::ExitHelp()
581 {
582     static tOutput exitHelp("$menuitem_exit_help");
583 
584     return exitHelp;
585 }
586 
587 // *************************************
588 
NewChoice(uSelectItem<bool> *)589 void uMenuItemToggle::NewChoice(uSelectItem<bool> *){}
NewChoice(const char *,bool)590 void uMenuItemToggle::NewChoice(const char *,bool ){}
591 
592 #ifdef SLOPPYLOCALE
uMenuItemToggle(uMenu * m,const char * tit,const char * help,bool & targ)593 uMenuItemToggle::uMenuItemToggle(uMenu *m,
594                                  const char *tit,
595                                  const char *help,
596                                  bool &targ)
597         :uMenuItemSelection<bool>(m,tit,help,targ){
598     uMenuItemSelection<bool>::NewChoice("$menuitem_toggle_on","",true);
599     uMenuItemSelection<bool>::NewChoice("$menuitem_toggle_off","",false);
600 }
601 #endif
602 
uMenuItemToggle(uMenu * m,const tOutput & tit,const tOutput & help,bool & targ)603 uMenuItemToggle::uMenuItemToggle(uMenu *m,
604                                  const tOutput& tit,
605                                  const tOutput& help,
606                                  bool &targ)
607         :uMenuItemSelection<bool>(m,tit,help,targ){
608     uMenuItemSelection<bool>::NewChoice("$menuitem_toggle_on","",true);
609     uMenuItemSelection<bool>::NewChoice("$menuitem_toggle_off","",false);
610 }
611 
~uMenuItemToggle()612 uMenuItemToggle::~uMenuItemToggle(){}
613 
LeftRight(int)614 void uMenuItemToggle::LeftRight(int){
615     select=1-select;
616     *target=!(*target);
617 }
618 
Enter()619 void uMenuItemToggle::Enter(){
620     LeftRight(0);
621 }
622 // *****************************************
623 //               Integer Choose
624 // *****************************************
625 
626 #ifdef SLOPPYLOCALE
uMenuItemInt(uMenu * m,const char * tit,const char * help,int & targ,int mi,int ma,int step)627 uMenuItemInt::uMenuItemInt
628 (uMenu *m,const char *tit,const char *help,int &targ,
629  int mi,int ma,int step)
630         :uMenuItem(m,help),title(tit),target(targ),Min(mi),Max(ma),
631         Step(step){
632     if (target<Min) target=Min;
633     if (target>Max) target=Max;
634 }
635 #endif
636 
uMenuItemInt(uMenu * m,const tOutput & tit,const tOutput & help,int & targ,int mi,int ma,int step)637 uMenuItemInt::uMenuItemInt
638 (uMenu *m,const tOutput &tit,const tOutput &help,int &targ,
639  int mi,int ma,int step)
640         :uMenuItem(m,help),title(tit),target(targ),Min(mi),Max(ma),
641         Step(step){
642     if (target<Min) target=Min;
643     if (target>Max) target=Max;
644 }
645 
646 
LeftRight(int dir)647 void uMenuItemInt::LeftRight(int dir){
648     target+=dir*Step;
649     if (target<Min) target=Min;
650     if (target>Max) target=Max;
651 }
652 
Render(REAL x,REAL y,REAL alpha,bool selected)653 void uMenuItemInt::Render(REAL x,REAL y,REAL alpha,
654                           bool selected){
655     DisplayText(x-.02,y,title,selected,alpha,1);
656 
657     tString s;
658     s << target;
659     DisplayText(x+.02,y,s,selected,alpha,-1);
660 }
661 
662 // *****************************************
663 //               Float Choose
664 // *****************************************
665 
666 #ifdef SLOPPYLOCALE
uMenuItemReal(uMenu * m,const char * tit,const char * help,REAL & targ,REAL mi,REAL ma,REAL step)667 uMenuItemReal::uMenuItemReal
668 (uMenu *m,const char *tit,const char *help,REAL &targ,
669  REAL mi,REAL ma,REAL step)
670         :uMenuItem(m,help),title(tit),target(targ),Min(mi),Max(ma),
671         Step(step){
672     if (target<Min) target=Min;
673     if (target>Max) target=Max;
674 }
675 #endif
676 
uMenuItemReal(uMenu * m,const tOutput & tit,const tOutput & help,REAL & targ,REAL mi,REAL ma,REAL step)677 uMenuItemReal::uMenuItemReal
678 (uMenu *m,const tOutput &tit,const tOutput &help,REAL &targ,
679  REAL mi,REAL ma,REAL step)
680         :uMenuItem(m,help),title(tit),target(targ),Min(mi),Max(ma),
681         Step(step){
682     if (target<Min) target=Min;
683     if (target>Max) target=Max;
684 }
685 
686 
LeftRight(int dir)687 void uMenuItemReal::LeftRight(int dir){
688     target+=dir*Step;
689     if (target<Min) target=Min;
690     if (target>Max) target=Max;
691 }
692 
Render(REAL x,REAL y,REAL alpha,bool selected)693 void uMenuItemReal::Render(REAL x,REAL y,REAL alpha,
694                           bool selected){
695     DisplayText(x-.02,y,title,selected,alpha,1);
696 
697     tString s;
698     s << target;
699     DisplayText(x+.02,y,s,selected,alpha,-1);
700 }
701 
702 
703 // *****************************************************
704 
uMenuItemString(uMenu * M,const tOutput & de,const tOutput & help,tString & c,int maxLength)705 uMenuItemString::uMenuItemString(uMenu *M,
706                                  const tOutput& de,
707                                  const tOutput& help,
708                                  tString &c,
709                                  int maxLength )
710         :uMenuItem(M,help),description(de),content(&c),cursorPos(0), maxLength_( maxLength ){
711     int len=content->Len();
712     if (len==0 || (*content)(len-1)!=0)
713         (*content)[len]=0;
714     cursorPos=content->Len()-1;
715     colorMode_ = rTextField::COLOR_SHOW;
716 }
717 
Render(REAL x,REAL y,REAL alpha,bool selected)718 void uMenuItemString::Render(REAL x,REAL y,
719                              REAL alpha,bool selected){
720 #ifndef DEDICATED
721     static int counter=0;
722     counter++;
723 
724     int cmode=0;
725     if (selected){
726         cmode=1;
727         if (counter & 32) cmode=2;
728     }
729 
730     // unslected items with COLOR_SHOW should be rendered with COLOR_USE
731     rTextField::ColorMode colorMode = colorMode_;
732     if ( colorMode == rTextField::COLOR_SHOW && !selected )
733         colorMode = rTextField::COLOR_USE;
734 
735     DisplayText(x-.02,y,description,selected,alpha,1);
736     DisplayText(x+.02,y,&((*content)[0]),selected,alpha,-1,cmode,cursorPos, colorMode );
737 #endif
738 }
739 
Event(SDL_Event & e)740 bool uMenuItemString::Event(SDL_Event &e){
741 #ifndef DEDICATED
742     if (e.type!=SDL_KEYDOWN)
743         return false;
744     bool ret=true;
745     SDL_keysym &c=e.key.keysym;
746     SDLMod mod = c.mod;
747     bool moveWordLeft, moveWordRight, deleteWordLeft, deleteWordRight, moveBeginning, moveEnd, killForwards;
748     moveWordLeft = moveWordRight = deleteWordLeft = deleteWordRight = moveBeginning = moveEnd = killForwards = false;
749 
750 #if defined (MACOSX)
751     // For moving over/deleting words
752     if (mod & KMOD_ALT) {
753         if (c.sym == SDLK_LEFT) {
754             moveWordLeft = true;
755         }
756         else if (c.sym == SDLK_RIGHT) {
757             moveWordRight = true;
758         }
759         else if (c.sym == SDLK_DELETE) {
760             deleteWordRight = true;
761         }
762         else if (c.sym == SDLK_BACKSPACE) {
763             deleteWordLeft = true;
764         }
765     }
766     // For moving to extremes of the line
767     else if (mod & KMOD_META) {
768         if (c.sym == SDLK_LEFT) {
769             moveBeginning = true;
770         }
771         else if (c.sym == SDLK_RIGHT) {
772             moveEnd = true;
773         }
774     }
775     // Linux and Windows
776 #else
777     // Word operations
778     if (mod & KMOD_CTRL) {
779         if (c.sym == SDLK_LEFT) {
780             moveWordLeft = true;
781         }
782         else if (c.sym == SDLK_RIGHT) {
783             moveWordRight = true;
784         }
785         else if (c.sym == SDLK_DELETE) {
786             deleteWordRight = true;
787         }
788         else if (c.sym == SDLK_BACKSPACE) {
789             deleteWordLeft = true;
790         }
791     }
792     else if (c.sym == SDLK_HOME) {
793         moveBeginning = true;
794     }
795     else if (c.sym == SDLK_END) {
796         moveEnd = true;
797     }
798 #endif
799     // "bash" keys
800     if (mod & KMOD_CTRL) {
801         if (c.sym == SDLK_a) {
802             moveBeginning = true;
803         }
804         else if (c.sym == SDLK_e) {
805             moveEnd = true;
806         }
807         else if (c.sym == SDLK_k) {
808             killForwards = true;
809         }
810     }
811     // moveWordLeft = moveWordRight = deleteWordLeft = deleteWordRight = moveBeginning = moveEnd = killForwards
812 
813     if (moveWordLeft) {
814         cursorPos += content->PosWordLeft(cursorPos);
815     }
816     else if (moveWordRight) {
817         cursorPos += content->PosWordRight(cursorPos);
818     }
819     else if (deleteWordLeft) {
820         cursorPos += content->RemoveWordLeft(cursorPos);
821     }
822     else if (deleteWordRight) {
823         content->RemoveWordRight(cursorPos);
824     }
825     else if (moveBeginning) {
826         cursorPos = 0;
827     }
828     else if (moveEnd) {
829         cursorPos = content->Len()-1;
830     }
831     else if (killForwards) {
832         content->RemoveSubStr(cursorPos,content->Len()-1-cursorPos);
833     }
834     else if (c.sym == SDLK_LEFT) {
835         if (cursorPos > 0) {
836             cursorPos--;
837         }
838     }
839     else if (c.sym == SDLK_RIGHT) {
840         if (cursorPos < content->Len()-1) {
841             cursorPos++;
842         }
843     }
844     else if (c.sym == SDLK_DELETE) {
845         if (cursorPos < content->Len()-1) {
846             content->RemoveSubStr(cursorPos,1);
847         }
848     }
849     else if (c.sym == SDLK_BACKSPACE) {
850         if (cursorPos > 0) {
851             content->RemoveSubStr(cursorPos,-1);
852             cursorPos--;
853         }
854     }
855     else if (c.sym == SDLK_KP_ENTER || c.sym == SDLK_RETURN) {
856         ret = false;
857         //        c.sym = SDLK_DOWN;
858     }
859     else {
860         if (32 <= c.unicode  && c.unicode < 256)
861         {
862             ret=true;
863 
864             // insert character if there is room
865             if (content->Len() < maxLength_)
866             {
867                 for (int i=content->Len()-1;i>=cursorPos;i--)
868                     (*content)[i+1]=(*content)[i];
869 
870                 // guarantee proper null termination
871                 (*content)[content->Len()-1]='\0';
872                 (*content)[cursorPos]=c.unicode;
873                 cursorPos++;
874             }
875         }
876         else {
877             ret=false;
878         }
879     }
880 
881     if (cursorPos<0)    cursorPos=0;
882     if (cursorPos > content->Len()-1) cursorPos=content->Len()-1;
883 
884     return ret;
885 #else
886     return false;
887 #endif
888 }
889 
uMenuItemStringWithHistory(uMenu * M,const tOutput & desc,const tOutput & help,tString & c,int maxLength,std::deque<tString> & history,int limit)890 uMenuItemStringWithHistory::uMenuItemStringWithHistory(uMenu *M,const tOutput& desc, const tOutput& help,tString &c, int maxLength, std::deque<tString> &history, int limit ):
891         uMenuItemString(M, desc,help,c, maxLength ),
892         m_History(history),
893         m_HistoryPos(0),
894         m_HistoryLimit(limit)
895 {
896     m_History.push_front(tString());
897 }
898 
~uMenuItemStringWithHistory()899 uMenuItemStringWithHistory::~uMenuItemStringWithHistory()
900 {
901     if (content->Len() > 1)
902     {
903         for (std::deque<tString>::iterator i=m_History.begin(); i!=m_History.end(); ++i)
904         {
905             if (*i == *content)
906             {
907                 m_History.erase(i);
908                 break;
909             }
910         }
911         m_History.front() = *content;
912     }
913     else
914     {
915         m_History.pop_front();
916     }
917     if (m_History.size() > m_HistoryLimit)
918         m_History.pop_back();
919 }
920 
Event(SDL_Event & e)921 bool uMenuItemStringWithHistory::Event(SDL_Event &e)
922 {
923     // flag indicating that the event was handled
924     bool ret = false;
925 #ifndef DEDICATED
926     SDLMod mod = e.key.keysym.mod;
927 
928     if (e.type == SDL_KEYDOWN
929             && ((e.key.keysym.sym == SDLK_UP)
930                 || (e.key.keysym.sym == SDLK_p && mod & KMOD_CTRL)))
931     {
932         if (m_History.size() - 1 > m_HistoryPos)
933         {
934             // the new entry... save it before overwriting it
935             if (m_HistoryPos == 0)
936                 m_History.front() = *content;
937             m_HistoryPos++;
938             *content = m_History[m_HistoryPos];
939             cursorPos = content->Len() - 1;
940         }
941 
942         ret = true;
943     }
944     else if (e.type == SDL_KEYDOWN
945              && ((e.key.keysym.sym == SDLK_DOWN)
946                  || (e.key.keysym.sym == SDLK_n && mod & KMOD_CTRL)))
947     {
948         if (m_HistoryPos > 0)
949         {
950             m_HistoryPos--;
951             *content = m_History[m_HistoryPos];
952             cursorPos = content->Len() - 1;
953         }
954 
955         ret = true;
956     }
957 
958     // clamp cursor position
959     if (cursorPos<0)
960         cursorPos=0;
961     if (cursorPos > content->Len() - 1)
962         cursorPos=content->Len() - 1;
963 #endif
964 
965     // return result or delegate
966     return ret || uMenuItemString::Event(e);
967 }
968 
969 // *****************************************************
970 //  Submenu
971 // *****************************************************
972 
973 
uMenuItemSubmenu(uMenu * M,uMenu * s,const tOutput & help)974 uMenuItemSubmenu::uMenuItemSubmenu(uMenu *M,
975                                    uMenu *s,
976                                    const tOutput& help)
977         :uMenuItem(M,help),submenu(s){}
978 
979 
Render(REAL x,REAL y,REAL alpha,bool selected)980 void uMenuItemSubmenu::Render(REAL x,REAL y,REAL alpha,bool selected){
981     DisplayTextSpecial(x,y,submenu->title,selected,alpha,0);
982 }
983 
Enter()984 void uMenuItemSubmenu::Enter(){
985     submenu->Enter();
986 }
987 
988 // *****************************************************
989 //  action
990 // *****************************************************
991 
992 
uMenuItemAction(uMenu * M,const tOutput & n,const tOutput & help)993 uMenuItemAction::uMenuItemAction(uMenu *M,
994                                  const tOutput& n, const tOutput& help )
995         :uMenuItem(M,help),name_(n){}
996 
997 
Render(REAL x,REAL y,REAL alpha,bool selected)998 void uMenuItemAction::Render(REAL x,REAL y,REAL alpha,bool selected){
999     DisplayTextSpecial(x,y,name_,selected,alpha,0);
1000 }
1001 
1002 
Enter()1003 void uMenuItemAction::Enter()
1004 {
1005     tASSERT( 0 )
1006 }
1007 
1008 
1009 // *****************************************************
1010 //  function
1011 // *****************************************************
1012 
1013 
uMenuItemFunction(uMenu * M,const tOutput & n,const tOutput & help,FUNCPTR f)1014 uMenuItemFunction::uMenuItemFunction(uMenu *M,
1015                                      const tOutput& n, const tOutput& help,
1016                                      FUNCPTR f)
1017         :uMenuItemAction(M,n,help),func(f){}
1018 
Enter()1019 void uMenuItemFunction::Enter(){
1020     (*func)();
1021 }
1022 
1023 
1024 
uMenuItemFunctionInt(uMenu * M,const tOutput & n,const tOutput & help,INTFUNCPTR f,int a)1025 uMenuItemFunctionInt::uMenuItemFunctionInt(uMenu *M,
1026         const tOutput& n,
1027         const tOutput& help,
1028         INTFUNCPTR f,int a)
1029         :uMenuItemAction(M,n,help),func(f),arg(a){}
1030 
1031 
Enter()1032 void uMenuItemFunctionInt::Enter(){
1033     (*func)(arg);
1034 }
1035 
1036 // *****************************************************
1037 //  File Selection (added by k)
1038 // *****************************************************
1039 
NewChoice(uSelectItem<bool> *)1040 void uMenuItemFileSelection::NewChoice( uSelectItem<bool> * ) {}
NewChoice(char *,bool)1041 void uMenuItemFileSelection::NewChoice( char *, bool ) {}
1042 
Reload()1043 void uMenuItemFileSelection::Reload()
1044 {
1045     Clear();
1046     if ( defaultFileName_.Len() > 1 && defaultFilePath_.Len() > 1 )
1047         AddFile( defaultFileName_, defaultFilePath_, formatName_ );
1048     LoadDirectory( dir_, fileSpec_, formatName_ );
1049 }
1050 
LoadDirectory(const char * dir,const char * fileSpec,bool formatName)1051 void uMenuItemFileSelection::LoadDirectory( const char *dir, const char *fileSpec,
1052         bool formatName /*= true*/ )
1053 {
1054     tArray <tString> files;
1055     tString filePath ( dir );
1056     tDirectories::GetFiles( tString( dir ), tString( fileSpec ), files, getFilesFlag_ );
1057     for ( int i = 0; i < files.Len(); i++ )
1058     {
1059         AddFile( files( i ), filePath + files( i ), formatName );
1060     }
1061 }
1062 
AddFile(const char * fileName,const char * filePath,bool formatName)1063 void uMenuItemFileSelection::AddFile( const char *fileName, const char *filePath,
1064                                       bool formatName /*= true*/ )
1065 {
1066     tString menuName ( fileName );
1067     if ( formatName )
1068         tDirectories::FileNameToMenuName( fileName, menuName );
1069     uMenuItemSelection<tString>::NewChoice( menuName, "", tString( filePath ) );
1070 }
1071 
1072 // *****************************************************
1073 // Menu Enter/Leave-Callback
1074 // *****************************************************
1075 
1076 static tCallback *enter_anchor=NULL,*leave_anchor=NULL, *background_anchor=NULL;
1077 
uCallbackMenuEnter(VOIDFUNC * f)1078 uCallbackMenuEnter::uCallbackMenuEnter(VOIDFUNC *f)
1079         :tCallback(enter_anchor,f){}
1080 
MenuEnter()1081 void uCallbackMenuEnter::MenuEnter(){
1082     Exec(enter_anchor);
1083 }
1084 
uCallbackMenuLeave(VOIDFUNC * f)1085 uCallbackMenuLeave::uCallbackMenuLeave(VOIDFUNC *f)
1086         :tCallback(leave_anchor,f){}
1087 
MenuLeave()1088 void uCallbackMenuLeave::MenuLeave(){
1089     Exec(leave_anchor);
1090 }
1091 
uCallbackMenuBackground(VOIDFUNC * f)1092 uCallbackMenuBackground::uCallbackMenuBackground(VOIDFUNC *f)
1093         :tCallback(background_anchor,f){}
1094 
MenuBackground()1095 void uCallbackMenuBackground::MenuBackground(){
1096     Exec(background_anchor);
1097 }
1098 
1099 // poll input, return true if ESC was pressed
IdleInput()1100 bool uMenu::IdleInput()
1101 {
1102 #ifndef DEDICATED
1103     SDL_Event event;
1104     uInputProcessGuard inputProcessGuard;
1105     while (su_GetSDLInput(event))
1106     {
1107         switch (event.type)
1108         {
1109         case SDL_KEYDOWN:
1110             switch (event.key.keysym.sym)
1111             {
1112             case(SDLK_ESCAPE):
1113                 repeat = false;
1114                 lastkey=tSysTimeFloat();
1115                 return true;
1116                 break;
1117             default:
1118                 break;
1119             }
1120         default:
1121             break;
1122         }
1123     }
1124 #endif
1125 
1126     return false;
1127 }
1128 
1129 // return value: false only if the user pressed ESC
Message(const tOutput & message,const tOutput & interpretation,REAL to)1130 bool uMenu::Message(const tOutput& message, const tOutput& interpretation, REAL to){
1131     bool ret = true;
1132 #ifdef DEDICATED
1133     con << message << ":\n";
1134     con << interpretation << '\n';
1135 #else
1136 
1137     // reload textures (just in case)
1138     rITexture::UnloadAll();
1139 
1140     bool textOutBack = sr_textOut;
1141     sr_textOut = false;
1142 
1143     FUNCPTR idle_back = idle;
1144     uMenu::SetIdle(NULL);
1145 
1146     rTextField::SetDefaultColor( tColor(1,1,1,1) );
1147     rTextField::SetBlendColor( tColor(1,1,1,1) );
1148 
1149     rSysDep::ClearGL();
1150     rSysDep::SwapGL();
1151     if (sr_glOut)
1152     {
1153         rFont::s_defaultFont.Select();
1154         rFont::s_defaultFontSmall.Select();
1155     }
1156     rSysDep::ClearGL();
1157     rSysDep::SwapGL();
1158 
1159     REAL timeout = tSysTimeFloat() + to;
1160     SDL_Event tEvent;
1161 
1162     // catch some keyboard input
1163     {
1164         uInputProcessGuard inputProcessGuard;
1165         while (su_GetSDLInput(tEvent)) ;
1166     }
1167 
1168     {
1169         uInputProcessGuard inputProcessGuard;
1170 
1171         unsigned offset = 0; //amount of scrolling taking place
1172         //convert to an array for scrolling
1173         tString interpretationString;
1174         interpretationString << interpretation << "\n";
1175         std::vector<tString> lines;
1176         int lastNewline = 0;
1177         for (int i = 0; i < interpretationString.Len() - 1; ++i) {
1178             if (interpretationString[i] == '\n' && i != 0) {
1179                 lines.push_back(interpretationString.SubStr(lastNewline, i - lastNewline));
1180                 lastNewline = i + 1;
1181             }
1182         }
1183         while (  !quickexit &&
1184                  (to < 0 || tSysTimeFloat() < timeout)){
1185             //while(  !quickexit && ( !su_GetSDLInput(tEvent) || tEvent.type!=SDL_KEYDOWN) &&
1186             //        (to < 0 || tSysTimeFloat() < timeout)){
1187             if ( su_GetSDLInput(tEvent) && tEvent.type==SDL_KEYDOWN) {
1188                 switch (tEvent.key.keysym.sym) {
1189                 case SDLK_UP:
1190                     if (offset > 0)
1191                         offset -= 1;
1192                     continue;
1193                 case SDLK_DOWN:
1194                     offset += 1;
1195                     continue;
1196                 case SDLK_ESCAPE:
1197                     ret = false;
1198                     break;
1199                 default:
1200                     break;
1201                 }
1202                 break;
1203             }
1204             if ( sr_glOut )
1205             {
1206                 sr_ResetRenderState(true);
1207                 rViewport::s_viewportFullscreen.Select();
1208 
1209                 rSysDep::ClearGL();
1210 
1211                 GenericBackground();
1212 
1213                 REAL w=16*3/640.0;
1214                 REAL h=32*3/480.0;
1215 
1216 
1217                 //REAL middle=-.6;
1218 
1219                 tString m(message);
1220                 int len = m.Len();
1221                 if (w * len > 1.8)
1222                 {
1223                     h = h * 1.8 / (w * len);
1224                     w = 1.8 / len;
1225                 }
1226 
1227                 Color(1,1,1);
1228                 DisplayText(0,.8,w,h, message);
1229 
1230                 w = 16/640.0;
1231                 h = 32/480.0;
1232 
1233                 if (offset >= lines.size()) offset = lines.size() - 1;
1234                 {
1235                     rTextField c(-.8,.6, w, h);
1236 
1237                     for (unsigned i = offset; i < lines.size(); ++i)
1238                         c << lines[i] << "\n";
1239                 }
1240             }
1241             rSysDep::SwapGL();
1242             tAdvanceFrame();
1243         }
1244     }
1245 
1246     // catch some keyboard input
1247     {
1248         uInputProcessGuard inputProcessGuard;
1249         while (su_GetSDLInput(tEvent)) ;
1250     }
1251 
1252     uMenu::SetIdle(idle_back);
1253 
1254     // reload textures (just in case)
1255     rITexture::UnloadAll();
1256 
1257     sr_textOut = textOutBack;
1258 #endif
1259 
1260     return ret;
1261 }
1262 
1263