aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland Knall <rknall@gmail.com>2022-06-20 23:35:56 +0200
committerRoland Knall <rknall@gmail.com>2022-07-08 16:21:32 -0500
commit4742e0cadee2f369ff116c5953ebe3a2718e31b4 (patch)
treee3eb63d1ccc720b2467fe653e53935a48cf16631
parent02b00a8ee55ee834e5f5d4cf1a9fb40f3c00e2d4 (diff)
Qt: Filter column information for traffic dialogs
This enables the traffic dialogs to filter data based on the context menu entries. For instance, one can only show rows, where the number of packets is not greater than 10
-rw-r--r--ui/qt/models/atap_data_model.h2
-rw-r--r--ui/qt/widgets/traffic_tab.cpp154
-rw-r--r--ui/qt/widgets/traffic_tab.h19
-rw-r--r--ui/qt/widgets/traffic_tree.cpp352
-rw-r--r--ui/qt/widgets/traffic_tree.h68
5 files changed, 420 insertions, 175 deletions
diff --git a/ui/qt/models/atap_data_model.h b/ui/qt/models/atap_data_model.h
index 38a637c8d8..fd55b0f57b 100644
--- a/ui/qt/models/atap_data_model.h
+++ b/ui/qt/models/atap_data_model.h
@@ -317,7 +317,7 @@ public:
* @return true a conversation id exists
* @return false none available
*/
- bool showConversationId(int row) const;
+ bool showConversationId(int row = 0) const;
};
diff --git a/ui/qt/widgets/traffic_tab.cpp b/ui/qt/widgets/traffic_tab.cpp
index 80dffe821e..1fbc850d28 100644
--- a/ui/qt/widgets/traffic_tab.cpp
+++ b/ui/qt/widgets/traffic_tab.cpp
@@ -65,160 +65,6 @@ int TabData::protoId() const
}
-TrafficDataFilterProxy::TrafficDataFilterProxy(QObject *parent) :
- QSortFilterProxyModel(parent)
-{}
-
-bool TrafficDataFilterProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
-{
- ATapDataModel * dataModel = qobject_cast<ATapDataModel *>(sourceModel());
- if (dataModel) {
- bool isFiltered = dataModel->data(dataModel->index(source_row, 0), ATapDataModel::ROW_IS_FILTERED).toBool();
- if (dataModel->filter().length() > 0)
- return ! isFiltered;
- }
-
- return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
-}
-
-bool TrafficDataFilterProxy::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
-{
- if (! source_left.isValid() || ! qobject_cast<const ATapDataModel *>(source_left.model()))
- return false;
- if (! source_right.isValid() || ! qobject_cast<const ATapDataModel *>(source_right.model()))
- return false;
-
- ATapDataModel * model = qobject_cast<ATapDataModel *>(sourceModel());
-
- if (! model || source_left.model() != model || source_right.model() != model)
- return false;
-
- QVariant datA = source_left.data(ATapDataModel::UNFORMATTED_DISPLAYDATA);
- QVariant datB = source_right.data(ATapDataModel::UNFORMATTED_DISPLAYDATA);
-
- bool is_address = false;
- if (qobject_cast<EndpointDataModel *>(model) && source_left.column() == EndpointDataModel::ENDP_COLUMN_ADDR &&
- source_left.column() == source_right.column()) {
- is_address = true;
- } else if (qobject_cast<ConversationDataModel *>(model) && (source_left.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR ||
- source_left.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) && source_left.column() == source_right.column()) {
- is_address = true;
- }
-
- if (is_address) {
- bool result = false;
- bool identical = false;
- int addressTypeA = model->data(source_left, ATapDataModel::DATA_ADDRESS_TYPE).toInt();
- int addressTypeB = model->data(source_right, ATapDataModel::DATA_ADDRESS_TYPE).toInt();
- if (addressTypeA != 0 && addressTypeB != 0 && addressTypeA != addressTypeB) {
- result = addressTypeA < addressTypeB;
- } else if (addressTypeA != 0 && addressTypeA == addressTypeB) {
-
- if (addressTypeA == AT_IPv4) {
- quint32 valA = model->data(source_left, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
- quint32 valB = model->data(source_right, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
-
- result = valA < valB;
- identical = valA == valB;
- } else if (addressTypeA == AT_NUMERIC) {
- quint32 valA = datA.toInt();
- quint32 valB = datB.toInt();
- result = valA < valB;
- identical = valA == valB;
- } else {
- result = QString::compare(datA.toString(), datB.toString(), Qt::CaseInsensitive) < 0;
- identical = QString::compare(datA.toString(), datB.toString(), Qt::CaseInsensitive) == 0;
- }
-
- int portColumn = EndpointDataModel::ENDP_COLUMN_PORT;
- if (identical && qobject_cast<ConversationDataModel *>(model)) {
- QModelIndex tstA, tstB;
- if (source_left.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR) {
- portColumn = ConversationDataModel::CONV_COLUMN_SRC_PORT;
- int col = ConversationDataModel::CONV_COLUMN_DST_ADDR;
- tstA = model->index(source_left.row(), col);
- tstB = model->index(source_right.row(), col);
- } else if (source_left.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) {
- portColumn = ConversationDataModel::CONV_COLUMN_DST_PORT;
- int col = ConversationDataModel::CONV_COLUMN_SRC_ADDR;
- tstA = model->index(source_left.row(), col);
- tstB = model->index(source_right.row(), col);
- }
-
- if (addressTypeA == AT_IPv4) {
- quint32 valX = model->data(tstA, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
- quint32 valY = model->data(tstB, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
-
- result = valX < valY;
- identical = valX == valY;
- } else {
- result = QString::compare(model->data(tstA).toString().toLower(), model->data(tstB).toString(), Qt::CaseInsensitive) < 0;
- identical = QString::compare(model->data(tstA).toString().toLower(), model->data(tstB).toString(), Qt::CaseInsensitive) == 0;
- }
- }
-
- if (! result && identical && ! model->portsAreHidden()) {
- int portA = model->data(model->index(source_left.row(), portColumn)).toInt();
- int portB = model->data(model->index(source_right.row(), portColumn)).toInt();
- return portA < portB;
- }
- }
-
- return result;
- }
-
- if (datA.canConvert<double>() && datB.canConvert<double>())
- return datA.toDouble() < datB.toDouble();
-
- return QSortFilterProxyModel::lessThan(source_left, source_right);
-}
-
-bool TrafficDataFilterProxy::filterAcceptsColumn(int source_column, const QModelIndex &) const
-{
- if (hideColumns_.contains(source_column))
- return false;
-
- ATapDataModel * model = qobject_cast<ATapDataModel *>(sourceModel());
- if (model) {
- if (model->portsAreHidden()) {
- if (qobject_cast<EndpointDataModel *>(model) && source_column == EndpointDataModel::ENDP_COLUMN_PORT)
- return false;
- if (qobject_cast<ConversationDataModel *>(model) &&
- (source_column == ConversationDataModel::CONV_COLUMN_SRC_PORT || source_column == ConversationDataModel::CONV_COLUMN_DST_PORT))
- return false;
- }
- if (! model->showTotalColumn()) {
- if (qobject_cast<EndpointDataModel *>(model) &&
- (source_column == EndpointDataModel::ENDP_COLUMN_PACKETS_TOTAL || source_column == EndpointDataModel::ENDP_COLUMN_BYTES_TOTAL))
- return false;
- if (qobject_cast<ConversationDataModel *>(model) &&
- (source_column == ConversationDataModel::CONV_COLUMN_PACKETS_TOTAL || source_column == ConversationDataModel::CONV_COLUMN_BYTES_TOTAL))
- return false;
- }
- if (qobject_cast<ConversationDataModel *>(model)) {
- ConversationDataModel * convModel = qobject_cast<ConversationDataModel *>(model);
- if (source_column == ConversationDataModel::CONV_COLUMN_CONV_ID && ! convModel->showConversationId(source_column))
- return false;
- }
- }
-
- return true;
-}
-
-void TrafficDataFilterProxy::setColumnVisibility(int column, bool visible)
-{
- hideColumns_.removeAll(column);
- if (!visible)
- hideColumns_.append(column);
- invalidateFilter();
-}
-
-bool TrafficDataFilterProxy::columnVisible(int column) const
-{
- return ! hideColumns_.contains(column);
-}
-
-
TrafficTab::TrafficTab(QWidget * parent) :
DetachableTabWidget(parent)
{
diff --git a/ui/qt/widgets/traffic_tab.h b/ui/qt/widgets/traffic_tab.h
index a1605255a6..7d7a93c596 100644
--- a/ui/qt/widgets/traffic_tab.h
+++ b/ui/qt/widgets/traffic_tab.h
@@ -64,25 +64,6 @@ private:
Q_DECLARE_METATYPE(TabData)
-class TrafficDataFilterProxy : public QSortFilterProxyModel
-{
- Q_OBJECT
-public:
- TrafficDataFilterProxy(QObject *parent = nullptr);
-
- void setColumnVisibility(int column, bool visible);
- bool columnVisible(int column) const;
-
-protected:
- virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
- virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const;
- virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
-
-private:
- QList<int> hideColumns_;
-
-};
-
/**
* @brief A QTabWidget class, providing tap information
*
diff --git a/ui/qt/widgets/traffic_tree.cpp b/ui/qt/widgets/traffic_tree.cpp
index fac8df2e61..fb9de60c56 100644
--- a/ui/qt/widgets/traffic_tree.cpp
+++ b/ui/qt/widgets/traffic_tree.cpp
@@ -41,6 +41,37 @@
#include <QJsonObject>
#include <QJsonDocument>
#include <QHeaderView>
+#include <QWidgetAction>
+#include <QLineEdit>
+#include <QActionGroup>
+
+MenuEditAction::MenuEditAction(QString text, QString hintText, QObject * parent) :
+ QWidgetAction(parent),
+ _hintText(hintText),
+ _text(text),
+ _lineEdit(nullptr)
+{}
+
+QWidget * MenuEditAction::createWidget(QWidget *parent) {
+ _lineEdit = new QLineEdit(parent);
+ _lineEdit->setAlignment(Qt::AlignRight);
+ _lineEdit->setText(_text);
+ _lineEdit->setPlaceholderText(_hintText);
+ connect(_lineEdit, &QLineEdit::returnPressed, this, &MenuEditAction::triggerEntry);
+ return _lineEdit;
+}
+
+void MenuEditAction::triggerEntry() {
+ if (_lineEdit)
+ _text = _lineEdit->text();
+
+ emit trigger();
+}
+
+QString MenuEditAction::text() const {
+ return _text;
+}
+
TrafficTreeHeaderView::TrafficTreeHeaderView(GList ** recentColumnList, QWidget * parent):
QHeaderView(Qt::Horizontal, parent)
@@ -49,6 +80,19 @@ TrafficTreeHeaderView::TrafficTreeHeaderView(GList ** recentColumnList, QWidget
setContextMenuPolicy(Qt::CustomContextMenu);
+ _actions = new QActionGroup(this);
+
+ QAction * filterAction = _actions->addAction(tr("Less than"));
+ filterAction->setCheckable(true);
+ filterAction->setChecked(true);
+ filterAction->setProperty("filter_action", (int)TrafficDataFilterProxy::TRAFFIC_DATA_LESS);
+ filterAction = _actions->addAction(tr("Greater than"));
+ filterAction->setCheckable(true);
+ filterAction->setProperty("filter_action", (int)TrafficDataFilterProxy::TRAFFIC_DATA_GREATER);
+ filterAction = _actions->addAction(tr("Equal"));
+ filterAction->setCheckable(true);
+ filterAction->setProperty("filter_action", (int)TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL);
+
connect(this, &QHeaderView::customContextMenuRequested, this, &TrafficTreeHeaderView::headerContextMenu);
}
@@ -68,6 +112,9 @@ void TrafficTreeHeaderView::headerContextMenu(const QPoint &pos)
QMenu * ctxMenu = new QMenu(this);
ctxMenu->setAttribute(Qt::WA_DeleteOnClose);
+ QAction * headerAction = ctxMenu->addAction(tr("Columns to display"));
+ headerAction->setEnabled(false);
+
for (int col = 0; col < tree->dataModel()->columnCount(); col++)
{
QString name = tree->dataModel()->headerData(col).toString();
@@ -80,6 +127,33 @@ void TrafficTreeHeaderView::headerContextMenu(const QPoint &pos)
connect(action, &QAction::triggered, this, &TrafficTreeHeaderView::columnTriggered);
}
+ ctxMenu->addSeparator();
+
+ int column = logicalIndexAt(pos);
+
+ bool is_address = false;
+ QModelIndex sourceIdx = proxy->mapToSource(proxy->index(0, column));
+ if (qobject_cast<EndpointDataModel *>(proxy->sourceModel()) && sourceIdx.column() == EndpointDataModel::ENDP_COLUMN_ADDR) {
+ is_address = true;
+ } else if (qobject_cast<ConversationDataModel *>(proxy->sourceModel()) && (sourceIdx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR ||
+ sourceIdx.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR)) {
+ is_address = true;
+ }
+
+ if (! is_address) {
+ QString columnText = model()->headerData(column, Qt::Horizontal).toString();
+ QAction * filterAction = ctxMenu->addAction(tr("Filter %1 by").arg(columnText));
+ filterAction->setEnabled(false);
+ ctxMenu->addActions(_actions->actions());
+
+ MenuEditAction * editAction = new MenuEditAction(_filterText, tr("Enter filter value"));
+ editAction->setProperty("column", column);
+ ctxMenu->addAction(editAction);
+ connect(editAction, &MenuEditAction::triggered, this, &TrafficTreeHeaderView::filterColumn);
+ }
+
+ connect(ctxMenu, &QMenu::triggered, this, &TrafficTreeHeaderView::menuActionTriggered);
+
ctxMenu->popup(mapToGlobal(pos));
}
@@ -136,6 +210,272 @@ void TrafficTreeHeaderView::columnTriggered(bool checked)
emit columnsHaveChanged(visible);
}
+void TrafficTreeHeaderView::menuActionTriggered(QAction * act)
+{
+ if (_actions && _actions->actions().contains(act)) {
+ QMenu * menu = qobject_cast<QMenu *>(sender());
+ if (menu) {
+ MenuEditAction * menuAction = nullptr;
+ foreach(QAction * _act, menu->actions()) {
+ if (qobject_cast<MenuEditAction *>(_act)) {
+ menuAction = qobject_cast<MenuEditAction *>(_act);
+ break;
+ }
+ }
+
+ int column = menuAction ? menuAction->property("column").toInt() : -1;
+ if (column >= 0) {
+ _filterText = menuAction->text().trimmed();
+ if (_filterText.length() == 0)
+ column = -1;
+ int filterOn = act->property("filter_action").toInt();
+
+ emit filterOnColumn(column, filterOn, _filterText);
+ }
+ }
+ }
+}
+
+void TrafficTreeHeaderView::filterColumn(bool)
+{
+ MenuEditAction * menuAction = qobject_cast<MenuEditAction *>(sender());
+ if (!menuAction)
+ return;
+
+ int filterOn = TrafficDataFilterProxy::TRAFFIC_DATA_LESS;
+ foreach(QAction * act, _actions->actions()) {
+ if (act->isChecked() && act->property("filter_action").isValid()) {
+ filterOn = act->property("filter_action").toInt();
+ break;
+ }
+ }
+
+ int column = menuAction->property("column").toInt();
+ _filterText = menuAction->text().trimmed();
+ if (_filterText.length() == 0)
+ column = -1;
+
+ emit filterOnColumn(column, filterOn, _filterText);
+}
+
+
+TrafficDataFilterProxy::TrafficDataFilterProxy(QObject *parent) :
+ QSortFilterProxyModel(parent),
+ _filterColumn(-1)
+{}
+
+void TrafficDataFilterProxy::filterForColumn(int column, int filterOn, QString filterText)
+{
+ if (filterOn < 0 || filterOn > TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL)
+ column = -1;
+
+ _filterColumn = column;
+ _filterOn = filterOn;
+ _filterText = filterText;
+ invalidateFilter();
+}
+
+int TrafficDataFilterProxy::mapToSourceColumn(int proxyColumn) const
+{
+ ATapDataModel * model = qobject_cast<ATapDataModel *>(sourceModel());
+ int column = proxyColumn;
+ if (model) {
+
+ if (qobject_cast<EndpointDataModel *>(model))
+ {
+ if (model->portsAreHidden() && column > EndpointDataModel::ENDP_COLUMN_ADDR)
+ column++;
+ if (! model->showTotalColumn()) {
+ if (column > EndpointDataModel::ENDP_COLUMN_BYTES)
+ column+=2;
+ }
+ } else if (qobject_cast<ConversationDataModel *>(model)) {
+ if (model->portsAreHidden()) {
+ if (column > ConversationDataModel::CONV_COLUMN_SRC_ADDR)
+ column++;
+ if (column > ConversationDataModel::CONV_COLUMN_DST_ADDR)
+ column++;
+ }
+ ConversationDataModel * convModel = qobject_cast<ConversationDataModel *>(model);
+ if (convModel->showConversationId() && column > ConversationDataModel::CONV_COLUMN_BYTES)
+ column++;
+ if (! model->showTotalColumn()) {
+ if (column > ConversationDataModel::CONV_COLUMN_BYTES)
+ column+=2;
+ }
+ }
+ }
+
+ return column;
+}
+
+bool TrafficDataFilterProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ ATapDataModel * dataModel = qobject_cast<ATapDataModel *>(sourceModel());
+ if (dataModel) {
+ bool isFiltered = dataModel->data(dataModel->index(source_row, 0), ATapDataModel::ROW_IS_FILTERED).toBool();
+ if (isFiltered && dataModel->filter().length() > 0)
+ return false;
+
+ int sourceColumn = mapToSourceColumn(_filterColumn);
+ QModelIndex srcIdx = dataModel->index(source_row, sourceColumn);
+ if (srcIdx.isValid()) {
+ QVariant data = srcIdx.data(ATapDataModel::UNFORMATTED_DISPLAYDATA);
+
+ bool filtered = false;
+ if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS)
+ filtered = data.toLongLong() < _filterText.toLongLong();
+ else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER)
+ filtered = data.toLongLong() > _filterText.toLongLong();
+ else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) {
+ filtered = data.toLongLong() == _filterText.toLongLong();
+ }
+
+ if (!filtered)
+ return false;
+ }
+ }
+
+ return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
+}
+
+bool TrafficDataFilterProxy::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ if (! source_left.isValid() || ! qobject_cast<const ATapDataModel *>(source_left.model()))
+ return false;
+ if (! source_right.isValid() || ! qobject_cast<const ATapDataModel *>(source_right.model()))
+ return false;
+
+ ATapDataModel * model = qobject_cast<ATapDataModel *>(sourceModel());
+
+ if (! model || source_left.model() != model || source_right.model() != model)
+ return false;
+
+ QVariant datA = source_left.data(ATapDataModel::UNFORMATTED_DISPLAYDATA);
+ QVariant datB = source_right.data(ATapDataModel::UNFORMATTED_DISPLAYDATA);
+
+ bool is_address = false;
+ if (qobject_cast<EndpointDataModel *>(model) && source_left.column() == EndpointDataModel::ENDP_COLUMN_ADDR &&
+ source_left.column() == source_right.column()) {
+ is_address = true;
+ } else if (qobject_cast<ConversationDataModel *>(model) && (source_left.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR ||
+ source_left.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) && source_left.column() == source_right.column()) {
+ is_address = true;
+ }
+
+ if (is_address) {
+ bool result = false;
+ bool identical = false;
+ int addressTypeA = model->data(source_left, ATapDataModel::DATA_ADDRESS_TYPE).toInt();
+ int addressTypeB = model->data(source_right, ATapDataModel::DATA_ADDRESS_TYPE).toInt();
+ if (addressTypeA != 0 && addressTypeB != 0 && addressTypeA != addressTypeB) {
+ result = addressTypeA < addressTypeB;
+ } else if (addressTypeA != 0 && addressTypeA == addressTypeB) {
+
+ if (addressTypeA == AT_IPv4) {
+ quint32 valA = model->data(source_left, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
+ quint32 valB = model->data(source_right, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
+
+ result = valA < valB;
+ identical = valA == valB;
+ } else if (addressTypeA == AT_NUMERIC) {
+ quint32 valA = datA.toInt();
+ quint32 valB = datB.toInt();
+ result = valA < valB;
+ identical = valA == valB;
+ } else {
+ result = QString::compare(datA.toString(), datB.toString(), Qt::CaseInsensitive) < 0;
+ identical = QString::compare(datA.toString(), datB.toString(), Qt::CaseInsensitive) == 0;
+ }
+
+ int portColumn = EndpointDataModel::ENDP_COLUMN_PORT;
+ if (identical && qobject_cast<ConversationDataModel *>(model)) {
+ QModelIndex tstA, tstB;
+ if (source_left.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR) {
+ portColumn = ConversationDataModel::CONV_COLUMN_SRC_PORT;
+ int col = ConversationDataModel::CONV_COLUMN_DST_ADDR;
+ tstA = model->index(source_left.row(), col);
+ tstB = model->index(source_right.row(), col);
+ } else if (source_left.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) {
+ portColumn = ConversationDataModel::CONV_COLUMN_DST_PORT;
+ int col = ConversationDataModel::CONV_COLUMN_SRC_ADDR;
+ tstA = model->index(source_left.row(), col);
+ tstB = model->index(source_right.row(), col);
+ }
+
+ if (addressTypeA == AT_IPv4) {
+ quint32 valX = model->data(tstA, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
+ quint32 valY = model->data(tstB, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
+
+ result = valX < valY;
+ identical = valX == valY;
+ } else {
+ result = QString::compare(model->data(tstA).toString().toLower(), model->data(tstB).toString(), Qt::CaseInsensitive) < 0;
+ identical = QString::compare(model->data(tstA).toString().toLower(), model->data(tstB).toString(), Qt::CaseInsensitive) == 0;
+ }
+ }
+
+ if (! result && identical && ! model->portsAreHidden()) {
+ int portA = model->data(model->index(source_left.row(), portColumn)).toInt();
+ int portB = model->data(model->index(source_right.row(), portColumn)).toInt();
+ return portA < portB;
+ }
+ }
+
+ return result;
+ }
+
+ if (datA.canConvert<double>() && datB.canConvert<double>())
+ return datA.toDouble() < datB.toDouble();
+
+ return QSortFilterProxyModel::lessThan(source_left, source_right);
+}
+
+bool TrafficDataFilterProxy::filterAcceptsColumn(int source_column, const QModelIndex &) const
+{
+ if (hideColumns_.contains(source_column))
+ return false;
+
+ ATapDataModel * model = qobject_cast<ATapDataModel *>(sourceModel());
+ if (model) {
+ if (model->portsAreHidden()) {
+ if (qobject_cast<EndpointDataModel *>(model) && source_column == EndpointDataModel::ENDP_COLUMN_PORT)
+ return false;
+ if (qobject_cast<ConversationDataModel *>(model) &&
+ (source_column == ConversationDataModel::CONV_COLUMN_SRC_PORT || source_column == ConversationDataModel::CONV_COLUMN_DST_PORT))
+ return false;
+ }
+ if (! model->showTotalColumn()) {
+ if (qobject_cast<EndpointDataModel *>(model) &&
+ (source_column == EndpointDataModel::ENDP_COLUMN_PACKETS_TOTAL || source_column == EndpointDataModel::ENDP_COLUMN_BYTES_TOTAL))
+ return false;
+ if (qobject_cast<ConversationDataModel *>(model) &&
+ (source_column == ConversationDataModel::CONV_COLUMN_PACKETS_TOTAL || source_column == ConversationDataModel::CONV_COLUMN_BYTES_TOTAL))
+ return false;
+ }
+ if (qobject_cast<ConversationDataModel *>(model)) {
+ ConversationDataModel * convModel = qobject_cast<ConversationDataModel *>(model);
+ if (source_column == ConversationDataModel::CONV_COLUMN_CONV_ID && ! convModel->showConversationId())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TrafficDataFilterProxy::setColumnVisibility(int column, bool visible)
+{
+ hideColumns_.removeAll(column);
+ if (!visible)
+ hideColumns_.append(column);
+ invalidateFilter();
+}
+
+bool TrafficDataFilterProxy::columnVisible(int column) const
+{
+ return ! hideColumns_.contains(column);
+}
+
TrafficTree::TrafficTree(QString baseName, GList ** recentColumnList, QWidget *parent) :
QTreeView(parent)
@@ -158,6 +498,18 @@ TrafficTree::TrafficTree(QString baseName, GList ** recentColumnList, QWidget *p
connect(this, &QTreeView::customContextMenuRequested, this, &TrafficTree::customContextMenu);
}
+void TrafficTree::setModel(QAbstractItemModel * model)
+{
+ if (model) {
+ TrafficDataFilterProxy * proxy = qobject_cast<TrafficDataFilterProxy *>(model);
+ if (proxy) {
+ connect(_header, &TrafficTreeHeaderView::filterOnColumn, proxy, &TrafficDataFilterProxy::filterForColumn);
+ }
+ }
+
+ QTreeView::setModel(model);
+}
+
void TrafficTree::tapListenerEnabled(bool enable)
{
_tapEnabled = enable;
diff --git a/ui/qt/widgets/traffic_tree.h b/ui/qt/widgets/traffic_tree.h
index 037a28d099..81d1431eb8 100644
--- a/ui/qt/widgets/traffic_tree.h
+++ b/ui/qt/widgets/traffic_tree.h
@@ -23,6 +23,30 @@
#include <QMenu>
#include <QHeaderView>
+#include <QWidgetAction>
+#include <QLineEdit>
+#include <QActionGroup>
+
+class MenuEditAction : public QWidgetAction
+{
+ Q_OBJECT
+public:
+ MenuEditAction(QString text, QString hintText, QObject * parent = nullptr);
+
+ QString text() const;
+
+protected:
+ virtual QWidget * createWidget(QWidget *parent);
+private:
+ QString _hintText;
+ QString _text;
+ QLineEdit * _lineEdit;
+
+private slots:
+ void triggerEntry();
+};
+
+
class TrafficTreeHeaderView : public QHeaderView
{
Q_OBJECT
@@ -34,13 +58,53 @@ public:
signals:
void columnsHaveChanged(QList<int> visible);
-
+ void filterOnColumn(int column, int filterOn, QString filterText);
private:
GList ** _recentColumnList;
+ QActionGroup * _actions;
+ QString _filterText;
private slots:
void headerContextMenu(const QPoint &pos);
void columnTriggered(bool checked = false);
+ void menuActionTriggered(QAction *);
+ void filterColumn(bool checked = false);
+
+};
+
+
+class TrafficDataFilterProxy : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+
+ enum {
+ TRAFFIC_DATA_LESS,
+ TRAFFIC_DATA_GREATER,
+ TRAFFIC_DATA_EQUAL,
+ };
+
+ TrafficDataFilterProxy(QObject *parent = nullptr);
+
+ void setColumnVisibility(int column, bool visible);
+ bool columnVisible(int column) const;
+
+public slots:
+ void filterForColumn(int column, int filterOn, QString filterText);
+
+protected:
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+ virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const;
+ virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
+
+private:
+ QList<int> hideColumns_;
+
+ int _filterColumn;
+ int _filterOn;
+ QString _filterText;
+
+ int mapToSourceColumn(int proxyColumn) const;
};
@@ -75,6 +139,8 @@ public:
void applyRecentColumns();
+ virtual void setModel(QAbstractItemModel *model) override;
+
signals:
void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type);
void columnsHaveChanged(QList<int> columns);