From 2cccb26bc40dfeb0af8b41582690f72811c750df Mon Sep 17 00:00:00 2001 From: Roland Knall Date: Fri, 25 Oct 2019 15:29:54 +0200 Subject: Qt: Implement FilterListModel Change-Id: I365281a709ded644fc8d6ccfce5870a000221a3b Reviewed-on: https://code.wireshark.org/review/34856 Petri-Dish: Roland Knall Tested-by: Petri Dish Buildbot Reviewed-by: Roland Knall --- ui/qt/CMakeLists.txt | 2 + ui/qt/filter_dialog.cpp | 169 +++++++++++------------- ui/qt/filter_dialog.h | 28 ++-- ui/qt/filter_dialog.ui | 28 +--- ui/qt/models/filter_list_model.cpp | 256 +++++++++++++++++++++++++++++++++++++ ui/qt/models/filter_list_model.h | 77 +++++++++++ 6 files changed, 431 insertions(+), 129 deletions(-) create mode 100644 ui/qt/models/filter_list_model.cpp create mode 100644 ui/qt/models/filter_list_model.h diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index de20413198..b843766756 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -80,6 +80,7 @@ set(WIRESHARK_MODEL_HEADERS models/expert_info_proxy_model.h models/export_objects_model.h models/fileset_entry_model.h + models/filter_list_model.h models/info_proxy_model.h models/interface_sort_filter_model.h models/interface_tree_cache_model.h @@ -308,6 +309,7 @@ set(WIRESHARK_MODEL_SRCS models/expert_info_proxy_model.cpp models/export_objects_model.cpp models/fileset_entry_model.cpp + models/filter_list_model.cpp models/info_proxy_model.cpp models/interface_sort_filter_model.cpp models/interface_tree_cache_model.cpp diff --git a/ui/qt/filter_dialog.cpp b/ui/qt/filter_dialog.cpp index 98985baa92..cc7e87a3f7 100644 --- a/ui/qt/filter_dialog.cpp +++ b/ui/qt/filter_dialog.cpp @@ -23,10 +23,10 @@ #include #include #include +#include #include #include -//#include "capture_filter_syntax_worker.h" #include #include "wireshark_application.h" @@ -41,15 +41,14 @@ enum { filter_col_ }; -FilterDialog::FilterDialog(QWidget *parent, FilterType filter_type, const QString new_filter) : +FilterDialog::FilterDialog(QWidget *parent, FilterType filter_type, QString new_filter_) : GeometryStateDialog(parent), ui(new Ui::FilterDialog), filter_type_(filter_type), -// syntax_worker_(NULL), - filter_tree_delegate_(new FilterTreeDelegate(this, filter_type)), - new_filter_(new_filter) + filter_tree_delegate_(new FilterTreeDelegate(this, filter_type)) { ui->setupUi(this); + if (parent) loadGeometry(parent->width() * 2 / 3, parent->height() * 2 / 3); setWindowIcon(wsApp->normalIcon()); @@ -64,30 +63,40 @@ FilterDialog::FilterDialog(QWidget *parent, FilterType filter_type, const QStrin ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true); #endif +#if 0 ui->filterTreeWidget->setDragEnabled(true); ui->filterTreeWidget->viewport()->setAcceptDrops(true); ui->filterTreeWidget->setDropIndicatorShown(true); ui->filterTreeWidget->setDragDropMode(QAbstractItemView::InternalMove); +#endif const gchar * filename = NULL; + QString newFilterText; if (filter_type == CaptureFilter) { setWindowTitle(wsApp->windowTitleString(tr("Capture Filters"))); filename = CFILTER_FILE_NAME; - -// QThread *syntax_thread = new QThread; -// syntax_worker_ = new CaptureFilterSyntaxWorker; -// syntax_worker_->moveToThread(syntax_thread); -// connect(syntax_thread, SIGNAL(started()), syntax_worker_, SLOT(start())); -// // connect(syntax_thread, SIGNAL(started()), this, SLOT(checkFilter())); -// connect(syntax_worker_, SIGNAL(syntaxResult(QString, bool, QString)), -// this, SLOT(setFilterSyntaxState(QString, bool, QString))); -// connect(syntax_thread, SIGNAL(finished()), syntax_worker_, SLOT(deleteLater())); -// syntax_thread->start(); + newFilterText = tr("New capture filter"); + model_ = new FilterListModel(FilterListModel::Capture, this); } else { setWindowTitle(wsApp->windowTitleString(tr("Display Filters"))); filename = DFILTER_FILE_NAME; + newFilterText = tr("New display filter"); + model_ = new FilterListModel(FilterListModel::Display, this); } + if ( new_filter_.length() > 0 ) + model_->addFilter(newFilterText, new_filter_); + + sortModel_ = new QSortFilterProxyModel(this); + sortModel_->setSourceModel(model_); + ui->filterTreeView->setModel(sortModel_); + + ui->filterTreeView->setItemDelegate(new FilterTreeDelegate(this, filter_type)); + + ui->filterTreeView->resizeColumnToContents(FilterListModel::ColumnName); + + connect( ui->filterTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FilterDialog::selectionChanged ); + QString abs_path = gchar_free_to_qstring(get_persconffile_path(filename, TRUE)); if (file_exists(abs_path.toUtf8().constData())) { ui->pathLabel->setText(abs_path); @@ -95,8 +104,6 @@ FilterDialog::FilterDialog(QWidget *parent, FilterType filter_type, const QStrin ui->pathLabel->setToolTip(tr("Open ") + filename); ui->pathLabel->setEnabled(true); } - - ui->filterTreeWidget->setItemDelegateForColumn(filter_col_, filter_tree_delegate_); } FilterDialog::~FilterDialog() @@ -104,64 +111,28 @@ FilterDialog::~FilterDialog() delete ui; } -void FilterDialog::showEvent(QShowEvent *event) -{ - ui->filterTreeWidget->clear(); - - GList *filter_list; - if (filter_type_ == CaptureFilter) { - filter_list = get_filter_list_first(CFILTER_LIST); - } else { - filter_list = get_filter_list_first(DFILTER_LIST); - } - for (GList *fl_item = filter_list; fl_item; fl_item = gxx_list_next(fl_item)) { - if (!fl_item->data) continue; - filter_def *fl_data = gxx_list_data(filter_def *, fl_item); - if (!fl_data->name || !fl_data->strval) continue; - - addFilter(fl_data->name, fl_data->strval); - } - - if (!new_filter_.isEmpty()) { - addFilter(tr("New filter"), new_filter_, true); - new_filter_.clear(); - } - - ui->filterTreeWidget->resizeColumnToContents(name_col_); - ui->filterTreeWidget->resizeColumnToContents(filter_col_); - - QDialog::showEvent(event); -} - void FilterDialog::addFilter(QString name, QString filter, bool start_editing) { - QTreeWidgetItem *ti = new QTreeWidgetItem(ui->filterTreeWidget); - ti->setFlags(ti->flags() | Qt::ItemIsEditable); - ti->setFlags(ti->flags() & ~(Qt::ItemIsDropEnabled)); - ti->setText(name_col_, name); - ti->setText(filter_col_, filter); - - if (start_editing) { - ui->filterTreeWidget->setCurrentItem(ti); - updateWidgets(); - ui->filterTreeWidget->editItem(ti, filter_col_); + if ( model_ ) + { + QModelIndex idx = model_->addFilter(name, filter); + if ( start_editing ) + ui->filterTreeView->edit(sortModel_->mapFromSource(idx)); } } void FilterDialog::updateWidgets() { - int num_selected = ui->filterTreeWidget->selectedItems().count(); + if ( ! ui->filterTreeView->selectionModel() ) + return; + + int num_selected = ui->filterTreeView->selectionModel()->selectedRows().count(); ui->copyToolButton->setEnabled(num_selected == 1); ui->deleteToolButton->setEnabled(num_selected > 0); } -//void FilterDialog::setFilterSyntaxState(QString filter, bool valid, QString err_msg) -//{ - -//} - -void FilterDialog::on_filterTreeWidget_itemSelectionChanged() +void FilterDialog::selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) { updateWidgets(); } @@ -186,36 +157,34 @@ void FilterDialog::on_newToolButton_clicked() void FilterDialog::on_deleteToolButton_clicked() { - QList selected = ui->filterTreeWidget->selectedItems(); - foreach (QTreeWidgetItem *ti, selected) { - delete ti; + QModelIndexList selected = ui->filterTreeView->selectionModel()->selectedRows(); + QList rows; + foreach ( QModelIndex idx, selected ) + { + QModelIndex original = sortModel_->mapToSource(idx); + if ( original.isValid() && ! rows.contains(original.row()) ) + { + rows << original.row(); + model_->removeFilter(original); + } } } void FilterDialog::on_copyToolButton_clicked() { - if (!ui->filterTreeWidget->currentItem()) return; - QTreeWidgetItem *ti = ui->filterTreeWidget->currentItem(); + QModelIndexList selected = ui->filterTreeView->selectionModel()->selectedRows(); + if ( selected.count() <= 0 ) + return; + + int rowNr = selected.at(0).row(); + QModelIndex row = selected.at(0).sibling(rowNr, FilterListModel::ColumnName); - addFilter(ti->text(name_col_), ti->text(filter_col_), true); + addFilter(row.data().toString(), row.sibling(rowNr, FilterListModel::ColumnExpression).data().toString(), true); } void FilterDialog::on_buttonBox_accepted() { - filter_list_type_t fl_type = filter_type_ == CaptureFilter ? CFILTER_LIST : DFILTER_LIST; - - while (GList *fl_item = get_filter_list_first(fl_type)) { - remove_from_filter_list(fl_type, fl_item); - } - - QTreeWidgetItemIterator it(ui->filterTreeWidget); - while (*it) { - add_to_filter_list(fl_type, (*it)->text(name_col_).toUtf8().constData(), - (*it)->text(filter_col_).toUtf8().constData()); - ++it; - } - - save_filter_list(fl_type); + model_->saveList(); if (filter_type_ == CaptureFilter) { wsApp->emitAppSignal(WiresharkApplication::CaptureFilterListChanged); @@ -238,23 +207,41 @@ void FilterDialog::on_buttonBox_helpRequested() // Delegate for editing capture and display filters. // +FilterTreeDelegate::FilterTreeDelegate(QObject *parent, FilterDialog::FilterType filter_type) : + QStyledItemDelegate(parent), + filter_type_(filter_type) +{} + + QWidget *FilterTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { + QWidget * w = Q_NULLPTR; if (index.column() != filter_col_) { - return QStyledItemDelegate::createEditor(parent, option, index); + w = QStyledItemDelegate::createEditor(parent, option, index); } - - QWidget *w; - - if (filter_type_ == FilterDialog::CaptureFilter) { - w = new CaptureFilterEdit(parent, true); - } else { - w = new DisplayFilterEdit(parent, DisplayFilterToEnter); + else + { + if (filter_type_ == FilterDialog::CaptureFilter) { + w = new CaptureFilterEdit(parent, true); + } else { + w = new DisplayFilterEdit(parent, DisplayFilterToEnter); + } } return w; } +void FilterTreeDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + if ( ! editor || ! index.isValid() ) + return; + + QStyledItemDelegate::setEditorData(editor, index); + + if ( qobject_cast(editor) ) + qobject_cast(editor)->setText(index.data().toString()); +} + /* * Editor modelines * diff --git a/ui/qt/filter_dialog.h b/ui/qt/filter_dialog.h index 323d17ae31..e2dc8fa1e1 100644 --- a/ui/qt/filter_dialog.h +++ b/ui/qt/filter_dialog.h @@ -12,7 +12,10 @@ #include "geometry_state_dialog.h" -//class CaptureFilterSyntaxWorker; +#include + +class QSortFilterProxyModel; +class QItemSelection; class FilterTreeDelegate; namespace Ui { @@ -28,24 +31,22 @@ public: explicit FilterDialog(QWidget *parent = 0, FilterType filter_type = CaptureFilter, const QString new_filter = QString()); ~FilterDialog(); -protected: - void showEvent(QShowEvent * event); - private: Ui::FilterDialog *ui; + FilterListModel * model_; + QSortFilterProxyModel * sortModel_; + enum FilterType filter_type_; -// CaptureFilterSyntaxWorker *syntax_worker_; FilterTreeDelegate *filter_tree_delegate_; - QString new_filter_; void addFilter(QString name, QString filter, bool start_editing = false); private slots: void updateWidgets(); -// void setFilterSyntaxState(QString filter, bool valid, QString err_msg); - void on_filterTreeWidget_itemSelectionChanged(); + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void on_newToolButton_clicked(); void on_deleteToolButton_clicked(); void on_copyToolButton_clicked(); @@ -66,18 +67,13 @@ class FilterTreeDelegate : public QStyledItemDelegate Q_OBJECT public: - FilterTreeDelegate(QObject *parent, FilterDialog::FilterType filter_type) : - QStyledItemDelegate(parent), - filter_type_(filter_type) - {} - ~FilterTreeDelegate() {} + FilterTreeDelegate(QObject *parent, FilterDialog::FilterType filter_type); - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override; private: FilterDialog::FilterType filter_type_; - -private slots: }; #endif // FILTER_DIALOG_H diff --git a/ui/qt/filter_dialog.ui b/ui/qt/filter_dialog.ui index c01a6eff76..4856be13fd 100644 --- a/ui/qt/filter_dialog.ui +++ b/ui/qt/filter_dialog.ui @@ -15,29 +15,13 @@ - - - QAbstractItemView::ExtendedSelection + + + true - - Qt::ElideMiddle - - - 0 - - - false - - - - Name - - - - - Filter - - + + true + diff --git a/ui/qt/models/filter_list_model.cpp b/ui/qt/models/filter_list_model.cpp new file mode 100644 index 0000000000..ba75128b56 --- /dev/null +++ b/ui/qt/models/filter_list_model.cpp @@ -0,0 +1,256 @@ +/* filter_list_model.cpp + * Model for all filter types + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +/* + * Old filter file name. + */ +#define FILTER_FILE_NAME "filters" + +/* + * Capture filter file name. + */ +#define CFILTER_FILE_NAME "cfilters" + +/* + * Display filter file name. + */ +#define DFILTER_FILE_NAME "dfilters" + +FilterListModel::FilterListModel(QObject * parent) : + QAbstractListModel(parent), + type_(FilterListModel::Display) +{ + reload(); +} + +FilterListModel::FilterListModel(FilterListModel::FilterListType type, QObject * parent) : + QAbstractListModel(parent), + type_(type) +{ + reload(); +} + +void FilterListModel::reload() +{ + QFile file; + + storage.clear(); + + /* Try personal config file first */ + file.setFileName(qstring_strdup(get_persconffile_path(FilterListModel::Capture ? CFILTER_FILE_NAME : DFILTER_FILE_NAME, TRUE))); + /* Try personal old-style config file next */ + if ( ! file.exists() ) + file.setFileName(qstring_strdup(get_persconffile_path(FILTER_FILE_NAME, TRUE))); + /* Last but not least, try the global file */ + if ( ! file.exists() ) + file.setFileName(qstring_strdup(get_datafile_path(FilterListModel::Capture ? CFILTER_FILE_NAME : DFILTER_FILE_NAME))); + + /* Still can use the model, just have to start from an empty set */ + if ( ! file.exists() || ! file.open(QIODevice::ReadOnly | QIODevice::Text) ) + return; + + QTextStream in(&file); + QRegExp rx("\\s*\\\"(.*)\\\"\\s(.*)"); + while (!in.atEnd()) + { + QString line = in.readLine().trimmed(); + if ( line.startsWith("#") || line.indexOf("\"") <= -1 ) + continue; + + rx.indexIn(line); + QStringList groups = rx.capturedTexts(); + if ( groups.count() != 3 ) + continue; + addFilter(groups.at(1), groups.at(2)); + } +} + +void FilterListModel::setFilterType(FilterListModel::FilterListType type) +{ + type_ = type; + reload(); +} + +FilterListModel::FilterListType FilterListModel::filterType() const +{ + return type_; +} + +int FilterListModel::rowCount(const QModelIndex &/* parent */) const +{ + return storage.count(); +} + +int FilterListModel::columnCount(const QModelIndex &/* parent */) const +{ + return 2; +} + +QVariant FilterListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if ( section >= columnCount() || section < 0 || orientation != Qt::Horizontal ) + return QVariant(); + + if ( role == Qt::DisplayRole ) + { + switch ( section ) { + case ColumnName: + return tr("Filter Name"); + break; + case ColumnExpression: + return tr("Filter Expression"); + break; + } + } + + return QVariant(); +} + +QVariant FilterListModel::data(const QModelIndex &index, int role) const +{ + if ( ! index.isValid() || index.row() >= rowCount() ) + return QVariant(); + + QStringList row = storage.at(index.row()).split("\n"); + if ( role == Qt::DisplayRole ) + return row.at(index.column()); + + return QVariant(); +} + +bool FilterListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if ( ! index.isValid() || index.row() >= rowCount() || role != Qt::EditRole ) + return false; + + QStringList row = storage.at(index.row()).split("\n"); + if ( row.count() <= index.column() ) + return false; + + row[index.column()] = value.toString(); + storage[index.row()] = row.join("\n"); + + return true; +} + +Qt::ItemFlags FilterListModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags fl = QAbstractListModel::flags(index); + if ( ! index.isValid() || index.row() >= rowCount() ) + return fl; + + QStringList row = storage.at(index.row()).split("\n"); + + fl |= Qt::ItemIsEditable; + + return fl; +} +QModelIndex FilterListModel::addFilter(QString name, QString expression) +{ + if ( name.length() == 0 || expression.length() == 0 ) + return QModelIndex(); + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + storage << QString("%1\n%2").arg(name).arg(expression); + endInsertRows(); + + return index(rowCount() - 1, 0); +} + +QModelIndex FilterListModel::findByName(QString name) +{ + if ( name.length() == 0 ) + return QModelIndex(); + + for ( int cnt = 0; cnt < rowCount(); cnt++ ) + { + if ( storage.at(cnt).startsWith(QString("%1\n").arg(name)) ) + return index(cnt, 0); + } + + return QModelIndex(); +} + +QModelIndex FilterListModel::findByExpression(QString expression) +{ + if ( expression.length() == 0 ) + return QModelIndex(); + + for ( int cnt = 0; cnt < rowCount(); cnt++ ) + { + if ( storage.at(cnt).endsWith(QString("\n%1").arg(expression)) ) + return index(cnt, 0); + } + + return QModelIndex(); +} + +void FilterListModel::removeFilter(QModelIndex idx) +{ + if ( ! idx.isValid() || idx.row() >= rowCount() ) + return; + + beginRemoveRows(QModelIndex(), idx.row(), idx.row()); + storage.removeAt(idx.row()); + endRemoveRows(); +} + +void FilterListModel::saveList() +{ + QString filename = FilterListModel::Capture ? CFILTER_FILE_NAME : DFILTER_FILE_NAME; + + filename = QString("%1%2%3").arg(ProfileModel::activeProfilePath()).arg(QDir::separator()).arg(filename); + QFile file(filename); + + if ( ! file.open(QIODevice::WriteOnly | QIODevice::Text) ) + return; + + QTextStream out(&file); + for ( int row = 0; row < rowCount(); row++ ) + { + QString line = QString("\"%1\"").arg(index(row, ColumnName).data().toString().replace("\\", "\\\\").replace("\"", "\\\"")); + line.append(QString(" %1").arg(index(row, ColumnExpression).data().toString())); + +#ifdef _WIN32 + line = line.append("\r\n"); +#else + line = line.append("\n"); +#endif + out << line; + } + + file.close(); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local Variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=2 tabstop=8 expandtab: + * :indentSize=2:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/models/filter_list_model.h b/ui/qt/models/filter_list_model.h new file mode 100644 index 0000000000..8050609a72 --- /dev/null +++ b/ui/qt/models/filter_list_model.h @@ -0,0 +1,77 @@ +/* filter_list_model.h + * Model for all filter types + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FILTER_LIST_MODEL_h +#define FILTER_LIST_MODEL_h + +#include + +#include +#include +#include + +class FilterListModel : public QAbstractListModel +{ + +public: + enum FilterListType { + Display, + Capture + }; + + explicit FilterListModel(FilterListType type = FilterListModel::Display, QObject * parent = Q_NULLPTR); + explicit FilterListModel(QObject * parent = Q_NULLPTR); + + enum { + ColumnName, + ColumnExpression + }; + + void setFilterType(FilterListModel::FilterListType type); + FilterListModel::FilterListType filterType() const; + + QModelIndex findByName(QString name); + QModelIndex findByExpression(QString expression); + + QModelIndex addFilter(QString name, QString expression); + void removeFilter(QModelIndex idx); + + void saveList(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role); + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + +private: + + FilterListModel::FilterListType type_; + + QStringList storage; + + void reload(); +}; + +#endif // FILTER_LIST_MODEL_h + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ -- cgit v1.2.3