]> git.mxchange.org Git - flightgear.git/blob - src/GUI/gui_funcs.cxx
Fix some places relying on public 'using std::string' in SimGear
[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 #include <fstream>
36 #include <string>
37 #include <cstring>
38 #include <sstream>
39
40 #include <stdlib.h>
41
42 #include <simgear/debug/logstream.hxx>
43 #include <simgear/misc/sg_path.hxx>
44 #include <simgear/screen/screen-dump.hxx>
45 #include <simgear/structure/event_mgr.hxx>
46 #include <simgear/props/props_io.hxx>
47
48 #include <Cockpit/panel.hxx>
49 #include <Main/globals.hxx>
50 #include <Main/fg_props.hxx>
51 #include <Main/fg_os.hxx>
52 #include <Viewer/renderer.hxx>
53 #include <Viewer/viewmgr.hxx>
54 #include <Viewer/WindowSystemAdapter.hxx>
55 #include <Viewer/CameraGroup.hxx>
56 #include <GUI/new_gui.hxx>
57
58
59 #ifdef _WIN32
60 #  include <shellapi.h>
61 #endif
62
63 #ifdef SG_MAC
64 # include "FGCocoaMenuBar.hxx" // for cocoaOpenUrl
65 #endif
66
67 #include "gui.h"
68
69 using std::string;
70
71
72 #if defined( TR_HIRES_SNAP)
73 #include <simgear/screen/tr.h>
74 extern void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
75                          GLfloat x_end, GLfloat y_end );
76 #endif
77
78
79 const __fg_gui_fn_t __fg_gui_fn[] = {
80 #ifdef TR_HIRES_SNAP
81         {"dumpHiResSnapShot", fgHiResDumpWrapper},
82 #endif
83         {"dumpSnapShot", fgDumpSnapShotWrapper},
84         // Help
85         {"helpCb", helpCb},
86
87         // Structure termination
88         {"", NULL}
89 };
90
91
92 /* ================ General Purpose Functions ================ */
93
94 // General Purpose Message Box. Makes sure no more than 5 different
95 // messages are displayed at the same time, and none of them are
96 // duplicates. (5 is a *lot*, but this will hardly ever be reached
97 // and we don't want to miss any, either.)
98 void mkDialog (const char *txt)
99 {
100     NewGUI *gui = (NewGUI *)globals->get_subsystem("gui");
101     if (!gui)
102         return;
103     SGPropertyNode *master = gui->getDialogProperties("message");
104     if (!master)
105         return;
106
107     const int maxdialogs = 5;
108     string name;
109     SGPropertyNode *msg = fgGetNode("/sim/gui/dialogs", true);
110     int i;
111     for (i = 0; i < maxdialogs; i++) {
112         std::ostringstream s;
113         s << "message-" << i;
114         name = s.str();
115
116         if (!msg->getNode(name.c_str(), false))
117             break;
118
119         if (!strcmp(txt, msg->getNode(name.c_str())->getStringValue("message"))) {
120             SG_LOG(SG_GENERAL, SG_WARN, "mkDialog(): duplicate of message " << txt);
121             return;
122         }
123     }
124     if (i == maxdialogs)
125         return;
126     msg = msg->getNode(name.c_str(), true);
127     msg->setStringValue("message", txt);
128     msg = msg->getNode("dialog", true);
129     copyProperties(master, msg);
130     msg->setStringValue("name", name.c_str());
131     gui->newDialog(msg);
132     gui->showDialog(name.c_str());
133 }
134
135 // Message Box to report an error.
136 void guiErrorMessage (const char *txt)
137 {
138     SG_LOG(SG_GENERAL, SG_ALERT, txt);
139     mkDialog(txt);
140 }
141
142 // Message Box to report a throwable (usually an exception).
143 void guiErrorMessage (const char *txt, const sg_throwable &throwable)
144 {
145     string msg = txt;
146     msg += '\n';
147     msg += throwable.getFormattedMessage();
148     if (!std::strlen(throwable.getOrigin()) != 0) {
149         msg += "\n (reported by ";
150         msg += throwable.getOrigin();
151         msg += ')';
152     }
153     SG_LOG(SG_GENERAL, SG_ALERT, msg);
154     mkDialog(msg.c_str());
155 }
156
157
158
159 /* -----------------------------------------------------------------------
160 the Gui callback functions 
161 ____________________________________________________________________*/
162
163 void helpCb()
164 {
165     openBrowser( "Docs/index.html" );
166 }
167
168 bool openBrowser(const std::string& aAddress)
169 {
170     bool ok = true;
171     string address(aAddress);
172     
173     // do not resolve addresses with given protocol, i.e. "http://...", "ftp://..."
174     if (address.find("://")==string::npos)
175     {
176         // resolve local file path
177         SGPath path(address);
178         path = globals->resolve_maybe_aircraft_path(address);
179         if (!path.isNull())
180             address = path.str();
181         else
182         {
183             mkDialog ("Sorry, file not found!");
184             SG_LOG(SG_GENERAL, SG_ALERT, "openBrowser: Cannot find requested file '"  
185                     << address << "'.");
186             return false;
187         }
188     }
189
190 #ifdef SG_MAC
191   if (address.find("://")==string::npos) {
192     address = "file://" + address;
193   }
194   
195   cocoaOpenUrl(address);
196 #elif defined _WIN32
197
198     // Look for favorite browser
199     char win32_name[1024];
200 # ifdef __CYGWIN__
201     cygwin32_conv_to_full_win32_path(address.c_str(),win32_name);
202 # else
203     strncpy(win32_name,address.c_str(), 1024);
204 # endif
205     ShellExecute ( NULL, "open", win32_name, NULL, NULL,
206                    SW_SHOWNORMAL ) ;
207 #else
208     // Linux, BSD, SGI etc
209     string command = globals->get_browser();
210     string::size_type pos;
211     if ((pos = command.find("%u", 0)) != string::npos)
212         command.replace(pos, 2, address);
213     else
214         command += " \"" + address +"\"";
215
216     command += " &";
217     ok = (system( command.c_str() ) == 0);
218 #endif
219
220     mkDialog("The file is shown in your web browser window.");
221     return ok;
222 }
223
224 #if defined( TR_HIRES_SNAP)
225 void fgHiResDump()
226 {
227     FILE *f;
228     string message;
229     bool menu_status = fgGetBool("/sim/menubar/visibility");
230     char *filename = new char [24];
231     static int count = 1;
232
233     static const SGPropertyNode *master_freeze
234         = fgGetNode("/sim/freeze/master");
235
236     bool freeze = master_freeze->getBoolValue();
237     if ( !freeze ) {
238         fgSetBool("/sim/freeze/master", true);
239     }
240
241     fgSetBool("/sim/menubar/visibility", false);
242     int mouse = fgGetMouseCursor();
243     fgSetMouseCursor(MOUSE_CURSOR_NONE);
244
245     FGRenderer *renderer = globals->get_renderer();
246 //     renderer->init();
247     renderer->resize( fgGetInt("/sim/startup/xsize"),
248                       fgGetInt("/sim/startup/ysize") );
249
250     // we need two render frames here to clear the menu and cursor
251     // ... not sure why but doing an extra fgRenderFrame() shouldn't
252     // hurt anything
253     //renderer->update( true );
254     //renderer->update( true );
255
256     // This ImageSize stuff is a temporary hack
257     // should probably use 128x128 tile size and
258     // support any image size
259
260     // This should be a requester to get multiplier from user
261     int multiplier = fgGetInt("/sim/startup/hires-multiplier", 3);
262     int width = fgGetInt("/sim/startup/xsize");
263     int height = fgGetInt("/sim/startup/ysize");
264         
265     /* allocate buffer large enough to store one tile */
266     GLubyte *tile = (GLubyte *)malloc(width * height * 3 * sizeof(GLubyte));
267     if (!tile) {
268         delete [] filename;
269         printf("Malloc of tile buffer failed!\n");
270         return;
271     }
272
273     int imageWidth  = multiplier*width;
274     int imageHeight = multiplier*height;
275
276     /* allocate buffer to hold a row of tiles */
277     GLubyte *buffer
278         = (GLubyte *)malloc(imageWidth * height * 3 * sizeof(GLubyte));
279     if (!buffer) {
280         delete [] filename;
281         free(tile);
282         printf("Malloc of tile row buffer failed!\n");
283         return;
284     }
285     TRcontext *tr = trNew();
286     trTileSize(tr, width, height, 0);
287     trTileBuffer(tr, GL_RGB, GL_UNSIGNED_BYTE, tile);
288     trImageSize(tr, imageWidth, imageHeight);
289     trRowOrder(tr, TR_TOP_TO_BOTTOM);
290     // OSGFIXME
291 //     sgFrustum *frustum = ssgGetFrustum();
292 //     trFrustum(tr,
293 //               frustum->getLeft(), frustum->getRight(),
294 //               frustum->getBot(),  frustum->getTop(), 
295 //               frustum->getNear(), frustum->getFar());
296         
297     /* Prepare ppm output file */
298     while (count < 1000) {
299         snprintf(filename, 24, "fgfs-screen-%03d.ppm", count++);
300         if ( (f = fopen(filename, "r")) == NULL )
301             break;
302         fclose(f);
303     }
304     f = fopen(filename, "wb");
305     if (!f) {
306         printf("Couldn't open image file: %s\n", filename);
307         delete [] filename;
308         free(buffer);
309         free(tile);
310         return;
311     }
312     fprintf(f,"P6\n");
313     fprintf(f,"# ppm-file created by %s\n", "trdemo2");
314     fprintf(f,"%i %i\n", imageWidth, imageHeight);
315     fprintf(f,"255\n");
316
317     /* just to be safe... */
318     glPixelStorei(GL_PACK_ALIGNMENT, 1);
319
320     // OSGFIXME
321 #if 0
322     /* Because the HUD and Panel change the ViewPort we will
323      * need to handle some lowlevel stuff ourselves */
324     int ncols = trGet(tr, TR_COLUMNS);
325     int nrows = trGet(tr, TR_ROWS);
326
327     bool do_hud = fgGetBool("/sim/hud/visibility");
328     GLfloat hud_col_step = 640.0 / ncols;
329     GLfloat hud_row_step = 480.0 / nrows;
330         
331     bool do_panel = fgPanelVisible();
332     GLfloat panel_col_step = globals->get_current_panel()->getWidth() / ncols;
333     GLfloat panel_row_step = globals->get_current_panel()->getHeight() / nrows;
334 #endif
335     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
336     glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
337     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
338     glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
339     glHint(GL_FOG_HINT, GL_NICEST);
340         
341     /* Draw tiles */
342     int more = 1;
343     while (more) {
344         trBeginTile(tr);
345         int curColumn = trGet(tr, TR_CURRENT_COLUMN);
346         // int curRow =  trGet(tr, TR_CURRENT_ROW);
347
348         renderer->update();
349         // OSGFIXME
350 //         if ( do_hud )
351 //             fgUpdateHUD( curColumn*hud_col_step,      curRow*hud_row_step,
352 //                          (curColumn+1)*hud_col_step, (curRow+1)*hud_row_step );
353         // OSGFIXME
354 //         if (do_panel)
355 //             globals->get_current_panel()->update(
356 //                                    curColumn*panel_col_step, panel_col_step,
357 //                                    curRow*panel_row_step,    panel_row_step );
358         more = trEndTile(tr);
359
360         /* save tile into tile row buffer*/
361         int curTileWidth = trGet(tr, TR_CURRENT_TILE_WIDTH);
362         int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
363         int bytesPerTileRow = (width) * 3*sizeof(GLubyte);
364         int xOffset = curColumn * bytesPerTileRow;
365         int bytesPerCurrentTileRow = (curTileWidth) * 3*sizeof(GLubyte);
366         int i;
367         for (i=0;i<height;i++) {
368             memcpy(buffer + i*bytesPerImageRow + xOffset, /* Dest */
369                    tile + i*bytesPerTileRow,              /* Src */
370                    bytesPerCurrentTileRow);               /* Byte count*/
371         }
372
373         if (curColumn == trGet(tr, TR_COLUMNS)-1) {
374             /* write this buffered row of tiles to the file */
375             int curTileHeight = trGet(tr, TR_CURRENT_TILE_HEIGHT);
376             int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
377             int i;
378             for (i=0;i<curTileHeight;i++) {
379                 /* Remember, OpenGL images are bottom to top.  Have to reverse. */
380                 GLubyte *rowPtr = buffer + (curTileHeight-1-i) * bytesPerImageRow;
381                 fwrite(rowPtr, 1, imageWidth*3, f);
382             }
383         }
384
385     }
386
387     renderer->resize( width, height );
388
389     trDelete(tr);
390
391     glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
392     glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
393     glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
394     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);
395     if ( (!strcmp(fgGetString("/sim/rendering/fog"), "disabled")) ||
396          (!fgGetBool("/sim/rendering/shading"))) {
397         // if fastest fog requested, or if flat shading force fastest
398         glHint ( GL_FOG_HINT, GL_FASTEST );
399     } else if ( !strcmp(fgGetString("/sim/rendering/fog"), "nicest") ) {
400         glHint ( GL_FOG_HINT, GL_DONT_CARE );
401     }
402
403     fclose(f);
404
405     message = "Snapshot saved to \"";
406     message += filename;
407     message += "\".";
408     mkDialog (message.c_str());
409
410     free(tile);
411     free(buffer);
412
413     delete [] filename;
414
415     fgSetMouseCursor(mouse);
416     fgSetBool("/sim/menubar/visibility", menu_status);
417
418     if ( !freeze ) {
419         fgSetBool("/sim/freeze/master", false);
420     }
421 }
422 #endif // #if defined( TR_HIRES_SNAP)
423
424 void fgDumpSnapShotWrapper () {
425     fgDumpSnapShot();
426 }
427
428
429 void fgHiResDumpWrapper () {
430     fgHiResDump();
431 }
432
433 namespace
434 {
435     using namespace flightgear;
436
437     class GUISnapShotOperation : 
438         public GraphicsContextOperation
439     {
440     public:
441
442         // start new snap shot
443         static bool start()
444         {
445             // allow only one snapshot at a time
446             if (_snapShotOp.valid())
447                 return false;
448             _snapShotOp = new GUISnapShotOperation();
449             /* register with graphics context so actual snap shot is done
450              * in the graphics context (thread) */
451             osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
452             WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA();
453             osg::GraphicsContext* gc = 0;
454             if (guiCamera)
455                 gc = guiCamera->getGraphicsContext();
456             if (gc) {
457                 gc->add(_snapShotOp.get());
458             } else {
459                 wsa->windows[0]->gc->add(_snapShotOp.get());
460             }
461             return true;
462         }
463
464     private:
465         // constructor to be executed in main loop's thread
466         GUISnapShotOperation() :
467             flightgear::GraphicsContextOperation(std::string("GUI snap shot")),
468             _master_freeze(fgGetNode("/sim/freeze/master", true)),
469             _freeze(_master_freeze->getBoolValue()),
470             _result(false),
471             _mouse(fgGetMouseCursor())
472         {
473             if (!_freeze)
474                 _master_freeze->setBoolValue(true);
475
476             fgSetMouseCursor(MOUSE_CURSOR_NONE);
477
478             string dir = fgGetString("/sim/paths/screenshot-dir");
479             if (dir.empty())
480                 dir = fgGetString("/sim/fg-current");
481
482             _path.set(dir + '/');
483             if (_path.create_dir( 0755 )) {
484                 SG_LOG(SG_GENERAL, SG_ALERT, "Cannot create screenshot directory '"
485                         << dir << "'. Trying home directory.");
486                 dir = fgGetString("/sim/fg-home");
487             }
488
489             char filename[24];
490             static int count = 1;
491             while (count < 1000) {
492                 snprintf(filename, 24, "fgfs-screen-%03d.png", count++);
493
494                 SGPath p(dir);
495                 p.append(filename);
496                 if (!p.exists()) {
497                     _path.set(p.str());
498                     break;
499                 }
500             }
501
502             _xsize = fgGetInt("/sim/startup/xsize");
503             _ysize = fgGetInt("/sim/startup/ysize");
504
505             FGRenderer *renderer = globals->get_renderer();
506             renderer->resize(_xsize, _ysize);
507             globals->get_event_mgr()->addTask("SnapShotTimer",
508                     this, &GUISnapShotOperation::timerExpired,
509                     0.1, false);
510         }
511
512         // to be executed in graphics context (maybe separate thread)
513         void run(osg::GraphicsContext* gc)
514         {
515             _result = sg_glDumpWindow(_path.c_str(),
516                                      _xsize,
517                                      _ysize);
518         }
519
520         // timer method, to be executed in main loop's thread
521         virtual void timerExpired()
522         {
523             if (isFinished())
524             {
525                 globals->get_event_mgr()->removeTask("SnapShotTimer");
526
527                 fgSetString("/sim/paths/screenshot-last", _path.c_str());
528                 fgSetBool("/sim/signals/screenshot", _result);
529
530                 fgSetMouseCursor(_mouse);
531
532                 if ( !_freeze )
533                     _master_freeze->setBoolValue(false);
534
535                 _snapShotOp = 0;
536             }
537         }
538     
539         static osg::ref_ptr<GUISnapShotOperation> _snapShotOp;
540         SGPropertyNode_ptr _master_freeze;
541         bool _freeze;
542         bool _result;
543         int _mouse;
544         int _xsize, _ysize;
545         SGPath _path;
546     };
547
548 }
549
550 osg::ref_ptr<GUISnapShotOperation> GUISnapShotOperation::_snapShotOp;
551
552 // do a screen snap shot
553 bool fgDumpSnapShot ()
554 {
555 #if 1
556     // start snap shot operation, while needs to be executed in
557     // graphics context
558     return GUISnapShotOperation::start();
559 #else
560     // obsolete code => remove when new code is stable
561     static SGConstPropertyNode_ptr master_freeze = fgGetNode("/sim/freeze/master");
562
563     bool freeze = master_freeze->getBoolValue();
564     if ( !freeze ) {
565         fgSetBool("/sim/freeze/master", true);
566     }
567
568     int mouse = fgGetMouseCursor();
569     fgSetMouseCursor(MOUSE_CURSOR_NONE);
570
571     fgSetBool("/sim/signals/screenshot", true);
572
573     FGRenderer *renderer = globals->get_renderer();
574     renderer->resize( fgGetInt("/sim/startup/xsize"),
575                       fgGetInt("/sim/startup/ysize") );
576
577     // we need two render frames here to clear the menu and cursor
578     // ... not sure why but doing an extra fgRenderFrame() shouldn't
579     // hurt anything
580     renderer->update( true );
581     renderer->update( true );
582
583     string dir = fgGetString("/sim/paths/screenshot-dir");
584     if (dir.empty())
585         dir = fgGetString("/sim/fg-current");
586
587     SGPath path(dir + '/');
588     if (path.create_dir( 0755 )) {
589         SG_LOG(SG_GENERAL, SG_ALERT, "Cannot create screenshot directory '"
590                 << dir << "'. Trying home directory.");
591         dir = fgGetString("/sim/fg-home");
592     }
593
594     char filename[24];
595     static int count = 1;
596     while (count < 1000) {
597         snprintf(filename, 24, "fgfs-screen-%03d.png", count++);
598
599         SGPath p(dir);
600         p.append(filename);
601         if (!p.exists()) {
602             path.set(p.str());
603             break;
604         }
605     }
606
607     bool result = sg_glDumpWindow(path.c_str(),
608                                  fgGetInt("/sim/startup/xsize"),
609                                  fgGetInt("/sim/startup/ysize"));
610
611     fgSetString("/sim/paths/screenshot-last", path.c_str());
612     fgSetBool("/sim/signals/screenshot", false);
613
614     fgSetMouseCursor(mouse);
615
616     if ( !freeze ) {
617         fgSetBool("/sim/freeze/master", false);
618     }
619     return result;
620 #endif
621 }
622
623 // do an entire scenegraph dump
624 void fgDumpSceneGraph()
625 {
626     char *filename = new char [24];
627     string message;
628     static int count = 1;
629
630     static const SGPropertyNode *master_freeze
631         = fgGetNode("/sim/freeze/master");
632
633     bool freeze = master_freeze->getBoolValue();
634     if ( !freeze ) {
635         fgSetBool("/sim/freeze/master", true);
636     }
637
638     while (count < 1000) {
639         FILE *fp;
640         snprintf(filename, 24, "fgfs-graph-%03d.osg", count++);
641         if ( (fp = fopen(filename, "r")) == NULL )
642             break;
643         fclose(fp);
644     }
645
646     if ( fgDumpSceneGraphToFile(filename)) {
647         message = "Entire scene graph saved to \"";
648         message += filename;
649         message += "\".";
650     } else {
651         message = "Failed to save to \"";
652         message += filename;
653         message += "\".";
654     }
655
656     mkDialog (message.c_str());
657
658     delete [] filename;
659
660     if ( !freeze ) {
661         fgSetBool("/sim/freeze/master", false);
662     }
663 }
664
665     
666 // do an terrain branch dump
667 void fgDumpTerrainBranch()
668 {
669     char *filename = new char [24];
670     string message;
671     static int count = 1;
672
673     static const SGPropertyNode *master_freeze
674         = fgGetNode("/sim/freeze/master");
675
676     bool freeze = master_freeze->getBoolValue();
677     if ( !freeze ) {
678         fgSetBool("/sim/freeze/master", true);
679     }
680
681     while (count < 1000) {
682         FILE *fp;
683         snprintf(filename, 24, "fgfs-graph-%03d.osg", count++);
684         if ( (fp = fopen(filename, "r")) == NULL )
685             break;
686         fclose(fp);
687     }
688
689     if ( fgDumpTerrainBranchToFile(filename)) {
690         message = "Terrain graph saved to \"";
691         message += filename;
692         message += "\".";
693     } else {
694         message = "Failed to save to \"";
695         message += filename;
696         message += "\".";
697     }
698
699     mkDialog (message.c_str());
700
701     delete [] filename;
702
703     if ( !freeze ) {
704         fgSetBool("/sim/freeze/master", false);
705     }
706 }
707
708 void fgPrintVisibleSceneInfoCommand()
709 {
710     static const SGPropertyNode *master_freeze
711         = fgGetNode("/sim/freeze/master");
712
713     bool freeze = master_freeze->getBoolValue();
714     if ( !freeze ) {
715         fgSetBool("/sim/freeze/master", true);
716     }
717
718     flightgear::printVisibleSceneInfo(globals->get_renderer());
719
720     if ( !freeze ) {
721         fgSetBool("/sim/freeze/master", false);
722     }
723 }
724
725     
726