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