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