00001
00002
00003
00004
00005
00006
00007
00008
00138 #include <FL/Fl_Image.H>
00139 #include <FL/Fl_Shared_Image.H>
00140 #include <FL/Fl_PNG_Image.H>
00141 #include <FL/Fl_Output.H>
00142 #include <FL/Fl_Text_Display.H>
00143 #include <FL/Fl_File_Chooser.H>
00144
00145 #include <set>
00146 #include <sstream>
00147 #include <iomanip>
00148 #include <algorithm>
00149
00150 #include "canvas.hh"
00151 #include "region.hh"
00152 #include "worldfile.hh"
00153 #include "file_manager.hh"
00154 #include "options_dlg.hh"
00155 #include "config.h"
00156
00157 using namespace Stg;
00158
00159 static const char* AboutText =
00160 "\n"
00161 "Part of the Player Project\n"
00162 "http://playerstage.org\n"
00163 "\n"
00164 "Copyright 2000-2008 Richard Vaughan,\n"
00165 "Brian Gerkey, Andrew Howard, Reed Hedges, \n"
00166 "Toby Collett, Alex Couture-Beil, Jeremy Asher \n"
00167 "and contributors\n"
00168 "\n"
00169 "Distributed under the terms of the \n"
00170 "GNU General Public License v2";
00171
00172 static const char* MoreHelpText =
00173 "http://playerstage.org\n"
00174 "\n"
00175 "has these resources to help you:\n"
00176 "\n"
00177 "\t* A user manual including API documentation\n"
00178 "\t* A bug and feature request tracking system\n"
00179 "\t* Mailing lists for users and developers\n"
00180 "\t* A Wiki"
00181 "\n\n"
00182 "The user manual is included with the Stage source code but\n"
00183 "is not built by default. To build the manual, run \"make\"\n"
00184 "in the directory \"docsrc\" to produce \"docsrc/stage/index.html\" .\n"
00185 "(requires Doxygen and supporting programs to be installed first).\n";
00186
00187 WorldGui::WorldGui(int W,int H,const char* L) :
00188 Fl_Window(W,H,L ),
00189 canvas( new Canvas( this,0,30,W,H-30 ) ),
00190 drawOptions(),
00191 fileMan( new FileManager() ),
00192 interval_log(),
00193 speedup(1.0),
00194 mbar( new Fl_Menu_Bar(0,0, W, 30)),
00195 oDlg( NULL ),
00196 pause_time( false ),
00197 real_time_interval( sim_interval ),
00198 real_time_now( RealTimeNow() ),
00199 real_time_recorded( real_time_now ),
00200 timing_interval( 20 )
00201 {
00202 Fl::scheme( "" );
00203 resizable(canvas);
00204 label( PROJECT );
00205
00206 end();
00207
00208
00209 mbar->global();
00210 mbar->textsize(12);
00211
00212 mbar->add( "&File", 0, 0, 0, FL_SUBMENU );
00213 mbar->add( "File/&Load World...", FL_CTRL + 'l', (Fl_Callback*)fileLoadCb, this, FL_MENU_DIVIDER );
00214 mbar->add( "File/&Save World", FL_CTRL + 's', (Fl_Callback*)fileSaveCb, this );
00215 mbar->add( "File/Save World &As...", FL_CTRL + FL_SHIFT + 's', (Fl_Callback*)WorldGui::fileSaveAsCb, this, FL_MENU_DIVIDER );
00216
00217 mbar->add( "File/E&xit", FL_CTRL+'q', (Fl_Callback*)fileExitCb, this );
00218
00219 mbar->add( "&View", 0, 0, 0, FL_SUBMENU );
00220
00221 mbar->add( "View/Reset", ' ', (Fl_Callback*)resetViewCb, this );
00222
00223 mbar->add( "View/Filter data...", FL_SHIFT + 'd', (Fl_Callback*)viewOptionsCb, this );
00224 canvas->createMenuItems( mbar, "View" );
00225
00226 mbar->add( "Run", 0,0,0, FL_SUBMENU );
00227 mbar->add( "Run/Pause", 'p', (Fl_Callback*)pauseCb, this );
00228 mbar->add( "Run/One step", '.', (Fl_Callback*)onceCb, this, FL_MENU_DIVIDER );
00229 mbar->add( "Run/Faster", ']', (Fl_Callback*)fasterCb, this );
00230 mbar->add( "Run/Slower", '[', (Fl_Callback*)slowerCb, this, FL_MENU_DIVIDER );
00231 mbar->add( "Run/Realtime", '{', (Fl_Callback*)realtimeCb, this );
00232 mbar->add( "Run/Fast", '}', (Fl_Callback*)fasttimeCb, this );
00233
00234 mbar->add( "&Help", 0, 0, 0, FL_SUBMENU );
00235 mbar->add( "Help/Getting help...", 0, (Fl_Callback*)moreHelptCb, this, FL_MENU_DIVIDER );
00236 mbar->add( "Help/&About Stage...", 0, (Fl_Callback*)helpAboutCb, this );
00237
00238 callback( (Fl_Callback*)windowCb, this );
00239
00240 show();
00241 }
00242
00243 WorldGui::~WorldGui()
00244 {
00245 if( mbar ) delete mbar;
00246 if( oDlg ) delete oDlg;
00247 if( canvas ) delete canvas;
00248 }
00249
00250
00251 void WorldGui::Show()
00252 {
00253 show();
00254 }
00255
00256 void WorldGui::Load( const std::string& filename )
00257 {
00258 PRINT_DEBUG1( "%s.Load()", token );
00259
00260
00261 Fl::check();
00262
00263 fileMan->newWorld( filename );
00264
00265 const usec_t load_start_time = RealTimeNow();
00266
00267 World::Load( filename );
00268
00269
00270 const int world_section = 0;
00271 speedup = wf->ReadFloat( world_section, "speedup", speedup );
00272 paused = wf->ReadInt( world_section, "paused", paused );
00273
00274
00275 const int window_section = wf->LookupEntity( "window" );
00276
00277 if( window_section > 0 )
00278 {
00279 unsigned int width = w();
00280 unsigned int height = h();
00281 wf->ReadTuple(window_section, "size", 0, 2, "uu", &width, &height );
00282
00283
00284 size( width,height );
00285 size_range( 100, 100 );
00286
00287
00288 canvas->Load( wf, window_section );
00289
00290 std::string title = PROJECT;
00291 if ( wf->filename.size() ) {
00292
00293 title += ": ";
00294 title += wf->filename;
00295 }
00296 label( title.c_str() );
00297
00298 FOR_EACH( it, option_table )
00299 (*it)->Load( wf, window_section );
00300
00301
00302 wf->WarnUnused();
00303 }
00304
00305 const usec_t load_end_time = RealTimeNow();
00306
00307 if( debug )
00308 printf( "[Load time %.3fsec]\n",
00309 (load_end_time - load_start_time) / 1e6 );
00310
00311 Show();
00312 }
00313
00314 void WorldGui::UnLoad()
00315 {
00316 World::UnLoad();
00317 }
00318
00319 bool WorldGui::Save( const char* filename )
00320 {
00321 PRINT_DEBUG1( "%s.Save()", token );
00322
00323
00324 const int world_section = 0;
00325 wf->WriteFloat( world_section, "speedup", speedup );
00326 wf->WriteInt( world_section, "paused", paused );
00327
00328
00329 const int window_section = wf->LookupEntity( "window" );
00330
00331 if( window_section > 0 )
00332 {
00333 unsigned int width = w();
00334 unsigned int height = h();
00335 wf->WriteTuple( window_section, "size", 0, 2, "uu", width, height );
00336
00337 canvas->Save( wf, window_section );
00338
00339 FOR_EACH( it, option_table )
00340 (*it)->Save( wf, window_section );
00341 }
00342
00343 World::Save( filename );
00344
00345
00346 return true;
00347 }
00348
00349 static void UpdateCallback( WorldGui* world )
00350 {
00351 world->Update();
00352 }
00353
00354 bool WorldGui::Update()
00355 {
00356 if( speedup > 0 )
00357 Fl::repeat_timeout( (sim_interval/1e6) / speedup, (Fl_Timeout_Handler)UpdateCallback, this );
00358
00359
00360
00361
00362
00363
00364 if( updates % timing_interval == 0 )
00365 {
00366 const usec_t timenow = RealTimeNow();
00367 real_time_interval = timenow - real_time_recorded;
00368 real_time_recorded = timenow;
00369 }
00370
00371
00372 const bool done = World::Update();
00373
00374 if( Model::trail_length > 0 && updates % Model::trail_interval == 0 )
00375 FOR_EACH( it, active_velocity )
00376 (*it)->UpdateTrail();
00377
00378 if( done )
00379 {
00380 quit_time = 0;
00381 Stop();
00382 }
00383
00384 return done;
00385 }
00386
00387 std::string WorldGui::ClockString() const
00388 {
00389 std::string str = World::ClockString();
00390
00391 const double localratio =
00392 (double)sim_interval / (double)(real_time_interval/timing_interval);
00393
00394 char buf[32];
00395 snprintf( buf, 32, " [%.1f]", localratio );
00396 str += buf;
00397
00398 if( paused == true )
00399 str += " [ PAUSED ]";
00400
00401 return str;
00402 }
00403
00404
00405 void WorldGui::AddModel( Model* mod )
00406 {
00407 if( mod->parent == NULL )
00408 canvas->AddModel( mod );
00409
00410 World::AddModel( mod );
00411 }
00412
00413
00414 void WorldGui::RemoveChild( Model* mod )
00415 {
00416 canvas->RemoveModel( mod );
00417 World::RemoveChild( mod );
00418 }
00419
00420
00421 std::string WorldGui::EnergyString() const
00422 {
00423 char str[512];
00424 snprintf( str, 255, "Energy\n stored: %.0f / %.0f KJ\n input: %.0f KJ\n output: %.0f KJ at %.2f KW\n",
00425 PowerPack::global_stored / 1e3,
00426 PowerPack::global_capacity /1e3,
00427 PowerPack::global_input / 1e3,
00428 PowerPack::global_dissipated / 1e3,
00429 (PowerPack::global_dissipated / (sim_time / 1e6)) / 1e3 );
00430
00431 return std::string( str );
00432 }
00433
00434 void WorldGui::DrawOccupancy() const
00435 {
00436
00437
00438
00439
00440
00441
00442
00443 FOR_EACH( it, superregions )
00444 it->second->DrawOccupancy();
00445
00446
00447
00448
00449
00450
00451
00452 }
00453
00454 void WorldGui::DrawVoxels() const
00455 {
00456 unsigned int layer( updates % 2 );
00457
00458 FOR_EACH( it, superregions )
00459 it->second->DrawVoxels( layer );
00460 }
00461
00462 void WorldGui::windowCb( Fl_Widget* w, WorldGui* wg )
00463 {
00464 switch ( Fl::event() ) {
00465 case FL_SHORTCUT:
00466 if ( Fl::event_key() == FL_Escape )
00467 return;
00468 case FL_CLOSE:
00469 bool done = wg->closeWindowQuery();
00470 if ( !done )
00471 return;
00472 }
00473
00474 puts( "Stage: User closed window" );
00475 exit(0);
00476 }
00477
00478 void WorldGui::fileLoadCb( Fl_Widget* w, WorldGui* wg )
00479 {
00480 const char* filename;
00481 const char* pattern = "World Files (*.world)";
00482
00483 std::string worldsPath = wg->fileMan->worldsRoot();
00484 worldsPath.append( "/" );
00485 Fl_File_Chooser fc( worldsPath.c_str(), pattern, Fl_File_Chooser::CREATE, "Load World File..." );
00486 fc.ok_label( "Load" );
00487
00488 fc.show();
00489 while (fc.shown())
00490 Fl::wait();
00491
00492 filename = fc.value();
00493
00494 if (filename != NULL) {
00495 if ( FileManager::readable( filename ) ) {
00496
00497
00498
00499 wg->Stop();
00500 wg->UnLoad();
00501
00502
00503
00504 wg->Load( filename );
00505 wg->Start();
00506 }
00507 else {
00508 fl_alert( "Unable to read selected world file." );
00509 }
00510
00511
00512 }
00513 }
00514
00515 void WorldGui::fileSaveCb( Fl_Widget* w, WorldGui* wg )
00516 {
00517
00518 const bool success = wg->Save( NULL );
00519 if ( !success ) {
00520 fl_alert( "Error saving world file." );
00521 }
00522 }
00523
00524 void WorldGui::fileSaveAsCb( Fl_Widget* w, WorldGui* wg )
00525 {
00526 wg->saveAsDialog();
00527 }
00528
00529 void WorldGui::fileExitCb( Fl_Widget* w, WorldGui* wg )
00530 {
00531 const bool done = wg->closeWindowQuery();
00532 if (done) {
00533 puts( "User exited via menu" );
00534 exit(0);
00535 }
00536 }
00537
00538 void WorldGui::resetViewCb( Fl_Widget* w, WorldGui* wg )
00539 {
00540 wg->canvas->current_camera->reset();
00541
00542 if( Fl::event_state( FL_CTRL ) )
00543 {
00544 wg->canvas->resetCamera();
00545 }
00546 wg->canvas->redraw();
00547 }
00548
00549 void WorldGui::slowerCb( Fl_Widget* w, WorldGui* wg )
00550 {
00551 if( wg->speedup <= 0 )
00552 {
00553 wg->speedup = 100.0;
00554 wg->SetTimeouts();
00555 }
00556 else
00557 wg->speedup *= 0.8;
00558 }
00559
00560 void WorldGui::fasterCb( Fl_Widget* w, WorldGui* wg )
00561 {
00562 if( wg->speedup <= 0 )
00563 putchar( 7 );
00564 else
00565 wg->speedup *= 1.2;
00566 }
00567
00568 void WorldGui::realtimeCb( Fl_Widget* w, WorldGui* wg )
00569 {
00570
00571 wg->speedup = 1.0;
00572
00573 if( !wg->paused )
00574 wg->SetTimeouts();
00575 }
00576
00577 void WorldGui::fasttimeCb( Fl_Widget* w, WorldGui* wg )
00578 {
00579
00580 wg->speedup = -1;
00581
00582 if( !wg->paused )
00583 wg->SetTimeouts();
00584 }
00585
00586 void WorldGui::Redraw()
00587 {
00588
00589 canvas->redraw();
00590 }
00591
00592 void WorldGui::Start()
00593 {
00594 World::Start();
00595
00596
00597 Fl::add_timeout( ((double)canvas->interval/1000),
00598 (Fl_Timeout_Handler)Canvas::TimerCallback,
00599 canvas );
00600
00601 SetTimeouts();
00602 }
00603
00604
00605 void WorldGui::SetTimeouts()
00606 {
00607
00608 Fl::remove_idle( (Fl_Timeout_Handler)UpdateCallback, this );
00609 Fl::remove_timeout( (Fl_Timeout_Handler)UpdateCallback, this );
00610
00611 if( speedup > 0.0 )
00612
00613 Fl::add_timeout( (sim_interval/1e6) / speedup, (Fl_Timeout_Handler)UpdateCallback, this );
00614 else
00615
00616 Fl::add_idle( (Fl_Timeout_Handler)UpdateCallback, this );
00617 }
00618
00619 void WorldGui::Stop()
00620 {
00621 World::Stop();
00622
00623 Fl::remove_timeout( (Fl_Timeout_Handler)Canvas::TimerCallback );
00624 Fl::remove_timeout( (Fl_Timeout_Handler)UpdateCallback );
00625 Fl::remove_idle( (Fl_Timeout_Handler)UpdateCallback, this );
00626
00627
00628 canvas->redraw();
00629
00630 }
00631
00632 void WorldGui::pauseCb( Fl_Widget* w, WorldGui* wg )
00633 {
00634 wg->TogglePause();
00635 }
00636
00637 void WorldGui::onceCb( Fl_Widget* w, WorldGui* wg )
00638 {
00639
00640 wg->Stop();
00641
00642
00643 wg->World::Update();
00644 }
00645
00646 void WorldGui::viewOptionsCb( OptionsDlg* oDlg, WorldGui* wg )
00647 {
00648
00649
00650
00651
00652 if ( !wg->oDlg )
00653 {
00654 int x = wg->w()+wg->x() + 10;
00655 int y = wg->y();
00656 OptionsDlg* oDlg = new OptionsDlg( x,y, 180,250 );
00657 oDlg->callback( (Fl_Callback*)optionsDlgCb, wg );
00658
00659 oDlg->setOptions( wg->option_table );
00660 oDlg->showAllOpt( &wg->canvas->visualizeAll );
00661 wg->oDlg = oDlg;
00662 oDlg->show();
00663 }
00664 else
00665 {
00666 wg->oDlg->hide();
00667 delete wg->oDlg;
00668 wg->oDlg = NULL;
00669 }
00670
00671 }
00672
00673 void WorldGui::optionsDlgCb( OptionsDlg* oDlg, WorldGui* wg )
00674 {
00675
00676 OptionsDlg::event_t event = oDlg->event();
00677
00678
00679 switch ( Fl::event() ) {
00680 case FL_SHORTCUT:
00681 if ( Fl::event_key() != FL_Escape )
00682 break;
00683
00684 case FL_CLOSE:
00685
00686 event = OptionsDlg::CLOSE;
00687 break;
00688 }
00689
00690 switch ( event ) {
00691 case OptionsDlg::CHANGE:
00692 {
00693
00694
00695 break;
00696 }
00697 case OptionsDlg::CLOSE:
00698
00699
00700 wg->oDlg = NULL;
00701 oDlg->hide();
00702 Fl::delete_widget( oDlg );
00703 return;
00704 case OptionsDlg::NO_EVENT:
00705 case OptionsDlg::CHANGE_ALL:
00706 break;
00707 }
00708 }
00709
00710 void aboutOKBtnCb( Fl_Return_Button* btn, void* p )
00711 {
00712 btn->window()->do_callback();
00713 }
00714
00715 void aboutCloseCb( Fl_Window* win, Fl_Text_Display* textDisplay )
00716 {
00717 Fl_Text_Buffer* tbuf = textDisplay->buffer();
00718 textDisplay->buffer( NULL );
00719 delete tbuf;
00720 Fl::delete_widget( win );
00721 }
00722
00723 void WorldGui::helpAboutCb( Fl_Widget* w, WorldGui* wg )
00724 {
00725 const int Width = 420;
00726 const int Height = 330;
00727 const int Spc = 10;
00728 const int ButtonH = 25;
00729 const int ButtonW = 60;
00730 const int pngH = 82;
00731
00732
00733 Fl_Window* win = new Fl_Window( Width, Height );
00734
00735 Fl_Box* box = new Fl_Box( Spc, Spc,
00736 Width-2*Spc, pngH );
00737
00738 std::string fullpath = FileManager::findFile( "assets/stagelogo.png" );
00739
00740 box->image( new Fl_PNG_Image( fullpath.c_str() ));
00741
00742 Fl_Text_Display* textDisplay =
00743 new Fl_Text_Display( Spc, pngH+2*Spc,
00744 Width-2*Spc, Height-pngH-ButtonH-4*Spc );
00745
00746 textDisplay->box( FL_NO_BOX );
00747 textDisplay->color( win->color() );
00748 win->callback( (Fl_Callback*)aboutCloseCb, textDisplay );
00749
00750 Fl_Text_Buffer* tbuf = new Fl_Text_Buffer;
00751 tbuf->text( PROJECT );
00752 tbuf->append( "-" );
00753 tbuf->append( VERSION );
00754 tbuf->append( AboutText );
00755
00756 textDisplay->buffer( tbuf );
00757
00758 Fl_Return_Button* button =
00759 new Fl_Return_Button( (Width - ButtonW)/2, Height-Spc-ButtonH,
00760 ButtonW, ButtonH,
00761 "&OK" );
00762 button->callback( (Fl_Callback*)aboutOKBtnCb );
00763
00764 win->show();
00765 }
00766
00767 void WorldGui::moreHelptCb( Fl_Widget* w, WorldGui* wg )
00768 {
00769 const int Width = 500;
00770 const int Height = 250;
00771 const int Spc = 10;
00772
00773 Fl_Window* win = new Fl_Window( Width, Height );
00774 win->label( "Getting help with Stage" );
00775
00776 Fl_Text_Display* textDisplay =
00777 new Fl_Text_Display( Spc, Spc,
00778 Width-2*Spc, Height-2*Spc );
00779
00780 win->resizable( textDisplay );
00781 textDisplay->box( FL_NO_BOX );
00782 textDisplay->color( win->color() );
00783
00784 Fl_Text_Buffer* tbuf = new Fl_Text_Buffer();
00785 tbuf->append( MoreHelpText );
00786
00787 textDisplay->buffer( tbuf );
00788
00789 win->show();
00790 }
00791
00792
00793 bool WorldGui::saveAsDialog()
00794 {
00795 const char* newFilename;
00796 bool success = false;
00797 const char* pattern = "World Files (*.world)";
00798
00799 Fl_File_Chooser fc( wf->filename.c_str(), pattern, Fl_File_Chooser::CREATE, "Save File As..." );
00800 fc.ok_label( "Save" );
00801
00802 fc.show();
00803 while (fc.shown())
00804 Fl::wait();
00805
00806 newFilename = fc.value();
00807
00808 if (newFilename != NULL) {
00809
00810 success = Save( newFilename );
00811 if ( !success ) {
00812 fl_alert( "Error saving world file." );
00813 }
00814 }
00815
00816 return success;
00817 }
00818
00819 bool WorldGui::closeWindowQuery()
00820 {
00821 int choice;
00822
00823 if ( wf ) {
00824
00825 choice = fl_choice("Quitting Stage",
00826 "&Cancel",
00827 "&Save, then quit",
00828 "&Quit without saving"
00829 );
00830
00831 switch (choice) {
00832 case 1:
00833 if ( saveAsDialog() )
00834 return true;
00835 else
00836 return false;
00837
00838 case 2:
00839 return true;
00840 }
00841
00842
00843 return false;
00844 }
00845 else {
00846
00847 return true;
00848 }
00849 }
00850
00851 void WorldGui::DrawBoundingBoxTree()
00852 {
00853 FOR_EACH( it, World::children )
00854 (*it)->DrawBoundingBoxTree();
00855 }
00856
00857 void WorldGui::PushColor( Color col )
00858 { canvas->PushColor( col ); }
00859
00860 void WorldGui::PushColor( double r, double g, double b, double a )
00861 { canvas->PushColor( r,g,b,a ); }
00862
00863 void WorldGui::PopColor()
00864 { canvas->PopColor(); }
00865
00866 Model* WorldGui::RecentlySelectedModel() const
00867 { return canvas->last_selection; }
00868
00869 usec_t WorldGui::RealTimeNow() const
00870 {
00871 struct timeval tv;
00872 gettimeofday( &tv, NULL );
00873 return( tv.tv_sec*1000000 + tv.tv_usec );
00874 }
00875
00876 bool WorldGui::IsTopView()
00877 { return canvas->IsTopView(); }