]> git.mxchange.org Git - flightgear.git/blob - src/GUI/gui_funcs.cxx
don't run the help browser from a property that a user could have
[flightgear.git] / src / GUI / gui_funcs.cxx
1 /**************************************************************************
2  * gui_funcs.cxx
3  *
4  * Based on gui.cxx and renamed on 2002/08/13 by Erik Hofman.
5  *
6  * Written 1998 by Durk Talsma, started Juni, 1998.  For the flight gear
7  * project.
8  *
9  * Additional mouse supported added by David Megginson, 1999.
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 as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24  *
25  * $Id$
26  **************************************************************************/
27
28
29 #ifdef HAVE_CONFIG_H
30 #  include <config.h>
31 #endif
32
33 #include <simgear/compiler.h>
34
35 #ifdef SG_MATH_EXCEPTION_CLASH
36 #  include <math.h>
37 #endif
38
39 #ifdef HAVE_WINDOWS_H
40 #  include <windows.h>
41 #endif
42
43 #include SG_GL_H
44
45 #if defined(FX) && defined(XMESA)
46 #  include <GL/xmesa.h>
47 #endif
48
49 #include STL_FSTREAM
50 #include STL_STRING
51
52 #include <stdlib.h>
53 #include <string.h>
54
55 // for help call back
56 #ifdef WIN32
57 # include <shellapi.h>
58 # ifdef __CYGWIN__
59 #  include <sys/cygwin.h>
60 # endif
61 #endif
62
63 #include <sstream>
64
65 #include <simgear/constants.h>
66 #include <simgear/debug/logstream.hxx>
67 #include <simgear/misc/sg_path.hxx>
68 #include <simgear/screen/screen-dump.hxx>
69
70 #include <Include/general.hxx>
71 #include <Aircraft/aircraft.hxx>
72 #include <Aircraft/controls.hxx>
73 #include <Airports/simple.hxx>
74 #include <Cockpit/panel.hxx>
75 #include <FDM/flight.hxx>
76 #include <Main/main.hxx>
77 #include <Main/fg_init.hxx>
78 #include <Main/fg_io.hxx>
79 #include <Main/globals.hxx>
80 #include <Main/fg_props.hxx>
81 #include <Main/renderer.hxx>
82 #include <Main/viewmgr.hxx>
83 #include <GUI/new_gui.hxx>
84
85 #if defined( WIN32 ) && !defined( __CYGWIN__ ) && !defined(__MINGW32__)
86 #  include <simgear/screen/win32-printer.h>
87 #  include <simgear/screen/GlBitmaps.h>
88 #endif
89
90 #include "gui.h"
91
92 SG_USING_STD(string);
93 SG_USING_STD(cout);
94
95
96 #if defined( TR_HIRES_SNAP)
97 #include <simgear/screen/tr.h>
98 extern void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
99                          GLfloat x_end, GLfloat y_end );
100 #endif
101
102
103 const __fg_gui_fn_t __fg_gui_fn[] = {
104 #ifdef TR_HIRES_SNAP
105         {"dumpHiResSnapShot", fgHiResDumpWrapper},
106 #endif
107         {"dumpSnapShot", fgDumpSnapShotWrapper},
108 #if defined( WIN32 ) && !defined( __CYGWIN__) && !defined(__MINGW32__)
109         {"printScreen", printScreen},
110 #endif
111         // Help
112         {"helpCb", helpCb},
113
114         // Structure termination
115         {"", NULL}
116 };
117
118
119 /* ================ General Purpose Functions ================ */
120
121 // General Purpose Message Box. Makes sure no more than 5 different
122 // messages are displayed at the same time, and none of them are
123 // duplicates. (5 is a *lot*, but this will hardly ever be reached
124 // and we don't want to miss any, either.)
125 void mkDialog (const char *txt)
126 {
127     NewGUI *gui = (NewGUI *)globals->get_subsystem("gui");
128     if (!gui)
129         return;
130     SGPropertyNode *master = gui->getDialogProperties("message");
131     if (!master)
132         return;
133
134     const int maxdialogs = 5;
135     string name;
136     SGPropertyNode *msg = fgGetNode("/sim/gui/dialogs", true);
137     int i;
138     for (i = 0; i < maxdialogs; i++) {
139         std::ostringstream s;
140         s << "message-" << i;
141         name = s.str();
142
143         if (!msg->getNode(name.c_str(), false))
144             break;
145
146         if (!strcmp(txt, msg->getNode(name.c_str())->getStringValue("message"))) {
147             SG_LOG(SG_GENERAL, SG_WARN, "mkDialog(): duplicate of message " << txt);
148             return;
149         }
150     }
151     if (i == maxdialogs)
152         return;
153     msg = msg->getNode(name.c_str(), true);
154     msg->setStringValue("message", txt);
155     msg = msg->getNode("dialog", true);
156     copyProperties(master, msg);
157     msg->setStringValue("name", name.c_str());
158     gui->newDialog(msg);
159     gui->showDialog(name.c_str());
160 }
161
162 // Message Box to report an error.
163 void guiErrorMessage (const char *txt)
164 {
165     SG_LOG(SG_GENERAL, SG_ALERT, txt);
166     mkDialog(txt);
167 }
168
169 // Message Box to report a throwable (usually an exception).
170 void guiErrorMessage (const char *txt, const sg_throwable &throwable)
171 {
172     string msg = txt;
173     msg += '\n';
174     msg += throwable.getFormattedMessage();
175     if (!throwable.getOrigin().empty()) {
176         msg += "\n (reported by ";
177         msg += throwable.getOrigin();
178         msg += ')';
179     }
180     SG_LOG(SG_GENERAL, SG_ALERT, msg);
181     mkDialog(msg.c_str());
182 }
183
184
185
186 /* -----------------------------------------------------------------------
187 the Gui callback functions 
188 ____________________________________________________________________*/
189
190
191 // Hier Neu :-) This is my newly added code
192 // Added by David Findlay <nedz@bigpond.com>
193 // on Sunday 3rd of December
194
195
196 void helpCb ()
197 {
198     string command;
199         
200 #if defined(FX) && !defined(WIN32)
201 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
202     if ( globals->get_fullscreen() ) {
203         globals->set_fullscreen(false);
204         XMesaSetFXmode( XMESA_FX_WINDOW );
205     }
206 #  endif
207 #endif
208         
209     SGPath path( globals->get_fg_root() );
210     path.append( "Docs/index.html" );
211         
212 #if !defined(WIN32)
213
214     command = globals->get_browser();
215     string::size_type pos;
216     if ((pos = command.find("%u", 0)) != string::npos)
217         command.replace(pos, 2, path.str());
218     else
219         command += " " + path.str();
220
221     command += " &";
222     system( command.c_str() );
223
224 #else // WIN32
225
226     // Look for favorite browser
227     char Dummy[1024], ExecName[1024], browserParameter[1024];
228     char win32_name[1024];
229 # ifdef __CYGWIN__
230     cygwin32_conv_to_full_win32_path(path.c_str(),win32_name);
231 # else
232     strncpy(win32_name,path.c_str(), 1024);
233 # endif
234     Dummy[0] = 0;
235     FindExecutable(win32_name, Dummy, ExecName);
236     snprintf(browserParameter, 1024, "file:///%s", win32_name);
237     ShellExecute ( NULL, "open", ExecName, browserParameter, Dummy,
238                    SW_SHOWNORMAL ) ;
239
240 #endif
241         
242     mkDialog ("Help started in your web browser window.");
243 }
244
245 #if defined( TR_HIRES_SNAP)
246 void fgHiResDump()
247 {
248     FILE *f;
249     string message;
250     bool show_pu_cursor = false;
251     bool menu_status = fgGetBool("/sim/menubar/visibility");
252     char *filename = new char [24];
253     static int count = 1;
254
255     static const SGPropertyNode *master_freeze
256         = fgGetNode("/sim/freeze/master");
257
258     bool freeze = master_freeze->getBoolValue();
259     if ( !freeze ) {
260         fgSetBool("/sim/freeze/master", true);
261     }
262
263     fgSetBool("/sim/menubar/visibility", false);
264     TurnCursorOff();
265     if ( !puCursorIsHidden() ) {
266         show_pu_cursor = true;
267         puHideCursor();
268     }
269
270     FGRenderer *renderer = globals->get_renderer();
271 //     renderer->init();
272     renderer->resize( fgGetInt("/sim/startup/xsize"),
273                       fgGetInt("/sim/startup/ysize") );
274
275     // we need two render frames here to clear the menu and cursor
276     // ... not sure why but doing an extra fgRenderFrame() shouldn't
277     // hurt anything
278     //renderer->update( true );
279     //renderer->update( true );
280
281     // This ImageSize stuff is a temporary hack
282     // should probably use 128x128 tile size and
283     // support any image size
284
285     // This should be a requester to get multiplier from user
286     int multiplier = fgGetInt("/sim/startup/hires-multiplier", 3);
287     int width = fgGetInt("/sim/startup/xsize");
288     int height = fgGetInt("/sim/startup/ysize");
289         
290     /* allocate buffer large enough to store one tile */
291     GLubyte *tile = (GLubyte *)malloc(width * height * 3 * sizeof(GLubyte));
292     if (!tile) {
293         printf("Malloc of tile buffer failed!\n");
294         return;
295     }
296
297     int imageWidth  = multiplier*width;
298     int imageHeight = multiplier*height;
299
300     /* allocate buffer to hold a row of tiles */
301     GLubyte *buffer
302         = (GLubyte *)malloc(imageWidth * height * 3 * sizeof(GLubyte));
303     if (!buffer) {
304         free(tile);
305         printf("Malloc of tile row buffer failed!\n");
306         return;
307     }
308     TRcontext *tr = trNew();
309     trTileSize(tr, width, height, 0);
310     trTileBuffer(tr, GL_RGB, GL_UNSIGNED_BYTE, tile);
311     trImageSize(tr, imageWidth, imageHeight);
312     trRowOrder(tr, TR_TOP_TO_BOTTOM);
313     // OSGFIXME
314 //     sgFrustum *frustum = ssgGetFrustum();
315 //     trFrustum(tr,
316 //               frustum->getLeft(), frustum->getRight(),
317 //               frustum->getBot(),  frustum->getTop(), 
318 //               frustum->getNear(), frustum->getFar());
319         
320     /* Prepare ppm output file */
321     while (count < 1000) {
322         snprintf(filename, 24, "fgfs-screen-%03d.ppm", count++);
323         if ( (f = fopen(filename, "r")) == NULL )
324             break;
325         fclose(f);
326     }
327     f = fopen(filename, "wb");
328     if (!f) {
329         printf("Couldn't open image file: %s\n", filename);
330         free(buffer);
331         free(tile);
332         return;
333     }
334     fprintf(f,"P6\n");
335     fprintf(f,"# ppm-file created by %s\n", "trdemo2");
336     fprintf(f,"%i %i\n", imageWidth, imageHeight);
337     fprintf(f,"255\n");
338
339     /* just to be safe... */
340     glPixelStorei(GL_PACK_ALIGNMENT, 1);
341
342     /* Because the HUD and Panel change the ViewPort we will
343      * need to handle some lowlevel stuff ourselves */
344     int ncols = trGet(tr, TR_COLUMNS);
345     int nrows = trGet(tr, TR_ROWS);
346
347     bool do_hud = fgGetBool("/sim/hud/visibility");
348     GLfloat hud_col_step = 640.0 / ncols;
349     GLfloat hud_row_step = 480.0 / nrows;
350         
351     bool do_panel = fgPanelVisible();
352     GLfloat panel_col_step = globals->get_current_panel()->getWidth() / ncols;
353     GLfloat panel_row_step = globals->get_current_panel()->getHeight() / nrows;
354
355     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
356     glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
357     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
358     glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
359     glHint(GL_FOG_HINT, GL_NICEST);
360         
361     /* Draw tiles */
362     int more = 1;
363     while (more) {
364         trBeginTile(tr);
365         int curColumn = trGet(tr, TR_CURRENT_COLUMN);
366         int curRow =  trGet(tr, TR_CURRENT_ROW);
367
368         renderer->update( false );
369         // OSGFIXME
370 //         if ( do_hud )
371 //             fgUpdateHUD( curColumn*hud_col_step,      curRow*hud_row_step,
372 //                          (curColumn+1)*hud_col_step, (curRow+1)*hud_row_step );
373         // OSGFIXME
374 //         if (do_panel)
375 //             globals->get_current_panel()->update(
376 //                                    curColumn*panel_col_step, panel_col_step,
377 //                                    curRow*panel_row_step,    panel_row_step );
378         more = trEndTile(tr);
379
380         /* save tile into tile row buffer*/
381         int curTileWidth = trGet(tr, TR_CURRENT_TILE_WIDTH);
382         int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
383         int bytesPerTileRow = (width) * 3*sizeof(GLubyte);
384         int xOffset = curColumn * bytesPerTileRow;
385         int bytesPerCurrentTileRow = (curTileWidth) * 3*sizeof(GLubyte);
386         int i;
387         for (i=0;i<height;i++) {
388             memcpy(buffer + i*bytesPerImageRow + xOffset, /* Dest */
389                    tile + i*bytesPerTileRow,              /* Src */
390                    bytesPerCurrentTileRow);               /* Byte count*/
391         }
392
393         if (curColumn == trGet(tr, TR_COLUMNS)-1) {
394             /* write this buffered row of tiles to the file */
395             int curTileHeight = trGet(tr, TR_CURRENT_TILE_HEIGHT);
396             int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
397             int i;
398             for (i=0;i<curTileHeight;i++) {
399                 /* Remember, OpenGL images are bottom to top.  Have to reverse. */
400                 GLubyte *rowPtr = buffer + (curTileHeight-1-i) * bytesPerImageRow;
401                 fwrite(rowPtr, 1, imageWidth*3, f);
402             }
403         }
404
405     }
406
407     renderer->resize( width, height );
408
409     trDelete(tr);
410
411     glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
412     glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
413     glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
414     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);
415     if ( (!strcmp(fgGetString("/sim/rendering/fog"), "disabled")) ||
416          (!fgGetBool("/sim/rendering/shading"))) {
417         // if fastest fog requested, or if flat shading force fastest
418         glHint ( GL_FOG_HINT, GL_FASTEST );
419     } else if ( !strcmp(fgGetString("/sim/rendering/fog"), "nicest") ) {
420         glHint ( GL_FOG_HINT, GL_DONT_CARE );
421     }
422
423     fclose(f);
424
425     message = "Snapshot saved to \"";
426     message += filename;
427     message += "\".";
428     mkDialog (message.c_str());
429
430     free(tile);
431     free(buffer);
432
433     delete [] filename;
434
435     if ( show_pu_cursor ) {
436         puShowCursor();
437     }
438
439     TurnCursorOn();
440     fgSetBool("/sim/menubar/visibility", menu_status);
441
442     if ( !freeze ) {
443         fgSetBool("/sim/freeze/master", false);
444     }
445 }
446 #endif // #if defined( TR_HIRES_SNAP)
447
448
449 #if defined( WIN32 ) && !defined( __CYGWIN__) && !defined(__MINGW32__)
450
451 void rotateView( double roll, double pitch, double yaw )
452 {
453         // rotate view
454 }
455
456 GlBitmap *b1 = NULL;
457 GLubyte *hiResScreenCapture( int multiplier )
458 {
459     float oldfov = fgGetDouble("/sim/current-view/field-of-view");
460     float fov = oldfov / multiplier;
461     FGViewer *v = globals->get_current_view();
462     fgSetDouble("/sim/current-view/field-of-view", fov);
463 //     globals->get_renderer()->init();
464     int cur_width = fgGetInt("/sim/startup/xsize");
465     int cur_height = fgGetInt("/sim/startup/ysize");
466     delete( b1 );
467     // New empty (mostly) bitmap
468     b1 = new GlBitmap( GL_RGB, 1, 1, (unsigned char *)"123" );
469     int x,y;
470     for ( y = 0; y < multiplier; y++ ) {
471         for ( x = 0; x < multiplier; x++ ) {
472             globals->get_renderer()->resize( cur_width, cur_height );
473             // pan to tile
474             rotateView( 0, (y*fov)-((multiplier-1)*fov/2), (x*fov)-((multiplier-1)*fov/2) );
475             globals->get_renderer()->update( false );
476             // restore view
477             GlBitmap b2;
478             b1->copyBitmap( &b2, cur_width*x, cur_height*y );
479         }
480     }
481     fgSetDouble("/sim/current-view/field-of-view", oldfov);
482     return b1->getBitmap();
483 }
484 #endif
485
486 #if defined( WIN32 ) && !defined( __CYGWIN__) && !defined(__MINGW32__)
487 // win32 print screen function
488 void printScreen () {
489     bool show_pu_cursor = false;
490     TurnCursorOff();
491     if ( !puCursorIsHidden() ) {
492         show_pu_cursor = true;
493         puHideCursor();
494     }
495
496     CGlPrinter p( CGlPrinter::PRINT_BITMAP );
497     int cur_width = fgGetInt("/sim/startup/xsize");
498     int cur_height = fgGetInt("/sim/startup/ysize");
499     p.Begin( "FlightGear", cur_width*3, cur_height*3 );
500     p.End( hiResScreenCapture(3) );
501
502     if ( show_pu_cursor ) {
503         puShowCursor();
504     }
505     TurnCursorOn();
506 }
507 #endif // #ifdef WIN32
508
509
510 void fgDumpSnapShotWrapper () {
511     fgDumpSnapShot();
512 }
513
514
515 void fgHiResDumpWrapper () {
516     fgHiResDump();
517 }
518
519
520 // do a screen snap shot
521 bool fgDumpSnapShot () {
522     bool show_pu_cursor = false;
523     static SGConstPropertyNode_ptr master_freeze = fgGetNode("/sim/freeze/master");
524
525     bool freeze = master_freeze->getBoolValue();
526     if ( !freeze ) {
527         fgSetBool("/sim/freeze/master", true);
528     }
529
530     TurnCursorOff();
531     if ( !puCursorIsHidden() ) {
532         show_pu_cursor = true;
533         puHideCursor();
534     }
535     fgSetBool("/sim/signals/screenshot", true);
536
537     FGRenderer *renderer = globals->get_renderer();
538     renderer->resize( fgGetInt("/sim/startup/xsize"),
539                       fgGetInt("/sim/startup/ysize") );
540
541     // we need two render frames here to clear the menu and cursor
542     // ... not sure why but doing an extra fgRenderFrame() shouldn't
543     // hurt anything
544     renderer->update( true );
545     renderer->update( true );
546
547     string dir = fgGetString("/sim/paths/screenshot-dir", fgGetString("/sim/fg-current"));
548     SGPath path(dir + '/');
549     if (path.create_dir( 0755 )) {
550         SG_LOG(SG_GENERAL, SG_ALERT, "Cannot create screenshot directory '"
551                 << dir << "'. Trying home directory.");
552         dir = fgGetString("/sim/fg-home");
553     }
554
555     char filename[24];
556     static int count = 1;
557     while (count < 1000) {
558         snprintf(filename, 24, "fgfs-screen-%03d.ppm", count++);
559
560         SGPath p(dir);
561         p.append(filename);
562         if (!p.exists()) {
563             path.set(p.str());
564             break;
565         }
566     }
567
568     int result = sg_glDumpWindow(path.c_str(),
569                                  fgGetInt("/sim/startup/xsize"),
570                                  fgGetInt("/sim/startup/ysize"));
571
572     fgSetString("/sim/paths/screenshot-last", path.c_str());
573     fgSetBool("/sim/signals/screenshot", false);
574
575     if ( show_pu_cursor ) {
576         puShowCursor();
577     }
578
579     TurnCursorOn();
580
581     if ( !freeze ) {
582         fgSetBool("/sim/freeze/master", false);
583     }
584     return result != 0;
585 }
586
587 // do an entire scenegraph dump
588 void fgDumpSceneGraph()
589 {
590     char *filename = new char [24];
591     string message;
592     static int count = 1;
593
594     FGRenderer *renderer = globals->get_renderer();
595
596     static const SGPropertyNode *master_freeze
597         = fgGetNode("/sim/freeze/master");
598
599     bool freeze = master_freeze->getBoolValue();
600     if ( !freeze ) {
601         fgSetBool("/sim/freeze/master", true);
602     }
603
604     while (count < 1000) {
605         FILE *fp;
606         snprintf(filename, 24, "fgfs-graph-%03d.osg", count++);
607         if ( (fp = fopen(filename, "r")) == NULL )
608             break;
609         fclose(fp);
610     }
611
612     if ( fgDumpSceneGraphToFile(filename)) {
613         message = "Entire scene graph saved to \"";
614         message += filename;
615         message += "\".";
616     } else {
617         message = "Failed to save to \"";
618         message += filename;
619         message += "\".";
620     }
621
622     mkDialog (message.c_str());
623
624     delete [] filename;
625
626     if ( !freeze ) {
627         fgSetBool("/sim/freeze/master", false);
628     }
629 }
630
631     
632 // do an terrain branch dump
633 void fgDumpTerrainBranch()
634 {
635     char *filename = new char [24];
636     string message;
637     static int count = 1;
638
639     FGRenderer *renderer = globals->get_renderer();
640
641     static const SGPropertyNode *master_freeze
642         = fgGetNode("/sim/freeze/master");
643
644     bool freeze = master_freeze->getBoolValue();
645     if ( !freeze ) {
646         fgSetBool("/sim/freeze/master", true);
647     }
648
649     while (count < 1000) {
650         FILE *fp;
651         snprintf(filename, 24, "fgfs-graph-%03d.osg", count++);
652         if ( (fp = fopen(filename, "r")) == NULL )
653             break;
654         fclose(fp);
655     }
656
657     if ( fgDumpTerrainBranchToFile(filename)) {
658         message = "Terrain graph saved to \"";
659         message += filename;
660         message += "\".";
661     } else {
662         message = "Failed to save to \"";
663         message += filename;
664         message += "\".";
665     }
666
667     mkDialog (message.c_str());
668
669     delete [] filename;
670
671     if ( !freeze ) {
672         fgSetBool("/sim/freeze/master", false);
673     }
674 }
675
676     
677