00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <fstream>
00031
00032 #include <QApplication>
00033 #include <QSplashScreen>
00034 #include <QDockWidget>
00035 #include <QDir>
00036 #include <QCloseEvent>
00037 #include <QToolBar>
00038 #include <QMenuBar>
00039 #include <QMenu>
00040 #include <QMessageBox>
00041 #include <QFileDialog>
00042 #include <QDesktopServices>
00043 #include <QUrl>
00044 #include <QToolButton>
00045 #include <QAction>
00046 #include <QTimer>
00047
00048 #include <boost/filesystem.hpp>
00049 #include <boost/bind.hpp>
00050 #include <boost/algorithm/string/split.hpp>
00051 #include <boost/algorithm/string/trim.hpp>
00052
00053 #include <yaml-cpp/emitter.h>
00054 #include <yaml-cpp/node.h>
00055 #include <yaml-cpp/parser.h>
00056
00057 #include <ros/package.h>
00058 #include <ros/console.h>
00059
00060 #include <OGRE/OgreRenderWindow.h>
00061
00062 #include <ogre_helpers/initialization.h>
00063
00064 #include "visualization_frame.h"
00065 #include "render_panel.h"
00066 #include "displays_panel.h"
00067 #include "views_panel.h"
00068 #include "time_panel.h"
00069 #include "selection_panel.h"
00071 #include "visualization_manager.h"
00072 #include "tool.h"
00073 #include "loading_dialog.h"
00074 #include "config.h"
00075 #include "panel_dock_widget.h"
00076 #include "new_object_dialog.h"
00077 #include "panel.h"
00078 #include "screenshot_dialog.h"
00079 #include "help_panel.h"
00080 #include "widget_geometry_change_detector.h"
00081
00082 namespace fs = boost::filesystem;
00083
00084 #define CONFIG_WINDOW_X "/Window/X"
00085 #define CONFIG_WINDOW_Y "/Window/Y"
00086 #define CONFIG_WINDOW_WIDTH "/Window/Width"
00087 #define CONFIG_WINDOW_HEIGHT "/Window/Height"
00088
00089
00090 #define CONFIG_QMAINWINDOW "/QMainWindow"
00091 #define CONFIG_AUIMANAGER_PERSPECTIVE "/AuiManagerPerspective"
00092 #define CONFIG_AUIMANAGER_PERSPECTIVE_VERSION "/AuiManagerPerspectiveVersion"
00093 #define CONFIG_RECENT_CONFIGS "/RecentConfigs"
00094 #define CONFIG_LAST_DIR "/LastConfigDir"
00095 #define CONFIG_LAST_IMAGE_DIR "/LastImageDir"
00096
00097 #define CONFIG_EXTENSION "rviz"
00098 #define CONFIG_EXTENSION_WILDCARD "*."CONFIG_EXTENSION
00099 #define PERSPECTIVE_VERSION 2
00100
00101 #define RECENT_CONFIG_COUNT 10
00102
00103 #if BOOST_FILESYSTEM_VERSION == 3
00104 #define BOOST_FILENAME_STRING filename().string
00105 #define BOOST_FILE_STRING string
00106 #else
00107 #define BOOST_FILENAME_STRING filename
00108 #define BOOST_FILE_STRING file_string
00109 #endif
00110
00111 namespace rviz
00112 {
00113
00114 VisualizationFrame::VisualizationFrame( QWidget* parent )
00115 : QMainWindow( parent )
00116 , render_panel_(NULL)
00117 , displays_panel_(NULL)
00118 , views_panel_(NULL)
00119 , time_panel_(NULL)
00120 , selection_panel_(NULL)
00121 , tool_properties_panel_(NULL)
00122 , help_panel_(NULL)
00123 , show_help_action_(NULL)
00124 , file_menu_(NULL)
00125 , recent_configs_menu_(NULL)
00126 , toolbar_(NULL)
00127 , manager_(NULL)
00128 , position_correction_( 0, 0 )
00129 , num_move_events_( 0 )
00130 , toolbar_actions_( NULL )
00131 , add_tool_action_( NULL )
00132 , remove_tool_menu_( NULL )
00133 , initialized_( false )
00134 , geom_change_detector_( new WidgetGeometryChangeDetector( this ))
00135 , loading_( false )
00136 , post_load_timer_( new QTimer( this ))
00137 {
00138 panel_class_loader_ = new pluginlib::ClassLoader<Panel>( "rviz", "rviz::Panel" );
00139
00140 installEventFilter( geom_change_detector_ );
00141 connect( geom_change_detector_, SIGNAL( changed() ), this, SLOT( setDisplayConfigModified() ));
00142
00143 post_load_timer_->setSingleShot( true );
00144 connect( post_load_timer_, SIGNAL( timeout() ), this, SLOT( markLoadingDone() ));
00145 }
00146
00147 VisualizationFrame::~VisualizationFrame()
00148 {
00149 delete render_panel_;
00150 delete manager_;
00151
00152 M_PanelRecord::iterator pi;
00153 for( pi = custom_panels_.begin(); pi != custom_panels_.end(); pi++ )
00154 {
00155 delete (*pi).second.dock;
00156 }
00157
00158 delete panel_class_loader_;
00159 }
00160
00161 void VisualizationFrame::closeEvent( QCloseEvent* event )
00162 {
00163 if( prepareToExit() )
00164 {
00165 event->accept();
00166 }
00167 else
00168 {
00169 event->ignore();
00170 }
00171 }
00172
00173 void VisualizationFrame::changeMaster()
00174 {
00175 if( prepareToExit() )
00176 {
00177 QApplication::exit( 255 );
00178 }
00179 }
00180
00181 void VisualizationFrame::setSplashStatus( const std::string& status )
00182 {
00183 splash_->showMessage( QString::fromStdString( status ), Qt::AlignLeft | Qt::AlignBottom );
00184 }
00185
00186 void VisualizationFrame::initialize(const std::string& display_config_file,
00187 const std::string& fixed_frame,
00188 const std::string& target_frame,
00189 const std::string& splash_path,
00190 const std::string& help_path,
00191 bool verbose,
00192 bool show_choose_new_master_option )
00193 {
00194 show_choose_new_master_option_ = show_choose_new_master_option;
00195
00196 initConfigs( display_config_file );
00197
00198 loadGeneralConfig();
00199
00200 package_path_ = ros::package::getPath("rviz");
00201
00202 std::string final_splash_path = splash_path;
00203
00204 if ( splash_path.empty() )
00205 {
00206 final_splash_path = (fs::path(package_path_) / "images/splash.png").BOOST_FILE_STRING();
00207 }
00208
00209 help_path_ = help_path;
00210 if ( help_path_.empty() )
00211 {
00212 help_path_ = (fs::path(package_path_) / "help/help.html").BOOST_FILE_STRING();
00213 }
00214 QPixmap splash_image( QString::fromStdString( final_splash_path ));
00215 splash_ = new QSplashScreen( splash_image );
00216 splash_->show();
00217 setSplashStatus( "Initializing" );
00218
00219 if( !ros::isInitialized() )
00220 {
00221 int argc = 0;
00222 ros::init( argc, 0, "rviz", ros::init_options::AnonymousName );
00223 }
00224
00225 render_panel_ = new RenderPanel( this );
00226 displays_panel_ = new DisplaysPanel( this );
00227 views_panel_ = new ViewsPanel( this );
00228 time_panel_ = new TimePanel( this );
00229 selection_panel_ = new SelectionPanel( this );
00231
00232 initMenus();
00233 toolbar_ = addToolBar( "Tools" );
00234 toolbar_->setObjectName( "Tools" );
00235 toolbar_actions_ = new QActionGroup( this );
00236 connect( toolbar_actions_, SIGNAL( triggered( QAction* )), this, SLOT( onToolbarActionTriggered( QAction* )));
00237 view_menu_->addAction( toolbar_->toggleViewAction() );
00238
00239 add_tool_action_ = new QAction( "+", toolbar_actions_ );
00240 add_tool_action_->setToolTip( "Add a new tool" );
00241 toolbar_->addAction( add_tool_action_ );
00242 connect( add_tool_action_, SIGNAL( triggered() ), this, SLOT( openNewToolDialog() ));
00243
00244 remove_tool_menu_ = new QMenu();
00245 QToolButton* remove_tool_button = new QToolButton();
00246 remove_tool_button->setText( "-" );
00247 remove_tool_button->setMenu( remove_tool_menu_ );
00248 remove_tool_button->setPopupMode( QToolButton::InstantPopup );
00249 remove_tool_button->setToolTip( "Remove a tool from the toolbar" );
00250 toolbar_->addWidget( remove_tool_button );
00251 connect( remove_tool_menu_, SIGNAL( triggered( QAction* )), this, SLOT( onToolbarRemoveTool( QAction* )));
00252
00253 setCentralWidget( render_panel_ );
00254
00255 addPane( "Displays", displays_panel_, Qt::LeftDockWidgetArea, false );
00257 addPane( "Views", views_panel_, Qt::RightDockWidgetArea, false );
00258 addPane( "Selection", selection_panel_, Qt::RightDockWidgetArea, false );
00259 addPane( "Time", time_panel_, Qt::BottomDockWidgetArea, false );
00260
00261 manager_ = new VisualizationManager( render_panel_, this );
00262 render_panel_->initialize( manager_->getSceneManager(), manager_ );
00263 displays_panel_->initialize( manager_ );
00264 views_panel_->initialize( manager_ );
00265 time_panel_->initialize(manager_);
00266 selection_panel_->initialize( manager_ );
00268
00269 connect( manager_, SIGNAL( configChanged() ), this, SLOT( setDisplayConfigModified() ));
00270 connect( manager_, SIGNAL( toolAdded( Tool* )), this, SLOT( addTool( Tool* )));
00271 connect( manager_, SIGNAL( toolRemoved( Tool* )), this, SLOT( removeTool( Tool* )));
00272 connect( manager_, SIGNAL( toolChanged( Tool* )), this, SLOT( indicateToolIsCurrent( Tool* )));
00273 connect( views_panel_, SIGNAL( configChanged() ), this, SLOT( setDisplayConfigModified() ));
00274
00275 manager_->initialize( StatusCallback(), verbose );
00276
00277 loadDisplayConfig( display_config_file_ );
00278
00279 if( !fixed_frame.empty() )
00280 {
00281 manager_->setFixedFrame( QString::fromStdString( fixed_frame ));
00282 }
00283
00284 if( !target_frame.empty() )
00285 {
00286 manager_->setTargetFrame( QString::fromStdString( target_frame ));
00287 }
00288
00289 setSplashStatus( "Loading perspective" );
00290
00291 delete splash_;
00292 splash_ = 0;
00293
00294 manager_->startUpdate();
00295 initialized_ = true;
00296 }
00297
00298 void VisualizationFrame::initConfigs( const std::string& display_config_file_override )
00299 {
00300 home_dir_ = QDir::toNativeSeparators( QDir::homePath() ).toStdString();
00301
00302 config_dir_ = (fs::path(home_dir_) / ".rviz").BOOST_FILE_STRING();
00303 general_config_file_ = (fs::path(config_dir_) / "config").BOOST_FILE_STRING();
00304 default_display_config_file_ = (fs::path(config_dir_) / "display_config").BOOST_FILE_STRING();
00305 std::string display_config_file = default_display_config_file_;
00306
00307 if( display_config_file_override != "" )
00308 {
00309 if( !fs::exists( display_config_file_override ))
00310 {
00311 ROS_ERROR("File [%s] does not exist", display_config_file_override.c_str());
00312 }
00313 else
00314 {
00315 display_config_file = display_config_file_override;
00316 ROS_INFO("Loading display config from [%s]", display_config_file_.c_str());
00317 }
00318 }
00319 setDisplayConfigFile( display_config_file );
00320
00321 if( fs::is_regular_file( config_dir_ ))
00322 {
00323 ROS_ERROR("Moving file [%s] out of the way to recreate it as a directory.", config_dir_.c_str());
00324 std::string backup_file = config_dir_ + ".bak";
00325
00326 fs::rename(config_dir_, backup_file);
00327 fs::create_directory(config_dir_);
00328 }
00329 else if (!fs::exists(config_dir_))
00330 {
00331 fs::create_directory(config_dir_);
00332 }
00333 }
00334
00335 void VisualizationFrame::loadGeneralConfig()
00336 {
00337 ROS_INFO("Loading general config from [%s]", general_config_file_.c_str());
00338 Config general_config;
00339 general_config.readFromFile( general_config_file_ );
00340
00341 std::string recent;
00342 if( general_config.get( CONFIG_RECENT_CONFIGS, &recent ))
00343 {
00344 boost::trim( recent );
00345 boost::split( recent_configs_, recent, boost::is_any_of (":"), boost::token_compress_on );
00346 }
00347
00348 general_config.get( CONFIG_LAST_DIR, &last_config_dir_ );
00349 general_config.get( CONFIG_LAST_IMAGE_DIR, &last_image_dir_ );
00350 }
00351
00352 void VisualizationFrame::saveGeneralConfig()
00353 {
00354 ROS_INFO("Saving general config to [%s]", general_config_file_.c_str());
00355 Config general_config;
00356 {
00357 std::stringstream ss;
00358 D_string::iterator it = recent_configs_.begin();
00359 D_string::iterator end = recent_configs_.end();
00360 for (; it != end; ++it)
00361 {
00362 if (it != recent_configs_.begin())
00363 {
00364 ss << ":";
00365 }
00366 ss << *it;
00367 }
00368 general_config.set( CONFIG_RECENT_CONFIGS, ss.str() );
00369 }
00370 general_config.set( CONFIG_LAST_DIR, last_config_dir_ );
00371 general_config.set( CONFIG_LAST_IMAGE_DIR, last_image_dir_ );
00372 general_config.writeToFile( general_config_file_ );
00373 }
00374
00375 void VisualizationFrame::initMenus()
00376 {
00377 file_menu_ = menuBar()->addMenu( "&File" );
00378 file_menu_->addAction( "&Open Config", this, SLOT( onOpen() ), QKeySequence( "Ctrl+O" ));
00379 file_menu_->addAction( "&Save Config", this, SLOT( save() ), QKeySequence( "Ctrl+S" ));
00380 file_menu_->addAction( "Save Config &As", this, SLOT( saveAs() ));
00381 recent_configs_menu_ = file_menu_->addMenu( "&Recent Configs" );
00382 file_menu_->addAction( "Save &Image", this, SLOT( onSaveImage() ));
00383 if( show_choose_new_master_option_ )
00384 {
00385 file_menu_->addSeparator();
00386 file_menu_->addAction( "Change &Master", this, SLOT( changeMaster() ));
00387 }
00388 file_menu_->addSeparator();
00389 file_menu_->addAction( "&Quit", this, SLOT( close() ), QKeySequence( "Ctrl+Q" ));
00390
00391 view_menu_ = menuBar()->addMenu( "&Panels" );
00392 view_menu_->addAction( "Add &New Panel", this, SLOT( openNewPanelDialog() ));
00393 delete_view_menu_ = view_menu_->addMenu( "&Delete Panel" );
00394 delete_view_menu_->setEnabled( false );
00395 view_menu_->addSeparator();
00396
00397 QMenu* help_menu = menuBar()->addMenu( "&Help" );
00398 help_menu->addAction( "Show &Help panel", this, SLOT( showHelpPanel() ));
00399 help_menu->addAction( "Open rviz wiki in browser", this, SLOT( onHelpWiki() ));
00400 }
00401
00402 void VisualizationFrame::openNewPanelDialog()
00403 {
00418 }
00419
00420 void VisualizationFrame::openNewToolDialog()
00421 {
00433 }
00434
00435 void VisualizationFrame::updateRecentConfigMenu()
00436 {
00437 recent_configs_menu_->clear();
00438
00439 D_string::iterator it = recent_configs_.begin();
00440 D_string::iterator end = recent_configs_.end();
00441 for (; it != end; ++it)
00442 {
00443 if( *it != "" )
00444 {
00445 std::string display_name = *it;
00446 if( display_name == default_display_config_file_ )
00447 {
00448 display_name += " (default)";
00449 }
00450 if( display_name.find( home_dir_ ) == 0 )
00451 {
00452 display_name = ("~" / fs::path( display_name.substr( home_dir_.size() ))).BOOST_FILE_STRING();
00453 }
00454 QString qdisplay_name = QString::fromStdString( display_name );
00455 QAction* action = new QAction( qdisplay_name, this );
00456 action->setData( QString::fromStdString( *it ));
00457 connect( action, SIGNAL( triggered() ), this, SLOT( onRecentConfigSelected() ));
00458 recent_configs_menu_->addAction( action );
00459 }
00460 }
00461 }
00462
00463 void VisualizationFrame::markRecentConfig( const std::string& path )
00464 {
00465 D_string::iterator it = std::find( recent_configs_.begin(), recent_configs_.end(), path );
00466 if( it != recent_configs_.end() )
00467 {
00468 recent_configs_.erase( it );
00469 }
00470
00471 recent_configs_.push_front( path );
00472
00473 if( recent_configs_.size() > RECENT_CONFIG_COUNT )
00474 {
00475 recent_configs_.pop_back();
00476 }
00477
00478 updateRecentConfigMenu();
00479 }
00480
00481 void VisualizationFrame::loadDisplayConfig( const std::string& path )
00482 {
00483 if( !fs::exists( path ))
00484 {
00485 QString message = QString::fromStdString( path ) + " does not exist!";
00486 QMessageBox::critical( this, "Config file does not exist", message );
00487 return;
00488 }
00489
00490
00491
00492 if( !prepareToExit() )
00493 {
00494 return;
00495 }
00496
00497 setWindowModified( false );
00498 loading_ = true;
00499
00500 StatusCallback cb;
00501 LoadingDialog* dialog = NULL;
00502 if( !initialized_ )
00503 {
00504
00505
00506 cb = boost::bind( &VisualizationFrame::setSplashStatus, this, _1 );
00507 }
00508 else
00509 {
00510 dialog = new LoadingDialog( this );
00511 dialog->show();
00512 cb = boost::bind( &LoadingDialog::setState, dialog, _1 );
00513 }
00514
00515 std::ifstream in( path.c_str() );
00516 if( in )
00517 {
00518 YAML::Parser parser( in );
00519 YAML::Node node;
00520 parser.GetNextDocument( node );
00521 load( node, cb );
00522 }
00523
00524 markRecentConfig( path );
00525
00526 setDisplayConfigFile( path );
00527
00528 last_config_dir_ = fs::path( path ).parent_path().BOOST_FILE_STRING();
00529
00530 delete dialog;
00531
00532 post_load_timer_->start( 1000 );
00533 }
00534
00535 void VisualizationFrame::markLoadingDone()
00536 {
00537 loading_ = false;
00538 }
00539
00540 void VisualizationFrame::setImageSaveDirectory( const QString& directory )
00541 {
00542 last_image_dir_ = directory.toStdString();
00543 }
00544
00545 void VisualizationFrame::setDisplayConfigModified()
00546 {
00547 if( !loading_ )
00548 {
00549 setWindowModified( true );
00550 }
00551 }
00552
00553 void VisualizationFrame::setDisplayConfigFile( const std::string& path )
00554 {
00555 display_config_file_ = path;
00556
00557 std::string title;
00558 if( path == default_display_config_file_ )
00559 {
00560 title = "RViz[*]";
00561 }
00562 else
00563 {
00564 title = fs::path( path ).BOOST_FILENAME_STRING() + "[*] - RViz";
00565 }
00566 setWindowTitle( QString::fromStdString( title ));
00567 }
00568
00569 void VisualizationFrame::saveDisplayConfig( const std::string& path )
00570 {
00571 std::ofstream out( path.c_str() );
00572 if( out )
00573 {
00574 ROS_INFO( "Saving display config to [%s]", path.c_str() );
00575
00576 YAML::Emitter emitter;
00577 emitter << YAML::BeginMap;
00578 save( emitter );
00579 emitter << YAML::EndMap;
00580 out << emitter.c_str() << std::endl;
00581
00582 setWindowModified( false );
00583 }
00584 else
00585 {
00586 ROS_ERROR( "Failed to open file [%s] for writing", path.c_str() );
00587 }
00588 }
00589
00590 void VisualizationFrame::save( YAML::Emitter& emitter )
00591 {
00592 emitter << YAML::Key << "Visualization Manager";
00593 emitter << YAML::Value;
00594 manager_->save( emitter );
00595
00596 emitter << YAML::Key << "Panels";
00597 emitter << YAML::Value;
00598 {
00599 emitter << YAML::BeginMap;
00600
00601 emitter << YAML::Key << "Displays";
00602 emitter << YAML::Value;
00603 displays_panel_->save( emitter );
00604
00605 emitter << YAML::EndMap;
00606 }
00607
00609
00610 emitter << YAML::Key << "Window Geometry";
00611 emitter << YAML::Value;
00612 {
00613 emitter << YAML::BeginMap;
00614 QRect geom = hackedFrameGeometry();
00615 emitter << YAML::Key << "X" << YAML::Value << geom.x();
00616 emitter << YAML::Key << "Y" << YAML::Value << geom.y();
00617 emitter << YAML::Key << "Width" << YAML::Value << geom.width();
00618 emitter << YAML::Key << "Height" << YAML::Value << geom.height();
00619
00620 QByteArray window_state = saveState().toHex();
00621 emitter << YAML::Key << "QMainWindow State" << YAML::Value << window_state.constData();
00622 emitter << YAML::EndMap;
00623 }
00624 }
00625
00626 void VisualizationFrame::load( const YAML::Node& yaml_node, const StatusCallback& cb )
00627 {
00628 if( yaml_node.Type() != YAML::NodeType::Map )
00629 {
00630 printf( "VisualizationFrame::load() TODO: error handling - unexpected YAML type.\n" );
00631 return;
00632 }
00633
00634 if( const YAML::Node *name_node = yaml_node.FindValue( "Visualization Manager" ))
00635 {
00636 manager_->load( *name_node, cb );
00637 }
00638
00639 if( const YAML::Node *panels_node = yaml_node.FindValue( "Panels" ))
00640 {
00641 if( const YAML::Node *displays_node = panels_node->FindValue( "Displays" ))
00642 {
00643 displays_panel_->load( *displays_node );
00644 }
00645 }
00646
00648
00649 if( const YAML::Node *name_node = yaml_node.FindValue( "Window Geometry" ))
00650 {
00651 int x, y, width, height;
00652 (*name_node)[ "X" ] >> x;
00653 (*name_node)[ "Y" ] >> y;
00654 (*name_node)[ "Width" ] >> width;
00655 (*name_node)[ "Height" ] >> height;
00656 move( x, y );
00657 resize( width, height );
00658
00659 std::string main_window_config;
00660 (*name_node)[ "QMainWindow State" ] >> main_window_config;
00661 restoreState( QByteArray::fromHex( main_window_config.c_str() ));
00662 }
00663 }
00664
00665
00727
00728 void VisualizationFrame::moveEvent( QMoveEvent* event )
00729 {
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748 switch( num_move_events_ )
00749 {
00750 case 0:
00751 first_position_ = pos();
00752 num_move_events_++;
00753 break;
00754 case 1:
00755 position_correction_ = first_position_ - pos();
00756 num_move_events_++;
00757 break;
00758 }
00759 }
00760
00761 QRect VisualizationFrame::hackedFrameGeometry()
00762 {
00763 QRect geom = frameGeometry();
00764 geom.moveTopLeft( pos() + position_correction_ );
00765 return geom;
00766 }
00767
00797
00798 bool VisualizationFrame::prepareToExit()
00799 {
00800 if( !initialized_ )
00801 {
00802 return true;
00803 }
00804
00805 saveGeneralConfig();
00806
00807 if( isWindowModified() )
00808 {
00809 if( fileIsWritable( display_config_file_ ))
00810 {
00811 QMessageBox box( this );
00812 box.setText( "There are unsaved changes." );
00813 box.setInformativeText( QString::fromStdString( "Save changes to " + display_config_file_ + "?" ));
00814 box.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
00815 box.setDefaultButton( QMessageBox::Save );
00816 int result = box.exec();
00817 switch( result )
00818 {
00819 case QMessageBox::Save:
00820 saveDisplayConfig( display_config_file_ );
00821 return true;
00822 case QMessageBox::Discard:
00823 return true;
00824 default:
00825 return false;
00826 }
00827 }
00828 else
00829 {
00830 QMessageBox box( this );
00831 box.setText( "There are unsaved changes but file is read-only." );
00832 box.setInformativeText( QString::fromStdString( "Save new version of " + display_config_file_ + " to another file?" ));
00833 box.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
00834 box.setDefaultButton( QMessageBox::Save );
00835 int result = box.exec();
00836 switch( result )
00837 {
00838 case QMessageBox::Save:
00839 saveAs();
00840 return true;
00841 case QMessageBox::Discard:
00842 return true;
00843 default:
00844 return false;
00845 }
00846 }
00847 }
00848 else
00849 {
00850 return true;
00851 }
00852 }
00853
00854 void VisualizationFrame::onOpen()
00855 {
00856 QString filename = QFileDialog::getOpenFileName( this, "Choose a file to open",
00857 QString::fromStdString( last_config_dir_ ),
00858 "RViz config files (" CONFIG_EXTENSION_WILDCARD ")" );
00859
00860 if( !filename.isEmpty() )
00861 {
00862 std::string filename_string = filename.toStdString();
00863 loadDisplayConfig( filename_string );
00864 }
00865 }
00866
00867 bool VisualizationFrame::fileIsWritable( const std::string& path )
00868 {
00869 std::fstream test_stream( path.c_str(), std::fstream::app );
00870 bool writable = test_stream.is_open();
00871 return writable;
00872 }
00873
00874 void VisualizationFrame::save()
00875 {
00876 if( !initialized_ )
00877 {
00878 return;
00879 }
00880
00881 saveGeneralConfig();
00882
00883 if( fileIsWritable( display_config_file_ ))
00884 {
00885 saveDisplayConfig( display_config_file_ );
00886 }
00887 else
00888 {
00889 QMessageBox box( this );
00890 box.setText( "Config file is read-only." );
00891 box.setInformativeText( QString::fromStdString( "Save new version of " + display_config_file_ + " to another file?" ));
00892 box.setStandardButtons( QMessageBox::Save | QMessageBox::Cancel );
00893 box.setDefaultButton( QMessageBox::Save );
00894 if( box.exec() == QMessageBox::Save )
00895 {
00896 saveAs();
00897 }
00898 }
00899 }
00900
00901 void VisualizationFrame::saveAs()
00902 {
00903 QString q_filename = QFileDialog::getSaveFileName( this, "Choose a file to save to",
00904 QString::fromStdString( last_config_dir_ ),
00905 "RViz config files (" CONFIG_EXTENSION_WILDCARD ")" );
00906
00907 if( !q_filename.isEmpty() )
00908 {
00909 std::string filename = q_filename.toStdString();
00910 fs::path path( filename );
00911 if( path.extension() != "."CONFIG_EXTENSION )
00912 {
00913 filename += "."CONFIG_EXTENSION;
00914 }
00915
00916 saveDisplayConfig( filename );
00917
00918 markRecentConfig( filename );
00919 last_config_dir_ = fs::path( filename ).parent_path().BOOST_FILE_STRING();
00920 setDisplayConfigFile( filename );
00921 }
00922 }
00923
00924 void VisualizationFrame::onSaveImage()
00925 {
00926 ScreenshotDialog* dialog = new ScreenshotDialog( this, render_panel_, QString::fromStdString( last_image_dir_ ));
00927 connect( dialog, SIGNAL( savedInDirectory( const QString& )),
00928 this, SLOT( setImageSaveDirectory( const QString& )));
00929 dialog->show();
00930 }
00931
00932 void VisualizationFrame::onRecentConfigSelected()
00933 {
00934 QAction* action = dynamic_cast<QAction*>( sender() );
00935 if( action )
00936 {
00937 std::string path = action->data().toString().toStdString();
00938 if( !path.empty() )
00939 {
00940 loadDisplayConfig( path );
00941 }
00942 }
00943 }
00944
00945 void VisualizationFrame::addTool( Tool* tool )
00946 {
00947 QAction* action = new QAction( QString::fromStdString( tool->getName() ), toolbar_actions_ );
00948 action->setCheckable( true );
00949 action->setShortcut( QKeySequence( QString( tool->getShortcutKey() )));
00950 toolbar_->insertAction( add_tool_action_, action );
00951 action_to_tool_map_[ action ] = tool;
00952 tool_to_action_map_[ tool ] = action;
00953
00954 remove_tool_menu_->addAction( QString::fromStdString( tool->getName() ));
00955 }
00956
00957 void VisualizationFrame::onToolbarActionTriggered( QAction* action )
00958 {
00959 Tool* tool = action_to_tool_map_[ action ];
00960 if( tool )
00961 {
00962 manager_->setCurrentTool( tool );
00963 }
00964 }
00965
00966 void VisualizationFrame::onToolbarRemoveTool( QAction* remove_tool_menu_action )
00967 {
00968 std::string name = remove_tool_menu_action->text().toStdString();
00969 for( int i = 0; i < manager_->numTools(); i++ )
00970 {
00971 Tool* tool = manager_->getTool( i );
00972 if( tool->getName() == name )
00973 {
00974 manager_->removeTool( i );
00975 return;
00976 }
00977 }
00978 }
00979
00980 void VisualizationFrame::removeTool( Tool* tool )
00981 {
00982 QAction* action = tool_to_action_map_[ tool ];
00983 if( action )
00984 {
00985 toolbar_actions_->removeAction( action );
00986 toolbar_->removeAction( action );
00987 tool_to_action_map_.erase( tool );
00988 action_to_tool_map_.erase( action );
00989 }
00990 QString tool_name = QString::fromStdString( tool->getName() );
00991 QList<QAction*> remove_tool_actions = remove_tool_menu_->actions();
00992 for( int i = 0; i < remove_tool_actions.size(); i++ )
00993 {
00994 QAction* removal_action = remove_tool_actions.at( i );
00995 if( removal_action->text() == tool_name )
00996 {
00997 remove_tool_menu_->removeAction( removal_action );
00998 break;
00999 }
01000 }
01001 }
01002
01003 void VisualizationFrame::indicateToolIsCurrent( Tool* tool )
01004 {
01005 QAction* action = tool_to_action_map_[ tool ];
01006 if( action )
01007 {
01008 action->setChecked( true );
01009 }
01010 }
01011
01012 void VisualizationFrame::showHelpPanel()
01013 {
01014 if( !show_help_action_ )
01015 {
01016 help_panel_ = new HelpPanel( this );
01017 QDockWidget* dock = addPane( "Help", help_panel_ );
01018 show_help_action_ = dock->toggleViewAction();
01019 }
01020 else
01021 {
01022
01023
01024
01025 show_help_action_->setChecked( false );
01026 show_help_action_->trigger();
01027 }
01028 help_panel_->setHelpFile( help_path_ );
01029 }
01030
01031 void VisualizationFrame::onHelpWiki()
01032 {
01033 QDesktopServices::openUrl( QUrl( "http://www.ros.org/wiki/rviz" ));
01034 }
01035
01036 QWidget* VisualizationFrame::getParentWindow()
01037 {
01038 return this;
01039 }
01040
01041
01042
01043
01044
01045
01046
01047
01048 void VisualizationFrame::onDeletePanel()
01049 {
01050 if( QAction* action = qobject_cast<QAction*>( sender() ))
01051 {
01052 std::string panel_name = action->text().toStdString();
01053 M_PanelRecord::iterator pi = custom_panels_.find( panel_name );
01054 if( pi != custom_panels_.end() )
01055 {
01056 delete (*pi).second.dock;
01057 custom_panels_.erase( pi );
01058 setDisplayConfigModified();
01059 }
01060 action->deleteLater();
01061 if( delete_view_menu_->actions().size() == 1 &&
01062 delete_view_menu_->actions().first() == action )
01063 {
01064 delete_view_menu_->setEnabled( false );
01065 }
01066 }
01067 }
01068
01069 PanelDockWidget* VisualizationFrame::addCustomPanel( const std::string& name,
01070 const std::string& class_lookup_name,
01071 Qt::DockWidgetArea area,
01072 bool floating )
01073 {
01074 try
01075 {
01076 Panel* panel = panel_class_loader_->createUnmanagedInstance( class_lookup_name );
01077 connect( panel, SIGNAL( configChanged() ), this, SLOT( setDisplayConfigModified() ));
01078
01079 PanelRecord record;
01080 record.dock = addPane( name, panel, area, floating );
01081 record.lookup_name = class_lookup_name;
01082 record.panel = panel;
01083 record.name = name;
01084 record.delete_action = delete_view_menu_->addAction( QString::fromStdString( name ), this, SLOT( onDeletePanel() ));
01085 custom_panels_[ name ] = record;
01086 delete_view_menu_->setEnabled( true );
01087
01088 record.panel->initialize( manager_ );
01089
01090 return record.dock;
01091 }
01092 catch( pluginlib::LibraryLoadException ex )
01093 {
01094 ROS_ERROR( "Failed to load library for Panel plugin class: %s", ex.what() );
01095 return NULL;
01096 }
01097 }
01098
01099 PanelDockWidget* VisualizationFrame::addPane( const std::string& name, QWidget* panel, Qt::DockWidgetArea area, bool floating )
01100 {
01101 QString q_name = QString::fromStdString( name );
01102 PanelDockWidget *dock;
01103 dock = new PanelDockWidget( q_name, panel );
01104 dock->setWidget( panel );
01105 dock->setFloating( floating );
01106 dock->setObjectName( q_name );
01107 addDockWidget( area, dock );
01108 QAction* toggle_action = dock->toggleViewAction();
01109 view_menu_->addAction( toggle_action );
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120 connect( toggle_action, SIGNAL( triggered( bool )), this, SLOT( setDisplayConfigModified() ));
01121
01122 dock->installEventFilter( geom_change_detector_ );
01123
01124 return dock;
01125 }
01126
01127 }