1 /*
2  * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
3  *
4  * Copyright (C) 2004-2009 Jan Reucker (original author)
5  * Copyright (C) 2005-2008, 2010 Jens Wilhelm Wulf
6  * Copyright (C) 2005, 2008 Olivier Bordes
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  */
23 
24 
25 /** \file crrc_gui_main.cpp
26  *
27  *  The central file responsible for managing the graphical user interface.
28  */
29 
30 #include "../i18n.h"
31 #include "../global.h"
32 #include "../SimStateHandler.h"
33 #include "crrc_gui_main.h"
34 #include "crrc_gui_viewwind.h"
35 #include "crrc_dialog.h"
36 #include "crrc_msgbox.h"
37 #include "puaImageFrame.h"
38 #include "crrc_planesel.h"
39 #include "crrc_video.h"
40 #include "crrc_launch.h"
41 #include "crrc_windthermal.h"
42 #include "crrc_joy.h"
43 #include "crrc_calibmap.h"
44 #include "../crrc_main.h"
45 #include "../config.h"
46 #include "../zoom.h"
47 #include "../crrc_sound.h"
48 #include "crrc_ctrlgen.h"
49 #include "crrc_audio.h"
50 #include "crrc_location.h"
51 #include "crrc_f3a.h"
52 #include "crrc_f3f.h"
53 #include "crrc_loadrobot.h"
54 #include "crrc_setrecordname.h"
55 #include "../robots.h"
56 #include "../mod_misc/filesystools.h"
57 #include "../mod_misc/lib_conversions.h"
58 #include "../mod_video/crrc_graphics.h"
59 #include "../mod_video/fonts.h"
60 
61 
62 #ifdef linux
63 #include "../global_video.h"
64 #endif
65 
66 //void GUI_IdleFunction(TSimInputs *in);
67 
68 static void quitDialogCallback(int);
69 
70 // Callback prototypes
71 static void file_exit_cb(puObject*);
72 static void file_save_cb(puObject*);
73 static void file_set_record_cb(puObject*);
74 
75 static void view_wind_cb(puObject*);
76 static void view_fullsc_cb(puObject*);
77 static void view_train_cb(puObject*);
78 static void view_testmode_cb(puObject*);
79 static void view_hudcompass_cb(puObject*);
80 static void view_windvectors_cb(puObject*);
81 static void view_verbosity_cb(puObject*);
82 static void view_modelwindow_cb(puObject*);
83 static void view_zoomin_cb(puObject*);
84 static void view_zoomout_cb(puObject*);
85 static void view_unzoom_cb(puObject*);
86 
87 static void sim_restart_cb(puObject*);
88 
89 static void opt_plane_cb(puObject*);
90 static void opt_launch_cb(puObject*);
91 static void opt_location_cb(puObject*);
92 static void opt_windthermal_cb(puObject*);
93 static void opt_video_cb(puObject*);
94 static void opt_audio_cb(puObject*);
95 static void opt_ctrl_general_cb(puObject*);
96 
97 static void game_f3a_cb(puObject*);
98 static void game_f3f_cb(puObject*);
99 
100 static void help_web_cb(puObject*);
101 static void help_keys_cb(puObject*);
102 static void help_about_cb(puObject*);
103 
104 static void robot_load_cb(puObject*);
105 static void robot_rm_all_cb(puObject*);
106 
107 
108 /** \brief Create the GUI object.
109  *
110  *  Creates the GUI and sets its "visible" state.
111  *  \param vis Set "visible" state. Defaults to true.
112  */
CGUIMain(bool vis)113 CGUIMain::CGUIMain(bool vis) : visible(vis)
114 {
115   fntInit();
116   puInit();
117   puSetDefaultStyle(PUSTYLE_SMALL_BEVELLED);
118 
119   // Light grey, no transparency
120   puSetDefaultColourScheme(0.85, 0.85, 0.85, 1.0);
121 
122   // Menu entries and callback mapping
123   // Caution: submenu-entries must be declared in reverse order!
124 
125   // File menu
126   const char *file_submenu[]    = {_("Exit"), _("Save this Flight's Log as.."),_("Save Settings"), NULL};
127   static puCallback file_submenu_cb[]  = {file_exit_cb, file_set_record_cb, file_save_cb, NULL};
128 
129   // View submenu
130   const char *view_submenu[]   = { _("Inspect Wind"),
131                                    _("Reset Zoom"),_("Zoom -"),
132                                    _("Zoom +"), _("Toggle Verbosity"),
133                                    _("Toggle Wind vectors"),
134                                    _("Toggle HUD Compass"),
135                                    _("Toggle Test Mode"),
136                                    _("Toggle Training Mode"),
137                                    _("Toggle Model Window Mode"),
138                                    _("Toggle Fullscreen"),
139                                    NULL};
140   puCallback view_submenu_cb[] = {  view_wind_cb,
141                                     view_unzoom_cb, view_zoomout_cb,
142                                     view_zoomin_cb, view_verbosity_cb,
143                                     view_windvectors_cb,
144                                     view_hudcompass_cb,
145                                     view_testmode_cb,
146                                     view_train_cb,
147                                     view_modelwindow_cb,
148                                     view_fullsc_cb,
149                                     NULL};
150   // Simulation menu
151   const char *sim_submenu[]   = {_("Restart"), NULL};
152   puCallback sim_submenu_cb[] = {sim_restart_cb, NULL};
153 
154   // Options menu
155   const char *opt_submenu[]  = {_("Audio"),
156                                 _("Controls"),
157                                 _("Video"), _("Wind, Thermals"),
158                                 _("Launch"), _("Location"),
159                                 _("Airplane"), NULL};
160   puCallback opt_submenu_cb[] = { opt_audio_cb,
161                                   opt_ctrl_general_cb,
162                                   opt_video_cb,
163                                   opt_windthermal_cb, opt_launch_cb,
164                                   opt_location_cb, opt_plane_cb, NULL};
165 
166   // Game menu
167   const char *game_submenu[]   = {"F3A", "F3F", NULL};
168   static puCallback game_submenu_cb[] = {game_f3a_cb, game_f3f_cb, NULL};
169 
170   // Help menu
171   const char *help_submenu[]   = {_("About"), _("Keys"), _("Help"), NULL};
172   puCallback help_submenu_cb[] = {help_about_cb, help_keys_cb, help_web_cb, NULL};
173 
174   // Robots menu
175   const char *robot_submenu[]   = {_("Remove all Robots"), _("Load Robot"), NULL};
176   puCallback robot_submenu_cb[] = {robot_rm_all_cb, robot_load_cb, NULL};
177 
178   // create the menu bar
179   main_menu_bar = new puMenuBar() ;
180   main_menu_bar->add_submenu(_("File"), (char**)file_submenu, file_submenu_cb);
181   main_menu_bar->add_submenu(_("View"), (char**)view_submenu, view_submenu_cb);
182   main_menu_bar->add_submenu(_("Simulation"), (char**)sim_submenu, sim_submenu_cb);
183   main_menu_bar->add_submenu(_("Options"), (char**)opt_submenu, opt_submenu_cb);
184   main_menu_bar->add_submenu(_("Game"), (char**)game_submenu, game_submenu_cb);
185   main_menu_bar->add_submenu(_("Robots"), (char**)robot_submenu, robot_submenu_cb);
186   main_menu_bar->add_submenu(_("Help"), (char**)help_submenu, help_submenu_cb);
187   main_menu_bar->close();
188 
189   // create the verbosity display
190   verboseOutput = new puText(30, 30);
191   verboseOutput->setColour(PUCOL_LABEL, 1, 0.1, 0.1);
192 
193   if (Video::textureFont)
194   {
195     verboseOutput->setLabelFont(Video::textureFont);
196   }
197   verboseOutput->reveal();
198 
199   // create the text widgets for HUD compass
200   for( int i = 0; i <= nCompass; i++ )
201   {
202     compass_x[i] = new puText(0, 0);
203     compass_x[i]->setColour(PUCOL_LABEL, 1., 0.1, 0.1, 0.7);
204     compass_x[i]->setLabelPlace(PUPLACE_BOTTOM_CENTERED);
205     if (Video::window_xsize > 800)
206       //compass_x[i]->setLabelFont(PUFONT_HELVETICA_18);
207     compass_x[i]->setLabel("");
208     compass_x[i]->hide();
209   }
210   for( int i = 0; i <= nCompass; i++ )
211   {
212     compass_y[i] = new puText(0, 0);
213     compass_y[i]->setColour(PUCOL_LABEL, 1., 0.1, 0.1, 0.7);
214     compass_y[i]->setLabelPlace(PUPLACE_CENTERED_RIGHT);
215     if (Video::window_xsize > 800)
216       //compass_y[i]->setLabelFont(PUFONT_HELVETICA_18);
217     compass_y[i]->setLabel("");
218     compass_y[i]->hide();
219   }
220 
221   // show or hide the GUI
222   if (visible)
223   {
224     main_menu_bar->reveal();
225     SDL_ShowCursor(SDL_ENABLE);
226   }
227   else
228   {
229     main_menu_bar->hide();
230     if (Global::TXInterface->inputMethod() == T_TX_Interface::eIM_mouse)
231       SDL_ShowCursor(SDL_ENABLE);
232     else
233       SDL_ShowCursor(SDL_DISABLE);
234   }
235 }
236 
237 
238 /** \brief Destroy the gui object.
239  *
240  *
241  */
~CGUIMain()242 CGUIMain::~CGUIMain()
243 {
244   puDeleteObject(main_menu_bar);
245   main_menu_bar = NULL;
246 }
247 
248 
249 /** \brief Hide the GUI.
250  *
251  *  This method hides the GUI and all included widgets.
252  */
hide()253 void CGUIMain::hide()
254 {
255   visible = false;
256   Global::Simulation->resume();
257   main_menu_bar->hide();
258   if (Global::TXInterface->inputMethod() == T_TX_Interface::eIM_mouse)
259     SDL_ShowCursor(SDL_ENABLE);
260   else
261     SDL_ShowCursor(SDL_DISABLE);
262   //Global::Simulation->resetIdle();
263 }
264 
265 
266 /** \brief Show the GUI.
267  *
268  *  This method sets the GUIs "visible" state to true and
269  *  activates all included widgets.
270  */
reveal()271 void CGUIMain::reveal()
272 {
273   //Global::Simulation->setNewIdle(GUI_IdleFunction);
274   visible = true;
275   Global::Simulation->pause();
276   main_menu_bar->reveal();
277   SDL_ShowCursor(SDL_ENABLE);
278   LOG(_("Press <ESC> to hide menu and resume simulation."));
279 }
280 
281 
282 /** \brief Draw the GUI.
283  *
284  *  This method has to be called for each OpenGL frame as
285  *  long as the GUI is visible. Note: to make the GUI
286  *  invisible it's not sufficient to stop calling draw(),
287  *  because any visible widget will remain clickable!
288  *  Make sure to call hide() before, or simply call draw()
289  *  as long as the GUI isVisible().
290  */
draw()291 void CGUIMain::draw()
292 {
293   glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
294   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
295   glAlphaFunc(GL_GREATER,0.1f);
296   glEnable(GL_BLEND);
297   puDisplay();
298   glPopAttrib();
299 }
300 
301 
302 /** \brief The GUI's key press event handler.
303  *
304  *  This function should be called from the SDL event loop. It takes a keyboard event
305  *  as an argument, translates it to PUI syntax and passes it to the
306  *  PUI-internal keyboard function. If there's no active widget which could
307  *  use the key event the function will return false, giving the caller
308  *  the opportunity to use the event for other purposes.
309  *  \param key A key symbol generated by an SDL keyboard event.
310  *  \return true if PUI was able to handle the event
311  */
keyDownEventHandler(SDL_keysym & key)312 bool CGUIMain::keyDownEventHandler(SDL_keysym& key)
313 {
314   int tkey;
315   bool ret;
316 
317   tkey = translateKey(key);
318   ret = puKeyboard(tkey, PU_DOWN);
319 
320   // ESC key handling
321   // note: translateKey() does not affect the ESC keysym,
322   // so it is safe to test the SDL key value here
323   if (!ret && (tkey == SDLK_ESCAPE))
324   {
325     if (isVisible())
326     {
327       CRRCDialog* top = CRRCDialog::getToplevel();
328       if (top != NULL)
329       {
330         if (top->hasCancelButton())
331         {
332           //std::cout << "Invoking CANCEL for toplevel dialog" << std::endl;
333           top->setValue(CRRC_DIALOG_CANCEL);
334           top->invokeCallback();
335         }
336       }
337       else
338       {
339         //std::cout << "No active dialog, hiding GUI" << std::endl;
340         hide();
341       }
342     }
343     else
344     {
345       reveal();
346     }
347     ret = true;
348   }
349 
350   return ret;
351 }
352 
353 /** \brief The GUI's key release event handler.
354  *
355  *  This function should be called from the SDL event loop. It takes a keyboard event
356  *  as an argument, translates it to PUI syntax and passes it to the
357  *  PUI-internal keyboard function. If there's no active widget which could
358  *  use the key event the function will return false, giving the caller
359  *  the opportunity to use the event for other purposes.
360  *  \param key A key symbol generated by an SDL keyboard event.
361  *  \return true if PUI was able to handle the event
362  */
keyUpEventHandler(SDL_keysym & key)363 bool CGUIMain::keyUpEventHandler(SDL_keysym& key)
364 {
365   int tkey = translateKey(key);
366   return puKeyboard(tkey, PU_UP);
367 }
368 
369 /** \brief The GUI's mouse button press event handler.
370  *
371  *  This function should be called from the SDL event loop.
372  *  It translates a mouse event to PUI syntax and passes it to the
373  *  PUI-internal mouse function. If there's no active widget which could
374  *  use the event the function will return false, giving the caller
375  *  the opportunity to use the event for other purposes.
376  *  \param btn Code of the button as reported by SDL
377  *  \param x Mouse x coordinate as reported by SDL
378  *  \param y Mouse y coordinate as reported by SDL
379  *  \return true if PUI was able to handle the event
380  */
mouseButtonDownHandler(int btn,int x,int y)381 bool CGUIMain::mouseButtonDownHandler(int btn, int x, int y)
382 {
383   return puMouse(translateMouse(btn), PU_DOWN, x, y);
384 }
385 
386 /** \brief The GUI's mouse button release event handler.
387  *
388  *  This function should be called from the SDL event loop.
389  *  It translates a mouse event to PUI syntax and passes it to the
390  *  PUI-internal mouse function. If there's no active widget which could
391  *  use the event the function will return false, giving the caller
392  *  the opportunity to use the event for other purposes.
393  *  \param btn Code of the button as reported by SDL
394  *  \param x Mouse x coordinate as reported by SDL
395  *  \param y Mouse y coordinate as reported by SDL
396  *  \return true if PUI was able to handle the event
397  */
mouseButtonUpHandler(int btn,int x,int y)398 bool CGUIMain::mouseButtonUpHandler(int btn, int x, int y)
399 {
400   return puMouse(translateMouse(btn), PU_UP, x, y);
401 }
402 
403 
404 /** \brief The GUI's mouse motion handler.
405  *
406  *
407  */
mouseMotionHandler(int x,int y)408 bool CGUIMain::mouseMotionHandler(int x, int y)
409 {
410   return puMouse(x, y);
411 }
412 
413 
414 /** \brief Translate SDL key macros to PUI macros.
415  *
416  *  Make sure that SDL unicode support is turned on to
417  *  make this work!
418  */
translateKey(const SDL_keysym & keysym)419 int CGUIMain::translateKey(const SDL_keysym& keysym)
420 {
421   // Printable characters
422   if (keysym.unicode > 0)
423     return keysym.unicode;
424 
425   // Numpad key, translate no non-numpad equivalent
426   if (keysym.sym >= SDLK_KP0 && keysym.sym <= SDLK_KP_EQUALS)
427   {
428     switch (keysym.sym)
429     {
430       case SDLK_KP0:
431         return PU_KEY_INSERT;
432       case SDLK_KP1:
433         return PU_KEY_END;
434       case SDLK_KP2:
435         return PU_KEY_DOWN;
436       case SDLK_KP3:
437         return PU_KEY_PAGE_DOWN;
438       case SDLK_KP4:
439         return PU_KEY_LEFT;
440       case SDLK_KP6:
441         return PU_KEY_RIGHT;
442       case SDLK_KP7:
443         return PU_KEY_HOME;
444       case SDLK_KP8:
445         return PU_KEY_UP;
446       case SDLK_KP9:
447         return PU_KEY_PAGE_UP;
448       default:
449         return -1;
450     }
451   }
452 
453   // Everything else
454   switch (keysym.sym)
455   {
456     case SDLK_UP:
457       return PU_KEY_UP;
458     case SDLK_DOWN:
459       return PU_KEY_DOWN;
460     case SDLK_LEFT:
461       return PU_KEY_LEFT;
462     case SDLK_RIGHT:
463       return PU_KEY_RIGHT;
464 
465     case SDLK_PAGEUP:
466       return PU_KEY_PAGE_UP;
467     case SDLK_PAGEDOWN:
468       return PU_KEY_PAGE_DOWN;
469     case SDLK_HOME:
470       return PU_KEY_HOME;
471     case SDLK_END:
472       return PU_KEY_END;
473     case SDLK_INSERT:
474       return PU_KEY_INSERT;
475     case SDLK_DELETE:
476       return -1;
477 
478     case SDLK_F1:
479       return PU_KEY_F1;
480     case SDLK_F2:
481       return PU_KEY_F2;
482     case SDLK_F3:
483       return PU_KEY_F3;
484     case SDLK_F4:
485       return PU_KEY_F4;
486     case SDLK_F5:
487       return PU_KEY_F5;
488     case SDLK_F6:
489       return PU_KEY_F6;
490     case SDLK_F7:
491       return PU_KEY_F7;
492     case SDLK_F8:
493       return PU_KEY_F8;
494     case SDLK_F9:
495       return PU_KEY_F9;
496     case SDLK_F10:
497       return PU_KEY_F10;
498     case SDLK_F11:
499       return PU_KEY_F11;
500     case SDLK_F12:
501       return PU_KEY_F12;
502 
503     default:
504       return -1;
505   }
506 }
507 
508 // description: see header file
setVerboseText(const char * msg)509 void CGUIMain::setVerboseText(const char* msg)
510 {
511   verboseOutput->setLabel(msg);
512 };
513 
514 // description: see header file
doHUDCompass(const float field_of_view)515 void CGUIMain::doHUDCompass(const float field_of_view)
516 {
517   if (Global::HUDCompass && !isVisible())
518   {
519     // compass visible..
520     puFont stdFont, bigFont;
521     if (Video::textureFont)
522     {
523       stdFont = puFont ( Video::textureFont, Video::window_xsize > 800 ? 20 : 14 );
524       bigFont = puFont ( Video::textureFont, Video::window_xsize > 800 ? 24 : 18 );
525     }
526     else{
527       stdFont = Video::window_xsize > 800 ? PUFONT_HELVETICA_18 : PUFONT_9_BY_15;
528       bigFont = Video::window_xsize > 800 ? PUFONT_TIMES_ROMAN_24 : PUFONT_HELVETICA_18;
529     }
530     CRRCMath::Vector3 look_dir = Video::looking_pos - player_pos;
531     float azimuth = atan2(look_dir.r[0], -look_dir.r[2])*180.0/M_PI;
532     float elevation = atan2(look_dir.r[1], sqrt(pow(look_dir.r[0],2) + pow(look_dir.r[2],2)))*180.0/M_PI;
533     azimuth = azimuth < 0.0 ? azimuth + 360.0 : azimuth;
534     // for some reason the actual field of view is quite smaller than specified by field_of_view
535     // and if not corrected this would result in inaccurate compass labels
536     float fov_x = 0.66 * field_of_view * Video::window_xsize/Video::window_ysize;
537     float fov_y = 0.66 * field_of_view;
538     float kx = 1.0/atan(0.5*fov_x/180.0*M_PI);
539     float ky = 1.0/atan(0.5*fov_y/180.0*M_PI);
540     float step = (int)(fov_x/nCompass);
541     if ( step < 1 )
542       step = 1;
543     else if ( step < 5 )
544       step = 5;
545     else if ( step < 15 )
546       step = 15;
547     else if ( step < 30 )
548       step = 30;
549     else if ( step < 45 )
550       step = 45;
551     else
552       step = 90;
553 
554     int az0 = (int)(azimuth/step) * step;
555     int el0 = (int)(elevation/step) * step;
556 
557     for( int i = 0; i <= nCompass; i++ )
558     {
559       int az = az0 + (i - nCompass/2)*step;
560       int xx = Video::window_xsize/2*(1.0 + kx*atan((az - azimuth)/180.0*M_PI));
561       az = az < 0 ? az + 360 : az;
562       az = az >= 360 ? az - 360 : az;
563       if (xx > 0 && xx < Video::window_xsize)
564       {
565         compass_x[i]->setLabelFont(bigFont);
566         compass_x[i]->setColour(PUCOL_LABEL, 1., 0.1, 0.1, 1.);
567         switch (az)
568         {
569           case 0:
570             compass_msg_x[i] = "N";
571             break;
572           case 45:
573             compass_msg_x[i] = "NE";
574             break;
575           case 90:
576             compass_msg_x[i] = "E";
577             break;
578           case 135:
579             compass_msg_x[i] = "SE";
580             break;
581           case 180:
582             compass_msg_x[i] = "S";
583             break;
584           case 225:
585             compass_msg_x[i] = "SW";
586             break;
587           case 270:
588             compass_msg_x[i] = "W";
589             break;
590           case 315:
591             compass_msg_x[i] = "NW";
592             break;
593           default:
594             compass_msg_x[i] = itoStr(az, '0', 3, true);
595             compass_x[i]->setLabelFont(stdFont);
596             compass_x[i]->setColour(PUCOL_LABEL, 1., 0.1, 0.1, 0.7);
597         }
598         compass_x[i]->setPosition(xx, Video::window_ysize);
599         compass_x[i]->setLabel(compass_msg_x[i].c_str());
600         compass_x[i]->reveal();
601       }
602       else
603         compass_x[i]->hide();
604     }
605 
606     for( int i = 0; i <= nCompass; i++ )
607     {
608       int el = el0 + (i - nCompass/2)*step;
609       int yy = Video::window_ysize/2*(1.0 + ky*atan((el - elevation)/180.0*M_PI));
610       if (yy > 0 && yy < Video::window_ysize)
611       {
612         if (el == 0)
613         {
614           compass_msg_y[i] = "0";
615           compass_y[i]->setLabelFont(bigFont);
616           compass_y[i]->setColour(PUCOL_LABEL, 1., 0.1, 0.1, 1.);
617         }
618         else
619         {
620           compass_msg_y[i] = itoStr(abs(el), '0', 2, true);
621           compass_msg_y[i] = (el > 0 ? '+' : '-') + compass_msg_y[i];
622           compass_y[i]->setLabelFont(stdFont);
623           compass_y[i]->setColour(PUCOL_LABEL, 1., 0.1, 0.1, 0.7);
624         }
625         compass_y[i]->setPosition(0, yy);
626         compass_y[i]->setLabel(compass_msg_y[i].c_str());
627         compass_y[i]->reveal();
628       }
629       else
630         compass_y[i]->hide();
631     }
632 
633     // ..compass visible
634   }
635   else
636   {
637     // compass hidden..
638     for( int i = 0; i <= nCompass; i++ )
639     {
640       compass_x[i]->hide();
641       compass_y[i]->hide();
642     }
643     // ..compass hidden
644   }
645 };
646 
647 // description: see header file
errorMsg(const char * message)648 void CGUIMain::errorMsg(const char* message)
649 {
650   fprintf(stderr, "--- GUI error popup ---\n%s\n--- end popup ---------\n", message);
651   new CGUIMsgBox(message);
652 }
653 
doQuitDialog()654 void CGUIMain::doQuitDialog()
655 {
656   reveal();
657   if (options_changed())
658   {
659     CGUIMsgBox *msg = new CGUIMsgBox(_("Configuration has changed, save?"),
660                                       CRRC_DIALOG_OK | CRRC_DIALOG_CANCEL,
661                                       quitDialogCallback);
662     msg->setOKButtonLegend(_("Yes"));
663     msg->setCancelButtonLegend(_("No"));
664   }
665   else
666   {
667     Global::Simulation->quit();
668   }
669 }
670 
671 // The menu entry callbacks.
672 
optionNotImplementedYetBox()673 void optionNotImplementedYetBox()
674 {
675   new CGUIMsgBox("Changing this from the GUI isn't implemented yet.\n"
676                  "Please see crrcsim.xml and documentation/options.txt\n"
677                  "for how to configure CRRCSim.");
678 }
679 
file_exit_cb(puObject * obj)680 static void file_exit_cb(puObject *obj)
681 {
682   Global::gui->doQuitDialog();
683 }
684 
file_save_cb(puObject * obj)685 static void file_save_cb(puObject *obj)
686 {
687   options_saveToFile();
688 }
689 
file_set_record_cb(puObject *)690 static void file_set_record_cb(puObject*)
691 {
692   new CGUISetRecordNameDialog();
693 }
694 
sim_restart_cb(puObject * obj)695 static void sim_restart_cb(puObject *obj)
696 {
697   Global::Simulation->reset();
698   Global::gui->hide();
699 }
700 
opt_plane_cb(puObject * obj)701 static void opt_plane_cb(puObject *obj)
702 {
703   new CGUIPlaneSelectDialog();
704 }
705 
opt_location_cb(puObject * obj)706 static void opt_location_cb(puObject *obj)
707 {
708   new CGUILocationDialog();
709 }
710 
opt_launch_cb(puObject * obj)711 static void opt_launch_cb(puObject *obj)
712 {
713   new CGUILaunchDialog();
714 }
715 
opt_windthermal_cb(puObject * obj)716 static void opt_windthermal_cb(puObject *obj)
717 {
718   new CGUIWindThermalDialog();
719 }
720 
opt_video_cb(puObject * obj)721 static void opt_video_cb(puObject *obj)
722 {
723   new CGUIVideoDialog();
724 }
725 
opt_audio_cb(puObject * obj)726 static void opt_audio_cb(puObject *obj)
727 {
728   new CGUIAudioDialog();
729 }
730 
opt_ctrl_general_cb(puObject * obj)731 static void opt_ctrl_general_cb(puObject *obj)
732 {
733   new CGUICtrlGeneralDialog();
734 }
735 
game_f3a_cb(puObject * obj)736 static void game_f3a_cb(puObject *obj)
737 {
738   new CGUIF3ADialog();
739 }
740 
game_f3f_cb(puObject * obj)741 static void game_f3f_cb(puObject *obj)
742 {
743   new CGUIF3FDialog();
744 }
745 
robot_load_cb(puObject * obj)746 static void robot_load_cb(puObject *obj)
747 {
748   new CGUILoadRobotDialog();
749 }
750 
robot_rm_all_cb(puObject * obj)751 static void robot_rm_all_cb(puObject *obj)
752 {
753   Global::robots->RemoveAll();
754 }
755 
help_web_cb(puObject * obj)756 static void help_web_cb(puObject *obj)
757 {
758   new CGUIMsgBox(_("See http://crrcsim.sourceforge.net/ for more information.\n\n"
759                  "With your copy of CRRCSim you also received documentation\n"
760                  "in a subdirectory named \"documentation\". Take a look at \"index.html\"."));
761 }
762 
help_keys_cb(puObject * obj)763 static void help_keys_cb(puObject *obj)
764 {
765   const int keyw = 125;
766   const int txtw = 500;
767   int y_in_win = 1 * DLG_DEF_BUTTON_HEIGHT + 2 * DLG_DEF_SPACE;
768   int dy = DLG_CHECK_H + 0.4 * DLG_DEF_SPACE;
769 
770   CRRCDialog *KeysDialog = new CRRCDialog(0, 0, CRRC_DIALOG_OK);
771 
772   puText *txta01 = new puText(DLG_DEF_SPACE, y_in_win);
773   txta01->setLabel("-");
774   puText *txtb01 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
775   txtb01->setLabel(_("zoom out (assuming zoom.control is KEYBOARD)"));
776 
777   puText *txta02 = new puText(DLG_DEF_SPACE, y_in_win += dy);
778   txta02->setLabel("+");
779   puText *txtb02 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
780   txtb02->setLabel(_("zoom in (assuming zoom.control is KEYBOARD)"));
781 
782   puText *txta03 = new puText(DLG_DEF_SPACE, y_in_win += dy);
783   txta03->setLabel(_("up/down arrow"));
784   puText *txtb03 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
785   txtb03->setLabel(_("elevator"));
786 
787   puText *txta04 = new puText(DLG_DEF_SPACE, y_in_win += dy);
788   txta04->setLabel(_("left/right arrow"));
789   puText *txtb04 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
790   txtb04->setLabel(_("rudder"));
791 
792   puText *txta05 = new puText(DLG_DEF_SPACE, y_in_win += dy);
793   txta05->setLabel(_("pg-dwn"));
794   puText *txtb05 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
795   txtb05->setLabel(_("decrease throttle (if you aren't using JOYSTICK or better)"));
796 
797   puText *txta06 = new puText(DLG_DEF_SPACE, y_in_win += dy);
798   txta06->setLabel(_("pg-up"));
799   puText *txtb06 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
800   txtb06->setLabel(_("increase throttle (if you aren't using JOYSTICK or better)"));
801 
802   puText *txta07 = new puText(DLG_DEF_SPACE, y_in_win += dy);
803   txta07->setLabel("b");
804   puText *txtb07 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
805   txtb07->setLabel(_("toggle spoiler/airbrake (if function is not mapped to a controller)"));
806 
807   puText *txta08 = new puText(DLG_DEF_SPACE, y_in_win += dy);
808   txta08->setLabel("g");
809   puText *txtb08 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
810   txtb08->setLabel(_("toggle landing gear (if function is not mapped to a controller)"));
811 
812   puText *txta09 = new puText(DLG_DEF_SPACE, y_in_win += dy);
813   txta09->setLabel("w");
814   puText *txtb09 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
815   txtb09->setLabel(_("toggle 3D wind vector visualisation mode (0..2)"));
816 
817   puText *txta10 = new puText(DLG_DEF_SPACE, y_in_win += dy);
818   txta10->setLabel("h");
819   puText *txtb10 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
820   txtb10->setLabel(_("toggle HUD compass visualisation mode"));
821 
822   puText *txta11 = new puText(DLG_DEF_SPACE, y_in_win += dy);
823   txta11->setLabel("v");
824   puText *txtb11 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
825   txtb11->setLabel(_("toggle verbosity level (0..3) to display control inputs/FOV/FPS"));
826 
827   puText *txta12 = new puText(DLG_DEF_SPACE, y_in_win += dy);
828   txta12->setLabel("t");
829   puText *txtb12 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
830   txtb12->setLabel(_("toggle training mode which displays the location of the thermals"));
831 
832   puText *txta13 = new puText(DLG_DEF_SPACE, y_in_win += dy);
833   txta12->setLabel("m");
834   puText *txtb13 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
835   txtb12->setLabel(_("toggle model's close-view window visualisation mode (0..2)"));
836 
837   puText *txta14 = new puText(DLG_DEF_SPACE, y_in_win += dy);
838   txta13->setLabel("d");
839   puText *txtb14 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
840   txtb13->setLabel(_("toggle control input debugging mode"));
841 
842   puText *txta15 = new puText(DLG_DEF_SPACE, y_in_win += dy);
843   txta14->setLabel("c");
844   puText *txtb15 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
845   txtb14->setLabel(_("reload model configuration"));
846 
847   puText *txta16 = new puText(DLG_DEF_SPACE, y_in_win += dy);
848   txta15->setLabel("p");
849   puText *txtb16 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
850   txtb15->setLabel(_("pause/resume simulation"));
851 
852   puText *txta17 = new puText(DLG_DEF_SPACE, y_in_win += dy);
853   txta16->setLabel("r");
854   puText *txtb17 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
855   txtb16->setLabel(_("restart after crash"));
856 
857   puText *txta18 = new puText(DLG_DEF_SPACE, y_in_win += dy);
858   txta17->setLabel("q");
859   puText *txtb18 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
860   txtb17->setLabel(_("quit"));
861 
862   puText* __attribute__((unused)) txta19 = new puText(DLG_DEF_SPACE, y_in_win += dy);
863   txta18->setLabel("ESC");
864   puText* __attribute__((unused)) txtb19 = new puText(keyw + DLG_DEF_SPACE, y_in_win);
865   txtb18->setLabel(_("show/hide menu"));
866 
867   puText *txta00 = new puText(DLG_DEF_SPACE, y_in_win += dy + DLG_DEF_SPACE);
868   txta00->setLabel(_("Key mapping:\n"));
869 
870   y_in_win += dy;
871 
872 
873   KeysDialog->close();
874   KeysDialog->setSize(1 * keyw + 1 * txtw + 3 * DLG_DEF_SPACE,
875                       1 * y_in_win        + 1 * DLG_DEF_SPACE);
876   KeysDialog->centerOnScreen();
877   KeysDialog->reveal();
878 }
879 
help_about_cb(puObject * obj)880 static void help_about_cb(puObject *obj)
881 {
882   const int txtw = 200;
883   const int imgw = 128;
884   const int imgh = 128;
885   int y_in_win = 1 * DLG_DEF_BUTTON_HEIGHT + 2 * DLG_DEF_SPACE;
886   static ssgTexture *image = NULL;
887 
888   CRRCDialog *AboutDialog = new CRRCDialog(0, 0, CRRC_DIALOG_OK);
889 
890   // package identification: string needs to be persistent after construction
891   static std::string package_txt;
892   package_txt = PACKAGE_NAME;
893   package_txt += "\n\n";
894   package_txt += _("version info:\n");
895   package_txt += PACKAGE_VERSION;
896   puText *txt01 = new puText(2 * DLG_DEF_SPACE + imgw, y_in_win + imgh);
897   txt01->setLabel(package_txt.c_str());
898   txt01->setLabelPlace(PUPLACE_BELOW_RIGHT);
899 
900   // load "About" image file
901   if (!image)
902   {
903     std::string imageAboutFilename = FileSysTools::getDataPath("textures/Crrcsim.jpg");
904     image = new ssgTexture(imageAboutFilename.c_str());
905   }
906 
907   // frame widget to store image
908   new puaImageFrame (
909     1*DLG_DEF_SPACE,        y_in_win,
910     1*DLG_DEF_SPACE + imgw, y_in_win + imgh,
911     image
912   );
913 
914   AboutDialog->close();
915   AboutDialog->setSize(1 * imgw + 1 * txtw + 2 * DLG_DEF_SPACE,
916                        1 * imgh + 1 * DLG_DEF_BUTTON_HEIGHT + 3 * DLG_DEF_SPACE);
917   AboutDialog->centerOnScreen();
918   AboutDialog->reveal();
919 }
920 
view_fullsc_cb(puObject * obj)921 static void view_fullsc_cb(puObject *obj)
922 {
923   int nFullscreen;
924   int nX, nY;
925   if (cfgfile->getInt("video.fullscreen.fUse"))
926   {
927     nFullscreen = 0;
928     nX          = cfgfile->getInt("video.resolution.window.x", 800);
929     nY          = cfgfile->getInt("video.resolution.window.y", 600);
930   }
931   else
932   {
933     nFullscreen = 1;
934     nX          = cfgfile->getInt("video.resolution.fullscreen.x", 800);
935     nY          = cfgfile->getInt("video.resolution.fullscreen.y", 600);
936   }
937   #ifdef linux
938   // On Linux we can switch the mode on-the-fly.
939   // This also puts the fullscreen flag back into the config file.
940   Video::setupScreen(nX, nY, nFullscreen);
941   #else
942   // Other platforms need to put the value back into the
943   // config file and restart manually.
944   cfgfile->setAttributeOverwrite("video.fullscreen.fUse", nFullscreen);
945   new CGUIMsgBox(_("Please save your configuration and restart CRRCSim!"));
946   #endif
947 }
948 
view_wind_cb(puObject * obj)949 static void  view_wind_cb(puObject *obj)
950 {
951   new CGUIViewWindDialog();
952 }
953 
view_train_cb(puObject * obj)954 static void view_train_cb(puObject *obj)
955 {
956   if (Global::training_mode)
957     Global::training_mode = 0;
958   else
959     Global::training_mode = 1;
960 }
961 
view_verbosity_cb(puObject * obj)962 static void view_verbosity_cb(puObject *obj)
963 {
964   if (Global::nVerbosity == 3)
965     Global::nVerbosity = 0;
966   else
967     Global::nVerbosity++;
968 }
969 
view_hudcompass_cb(puObject * obj)970 static void view_hudcompass_cb(puObject *obj)
971 {
972   if (Global::HUDCompass)
973     Global::HUDCompass = 0;
974   else
975     Global::HUDCompass = 1;
976 }
977 
view_windvectors_cb(puObject * obj)978 static void view_windvectors_cb(puObject *obj)
979 {
980   if (Global::windVectors == 2)
981     Global::windVectors = 0;
982   else
983     Global::windVectors++;
984 }
985 
view_modelwindow_cb(puObject * obj)986 static void view_modelwindow_cb(puObject *obj)
987 {
988   if (Global::modelView == 2)
989     Global::modelView = 0;
990   else
991     Global::modelView++;
992 }
993 
view_zoomin_cb(puObject * obj)994 static void view_zoomin_cb(puObject *obj)
995 {
996   zoom_in();
997 }
998 
view_zoomout_cb(puObject * obj)999 static void view_zoomout_cb(puObject *obj)
1000 {
1001   zoom_out();
1002 }
1003 
view_unzoom_cb(puObject * obj)1004 static void view_unzoom_cb(puObject *obj)
1005 {
1006   zoom_reset();
1007 }
1008 
quitDialogCallback(int choice)1009 static void quitDialogCallback(int choice)
1010 {
1011   if (choice == CRRC_DIALOG_OK)
1012   {
1013     options_saveToFile();
1014   }
1015   Global::Simulation->quit();
1016 }
1017 
view_testmode_cb(puObject * obj)1018 static void view_testmode_cb(puObject *obj)
1019 {
1020   Global::testmode = !Global::testmode;
1021   if (Global::testmode)
1022     activate_test_mode();
1023   else
1024     leave_test_mode();
1025 }
1026 
1027 
1028 /** \brief The GUI's "idle" function.
1029  *
1030  *  This function will be called from the main loop while
1031  *  the GUI is active. Its main purpose is to propagate
1032  *  the interface's input values to the CGUIMain object.
1033  *  A dialog that needs to evaluate the input signals may
1034  *  then access this local copy of the values by calling
1035  *  CGUIMain::getInputValues()
1036  */
GUI_IdleFunction(TSimInputs * in)1037 void CGUIMain:: GUI_IdleFunction(TSimInputs *in)
1038 {
1039   CRRCDialog *dlg = CRRCDialog::getToplevel();
1040 
1041   Global::gui->setInputValues(in);
1042   if (dlg != NULL)
1043   {
1044     dlg->update();
1045   }
1046 }
1047 
setInputValues(TSimInputs * in)1048 void CGUIMain::setInputValues(TSimInputs *in)
1049 {
1050   memcpy(&input, in, sizeof(TSimInputs));
1051 }
1052 
getInputValues()1053 TSimInputs* CGUIMain::getInputValues()
1054 {
1055   return &input;
1056 }
1057 
1058