00001 #include "filterablelistwidget.h"
00002 #include "ui_filterablelistwidget.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
00017 class TreeModelCompleter : public QCompleter
00018 {
00019
00020 public:
00021 TreeModelCompleter(QAbstractItemModel *model, QObject *parent = 0): QCompleter(model, parent)
00022 { }
00023
00024 QStringList splitPath(const QString &path) const override {
00025 return path.split('/');
00026 }
00027
00028 QString pathFromIndex(const QModelIndex &index) const override
00029 {
00030 QStringList dataList;
00031 for (QModelIndex i = index; i.isValid(); i = i.parent())
00032 {
00033 QString name = model()->data(i, completionRole()).toString();
00034 dataList.prepend(name);
00035 }
00036 return dataList.join('/');
00037 }
00038 };
00039
00040 FilterableListWidget::FilterableListWidget(QWidget *parent) :
00041 QWidget(parent),
00042 ui(new Ui::FilterableListWidget),
00043 _tree_model( new QStandardItemModel(this)),
00044 _completer( new TreeModelCompleter(_tree_model, this) )
00045 {
00046 ui->setupUi(this);
00047 ui->tableWidget->viewport()->installEventFilter( this );
00048 ui->lineEdit->installEventFilter( this );
00049
00050 ui->tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
00051 ui->tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
00052 ui->tableWidget->horizontalHeader()->resizeSection(1, 120);
00053
00054 ui->widgetOptions->setVisible(false);
00055
00056 ui->radioRegExp->setAutoExclusive(true);
00057 ui->radioContains->setAutoExclusive(true);
00058 ui->radioPrefix->setAutoExclusive(true);
00059
00060 _completer->setCompletionMode( QCompleter::PopupCompletion );
00061
00062 QSettings settings( "IcarusTechnology", "PlotJuggler");
00063
00064 QString active_filter = settings.value("FilterableListWidget.searchFilter", "radioContains").toString();
00065 if( active_filter == "radioRegExp") ui->radioRegExp->setChecked(true);
00066 else if( active_filter == "radioPrefix") ui->radioPrefix->setChecked(true);
00067 else if( active_filter == "radioContains") ui->radioContains->setChecked(true);
00068
00069 }
00070
00071 FilterableListWidget::~FilterableListWidget()
00072 {
00073 QSettings settings( "IcarusTechnology", "PlotJuggler");
00074
00075 if(ui->radioRegExp->isChecked())
00076 settings.setValue("FilterableListWidget.searchFilter", "radioRegExp");
00077 else if(ui->radioPrefix->isChecked())
00078 settings.setValue("FilterableListWidget.searchFilter", "radioPrefix");
00079 else if(ui->radioContains->isChecked())
00080 settings.setValue("FilterableListWidget.searchFilter", "radioContains");
00081
00082 delete ui;
00083 }
00084
00085 int FilterableListWidget::rowCount() const
00086 {
00087 return ui->tableWidget->rowCount();
00088 }
00089
00090 void FilterableListWidget::clear()
00091 {
00092 ui->tableWidget->setRowCount(0);
00093 _tree_model->clear();
00094 ui->labelNumberDisplayed->setText( "0 of 0");
00095 }
00096
00097 class CustomSortedTableItem: public QTableWidgetItem
00098 {
00099
00100 public:
00101 CustomSortedTableItem(const QString& name): QTableWidgetItem(name) {}
00102
00103 bool operator< (const QTableWidgetItem &other) const
00104 {
00105 return doj::alphanum_impl(this->text().toLocal8Bit().constData(),
00106 other.text().toLocal8Bit().constData()) < 0;
00107 }
00108 };
00109
00110 void FilterableListWidget::addItem(const QString &item_name, bool sort_columns)
00111 {
00112 auto item = new CustomSortedTableItem(item_name);
00113 const int row = rowCount();
00114 ui->tableWidget->setRowCount(row+1);
00115 ui->tableWidget->setItem(row, 0, item);
00116
00117 auto val_cell = new QTableWidgetItem("-");
00118 val_cell->setTextAlignment(Qt::AlignRight);
00119 val_cell->setFlags( Qt::NoItemFlags | Qt::ItemIsEnabled );
00120 val_cell->setFont( QFontDatabase::systemFont(QFontDatabase::FixedFont) );
00121
00122 ui->tableWidget->setItem(row, 1, val_cell );
00123 if( sort_columns )
00124 {
00125 ui->tableWidget->sortByColumn(0,Qt::AscendingOrder);
00126 }
00127
00128 addToCompletionTree(item);
00129 }
00130
00131 void FilterableListWidget::sortColumns()
00132 {
00133 ui->tableWidget->sortByColumn(0,Qt::AscendingOrder);
00134 }
00135
00136
00137 QList<int>
00138 FilterableListWidget::findRowsByName(const QString &text) const
00139 {
00140 QList<int> output;
00141 QList<QTableWidgetItem*> item_list = ui->tableWidget->findItems( text, Qt::MatchExactly);
00142 for(QTableWidgetItem* item : item_list)
00143 {
00144 if(item->column() == 0) {
00145 output.push_back( item->row() );
00146 }
00147 }
00148 return output;
00149 }
00150
00151 const QTableWidget *FilterableListWidget::getTable() const
00152 {
00153 return ui->tableWidget;
00154 }
00155
00156
00157 void FilterableListWidget::updateFilter()
00158 {
00159 on_lineEdit_textChanged( ui->lineEdit->text() );
00160 }
00161
00162 void FilterableListWidget::keyPressEvent(QKeyEvent *event)
00163 {
00164 if( event->key() == Qt::Key_Delete){
00165 removeSelectedCurves();
00166 }
00167 }
00168
00169 bool FilterableListWidget::eventFilter(QObject *object, QEvent *event)
00170 {
00171 QObject *obj = object;
00172 while ( obj != NULL )
00173 {
00174 if( obj == ui->tableWidget || obj == ui->lineEdit ) break;
00175 obj = obj->parent();
00176 }
00177
00178 if(obj == ui->tableWidget)
00179 {
00180 if(event->type() == QEvent::MouseButtonPress)
00181 {
00182 QMouseEvent *mouse_event = static_cast<QMouseEvent*>(event);
00183 if(mouse_event->button() == Qt::LeftButton )
00184 {
00185 _newX_modifier = false;
00186 _drag_start_pos = mouse_event->pos();
00187 }
00188 else if(mouse_event->button() == Qt::RightButton )
00189 {
00190 _newX_modifier = true;
00191 _drag_start_pos = mouse_event->pos();
00192 }
00193 }
00194 else if(event->type() == QEvent::MouseMove)
00195 {
00196 QMouseEvent *mouse_event = static_cast<QMouseEvent*>(event);
00197 double distance_from_click = (mouse_event->pos() - _drag_start_pos).manhattanLength();
00198
00199 if ((mouse_event->buttons() == Qt::LeftButton || mouse_event->buttons() == Qt::RightButton) &&
00200 distance_from_click >= QApplication::startDragDistance())
00201 {
00202 QDrag *drag = new QDrag(this);
00203 QMimeData *mimeData = new QMimeData;
00204
00205 QByteArray mdata;
00206 QDataStream stream(&mdata, QIODevice::WriteOnly);
00207
00208 for(QTableWidgetItem* item: ui->tableWidget->selectedItems()) {
00209 stream << item->text();
00210 }
00211 if( _newX_modifier )
00212 {
00213 if( ui->tableWidget->selectedItems().size() == 1)
00214 {
00215 mimeData->setData("curveslist/new_X_axis", mdata);
00216
00217 QPixmap cursor( QSize(160,30) );
00218 cursor.fill();
00219
00220 QPainter painter;
00221 painter.begin( &cursor);
00222 painter.setPen(QColor(22, 22, 22));
00223
00224 QString text("set as new X axis");
00225 painter.setFont( QFont("Arial", 14 ) );
00226
00227 painter.drawText( QRect(0, 0, 160, 30), Qt::AlignHCenter | Qt::AlignVCenter, text );
00228 painter.end();
00229
00230 drag->setDragCursor(cursor, Qt::MoveAction);
00231 }
00232 else{
00233
00234 QWidget::eventFilter(object,event);
00235 }
00236 }
00237 else{
00238 mimeData->setData("curveslist/add_curve", mdata);
00239 }
00240
00241 drag->setMimeData(mimeData);
00242 drag->exec(Qt::CopyAction | Qt::MoveAction);
00243 }
00244 }
00245 }
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263 return QWidget::eventFilter(object,event);
00264 }
00265
00266
00267 void FilterableListWidget::on_radioContains_toggled(bool checked)
00268 {
00269 if(checked) {
00270 updateFilter();
00271 ui->lineEdit->setCompleter( nullptr );
00272 }
00273 }
00274
00275 void FilterableListWidget::on_radioRegExp_toggled(bool checked)
00276 {
00277 if(checked) {
00278 updateFilter();
00279 ui->lineEdit->setCompleter( nullptr );
00280 }
00281 }
00282
00283 void FilterableListWidget::addToCompletionTree(QTableWidgetItem* item)
00284 {
00285 QString name = item->data(Qt::DisplayRole).toString();
00286 QStringList parts = name.split('/');
00287
00288 QStandardItem *parent_item = _tree_model->invisibleRootItem();
00289
00290 for (int col=0; col < parts.count(); col++)
00291 {
00292 bool already_stored = false;
00293 for (int row = 0; row < parent_item->rowCount() && !already_stored; row++)
00294 {
00295 if( parent_item->child(row)->text() == parts[col])
00296 {
00297 already_stored = true;
00298 parent_item = parent_item->child(row);
00299 }
00300 }
00301 if( !already_stored )
00302 {
00303 QStandardItem *item = new QStandardItem(parts[col]);
00304 parent_item->appendRow(item);
00305 parent_item = item;
00306 }
00307 }
00308 }
00309
00310 void FilterableListWidget::on_radioPrefix_toggled(bool checked)
00311 {
00312 if(checked) {
00313 updateFilter();
00314 ui->lineEdit->setCompleter( _completer );
00315 }
00316 }
00317
00318 void FilterableListWidget::on_checkBoxCaseSensitive_toggled(bool checked)
00319 {
00320 updateFilter();
00321 }
00322
00323
00324 void FilterableListWidget::on_lineEdit_textChanged(const QString &search_string)
00325 {
00326 int item_count = rowCount();
00327 int visible_count = 0;
00328 bool updated = false;
00329
00330 Qt::CaseSensitivity cs = Qt::CaseInsensitive;
00331 if( ui->checkBoxCaseSensitive->isChecked())
00332 {
00333 cs = Qt::CaseSensitive;
00334 }
00335 QRegExp regexp( search_string, cs, QRegExp::Wildcard );
00336 QRegExpValidator v(regexp, 0);
00337
00338 for (int row=0; row< rowCount(); row++)
00339 {
00340 QTableWidgetItem* item = ui->tableWidget->item(row,0);
00341 QString name = item->text();
00342 int pos = 0;
00343 bool toHide = false;
00344
00345 if( ui->radioRegExp->isChecked())
00346 {
00347 toHide = v.validate( name, pos ) != QValidator::Acceptable;
00348 }
00349 else if( ui->radioPrefix->isChecked())
00350 {
00351 toHide = !name.startsWith( search_string, cs ) ;
00352 }
00353 else if( ui->radioContains->isChecked())
00354 {
00355 QStringList items = search_string.split(' ');
00356 for (int i=0; i< items.size(); i++)
00357 {
00358 if( name.contains(items[i], cs) == false )
00359 {
00360 toHide = true;
00361 }
00362 }
00363 }
00364 if( !toHide ) visible_count++;
00365
00366 if( toHide != ui->tableWidget->isRowHidden(row) ) updated = true;
00367
00368 ui->tableWidget->setRowHidden(row, toHide );
00369 }
00370 ui->labelNumberDisplayed->setText( QString::number( visible_count ) + QString(" of ") + QString::number( item_count ) );
00371
00372 if(updated){
00373 emit hiddenItemsChanged();
00374 }
00375 }
00376
00377 void FilterableListWidget::on_pushButtonSettings_toggled(bool checked)
00378 {
00379 ui->widgetOptions->setVisible(checked);
00380 }
00381
00382 void FilterableListWidget::on_checkBoxHideSecondColumn_toggled(bool checked)
00383 {
00384 if(checked){
00385 ui->tableWidget->hideColumn(1);
00386 emit hiddenItemsChanged();
00387 }
00388 else{
00389 ui->tableWidget->showColumn(1);
00390 emit hiddenItemsChanged();
00391 }
00392 }
00393
00394 void FilterableListWidget::removeSelectedCurves()
00395 {
00396 QMessageBox::StandardButton reply;
00397 reply = QMessageBox::question(0, tr("Warning"),
00398 tr("Do you really want to remove these data?\n"),
00399 QMessageBox::Yes | QMessageBox::No,
00400 QMessageBox::No );
00401
00402 if( reply == QMessageBox::Yes ) {
00403
00404 while( ui->tableWidget->selectedItems().size() > 0 )
00405 {
00406 QTableWidgetItem* item = ui->tableWidget->selectedItems().first();
00407 emit deleteCurve( item->text() );
00408 }
00409 }
00410
00411
00412 _tree_model->clear();
00413 for (int row=0; row< rowCount(); row++)
00414 {
00415 QTableWidgetItem* item = ui->tableWidget->item(row,0);
00416 addToCompletionTree(item);
00417 }
00418
00419 updateFilter();
00420 }
00421
00422 void FilterableListWidget::removeRow(int row)
00423 {
00424 ui->tableWidget->removeRow(row);
00425 }
00426
00427