00001 #include "curvelist_panel.h"
00002 #include "ui_curvelist_panel.h"
00003 #include "PlotJuggler/alphanum.hpp"
00004 #include <QDebug>
00005 #include <QLayoutItem>
00006 #include <QMenu>
00007 #include <QSettings>
00008 #include <QDrag>
00009 #include <QMimeData>
00010 #include <QHeaderView>
00011 #include <QFontDatabase>
00012 #include <QMessageBox>
00013 #include <QApplication>
00014 #include <QPainter>
00015 #include <QCompleter>
00016 #include <QStandardItem>
00017 #include <QWheelEvent>
00018 #include <QItemSelectionModel>
00019
00020 class SortedTableItem: public QStandardItem
00021 {
00022
00023 public:
00024 SortedTableItem(const QString& name): QStandardItem(name), str(name.toStdString()) {}
00025
00026 bool operator< (const SortedTableItem &other) const
00027 {
00028 return doj::alphanum_impl(this->str.c_str(),
00029 other.str.c_str()) < 0;
00030 }
00031 private:
00032 std::string str;
00033 };
00034
00035
00036
00037
00038 CurveListPanel::CurveListPanel(const CustomPlotMap &mapped_math_plots,
00039 QWidget *parent) :
00040 QWidget(parent),
00041 ui(new Ui::CurveListPanel),
00042 _completer( new TreeModelCompleter(this) ),
00043 _custom_plots(mapped_math_plots),
00044 _point_size(9)
00045 {
00046 ui->setupUi(this);
00047 ui->tableView->viewport()->installEventFilter( this );
00048
00049 _model = new QStandardItemModel(0, 2, this);
00050
00051 for(auto table_view: {ui->tableView, ui->tableViewCustom})
00052 {
00053 table_view->viewport()->installEventFilter( this );
00054 table_view->setModel( _model );
00055 table_view->horizontalHeader()->setStretchLastSection (true);
00056 table_view->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
00057 }
00058
00059 ui->widgetOptions->setVisible(false);
00060
00061 ui->radioRegExp->setAutoExclusive(true);
00062 ui->radioContains->setAutoExclusive(true);
00063 ui->radioPrefix->setAutoExclusive(true);
00064
00065 _completer->setCompletionMode( QCompleter::PopupCompletion );
00066
00067 QSettings settings;
00068
00069 QString active_filter = settings.value("FilterableListWidget.searchFilter").toString();
00070 if( active_filter == "radioRegExp"){
00071
00072 ui->radioRegExp->setChecked(true);
00073 }
00074 else if( active_filter == "radioPrefix"){
00075
00076 ui->radioPrefix->setChecked(true);
00077 }
00078 else if( active_filter == "radioContains"){
00079
00080 ui->radioContains->setChecked(true);
00081 }
00082
00083 _point_size = settings.value("FilterableListWidget/table_point_size", 9).toInt();
00084
00085 _completer_need_update = ui->radioPrefix->isChecked();
00086 ui->lineEdit->setCompleter( _completer_need_update ? _completer : nullptr );
00087
00088 ui->splitter->setStretchFactor(0,5);
00089 ui->splitter->setStretchFactor(1,1);
00090
00091 connect( ui->tableViewCustom->selectionModel(), &QItemSelectionModel::selectionChanged,
00092 this, &CurveListPanel::onCustomSelectionChanged );
00093 }
00094
00095 CurveListPanel::~CurveListPanel()
00096 {
00097 delete ui;
00098 }
00099
00100 int CurveListPanel::rowCount() const
00101 {
00102 return _model->rowCount();
00103 }
00104
00105 void CurveListPanel::clear()
00106 {
00107 _model->setRowCount(0);
00108 _completer->clear();
00109 ui->labelNumberDisplayed->setText( "0 of 0");
00110 }
00111
00112 void CurveListPanel::addItem(const QString &item_name)
00113 {
00114 if( _model->findItems(item_name).size() > 0)
00115 {
00116 return;
00117 }
00118
00119 auto item = new SortedTableItem(item_name);
00120 QFont font = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
00121 font.setPointSize(_point_size);
00122 item->setFont(font);
00123 const int row = rowCount();
00124 _model->setRowCount(row+1);
00125 _model->setItem(row, 0, item);
00126
00127 auto val_cell = new QStandardItem("-");
00128 val_cell->setTextAlignment(Qt::AlignRight);
00129 val_cell->setFlags( Qt::NoItemFlags | Qt::ItemIsEnabled );
00130 font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
00131 font.setPointSize( _point_size );
00132 val_cell->setFont( font );
00133 val_cell->setFlags(Qt::NoItemFlags);
00134
00135 _model->setItem(row, 1, val_cell );
00136
00137 if( _completer_need_update )
00138 {
00139 _completer->addToCompletionTree(item_name);
00140 }
00141 }
00142
00143 void CurveListPanel::refreshColumns()
00144 {
00145 ui->tableView->sortByColumn(0,Qt::AscendingOrder);
00146 ui->tableViewCustom->sortByColumn(0,Qt::AscendingOrder);
00147
00148 ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
00149 ui->tableViewCustom->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
00150
00151 updateFilter();
00152 }
00153
00154
00155 int CurveListPanel::findRowByName(const std::string &text) const
00156 {
00157 auto item_list = _model->findItems( QString::fromStdString( text ), Qt::MatchExactly);
00158 if( item_list.isEmpty())
00159 {
00160 return -1;
00161 }
00162 if( item_list.count()>1)
00163 {
00164 qDebug() << "FilterableListWidget constins multiple rows with the same name";
00165 return -1;
00166 }
00167 return item_list.front()->row();
00168 }
00169
00170
00171 void CurveListPanel::updateFilter()
00172 {
00173 on_lineEdit_textChanged( ui->lineEdit->text() );
00174 }
00175
00176 void CurveListPanel::keyPressEvent(QKeyEvent *event)
00177 {
00178 if( event->key() == Qt::Key_Delete){
00179 removeSelectedCurves();
00180 }
00181 }
00182
00183 bool CurveListPanel::eventFilter(QObject *object, QEvent *event)
00184 {
00185 auto obj = object;
00186 while ( obj && obj != ui->tableView && obj != ui->tableViewCustom )
00187 {
00188 obj = obj->parent();
00189 }
00190
00191
00192 if(!obj)
00193 {
00194 return QWidget::eventFilter(object,event);
00195 }
00196
00197 bool shift_modifier_pressed = (QGuiApplication::keyboardModifiers() == Qt::ShiftModifier);
00198 bool ctrl_modifier_pressed = (QGuiApplication::keyboardModifiers() == Qt::ControlModifier);
00199
00200 if(event->type() == QEvent::MouseButtonPress)
00201 {
00202 QMouseEvent *mouse_event = static_cast<QMouseEvent*>(event);
00203
00204 _dragging = false;
00205 _drag_start_pos = mouse_event->pos();
00206
00207 if( !shift_modifier_pressed && !ctrl_modifier_pressed )
00208 {
00209 if( obj == ui->tableView)
00210 {
00211 ui->tableViewCustom->clearSelection() ;
00212 }
00213 if( obj == ui->tableViewCustom)
00214 {
00215 ui->tableView->clearSelection() ;
00216 }
00217 }
00218
00219 if(mouse_event->button() == Qt::LeftButton )
00220 {
00221 _newX_modifier = false;
00222 }
00223 else if(mouse_event->button() == Qt::RightButton )
00224 {
00225 _newX_modifier = true;
00226 }
00227 else {
00228 return false;
00229 }
00230 return QWidget::eventFilter(object,event);
00231 }
00232 else if(event->type() == QEvent::MouseMove)
00233 {
00234 QMouseEvent *mouse_event = static_cast<QMouseEvent*>(event);
00235 double distance_from_click = (mouse_event->pos() - _drag_start_pos).manhattanLength();
00236
00237 if ((mouse_event->buttons() == Qt::LeftButton ||
00238 mouse_event->buttons() == Qt::RightButton) &&
00239 distance_from_click >= QApplication::startDragDistance() &&
00240 !_dragging)
00241 {
00242 _dragging = true;
00243 QDrag *drag = new QDrag(this);
00244 QMimeData *mimeData = new QMimeData;
00245
00246 QByteArray mdata;
00247 QDataStream stream(&mdata, QIODevice::WriteOnly);
00248
00249 for(const auto& curve_name: getNonHiddenSelectedRows())
00250 {
00251 stream << QString::fromStdString(curve_name);
00252 }
00253
00254 if( !_newX_modifier )
00255 {
00256 mimeData->setData("curveslist/add_curve", mdata);
00257 }
00258 else
00259 {
00260 if(getNonHiddenSelectedRows().size() != 1)
00261 {
00262 return false;
00263 }
00264 mimeData->setData("curveslist/new_X_axis", mdata);
00265
00266 QPixmap cursor( QSize(160,30) );
00267 cursor.fill(Qt::transparent);
00268
00269 QPainter painter;
00270 painter.begin( &cursor);
00271 painter.setPen(QColor(22, 22, 22));
00272
00273 QString text("set as new X axis");
00274 painter.setFont( QFont("Arial", 14 ) );
00275
00276 painter.setBackground(Qt::transparent);
00277 painter.drawText( QRect(0, 0, 160, 30), Qt::AlignHCenter | Qt::AlignVCenter, text );
00278 painter.end();
00279
00280 drag->setDragCursor(cursor, Qt::MoveAction);
00281 }
00282
00283 drag->setMimeData(mimeData);
00284 drag->exec(Qt::CopyAction | Qt::MoveAction);
00285 }
00286 return true;
00287 }
00288 else if(event->type() == QEvent::Wheel)
00289 {
00290 QWheelEvent *wheel_event = dynamic_cast<QWheelEvent*>(event);
00291 int prev_size = _point_size;
00292 if( ctrl_modifier_pressed )
00293 {
00294 if( _point_size > 6 && wheel_event->delta() < 0 )
00295 {
00296 _point_size--;
00297 }
00298 else if( _point_size < 14 && wheel_event->delta() > 0 )
00299 {
00300 _point_size++;
00301 }
00302 if( _point_size != prev_size)
00303 {
00304 auto horizontal = ui->tableView->horizontalHeader();
00305 horizontal->setSectionResizeMode(0, QHeaderView::Fixed);
00306 horizontal->setSectionResizeMode(1, QHeaderView::Fixed);
00307
00308 auto vertical = ui->tableView->verticalHeader();
00309 vertical->setSectionResizeMode(0, QHeaderView::Fixed);
00310 vertical->setSectionResizeMode(1, QHeaderView::Fixed);
00311
00312 for (int row=0; row< rowCount(); row++)
00313 {
00314 for (int col=0; col< 2; col++)
00315 {
00316 auto item = _model->item( row, col );
00317 auto font = item->font();
00318 font.setPointSize( _point_size );
00319 item->setFont( font );
00320 }
00321 }
00322
00323 horizontal->setSectionResizeMode(0, QHeaderView::ResizeToContents);
00324 horizontal->setSectionResizeMode(1, QHeaderView::Stretch);
00325 vertical->setSectionResizeMode(0, QHeaderView::ResizeToContents);
00326 vertical->setSectionResizeMode(1, QHeaderView::ResizeToContents);
00327
00328 QSettings settings;
00329 settings.setValue("FilterableListWidget/table_point_size", _point_size);
00330 }
00331 return true;
00332 }
00333 }
00334
00335 return QWidget::eventFilter(object,event);
00336 }
00337
00338 std::vector<std::string> CurveListPanel::getNonHiddenSelectedRows()
00339 {
00340 std::vector<std::string> non_hidden_list;
00341
00342 for(auto table_view: {ui->tableView, ui->tableViewCustom})
00343 {
00344 for (const auto &selected_index : table_view->selectionModel()->selectedRows(0))
00345 {
00346 if (!table_view->isRowHidden(selected_index.row()))
00347 {
00348 auto item = _model->item( selected_index.row(), 0 );
00349 non_hidden_list.push_back(item->text().toStdString());
00350 }
00351 }
00352 }
00353 return non_hidden_list;
00354 }
00355
00356 QTableView *CurveListPanel::getTableView() const
00357 {
00358 return ui->tableView;
00359 }
00360
00361 QTableView *CurveListPanel::getCustomView() const
00362 {
00363 return ui->tableViewCustom;
00364 }
00365
00366 void CurveListPanel::on_radioContains_toggled(bool checked)
00367 {
00368 if(checked) {
00369 updateFilter();
00370 ui->lineEdit->setCompleter( nullptr );
00371 QSettings settings;
00372 settings.setValue("FilterableListWidget.searchFilter", "radioContains");
00373 }
00374 }
00375
00376 void CurveListPanel::on_radioRegExp_toggled(bool checked)
00377 {
00378 if(checked) {
00379 updateFilter();
00380 ui->lineEdit->setCompleter( nullptr );
00381 QSettings settings;
00382 settings.setValue("FilterableListWidget.searchFilter", "radioRegExp");
00383 }
00384 }
00385
00386 void CurveListPanel::on_radioPrefix_toggled(bool checked)
00387 {
00388 _completer_need_update = checked;
00389
00390 if( checked )
00391 {
00392 _completer->clear();
00393 for (int row=0; row< rowCount(); row++)
00394 {
00395 auto item = _model->item(row,0);
00396 _completer->addToCompletionTree(item->text());
00397 }
00398
00399 updateFilter();
00400 ui->lineEdit->setCompleter( _completer );
00401 QSettings settings;
00402 settings.setValue("FilterableListWidget.searchFilter", "radioPrefix");
00403 }
00404 }
00405
00406 void CurveListPanel::on_checkBoxCaseSensitive_toggled(bool )
00407 {
00408 updateFilter();
00409 }
00410
00411 void CurveListPanel::on_lineEdit_textChanged(const QString &search_string)
00412 {
00413 int item_count = rowCount();
00414 int visible_count = 0;
00415 bool updated = false;
00416
00417 Qt::CaseSensitivity cs = Qt::CaseInsensitive;
00418 if( ui->checkBoxCaseSensitive->isChecked())
00419 {
00420 cs = Qt::CaseSensitive;
00421 }
00422 QRegExp regexp( search_string, cs, QRegExp::Wildcard );
00423 QRegExpValidator v(regexp, nullptr);
00424
00425 QStringList spaced_items = search_string.split(' ');
00426
00427 for (int row=0; row < rowCount(); row++)
00428 {
00429 auto item = _model->item(row,0);
00430 QString name = item->text();
00431 int pos = 0;
00432 bool toHide = false;
00433
00434 if( search_string.isEmpty() == false )
00435 {
00436 if( ui->radioRegExp->isChecked())
00437 {
00438 toHide = v.validate( name, pos ) != QValidator::Acceptable;
00439 }
00440 else if( ui->radioPrefix->isChecked())
00441 {
00442 toHide = !name.startsWith( search_string, cs ) ;
00443 }
00444 else if( ui->radioContains->isChecked())
00445 {
00446 for (const auto& item: spaced_items)
00447 {
00448 if( name.contains(item, cs) == false )
00449 {
00450 toHide = true;
00451 break;
00452 }
00453 }
00454 }
00455 }
00456 if( !toHide ) visible_count++;
00457
00458 if( toHide != ui->tableView->isRowHidden(row) ) updated = true;
00459
00460 const auto name_std = name.toStdString();
00461 const bool is_custom_plot = _custom_plots.count(name_std) > 0;
00462
00463
00464 ui->tableView->setRowHidden(row, toHide || is_custom_plot );
00465 ui->tableViewCustom->setRowHidden(row, toHide || !is_custom_plot );
00466 }
00467 ui->labelNumberDisplayed->setText( QString::number( visible_count ) + QString(" of ") +
00468 QString::number( item_count ) );
00469
00470 if(updated){
00471 emit hiddenItemsChanged();
00472 }
00473 }
00474
00475 void CurveListPanel::on_pushButtonSettings_toggled(bool checked)
00476 {
00477 ui->widgetOptions->setVisible(checked);
00478 }
00479
00480 void CurveListPanel::on_checkBoxHideSecondColumn_toggled(bool checked)
00481 {
00482 for(auto table_view: {ui->tableView, ui->tableViewCustom})
00483 {
00484 if(checked){
00485 table_view->hideColumn(1);
00486 }
00487 else{
00488 table_view->showColumn(1);
00489 }
00490 }
00491 emit hiddenItemsChanged();
00492 }
00493
00494 void CurveListPanel::removeSelectedCurves()
00495 {
00496 QMessageBox::StandardButton reply;
00497 reply = QMessageBox::question(nullptr, tr("Warning"),
00498 tr("Do you really want to remove these data?\n"),
00499 QMessageBox::Yes | QMessageBox::No,
00500 QMessageBox::No );
00501
00502 if (reply == QMessageBox::Yes)
00503 {
00504 emit deleteCurves(getNonHiddenSelectedRows());
00505 }
00506
00507
00508 if( _completer_need_update )
00509 {
00510 _completer->clear();
00511 for (int row=0; row< rowCount(); row++)
00512 {
00513 auto item = _model->item(row);
00514 _completer->addToCompletionTree(item->text());
00515 }
00516 }
00517
00518 updateFilter();
00519 }
00520
00521 void CurveListPanel::removeRow(int row)
00522 {
00523 _model->removeRow(row);
00524 }
00525
00526 void CurveListPanel::on_buttonAddCustom_clicked()
00527 {
00528 auto curve_names = getNonHiddenSelectedRows();
00529 if( curve_names.empty() )
00530 {
00531 emit createMathPlot("");
00532 }
00533 else
00534 {
00535 createMathPlot( curve_names.front() );
00536 }
00537 on_lineEdit_textChanged( ui->lineEdit->text() );
00538 }
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548 void CurveListPanel::onCustomSelectionChanged(const QItemSelection&, const QItemSelection &)
00549 {
00550 int selected_items = 0;
00551
00552 for (const auto &index : ui->tableViewCustom->selectionModel()->selectedRows(0))
00553 {
00554 if (! ui->tableViewCustom->isRowHidden(index.row()) )
00555 {
00556 selected_items++;
00557 }
00558 }
00559
00560 bool enabled = selected_items == 1;
00561 ui->buttonEditCustom->setEnabled( enabled );
00562 ui->buttonEditCustom->setToolTip( enabled ? "Edit the selected custom timeserie" :
00563 "Select a single custom Timeserie to Edit it");
00564 }
00565
00566 void CurveListPanel::on_buttonEditCustom_clicked()
00567 {
00568 int selected_count = 0;
00569 QModelIndex selected_index;
00570 auto table_view = ui->tableViewCustom;
00571
00572 for (QModelIndex index : table_view->selectionModel()->selectedRows(0))
00573 {
00574 if (!table_view->isRowHidden( index.row()) )
00575 {
00576 selected_count++;
00577 selected_index = index;
00578 }
00579 }
00580 if( selected_count != 1)
00581 {
00582 return;
00583 }
00584
00585 auto item = _model->item( selected_index.row(), 0 );
00586 editMathPlot( item->text().toStdString() );
00587 }
00588
00589 void CurveListPanel::clearSelections()
00590 {
00591 ui->tableViewCustom->clearSelection();
00592 ui->tableView->clearSelection();
00593 }