visualization_frame.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2012, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <fstream>
00031 
00032 #include <QAction>
00033 #include <QShortcut>
00034 #include <QApplication>
00035 #include <QCloseEvent>
00036 #include <QDesktopServices>
00037 #include <QDockWidget>
00038 #include <QDir>
00039 #include <QFileDialog>
00040 #include <QMenu>
00041 #include <QMenuBar>
00042 #include <QMessageBox>
00043 #include <QTimer>
00044 #include <QToolBar>
00045 #include <QToolButton>
00046 #include <QUrl>
00047 #include <QStatusBar>
00048 #include <QLabel>
00049 #include <QToolButton>
00050 #include <QHBoxLayout>
00051 
00052 #include <boost/algorithm/string/split.hpp>
00053 #include <boost/algorithm/string/trim.hpp>
00054 #include <boost/bind.hpp>
00055 #include <boost/filesystem.hpp>
00056 
00057 #include <ros/console.h>
00058 #include <ros/package.h>
00059 #include <ros/init.h>
00060 
00061 #include <OgreRenderWindow.h>
00062 #include <OgreMeshManager.h>
00063 
00064 #include <ogre_helpers/initialization.h>
00065 
00066 #include "rviz/displays_panel.h"
00067 #include "rviz/env_config.h"
00068 #include "rviz/failed_panel.h"
00069 #include "rviz/help_panel.h"
00070 #include "rviz/loading_dialog.h"
00071 #include "rviz/new_object_dialog.h"
00072 #include "rviz/panel_dock_widget.h"
00073 #include "rviz/panel_factory.h"
00074 #include "rviz/render_panel.h"
00075 #include "rviz/screenshot_dialog.h"
00076 #include "rviz/selection/selection_manager.h"
00077 #include "rviz/selection_panel.h"
00078 #include "rviz/splash_screen.h"
00079 #include "rviz/time_panel.h"
00080 #include "rviz/tool.h"
00081 #include "rviz/tool_manager.h"
00082 #include "rviz/tool_properties_panel.h"
00083 #include "rviz/views_panel.h"
00084 #include "rviz/visualization_manager.h"
00085 #include "rviz/widget_geometry_change_detector.h"
00086 #include "rviz/load_resource.h"
00087 #include "rviz/yaml_config_reader.h"
00088 #include "rviz/yaml_config_writer.h"
00089 
00090 #include "rviz/visualization_frame.h"
00091 
00092 namespace fs = boost::filesystem;
00093 
00094 #define CONFIG_EXTENSION "rviz"
00095 #define CONFIG_EXTENSION_WILDCARD "*." CONFIG_EXTENSION
00096 #define RECENT_CONFIG_COUNT 10
00097 
00098 #if BOOST_FILESYSTEM_VERSION == 3
00099 #define BOOST_FILENAME_STRING filename().string
00100 #define BOOST_FILE_STRING string
00101 #else
00102 #define BOOST_FILENAME_STRING filename
00103 #define BOOST_FILE_STRING file_string
00104 #endif
00105 
00106 namespace rviz
00107 {
00108 
00109 VisualizationFrame::VisualizationFrame( QWidget* parent )
00110   : QMainWindow( parent )
00111   , app_(NULL)
00112   , render_panel_(NULL)
00113   , show_help_action_(NULL)
00114   , file_menu_(NULL)
00115   , recent_configs_menu_(NULL)
00116   , toolbar_(NULL)
00117   , manager_(NULL)
00118   , splash_( NULL )
00119   , toolbar_actions_( NULL )
00120   , show_choose_new_master_option_( false )
00121   , add_tool_action_( NULL )
00122   , remove_tool_menu_( NULL )
00123   , initialized_( false )
00124   , geom_change_detector_( new WidgetGeometryChangeDetector( this ))
00125   , loading_( false )
00126   , post_load_timer_( new QTimer( this ))
00127   , frame_count_(0)
00128 {
00129   panel_factory_ = new PanelFactory();
00130 
00131   installEventFilter( geom_change_detector_ );
00132   connect( geom_change_detector_, SIGNAL( changed() ), this, SLOT( setDisplayConfigModified() ));
00133 
00134   post_load_timer_->setSingleShot( true );
00135   connect( post_load_timer_, SIGNAL( timeout() ), this, SLOT( markLoadingDone() ));
00136 
00137   package_path_ = ros::package::getPath("rviz");
00138   help_path_ = QString::fromStdString( (fs::path(package_path_) / "help/help.html").BOOST_FILE_STRING() );
00139   splash_path_ = QString::fromStdString( (fs::path(package_path_) / "images/splash.png").BOOST_FILE_STRING() );
00140 
00141   QToolButton* reset_button = new QToolButton( );
00142   reset_button->setText( "Reset" );
00143   reset_button->setContentsMargins(0,0,0,0);
00144   statusBar()->addPermanentWidget( reset_button, 0 );
00145   connect( reset_button, SIGNAL( clicked( bool )), this, SLOT( reset() ));
00146 
00147   status_label_ = new QLabel("");
00148   statusBar()->addPermanentWidget( status_label_, 1 );
00149   connect( this, SIGNAL( statusUpdate( const QString& )), status_label_, SLOT( setText( const QString& )));
00150 
00151   fps_label_ = new QLabel("");
00152   fps_label_->setMinimumWidth(40);
00153   fps_label_->setAlignment(Qt::AlignRight);
00154   statusBar()->addPermanentWidget( fps_label_, 0 );
00155   original_status_bar_ = statusBar();
00156 
00157   setWindowTitle( "RViz[*]" );
00158 }
00159 
00160 VisualizationFrame::~VisualizationFrame()
00161 {
00162   delete render_panel_;
00163   delete manager_;
00164 
00165   for( int i = 0; i < custom_panels_.size(); i++ )
00166   {
00167     delete custom_panels_[ i ].dock;
00168   }
00169 
00170   delete panel_factory_;
00171 }
00172 
00173 void VisualizationFrame::setApp( QApplication * app )
00174 {
00175   app_ = app;
00176 }
00177 
00178 void VisualizationFrame::setStatus( const QString & message )
00179 {
00180   Q_EMIT statusUpdate( message );
00181 }
00182 
00183 void VisualizationFrame::updateFps()
00184 {
00185   frame_count_ ++;
00186   ros::WallDuration wall_diff = ros::WallTime::now() - last_fps_calc_time_;
00187 
00188   if ( wall_diff.toSec() > 1.0 )
00189   {
00190     float fps = frame_count_ / wall_diff.toSec();
00191     frame_count_ = 0;
00192     last_fps_calc_time_ = ros::WallTime::now();
00193     if ( original_status_bar_ == statusBar() )
00194     {
00195       fps_label_->setText( QString::number(int(fps)) + QString(" fps") );
00196     }
00197   }
00198 }
00199 
00200 void VisualizationFrame::closeEvent( QCloseEvent* event )
00201 {
00202   if( prepareToExit() )
00203   {
00204     event->accept();
00205   }
00206   else
00207   {
00208     event->ignore();
00209   }
00210 }
00211 
00212 void VisualizationFrame::leaveEvent ( QEvent * event )
00213 {
00214   setStatus("");
00215 }
00216 
00217 void VisualizationFrame::reset()
00218 {
00219   Ogre::MeshManager::getSingleton().removeAll();
00220   manager_->resetTime();
00221 }
00222 
00223 void VisualizationFrame::changeMaster()
00224 {
00225   if( prepareToExit() )
00226   {
00227     QApplication::exit( 255 );
00228   }
00229 }
00230 
00231 void VisualizationFrame::setShowChooseNewMaster( bool show )
00232 {
00233   show_choose_new_master_option_ = show;
00234 }
00235 
00236 void VisualizationFrame::setHelpPath( const QString& help_path )
00237 {
00238   help_path_ = help_path;
00239   manager_->setHelpPath( help_path_ );
00240 }
00241 
00242 void VisualizationFrame::setSplashPath( const QString& splash_path )
00243 {
00244   splash_path_ = splash_path;
00245 }
00246 
00247 void VisualizationFrame::initialize(const QString& display_config_file )
00248 {
00249   initConfigs();
00250 
00251   loadPersistentSettings();
00252 
00253   QIcon app_icon( QString::fromStdString( (fs::path(package_path_) / "icons/package.png").BOOST_FILE_STRING() ) );
00254   setWindowIcon( app_icon );
00255 
00256   if( splash_path_ != "" )
00257   {
00258     QPixmap splash_image( splash_path_ );
00259     splash_ = new SplashScreen( splash_image );
00260     splash_->show();
00261     connect( this, SIGNAL( statusUpdate( const QString& )), splash_, SLOT( showMessage( const QString& )));
00262   }
00263   Q_EMIT statusUpdate( "Initializing" );
00264 
00265   // Periodically process events for the splash screen.
00266   // See: http://doc.qt.io/qt-5/qsplashscreen.html#details
00267   if (app_) app_->processEvents();
00268 
00269   if( !ros::isInitialized() )
00270   {
00271     int argc = 0;
00272     ros::init( argc, 0, "rviz", ros::init_options::AnonymousName );
00273   }
00274 
00275   // Periodically process events for the splash screen.
00276   if (app_) app_->processEvents();
00277 
00278   QWidget* central_widget = new QWidget(this);
00279   QHBoxLayout* central_layout = new QHBoxLayout;
00280   central_layout->setSpacing(0);
00281   central_layout->setMargin(0);
00282 
00283   render_panel_ = new RenderPanel( central_widget );
00284 
00285   hide_left_dock_button_ = new QToolButton();
00286   hide_left_dock_button_->setContentsMargins(0,0,0,0);
00287   hide_left_dock_button_->setArrowType( Qt::LeftArrow );
00288   hide_left_dock_button_->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ) );
00289   hide_left_dock_button_->setFixedWidth(16);
00290   hide_left_dock_button_->setAutoRaise(true);
00291   hide_left_dock_button_->setCheckable(true);
00292 
00293   connect(hide_left_dock_button_, SIGNAL(toggled(bool)), this, SLOT(hideLeftDock(bool)));
00294 
00295   hide_right_dock_button_ = new QToolButton();
00296   hide_right_dock_button_->setContentsMargins(0,0,0,0);
00297   hide_right_dock_button_->setArrowType( Qt::RightArrow );
00298   hide_right_dock_button_->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ) );
00299   hide_right_dock_button_->setFixedWidth(16);
00300   hide_right_dock_button_->setAutoRaise(true);
00301   hide_right_dock_button_->setCheckable(true);
00302 
00303   connect(hide_right_dock_button_, SIGNAL(toggled(bool)), this, SLOT(hideRightDock(bool)));
00304 
00305   central_layout->addWidget( hide_left_dock_button_, 0 );
00306   central_layout->addWidget( render_panel_, 1 );
00307   central_layout->addWidget( hide_right_dock_button_, 0 );
00308 
00309   central_widget->setLayout( central_layout );
00310 
00311   // Periodically process events for the splash screen.
00312   if (app_) app_->processEvents();
00313 
00314   initMenus();
00315 
00316   // Periodically process events for the splash screen.
00317   if (app_) app_->processEvents();
00318 
00319   initToolbars();
00320 
00321   // Periodically process events for the splash screen.
00322   if (app_) app_->processEvents();
00323 
00324   setCentralWidget( central_widget );
00325 
00326   // Periodically process events for the splash screen.
00327   if (app_) app_->processEvents();
00328 
00329   manager_ = new VisualizationManager( render_panel_, this );
00330   manager_->setHelpPath( help_path_ );
00331 
00332   // Periodically process events for the splash screen.
00333   if (app_) app_->processEvents();
00334 
00335   render_panel_->initialize( manager_->getSceneManager(), manager_ );
00336 
00337   // Periodically process events for the splash screen.
00338   if (app_) app_->processEvents();
00339 
00340   ToolManager* tool_man = manager_->getToolManager();
00341 
00342   connect( manager_, SIGNAL( configChanged() ), this, SLOT( setDisplayConfigModified() ));
00343   connect( tool_man, SIGNAL( toolAdded( Tool* )), this, SLOT( addTool( Tool* )));
00344   connect( tool_man, SIGNAL( toolRemoved( Tool* )), this, SLOT( removeTool( Tool* )));
00345   connect( tool_man, SIGNAL( toolRefreshed( Tool* )), this, SLOT( refreshTool( Tool* )));
00346   connect( tool_man, SIGNAL( toolChanged( Tool* )), this, SLOT( indicateToolIsCurrent( Tool* )));
00347 
00348   manager_->initialize();
00349 
00350   // Periodically process events for the splash screen.
00351   if (app_) app_->processEvents();
00352 
00353   if( display_config_file != "" )
00354   {
00355     loadDisplayConfig( display_config_file );
00356   }
00357   else
00358   {
00359     loadDisplayConfig( QString::fromStdString( default_display_config_file_ ));
00360   }
00361 
00362   // Periodically process events for the splash screen.
00363   if (app_) app_->processEvents();
00364 
00365   delete splash_;
00366   splash_ = 0;
00367 
00368   manager_->startUpdate();
00369   initialized_ = true;
00370   Q_EMIT statusUpdate( "RViz is ready." );
00371 
00372   connect( manager_, SIGNAL( preUpdate() ), this, SLOT( updateFps() ) );
00373   connect( manager_, SIGNAL( statusUpdate( const QString& )), this, SIGNAL( statusUpdate( const QString& )));
00374 }
00375 
00376 void VisualizationFrame::initConfigs()
00377 {
00378   home_dir_ = QDir::toNativeSeparators( QDir::homePath() ).toStdString();
00379 
00380   config_dir_ = (fs::path(home_dir_) / ".rviz").BOOST_FILE_STRING();
00381   persistent_settings_file_ = (fs::path(config_dir_) / "persistent_settings").BOOST_FILE_STRING();
00382   default_display_config_file_ = (fs::path(config_dir_) / "default." CONFIG_EXTENSION).BOOST_FILE_STRING();
00383 
00384   if( fs::is_regular_file( config_dir_ ))
00385   {
00386     ROS_ERROR("Moving file [%s] out of the way to recreate it as a directory.", config_dir_.c_str());
00387     std::string backup_file = config_dir_ + ".bak";
00388 
00389     fs::rename(config_dir_, backup_file);
00390     fs::create_directory(config_dir_);
00391   }
00392   else if (!fs::exists(config_dir_))
00393   {
00394     fs::create_directory(config_dir_);
00395   }
00396 }
00397 
00398 void VisualizationFrame::loadPersistentSettings()
00399 {
00400   YamlConfigReader reader;
00401   Config config;
00402   reader.readFile( config, QString::fromStdString( persistent_settings_file_ ));
00403   if( !reader.error() )
00404   {
00405     QString last_config_dir, last_image_dir;
00406     if( config.mapGetString( "Last Config Dir", &last_config_dir ) &&
00407         config.mapGetString( "Last Image Dir", &last_image_dir ))
00408     {
00409       last_config_dir_ = last_config_dir.toStdString();
00410       last_image_dir_ = last_image_dir.toStdString();
00411     }
00412     
00413     Config recent_configs_list = config.mapGetChild( "Recent Configs" );
00414     recent_configs_.clear();
00415     int num_recent = recent_configs_list.listLength();
00416     for( int i = 0; i < num_recent; i++ )
00417     {
00418       recent_configs_.push_back( recent_configs_list.listChildAt( i ).getValue().toString().toStdString() );
00419     }
00420   }
00421   else
00422   {
00423     ROS_ERROR( "%s", qPrintable( reader.errorMessage() ));
00424   }
00425 }
00426 
00427 void VisualizationFrame::savePersistentSettings()
00428 {
00429   Config config;
00430   config.mapSetValue( "Last Config Dir", QString::fromStdString( last_config_dir_ ));
00431   config.mapSetValue( "Last Image Dir", QString::fromStdString( last_image_dir_ ));
00432   Config recent_configs_list = config.mapMakeChild( "Recent Configs" );
00433   for( D_string::iterator it = recent_configs_.begin(); it != recent_configs_.end(); ++it )
00434   {
00435     recent_configs_list.listAppendNew().setValue( QString::fromStdString( *it ));
00436   }
00437 
00438   YamlConfigWriter writer;
00439   writer.writeFile( config, QString::fromStdString( persistent_settings_file_ ));
00440 
00441   if( writer.error() )
00442   {
00443     ROS_ERROR( "%s", qPrintable( writer.errorMessage() ));
00444   }
00445 }
00446 
00447 void VisualizationFrame::initMenus()
00448 {
00449   file_menu_ = menuBar()->addMenu( "&File" );
00450   file_menu_->addAction( "&Open Config", this, SLOT( onOpen() ), QKeySequence( "Ctrl+O" ));
00451   file_menu_->addAction( "&Save Config", this, SLOT( onSave() ), QKeySequence( "Ctrl+S" ));
00452   file_menu_->addAction( "Save Config &As", this, SLOT( onSaveAs() ));
00453   recent_configs_menu_ = file_menu_->addMenu( "&Recent Configs" );
00454   file_menu_->addAction( "Save &Image", this, SLOT( onSaveImage() ));
00455   if( show_choose_new_master_option_ )
00456   {
00457     file_menu_->addSeparator();
00458     file_menu_->addAction( "Change &Master", this, SLOT( changeMaster() ));
00459   }
00460   file_menu_->addSeparator();
00461   file_menu_->addAction( "&Quit", this, SLOT( close() ), QKeySequence( "Ctrl+Q" ));
00462 
00463   view_menu_ = menuBar()->addMenu( "&Panels" );
00464   view_menu_->addAction( "Add &New Panel", this, SLOT( openNewPanelDialog() ));
00465   delete_view_menu_ = view_menu_->addMenu( "&Delete Panel" );
00466   delete_view_menu_->setEnabled( false );
00467 
00468   QAction * fullscreen_action = view_menu_->addAction("&Fullscreen", this, SLOT( setFullScreen(bool) ), Qt::Key_F11);
00469   fullscreen_action->setCheckable(true);
00470   this->addAction(fullscreen_action); // Also add to window, or the shortcut doest work when the menu is hidden.
00471   connect(this, SIGNAL( fullScreenChange( bool ) ), fullscreen_action, SLOT( setChecked( bool ) ) );
00472   new QShortcut(Qt::Key_Escape, this, SLOT( exitFullScreen() ));
00473   view_menu_->addSeparator();
00474 
00475   QMenu* help_menu = menuBar()->addMenu( "&Help" );
00476   help_menu->addAction( "Show &Help panel", this, SLOT( showHelpPanel() ));
00477   help_menu->addAction( "Open rviz wiki in browser", this, SLOT( onHelpWiki() ));
00478   help_menu->addSeparator();
00479   help_menu->addAction( "&About", this, SLOT( onHelpAbout() ));
00480 }
00481 
00482 void VisualizationFrame::initToolbars()
00483 {
00484   QFont font;
00485   font.setPointSize( font.pointSizeF()*0.9 );
00486 
00487   // make toolbar with plugin tools
00488 
00489   toolbar_ = addToolBar( "Tools" );
00490   toolbar_->setFont( font );
00491   toolbar_->setContentsMargins(0,0,0,0);
00492   toolbar_->setObjectName( "Tools" );
00493   toolbar_->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
00494   toolbar_actions_ = new QActionGroup( this );
00495   connect( toolbar_actions_, SIGNAL( triggered( QAction* )), this, SLOT( onToolbarActionTriggered( QAction* )));
00496   view_menu_->addAction( toolbar_->toggleViewAction() );
00497 
00498   add_tool_action_ = new QAction( "", toolbar_actions_ );
00499   add_tool_action_->setToolTip( "Add a new tool" );
00500   add_tool_action_->setIcon( loadPixmap( "package://rviz/icons/plus.png" ) );
00501   toolbar_->addAction( add_tool_action_ );
00502   connect( add_tool_action_, SIGNAL( triggered() ), this, SLOT( openNewToolDialog() ));
00503 
00504   remove_tool_menu_ = new QMenu();
00505   QToolButton* remove_tool_button = new QToolButton();
00506   remove_tool_button->setMenu( remove_tool_menu_ );
00507   remove_tool_button->setPopupMode( QToolButton::InstantPopup );
00508   remove_tool_button->setToolTip( "Remove a tool from the toolbar" );
00509   remove_tool_button->setIcon( loadPixmap( "package://rviz/icons/minus.png" ) );
00510   toolbar_->addWidget( remove_tool_button );
00511   connect( remove_tool_menu_, SIGNAL( triggered( QAction* )), this, SLOT( onToolbarRemoveTool( QAction* )));
00512 
00513 }
00514 
00515 void VisualizationFrame::hideDockImpl( Qt::DockWidgetArea area, bool hide )
00516 {
00517   QList<PanelDockWidget *> dock_widgets = findChildren<PanelDockWidget *>();
00518 
00519   for ( QList<PanelDockWidget *>::iterator it=dock_widgets.begin(); it!=dock_widgets.end(); it++ )
00520   {
00521     Qt::DockWidgetArea curr_area = dockWidgetArea ( *it );
00522     if ( area == curr_area )
00523     {
00524       (*it)->setCollapsed(hide);
00525     }
00526     // allow/disallow docking to this area for all widgets
00527     if ( hide )
00528     {
00529       (*it)->setAllowedAreas( (*it)->allowedAreas() & ~area );
00530     }
00531     else
00532     {
00533       (*it)->setAllowedAreas( (*it)->allowedAreas() | area );
00534     }
00535   }
00536 }
00537 
00538 void VisualizationFrame::setHideButtonVisibility( bool visible )
00539 {
00540   hide_left_dock_button_->setVisible( visible );
00541   hide_right_dock_button_->setVisible( visible );
00542 }
00543 
00544 void VisualizationFrame::hideLeftDock( bool hide )
00545 {
00546   hideDockImpl( Qt::LeftDockWidgetArea, hide );
00547   hide_left_dock_button_->setArrowType( hide ? Qt::RightArrow : Qt::LeftArrow );
00548 }
00549 
00550 void VisualizationFrame::hideRightDock( bool hide )
00551 {
00552   hideDockImpl( Qt::RightDockWidgetArea, hide );
00553   hide_right_dock_button_->setArrowType( hide ? Qt::LeftArrow : Qt::RightArrow );
00554 }
00555 
00556 void VisualizationFrame::onDockPanelVisibilityChange( bool visible )
00557 {
00558   // if a dock widget becomes visible and is resting inside the
00559   // left or right dock area, we want to unhide the whole area
00560   if ( visible )
00561   {
00562     QDockWidget* dock_widget = dynamic_cast<QDockWidget*>( sender() );
00563     if ( dock_widget )
00564     {
00565       Qt::DockWidgetArea area = dockWidgetArea( dock_widget );
00566       if ( area == Qt::LeftDockWidgetArea )
00567       {
00568         hide_left_dock_button_->setChecked( false );
00569       }
00570       if ( area == Qt::RightDockWidgetArea )
00571       {
00572         hide_right_dock_button_->setChecked( false );
00573       }
00574     }
00575   }
00576 
00577 }
00578 
00579 void VisualizationFrame::openNewPanelDialog()
00580 {
00581   QString class_id;
00582   QString display_name;
00583   QStringList empty;
00584 
00585   NewObjectDialog* dialog = new NewObjectDialog( panel_factory_,
00586                                                  "Panel",
00587                                                  empty,
00588                                                  empty,
00589                                                  &class_id,
00590                                                  &display_name,
00591                                                  this );
00592   manager_->stopUpdate();
00593   if( dialog->exec() == QDialog::Accepted )
00594   {
00595     addPanelByName( display_name, class_id );
00596   }
00597   manager_->startUpdate();
00598 }
00599 
00600 void VisualizationFrame::openNewToolDialog()
00601 {
00602   QString class_id;
00603   QStringList empty;
00604   ToolManager* tool_man = manager_->getToolManager();
00605 
00606   NewObjectDialog* dialog = new NewObjectDialog( tool_man->getFactory(),
00607                                                  "Tool",
00608                                                  empty,
00609                                                  tool_man->getToolClasses(),
00610                                                  &class_id );
00611   manager_->stopUpdate();
00612   if( dialog->exec() == QDialog::Accepted )
00613   {
00614     tool_man->addTool( class_id );
00615   }
00616   manager_->startUpdate();
00617   activateWindow(); // Force keyboard focus back on main window.
00618 }
00619 
00620 void VisualizationFrame::updateRecentConfigMenu()
00621 {
00622   recent_configs_menu_->clear();
00623 
00624   D_string::iterator it = recent_configs_.begin();
00625   D_string::iterator end = recent_configs_.end();
00626   for (; it != end; ++it)
00627   {
00628     if( *it != "" )
00629     {
00630       std::string display_name = *it;
00631       if( display_name == default_display_config_file_ )
00632       {
00633         display_name += " (default)";
00634       }
00635       if( display_name.find( home_dir_ ) == 0 )
00636       {
00637         display_name = ("~" / fs::path( display_name.substr( home_dir_.size() ))).BOOST_FILE_STRING();
00638       }
00639       QString qdisplay_name = QString::fromStdString( display_name );
00640       QAction* action = new QAction( qdisplay_name, this );
00641       action->setData( QString::fromStdString( *it ));
00642       connect( action, SIGNAL( triggered() ), this, SLOT( onRecentConfigSelected() ));
00643       recent_configs_menu_->addAction( action );
00644     }
00645   }
00646 }
00647 
00648 void VisualizationFrame::markRecentConfig( const std::string& path )
00649 {
00650   D_string::iterator it = std::find( recent_configs_.begin(), recent_configs_.end(), path );
00651   if( it != recent_configs_.end() )
00652   {
00653     recent_configs_.erase( it );
00654   }
00655 
00656   recent_configs_.push_front( path );
00657 
00658   if( recent_configs_.size() > RECENT_CONFIG_COUNT )
00659   {
00660     recent_configs_.pop_back();
00661   }
00662 
00663   updateRecentConfigMenu();
00664 }
00665 
00666 void VisualizationFrame::loadDisplayConfig( const QString& qpath )
00667 {
00668   std::string path = qpath.toStdString();
00669   std::string actual_load_path = path;
00670   if( !fs::exists( path ) || fs::is_directory( path ) || fs::is_empty( path ))
00671   {
00672     actual_load_path = (fs::path(package_path_) / "default.rviz").BOOST_FILE_STRING();      
00673     if( !fs::exists( actual_load_path ))
00674     {
00675       ROS_ERROR( "Default display config '%s' not found.  RViz will be very empty at first.", actual_load_path.c_str() );
00676       return;
00677     }
00678   }
00679 
00680   // Check if we have unsaved changes to the current config the same
00681   // as we do during exit, with the same option to cancel.
00682   if( !prepareToExit() )
00683   {
00684     return;
00685   }
00686 
00687   setWindowModified( false );
00688   loading_ = true;
00689 
00690   LoadingDialog* dialog = NULL;
00691   if( initialized_ )
00692   {
00693     dialog = new LoadingDialog( this );
00694     dialog->show();
00695     connect( this, SIGNAL( statusUpdate( const QString& )), dialog, SLOT( showMessage( const QString& )));
00696   }
00697 
00698   YamlConfigReader reader;
00699   Config config;
00700   reader.readFile( config, QString::fromStdString( actual_load_path ));
00701   if( !reader.error() )
00702   {
00703     load( config );
00704   }
00705 
00706   markRecentConfig( path );
00707 
00708   setDisplayConfigFile( path );
00709 
00710   last_config_dir_ = fs::path( path ).parent_path().BOOST_FILE_STRING();
00711 
00712   delete dialog;
00713 
00714   post_load_timer_->start( 1000 );
00715 }
00716 
00717 void VisualizationFrame::markLoadingDone()
00718 {
00719   loading_ = false;
00720 }
00721 
00722 void VisualizationFrame::setImageSaveDirectory( const QString& directory )
00723 {
00724   last_image_dir_ = directory.toStdString();
00725 }
00726 
00727 void VisualizationFrame::setDisplayConfigModified()
00728 {
00729   if( !loading_ )
00730   {
00731     setWindowModified( true );
00732   }
00733 }
00734 
00735 void VisualizationFrame::setDisplayConfigFile( const std::string& path )
00736 {
00737   display_config_file_ = path;
00738 
00739   std::string title;
00740   if( path == default_display_config_file_ )
00741   {
00742     title = "RViz[*]";
00743   }
00744   else
00745   {
00746     title = fs::path( path ).BOOST_FILENAME_STRING() + "[*] - RViz";
00747   }
00748   setWindowTitle( QString::fromStdString( title ));
00749 }
00750 
00751 bool VisualizationFrame::saveDisplayConfig( const QString& path )
00752 {
00753   Config config;
00754   save( config );
00755 
00756   YamlConfigWriter writer;
00757   writer.writeFile( config, path );
00758 
00759   if( writer.error() )
00760   {
00761     ROS_ERROR( "%s", qPrintable( writer.errorMessage() ));
00762     error_message_ = writer.errorMessage();
00763     return false;
00764   }
00765   else
00766   {
00767     setWindowModified( false );
00768     error_message_ = "";
00769     return true;
00770   }
00771 }
00772 
00773 void VisualizationFrame::save( Config config )
00774 {
00775   manager_->save( config.mapMakeChild( "Visualization Manager" ));
00776   savePanels( config.mapMakeChild( "Panels" ));
00777   saveWindowGeometry( config.mapMakeChild( "Window Geometry" ));
00778 }
00779 
00780 void VisualizationFrame::load( const Config& config )
00781 {
00782   manager_->load( config.mapGetChild( "Visualization Manager" ));
00783   loadPanels( config.mapGetChild( "Panels" ));
00784   loadWindowGeometry( config.mapGetChild( "Window Geometry" ));
00785 }
00786 
00787 void VisualizationFrame::loadWindowGeometry( const Config& config )
00788 {
00789   int x, y;
00790   if( config.mapGetInt( "X", &x ) &&
00791       config.mapGetInt( "Y", &y ))
00792   {
00793     move( x, y );
00794   }
00795 
00796   int width, height;
00797   if( config.mapGetInt( "Width", &width ) &&
00798       config.mapGetInt( "Height", &height ))
00799   {
00800     resize( width, height );
00801   }    
00802 
00803   QString main_window_config;
00804   if( config.mapGetString( "QMainWindow State", &main_window_config ))
00805   {
00806     restoreState( QByteArray::fromHex( qPrintable( main_window_config )));
00807   }
00808 
00809   // load panel dock widget states (collapsed or not)
00810   QList<PanelDockWidget *> dock_widgets = findChildren<PanelDockWidget *>();
00811 
00812   for ( QList<PanelDockWidget *>::iterator it=dock_widgets.begin(); it!=dock_widgets.end(); it++ )
00813   {
00814     Config itConfig = config.mapGetChild((*it)->windowTitle());
00815 
00816     if (itConfig.isValid())
00817     {
00818       (*it)->load(itConfig);
00819     }
00820   }
00821 
00822   bool b;
00823   config.mapGetBool( "Hide Left Dock", &b );
00824   hide_left_dock_button_->setChecked( b );
00825   hideLeftDock(b);
00826   config.mapGetBool( "Hide Right Dock", &b );
00827   hideRightDock(b);
00828   hide_right_dock_button_->setChecked( b );
00829 }
00830 
00831 void VisualizationFrame::saveWindowGeometry( Config config )
00832 {
00833   config.mapSetValue( "X", x() );
00834   config.mapSetValue( "Y", y() );
00835   config.mapSetValue( "Width", width() );
00836   config.mapSetValue( "Height", height() );
00837 
00838   QByteArray window_state = saveState().toHex();
00839   config.mapSetValue( "QMainWindow State", window_state.constData() );
00840 
00841   config.mapSetValue( "Hide Left Dock", hide_left_dock_button_->isChecked() );
00842   config.mapSetValue( "Hide Right Dock", hide_right_dock_button_->isChecked() );
00843 
00844   // save panel dock widget states (collapsed or not)
00845   QList<PanelDockWidget *> dock_widgets = findChildren<PanelDockWidget *>();
00846 
00847   for ( QList<PanelDockWidget *>::iterator it=dock_widgets.begin(); it!=dock_widgets.end(); it++ )
00848   {
00849     (*it)->save(config.mapMakeChild( (*it)->windowTitle() ));
00850   }
00851 }
00852 
00853 void VisualizationFrame::loadPanels( const Config& config )
00854 {
00855   // First destroy any existing custom panels.
00856   for( int i = 0; i < custom_panels_.size(); i++ )
00857   {
00858     delete custom_panels_[ i ].dock;
00859     delete custom_panels_[ i ].delete_action;
00860   }
00861   custom_panels_.clear();
00862 
00863   // Then load the ones in the config.
00864   int num_custom_panels = config.listLength();
00865   for( int i = 0; i < num_custom_panels; i++ )
00866   {
00867     Config panel_config = config.listChildAt( i );
00868 
00869     QString class_id, name;
00870     if( panel_config.mapGetString( "Class", &class_id ) &&
00871         panel_config.mapGetString( "Name", &name ))
00872     {
00873       QDockWidget* dock = addPanelByName( name, class_id );
00874       // This is kind of ridiculous - should just be something like
00875       // createPanel() and addPanel() so I can do load() without this
00876       // qobject_cast.
00877       if( dock )
00878       {
00879         Panel* panel = qobject_cast<Panel*>( dock->widget() );
00880         if( panel )
00881         {
00882           panel->load( panel_config );
00883         }
00884       }
00885     }
00886   }
00887 }
00888 
00889 void VisualizationFrame::savePanels( Config config )
00890 {
00891   config.setType( Config::List ); // Not really necessary, but gives an empty list if there are no entries, instead of an Empty config node.
00892 
00893   for( int i = 0; i < custom_panels_.size(); i++ )
00894   {
00895     custom_panels_[ i ].panel->save( config.listAppendNew() );
00896   }
00897 }
00898 
00899 bool VisualizationFrame::prepareToExit()
00900 {
00901   if( !initialized_ )
00902   {
00903     return true;
00904   }
00905 
00906   savePersistentSettings();
00907 
00908   if( isWindowModified() )
00909   {
00910     QMessageBox box( this );
00911     box.setText( "There are unsaved changes." );
00912     box.setInformativeText( QString::fromStdString( "Save changes to " + display_config_file_ + "?" ));
00913     box.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
00914     box.setDefaultButton( QMessageBox::Save );
00915     manager_->stopUpdate();
00916     int result = box.exec();
00917     manager_->startUpdate();
00918     switch( result )
00919     {
00920     case QMessageBox::Save:
00921       if( saveDisplayConfig( QString::fromStdString( display_config_file_ )))
00922       {
00923         return true;
00924       }
00925       else
00926       {
00927         QMessageBox box( this );
00928         box.setWindowTitle( "Failed to save." );
00929         box.setText( getErrorMessage() );
00930         box.setInformativeText( QString::fromStdString( "Save copy of " + display_config_file_ + " to another file?" ));
00931         box.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
00932         box.setDefaultButton( QMessageBox::Save );
00933         int result = box.exec();
00934         switch( result )
00935         {
00936         case QMessageBox::Save:
00937           onSaveAs();
00938           return true;
00939         case QMessageBox::Discard:
00940           return true;
00941         default:
00942           return false;
00943         }
00944         
00945       }
00946     case QMessageBox::Discard:
00947       return true;
00948     default:
00949       return false;
00950     }
00951   }
00952   else
00953   {
00954     return true;
00955   }
00956 }
00957 
00958 void VisualizationFrame::onOpen()
00959 {
00960   manager_->stopUpdate();
00961   QString filename = QFileDialog::getOpenFileName( this, "Choose a file to open",
00962                                                    QString::fromStdString( last_config_dir_ ),
00963                                                    "RViz config files (" CONFIG_EXTENSION_WILDCARD ")" );
00964   manager_->startUpdate();
00965 
00966   if( !filename.isEmpty() )
00967   {
00968     std::string path = filename.toStdString();
00969 
00970     if( !fs::exists( path ))
00971     {
00972       QString message = filename + " does not exist!";
00973       QMessageBox::critical( this, "Config file does not exist", message );
00974       return;
00975     }
00976 
00977     loadDisplayConfig( filename );
00978   }
00979 }
00980 
00981 void VisualizationFrame::onSave()
00982 {
00983   if( !initialized_ )
00984   {
00985     return;
00986   }
00987 
00988   savePersistentSettings();
00989 
00990   if( !saveDisplayConfig( QString::fromStdString( display_config_file_ )))
00991   {
00992     manager_->stopUpdate();
00993     QMessageBox box( this );
00994     box.setWindowTitle( "Failed to save." );
00995     box.setText( getErrorMessage() );
00996     box.setInformativeText( QString::fromStdString( "Save copy of " + display_config_file_ + " to another file?" ));
00997     box.setStandardButtons( QMessageBox::Save | QMessageBox::Cancel );
00998     box.setDefaultButton( QMessageBox::Save );
00999     if( box.exec() == QMessageBox::Save )
01000     {
01001       onSaveAs();
01002     }
01003     manager_->startUpdate();
01004   }
01005 }
01006 
01007 void VisualizationFrame::onSaveAs()
01008 {
01009   manager_->stopUpdate();
01010   QString q_filename = QFileDialog::getSaveFileName( this, "Choose a file to save to",
01011                                                      QString::fromStdString( last_config_dir_ ),
01012                                                      "RViz config files (" CONFIG_EXTENSION_WILDCARD ")" );
01013   manager_->startUpdate();
01014 
01015   if( !q_filename.isEmpty() )
01016   {
01017     std::string filename = q_filename.toStdString();
01018     fs::path path( filename );
01019     if( path.extension() != "." CONFIG_EXTENSION )
01020     {
01021       filename += "." CONFIG_EXTENSION;
01022     }
01023 
01024     if( !saveDisplayConfig( QString::fromStdString( filename )))
01025     {
01026       QMessageBox::critical( this, "Failed to save.", getErrorMessage() );
01027     }
01028 
01029     markRecentConfig( filename );
01030     last_config_dir_ = fs::path( filename ).parent_path().BOOST_FILE_STRING();
01031     setDisplayConfigFile( filename );
01032   }
01033 }
01034 
01035 void VisualizationFrame::onSaveImage()
01036 {
01037   ScreenshotDialog* dialog = new ScreenshotDialog( this, render_panel_, QString::fromStdString( last_image_dir_ ));
01038   connect( dialog, SIGNAL( savedInDirectory( const QString& )),
01039            this, SLOT( setImageSaveDirectory( const QString& )));
01040   dialog->show();
01041 }
01042 
01043 void VisualizationFrame::onRecentConfigSelected()
01044 {
01045   QAction* action = dynamic_cast<QAction*>( sender() );
01046   if( action )
01047   {
01048     std::string path = action->data().toString().toStdString();
01049     if( !path.empty() )
01050     {
01051       if( !fs::exists( path ))
01052       {
01053         QString message = QString::fromStdString( path  ) + " does not exist!";
01054         QMessageBox::critical( this, "Config file does not exist", message );
01055         return;
01056       }
01057 
01058       loadDisplayConfig( QString::fromStdString( path ));
01059     }
01060   }
01061 }
01062 
01063 void VisualizationFrame::addTool( Tool* tool )
01064 {
01065   QAction* action = new QAction( tool->getName(), toolbar_actions_ );
01066   action->setIcon( tool->getIcon() );
01067   action->setIconText( tool->getName() );
01068   action->setCheckable( true );
01069   toolbar_->insertAction( add_tool_action_, action );
01070   action_to_tool_map_[ action ] = tool;
01071   tool_to_action_map_[ tool ] = action;
01072 
01073   remove_tool_menu_->addAction( tool->getName() );
01074 }
01075 
01076 void VisualizationFrame::onToolbarActionTriggered( QAction* action )
01077 {
01078   Tool* tool = action_to_tool_map_[ action ];
01079 
01080   if( tool )
01081   {
01082     manager_->getToolManager()->setCurrentTool( tool );
01083   }
01084 }
01085 
01086 void VisualizationFrame::onToolbarRemoveTool( QAction* remove_tool_menu_action )
01087 {
01088   QString name = remove_tool_menu_action->text();
01089   for( int i = 0; i < manager_->getToolManager()->numTools(); i++ )
01090   {
01091     Tool* tool = manager_->getToolManager()->getTool( i );
01092     if( tool->getName() == name )
01093     {
01094       manager_->getToolManager()->removeTool( i );
01095       return;
01096     }
01097   }
01098 }
01099 
01100 void VisualizationFrame::removeTool( Tool* tool )
01101 {
01102   QAction* action = tool_to_action_map_[ tool ];
01103   if( action )
01104   {
01105     toolbar_actions_->removeAction( action );
01106     toolbar_->removeAction( action );
01107     tool_to_action_map_.erase( tool );
01108     action_to_tool_map_.erase( action );
01109   }
01110   QString tool_name = tool->getName();
01111   QList<QAction*> remove_tool_actions = remove_tool_menu_->actions();
01112   for( int i = 0; i < remove_tool_actions.size(); i++ )
01113   {
01114     QAction* removal_action = remove_tool_actions.at( i );
01115     if( removal_action->text() == tool_name )
01116     {
01117       remove_tool_menu_->removeAction( removal_action );
01118       break;
01119     }
01120   }
01121 }
01122 
01123 void VisualizationFrame::refreshTool( Tool* tool )
01124 {
01125   QAction* action = tool_to_action_map_[ tool ];
01126   action->setIcon( tool->getIcon() );
01127   action->setIconText( tool->getName() );
01128 }
01129 
01130 void VisualizationFrame::indicateToolIsCurrent( Tool* tool )
01131 {
01132   QAction* action = tool_to_action_map_[ tool ];
01133   if( action )
01134   {
01135     action->setChecked( true );
01136   }
01137 }
01138 
01139 void VisualizationFrame::showHelpPanel()
01140 {
01141   if( !show_help_action_ )
01142   {
01143     QDockWidget* dock = addPanelByName( "Help", "rviz/Help" );
01144     show_help_action_ = dock->toggleViewAction();
01145     connect( dock, SIGNAL( destroyed( QObject* )), this, SLOT( onHelpDestroyed() ));
01146   }
01147   else
01148   {
01149     // show_help_action_ is a toggle action, so trigger() changes its
01150     // state.  Therefore we must force it to the opposite state from
01151     // what we want before we call trigger().  (I think.)
01152     show_help_action_->setChecked( false );
01153     show_help_action_->trigger();
01154   }
01155 }
01156 
01157 void VisualizationFrame::onHelpDestroyed()
01158 {
01159   show_help_action_ = NULL;
01160 }
01161 
01162 void VisualizationFrame::onHelpWiki()
01163 {
01164   QDesktopServices::openUrl( QUrl( "http://www.ros.org/wiki/rviz" ));
01165 }
01166 
01167 void VisualizationFrame::onHelpAbout()
01168 {
01169   QString about_text = QString(
01170     "This is RViz version %1 (%2).\n"
01171     "\n"
01172     "Compiled against Qt version %3."
01173     "\n"
01174     "Compiled against OGRE version %4.%5.%6%7 (%8)."
01175   )
01176   .arg(get_version().c_str())
01177   .arg(get_distro().c_str())
01178   .arg(QT_VERSION_STR)
01179   .arg(OGRE_VERSION_MAJOR)
01180   .arg(OGRE_VERSION_MINOR)
01181   .arg(OGRE_VERSION_PATCH)
01182   .arg(OGRE_VERSION_SUFFIX)
01183   .arg(OGRE_VERSION_NAME);
01184 
01185   QMessageBox::about(QApplication::activeWindow(), "About", about_text);
01186 }
01187 
01188 QWidget* VisualizationFrame::getParentWindow()
01189 {
01190   return this;
01191 }
01192 
01193 void VisualizationFrame::onDeletePanel()
01194 {
01195   // This should only be called as a SLOT from a QAction in the
01196   // "delete panel" submenu, so the sender will be one of the QActions
01197   // stored as "delete_action" in a PanelRecord.  This code looks for
01198   // a delete_action in custom_panels_ matching sender() and removes
01199   // the panel associated with it.
01200   if( QAction* action = qobject_cast<QAction*>( sender() ))
01201   {
01202     for( int i = 0; i < custom_panels_.size(); i++ )
01203     {
01204       if( custom_panels_[ i ].delete_action == action )
01205       {
01206         delete custom_panels_[ i ].dock;
01207         custom_panels_.removeAt( i );
01208         setDisplayConfigModified();
01209         action->deleteLater();
01210         if( delete_view_menu_->actions().size() == 1 &&
01211             delete_view_menu_->actions().first() == action )
01212         {
01213           delete_view_menu_->setEnabled( false );
01214         }
01215         return;
01216       }
01217     }
01218   }
01219 }
01220 
01221 void VisualizationFrame::setFullScreen( bool full_screen )
01222 {
01223   Q_EMIT( fullScreenChange( full_screen ) );
01224 
01225   if (full_screen)
01226     toolbar_visible_ = toolbar_->isVisible();
01227   menuBar()->setVisible(!full_screen);
01228   toolbar_->setVisible(!full_screen && toolbar_visible_);
01229   statusBar()->setVisible(!full_screen);
01230   setHideButtonVisibility(!full_screen);
01231 
01232   if (full_screen)
01233     setWindowState(windowState() | Qt::WindowFullScreen);
01234   else
01235     setWindowState(windowState() & ~Qt::WindowFullScreen);
01236   show();
01237 }
01238 
01239 void VisualizationFrame::exitFullScreen()
01240 {
01241   setFullScreen( false );
01242 }
01243 
01244 QDockWidget* VisualizationFrame::addPanelByName( const QString& name,
01245                                                  const QString& class_id,
01246                                                  Qt::DockWidgetArea area,
01247                                                  bool floating )
01248 {
01249   QString error;
01250   Panel* panel = panel_factory_->make( class_id, &error );
01251   if( !panel )
01252   {
01253     panel = new FailedPanel( class_id, error );
01254   }
01255   panel->setName( name );
01256   connect( panel, SIGNAL( configChanged() ), this, SLOT( setDisplayConfigModified() ));
01257 
01258   PanelRecord record;
01259   record.dock = addPane( name, panel, area, floating );
01260   record.panel = panel;
01261   record.name = name;
01262   record.delete_action = delete_view_menu_->addAction( name, this, SLOT( onDeletePanel() ));
01263   custom_panels_.append( record );
01264   delete_view_menu_->setEnabled( true );
01265 
01266   record.panel->initialize( manager_ );
01267 
01268   record.dock->setIcon( panel_factory_->getIcon( class_id ) );
01269 
01270   return record.dock;
01271 }
01272 
01273 PanelDockWidget* VisualizationFrame::addPane( const QString& name, QWidget* panel, Qt::DockWidgetArea area, bool floating )
01274 {
01275   PanelDockWidget *dock;
01276   dock = new PanelDockWidget( name );
01277   dock->setContentWidget( panel );
01278   dock->setFloating( floating );
01279   dock->setObjectName( name ); // QMainWindow::saveState() needs objectName to be set.
01280   addDockWidget( area, dock );
01281 
01282   // we want to know when that panel becomes visible
01283   connect( dock, SIGNAL( visibilityChanged( bool )), this, SLOT( onDockPanelVisibilityChange( bool ) ));
01284   connect( this, SIGNAL( fullScreenChange(bool) ), dock, SLOT( overrideVisibility(bool) ));
01285 
01286   QAction* toggle_action = dock->toggleViewAction();
01287   view_menu_->addAction( toggle_action );
01288 
01289   connect( toggle_action, SIGNAL( triggered( bool )), this, SLOT( setDisplayConfigModified() ));
01290   connect( dock, SIGNAL( closed()), this, SLOT( setDisplayConfigModified() ));
01291 
01292   dock->installEventFilter( geom_change_detector_ );
01293 
01294   // repair/update visibility status
01295   hideLeftDock( area == Qt::LeftDockWidgetArea ? false : hide_left_dock_button_->isChecked() );
01296   hideRightDock( area == Qt::RightDockWidgetArea ? false : hide_right_dock_button_->isChecked() );
01297 
01298   return dock;
01299 }
01300 
01301 } // end namespace rviz


rviz
Author(s): Dave Hershberger, David Gossow, Josh Faust
autogenerated on Thu Jun 6 2019 18:02:16