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 <QTimer>
00031 #include <QStyledItemDelegate>
00032 #include <QLineEdit>
00033 #include <QPainter>
00034 #include <QMouseEvent>
00035
00036 #include "rviz/properties/property_tree_widget.h"
00037 #include "rviz/properties/property_widget_item.h"
00038 #include "rviz/properties/property.h"
00039 #include "rviz/properties/topic_info_variant.h"
00040 #include "rviz/properties/ros_topic_editor.h"
00041 #include "rviz/properties/color_editor.h"
00042 #include "rviz/uniform_string_stream.h"
00043
00044 namespace rviz
00045 {
00046
00048
00050 class PropertyTreeDelegate: public QStyledItemDelegate
00051 {
00052 private:
00053 PropertyTreeWidget* tree_widget_;
00054
00055 public:
00056 PropertyTreeDelegate( PropertyTreeWidget* tree_widget )
00057 : QStyledItemDelegate( tree_widget )
00058 , tree_widget_( tree_widget )
00059 {
00060 }
00061
00062 virtual void paint( QPainter * painter,
00063 const QStyleOptionViewItem & option,
00064 const QModelIndex & index ) const
00065 {
00066 PropertyWidgetItem* item = tree_widget_->getItem( index );
00067 if( index.column() == 0 || !item->paint( painter, option ))
00068 {
00069 QStyledItemDelegate::paint( painter, option, index );
00070 }
00071 }
00072
00073 virtual QWidget *createEditor( QWidget *parent,
00074 const QStyleOptionViewItem & option,
00075 const QModelIndex &index ) const
00076 {
00077 if( index.column() != 1 )
00078 {
00079 return 0;
00080 }
00081 PropertyWidgetItem* item = tree_widget_->getItem( index );
00082 if( !item )
00083 {
00084 return 0;
00085 }
00086 QWidget* editor = item->createEditor( parent, option );
00087 if( editor != 0 )
00088 {
00089 if( LineEditWithButton* lewb = qobject_cast<LineEditWithButton*>( editor ))
00090 {
00091 tree_widget_->connect( lewb, SIGNAL( startPersistence() ), tree_widget_, SLOT( startPersistCurrent() ));
00092 tree_widget_->connect( lewb, SIGNAL( endPersistence() ), tree_widget_, SLOT( endPersistCurrent() ));
00093 }
00094 return editor;
00095 }
00096
00097
00098
00099
00100
00101
00102 QVariant originalValue = index.model()->data(index, Qt::UserRole);
00103
00104 if( originalValue.canConvert<ros::master::TopicInfo>() )
00105 {
00106 RosTopicEditor* editor = new RosTopicEditor( parent );
00107 tree_widget_->connect( editor, SIGNAL( startPersistence() ), tree_widget_, SLOT( startPersistCurrent() ));
00108 tree_widget_->connect( editor, SIGNAL( endPersistence() ), tree_widget_, SLOT( endPersistCurrent() ));
00109 editor->setFrame( false );
00110 return editor;
00111 }
00112 else
00113 {
00114 QValidator *validator;
00115 QLineEdit *lineEdit = new QLineEdit(parent);
00116
00117 switch (originalValue.type()) {
00118 case QVariant::Int:
00119 {
00120 validator = new QIntValidator( (int) item->min_, (int) item->max_, lineEdit );
00121 break;
00122 }
00123 case QMetaType::Float:
00124 {
00125 validator = new QDoubleValidator( item->min_, item->max_, 1000, lineEdit );
00126 break;
00127 }
00128 default:
00129 validator = NULL;
00130 }
00131 lineEdit->setValidator( validator );
00132 lineEdit->setFrame( false );
00133 return lineEdit;
00134 }
00135 }
00136
00137
00138
00139 void setEditorData(QWidget *editor, const QModelIndex &index) const
00140 {
00141 if( index.column() == 1 )
00142 {
00143 PropertyWidgetItem* item = tree_widget_->getItem( index );
00144 if( !item || item->setEditorData( editor ))
00145 {
00146 return;
00147 }
00148 }
00149
00150 QVariant value = index.model()->data(index, Qt::UserRole);
00151
00152 if( RosTopicEditor* topic_editor = qobject_cast<RosTopicEditor*>( editor ))
00153 {
00154 topic_editor->setTopic( value.value<ros::master::TopicInfo>() );
00155 }
00156 else if( QLineEdit *lineEdit = qobject_cast<QLineEdit *>( editor ))
00157 {
00158 lineEdit->setText(value.toString());
00159 }
00160 }
00161
00162
00163 void setModelData( QWidget *editor, QAbstractItemModel *model,
00164 const QModelIndex &index ) const
00165 {
00166 PropertyWidgetItem* item = tree_widget_->getItem( index );
00167 if( !item || item->setModelData( editor ))
00168 {
00169 return;
00170 }
00171
00172 QVariant originalValue = index.model()->data( index, Qt::UserRole );
00173 QVariant value;
00174 QString display_string;
00175
00176 if( RosTopicEditor* topic_editor = qobject_cast<RosTopicEditor*>( editor ))
00177 {
00178 if( !topic_editor->isModified() )
00179 {
00180 return;
00181 }
00182 ros::master::TopicInfo topic = topic_editor->getTopic();
00183 value = QVariant::fromValue( topic );
00184 display_string = QString::fromStdString( topic.name );
00185 }
00186 else if( QLineEdit* lineEdit = qobject_cast<QLineEdit *>( editor ))
00187 {
00188 if( !lineEdit->isModified() )
00189 {
00190 return;
00191 }
00192
00193 QString text = lineEdit->text();
00194 const QValidator *validator = lineEdit->validator();
00195 if( validator ) {
00196 int pos;
00197 if( validator->validate( text, pos ) != QValidator::Acceptable )
00198 {
00199 return;
00200 }
00201 }
00202
00203 switch (originalValue.type()) {
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254 default:
00255 value = text;
00256 value.convert( originalValue.type() );
00257 }
00258 display_string = value.toString();
00259 }
00260
00261
00262 model->setData(index, value, Qt::UserRole);
00263
00264
00265 bool ign = tree_widget_->setIgnoreChanges( true );
00266 model->setData(index, display_string, Qt::DisplayRole);
00267 tree_widget_->setIgnoreChanges( ign );
00268 }
00269 };
00270
00272
00274 class SplitterHandle: public QWidget
00275 {
00276 public:
00277 SplitterHandle( PropertyTreeWidget* parent = 0 )
00278 : QWidget( parent )
00279 , parent_( parent )
00280 , first_column_size_ratio_( 0.5f )
00281 {
00282 setCursor( Qt::SplitHCursor );
00283 int w = 7;
00284 setGeometry( parent_->width() / 2 - w/2, 0, w, parent_->height() );
00285 }
00286
00287 void onParentResized()
00288 {
00289 int new_column_width = int( first_column_size_ratio_ * parent_->width() );
00290 parent_->setColumnWidth( 0, new_column_width );
00291 setGeometry( new_column_width - width() / 2, 0, width(), parent_->height() );
00292 }
00293
00294 void setRatio( float ratio )
00295 {
00296 first_column_size_ratio_ = ratio;
00297 onParentResized();
00298 }
00299
00300 float getRatio()
00301 {
00302 return first_column_size_ratio_;
00303 }
00304
00305 protected:
00306 void mousePressEvent( QMouseEvent* event )
00307 {
00308 if( event->button() == Qt::LeftButton )
00309 {
00310 x_press_offset_ = event->x();
00311 }
00312 }
00313
00314 void mouseMoveEvent( QMouseEvent* event )
00315 {
00316 int padding = 55;
00317
00318 if( event->buttons() & Qt::LeftButton )
00319 {
00320 QPoint pos_rel_parent = parent_->mapFromGlobal( event->globalPos() );
00321
00322 int new_x = pos_rel_parent.x() - x_press_offset_;
00323
00324 if( new_x > parent_->width() - width() - padding )
00325 {
00326 new_x = parent_->width() - width() - padding;
00327 }
00328
00329 if( new_x < padding )
00330 {
00331 new_x = padding;
00332 }
00333
00334 if( new_x != x() )
00335 {
00336 move( new_x, 0 );
00337
00338 int new_column_width = new_x + width() / 2;
00339 parent_->setColumnWidth( 0, new_column_width );
00340
00341 first_column_size_ratio_ = new_column_width / (float) parent_->width();
00342 }
00343 }
00344 }
00345
00346 private:
00347 PropertyTreeWidget* parent_;
00348 int x_press_offset_;
00349
00352 float first_column_size_ratio_;
00353 };
00354
00356
00358 PropertyTreeWidget::PropertyTreeWidget( QWidget* parent )
00359 : QTreeWidget( parent )
00360 , ignore_changes_( false )
00361 , splitter_handle_( new SplitterHandle( this ))
00362 , persisted_item_( 0 )
00363 {
00364 setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
00365 setHeaderHidden( true );
00366 setUniformRowHeights( true );
00367 setItemDelegate( new PropertyTreeDelegate( this ));
00368 setEditTriggers( QAbstractItemView::AllEditTriggers );
00369 setColumnCount( 2 );
00370 setSelectionMode( QAbstractItemView::ExtendedSelection );
00371
00372 connect( this, SIGNAL( itemChanged( QTreeWidgetItem *, int )),
00373 this, SLOT( onItemChanged( QTreeWidgetItem *, int )));
00374 }
00375
00376 void PropertyTreeWidget::onItemChanged( QTreeWidgetItem* item, int column_number )
00377 {
00378 if( !ignore_changes_ )
00379 {
00380 PropertyWidgetItem* pwi = dynamic_cast<PropertyWidgetItem*>( item );
00381 if( pwi )
00382 {
00383 pwi->getProperty()->readFromGrid();
00384 }
00385 }
00386 }
00387
00388 PropertyWidgetItem* PropertyTreeWidget::getItem( const QModelIndex & index )
00389 {
00390 return dynamic_cast<PropertyWidgetItem*>( itemFromIndex( index ));
00391 }
00392
00393 void PropertyTreeWidget::resizeEvent( QResizeEvent* event )
00394 {
00395 splitter_handle_->onParentResized();
00396 }
00397
00398 void PropertyTreeWidget::dropEvent( QDropEvent* event )
00399 {
00400 QTreeWidget::dropEvent( event );
00401 Q_EMIT orderChanged();
00402 }
00403
00406 std::string PropertyTreeWidget::saveEditableState()
00407 {
00408 UniformStringStream output;
00409
00410 bool first = true;
00411 saveExpandedState( output, invisibleRootItem(), first );
00412
00413 output << ";splitterratio=" << splitter_handle_->getRatio();
00414
00415 return output.str();
00416
00417
00418
00419
00420
00421
00422
00423 }
00424
00425 void PropertyTreeWidget::saveExpandedState( std::ostream& output,
00426 QTreeWidgetItem* parent_item,
00427 bool& first )
00428 {
00429 for( int child_index = 0; child_index < parent_item->childCount(); child_index++ )
00430 {
00431 QTreeWidgetItem* item = parent_item->child( child_index );
00432 if( item->isExpanded() && item->childCount() > 0 )
00433 {
00434 if( first )
00435 {
00436 output << "expanded=";
00437 first = false;
00438 }
00439 else
00440 {
00441 output << ',';
00442 }
00443 PropertyWidgetItem* pwi = dynamic_cast<PropertyWidgetItem*>( item );
00444 if( pwi )
00445 {
00446 output << pwi->getProperty()->getPrefix() << pwi->getProperty()->getName();
00447 saveExpandedState( output, item, first );
00448 }
00449 }
00450 }
00451 }
00452
00454 void PropertyTreeWidget::restoreEditableState( const std::string& state )
00455 {
00456 UniformStringStream iss( state );
00457 std::string assignment;
00458 while( std::getline( iss, assignment, ';' ))
00459 {
00460 size_t equal_pos = assignment.find( '=' );
00461 if( equal_pos != std::string::npos )
00462 {
00463 UniformStringStream value_stream( assignment.substr( equal_pos + 1 ));
00464 if( 0 == assignment.compare( 0, equal_pos, "splitterratio" ))
00465 {
00466 float ratio = 0.5;
00467 value_stream.parseFloat( ratio );
00468 splitter_handle_->setRatio( ratio );
00469 }
00470 else if( 0 == assignment.compare( 0, equal_pos, "expanded" ))
00471 {
00472 std::set<std::string> expanded_entries;
00473 std::string entry;
00474 while( std::getline( value_stream, entry, ',' ))
00475 {
00476 expanded_entries.insert( entry );
00477 }
00478
00479 restoreExpandedState( expanded_entries, invisibleRootItem() );
00480 }
00481 }
00482 }
00483 }
00484
00487 void PropertyTreeWidget::restoreExpandedState( const std::set<std::string>& expanded_entries,
00488 QTreeWidgetItem* parent_item )
00489 {
00490
00491
00492
00493 for( int child_index = 0; child_index < parent_item->childCount(); child_index++ )
00494 {
00495 QTreeWidgetItem* item = parent_item->child( child_index );
00496 PropertyWidgetItem* pwi = dynamic_cast<PropertyWidgetItem*>( item );
00497 if( pwi )
00498 {
00499 std::string entry_name = pwi->getProperty()->getPrefix() + pwi->getProperty()->getName();
00500 if( expanded_entries.find( entry_name ) != expanded_entries.end() )
00501 {
00502 item->setExpanded( true );
00503 if( item->childCount() > 0 )
00504 {
00505 restoreExpandedState( expanded_entries, item );
00506 }
00507 }
00508 else
00509 {
00510 item->setExpanded( false );
00511 }
00512 }
00513 }
00514 }
00515
00516 void PropertyTreeWidget::startPersistCurrent()
00517 {
00518 persisted_item_ = currentItem();
00519 openPersistentEditor( persisted_item_, 1 );
00520 }
00521
00522 void PropertyTreeWidget::endPersistCurrent()
00523 {
00524 if( persisted_item_ )
00525 {
00526 QWidget* editor = itemWidget( persisted_item_, 1 );
00527 if( editor )
00528 {
00529 commitData( editor );
00530 }
00531 closePersistentEditor( persisted_item_, 1 );
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541 dragLeaveEvent( NULL );
00542 }
00543 }
00544
00545 }