aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ui/qt/CMakeLists.txt6
-rw-r--r--ui/qt/Makefile.am6
-rw-r--r--ui/qt/expert_info_dialog.cpp549
-rw-r--r--ui/qt/expert_info_dialog.h54
-rw-r--r--ui/qt/expert_info_dialog.ui54
-rw-r--r--ui/qt/main_window_slots.cpp2
-rw-r--r--ui/qt/models/expert_info_model.cpp406
-rw-r--r--ui/qt/models/expert_info_model.h140
-rw-r--r--ui/qt/models/expert_info_proxy_model.cpp278
-rw-r--r--ui/qt/models/expert_info_proxy_model.h73
-rw-r--r--ui/qt/widgets/expert_info_view.cpp55
-rw-r--r--ui/qt/widgets/expert_info_view.h42
12 files changed, 1141 insertions, 524 deletions
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index 4934fd484b..bb8d02749d 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -34,6 +34,7 @@ set(WIRESHARK_WIDGET_HEADERS
widgets/drag_label.h
widgets/editor_color_dialog.h
widgets/editor_file_dialog.h
+ widgets/expert_info_view.h
widgets/elided_label.h
widgets/field_filter_edit.h
widgets/find_line_edit.h
@@ -64,6 +65,8 @@ set(WIRESHARK_MODEL_HEADERS
models/cache_proxy_model.h
models/decode_as_delegate.h
models/decode_as_model.h
+ models/expert_info_model.h
+ models/expert_info_proxy_model.h
models/fileset_entry_model.h
models/html_text_delegate.h
models/interface_sort_filter_model.h
@@ -244,6 +247,7 @@ set(WIRESHARK_WIDGET_SRCS
widgets/editor_color_dialog.cpp
widgets/editor_file_dialog.cpp
widgets/elided_label.cpp
+ widgets/expert_info_view.cpp
widgets/field_filter_edit.cpp
widgets/find_line_edit.cpp
widgets/follow_stream_text.cpp
@@ -271,6 +275,8 @@ set(WIRESHARK_MODEL_SRCS
models/cache_proxy_model.cpp
models/decode_as_delegate.cpp
models/decode_as_model.cpp
+ models/expert_info_model.cpp
+ models/expert_info_proxy_model.cpp
models/fileset_entry_model.cpp
models/html_text_delegate.cpp
models/interface_sort_filter_model.cpp
diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am
index b7396547eb..b9d21eb273 100644
--- a/ui/qt/Makefile.am
+++ b/ui/qt/Makefile.am
@@ -163,6 +163,7 @@ MOC_WIDGET_HDRS = \
widgets/editor_color_dialog.h \
widgets/editor_file_dialog.h \
widgets/elided_label.h \
+ widgets/expert_info_view.h \
widgets/field_filter_edit.h \
widgets/find_line_edit.h \
widgets/follow_stream_text.h \
@@ -193,6 +194,8 @@ MOC_MODELS_HDRS = \
models/cache_proxy_model.h \
models/decode_as_delegate.h \
models/decode_as_model.h \
+ models/expert_info_model.h \
+ models/expert_info_proxy_model.h \
models/fileset_entry_model.h \
models/html_text_delegate.h \
models/interface_sort_filter_model.h \
@@ -486,6 +489,7 @@ WIRESHARK_QT_WIDGET_SRC = \
widgets/editor_color_dialog.cpp \
widgets/editor_file_dialog.cpp \
widgets/elided_label.cpp \
+ widgets/expert_info_view.cpp \
widgets/field_filter_edit.cpp \
widgets/find_line_edit.cpp \
widgets/follow_stream_text.cpp \
@@ -513,6 +517,8 @@ WIRESHARK_QT_MODELS_SRCS = \
models/cache_proxy_model.cpp \
models/decode_as_delegate.cpp \
models/decode_as_model.cpp \
+ models/expert_info_model.cpp \
+ models/expert_info_proxy_model.cpp \
models/fileset_entry_model.cpp \
models/html_text_delegate.cpp \
models/interface_sort_filter_model.cpp \
diff --git a/ui/qt/expert_info_dialog.cpp b/ui/qt/expert_info_dialog.cpp
index b099e561de..84ecd0c9d3 100644
--- a/ui/qt/expert_info_dialog.cpp
+++ b/ui/qt/expert_info_dialog.cpp
@@ -29,7 +29,6 @@
#include <epan/stat_tap_ui.h>
#include <epan/tap.h>
-#include <ui/qt/utils/color_utils.h>
#include "wireshark_application.h"
#include <QAction>
@@ -37,167 +36,27 @@
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
-#include <QTreeWidgetItemIterator>
// To do:
// - Test with custom expert levels (Preferences -> Expert).
-// - Test with large captures. Add a custom model if needed.
+// - Test with large captures.
// - Promote to a fourth pane in the main window?
// - Make colors configurable? In theory we could condense image/expert_indicators.svg,
// down to one item, make sure it uses a single (or a few) base color(s), and generate
// icons on the fly.
-enum {
- severity_col_,
- summary_col_,
- group_col_,
- protocol_col_,
- count_col_
-};
-
-enum { group_type_ = 1000, packet_type_ = 1001 };
-
-static const int auto_expand_threshold_ = 20; // Arbitrary
-
-class ExpertGroupTreeWidgetItem : public QTreeWidgetItem
-{
-public:
- ExpertGroupTreeWidgetItem(QTreeWidget *parent, int severity, const QString &summary, int group, const QString &protocol) : QTreeWidgetItem (parent, group_type_) {
- // XXX We set text and data here, colors in addExpertInfo, and counts
- // in updateCounts.
- setData(severity_col_, Qt::UserRole, QVariant(severity));
- setData(group_col_, Qt::UserRole, QVariant(group));
-
- setText(severity_col_, val_to_str_const(severity, expert_severity_vals, "Unknown"));
- QString summary_raw = summary; // Might contain CR, LF, etc.
- setText(summary_col_, summary_raw.simplified());
- setText(group_col_, val_to_str_const(group, expert_group_vals, "Unknown"));
- setText(protocol_col_, protocol);
- setText(count_col_, "0");
- }
- void updateCounts() {
- int tot_children = childCount();
- int vis_children = 0;
-
- for (int i = 0; i < tot_children; i++) {
- if (!child(i)->isHidden()) vis_children++;
- }
- QString count_str = QString::number(vis_children);
- if (vis_children != tot_children) {
- count_str += QString(" / %1").arg(tot_children);
- }
- setText(count_col_, count_str);
- setHidden(vis_children < 1);
- }
-
- bool operator< (const QTreeWidgetItem &other) const
- {
- int sort_col = treeWidget()->sortColumn();
- switch(sort_col) {
- case severity_col_:
- return data(severity_col_, Qt::UserRole).value<int>() < other.data(severity_col_, Qt::UserRole).value<int>();
- case count_col_:
- return text(count_col_).toInt() < other.text(count_col_).toInt();
- default:
- return QTreeWidgetItem::operator<(other);
- }
- }
-};
-
-class ExpertPacketTreeWidgetItem : public QTreeWidgetItem
-{
-public:
- ExpertPacketTreeWidgetItem(expert_info_t *expert_info = NULL, column_info *cinfo = NULL) :
- QTreeWidgetItem (packet_type_),
- group_by_summary_(true),
- packet_num_(0),
- group_(0),
- severity_(0),
- hf_id_(-1)
- {
- if (expert_info) {
- packet_num_ = expert_info->packet_num;
- group_ = expert_info->group;
- severity_ = expert_info->severity;
- hf_id_ = expert_info->hf_index;
- protocol_ = expert_info->protocol;
- summary_ = expert_info->summary;
- if (cinfo) {
- info_ = col_get_text(cinfo, COL_INFO);
- }
- }
- setTextAlignment(severity_col_, Qt::AlignRight);
- }
- virtual QVariant data(int column, int role) const {
- if (role == Qt::DisplayRole) {
- switch(column) {
- case severity_col_:
- return QString::number(packet_num_);
- break;
- case summary_col_:
- if (group_by_summary_) {
- return QString(info_).simplified();
- } else {
- return QString(summary_).simplified();
- }
- break;
- default:
- break;
- }
- }
- return QTreeWidgetItem::data(column, role);
- }
- guint32 packetNum() const { return packet_num_; }
- int group() const { return group_; }
- int severity() const { return severity_; }
- int hfId() const { return hf_id_; }
- QString protocol() const { return protocol_; }
- QString summary() const { return summary_; }
- QString groupKey(bool group_by_summary) {
- group_by_summary_ = group_by_summary;
- QString key = QString("%1|%2|%3")
- .arg(severity_)
- .arg(group_)
- .arg(QString(protocol_));
- if (group_by_summary) {
- key += "|";
- key += summary_;
- }
- return key;
- }
- bool operator< (const QTreeWidgetItem &other) const
- {
- // Probably not needed.
- if (other.type() != packet_type_) return QTreeWidgetItem::operator< (other);
- const ExpertPacketTreeWidgetItem *other_expert = static_cast<const ExpertPacketTreeWidgetItem *>(&other);
- // Force ascending.
- if (treeWidget()->header()->sortIndicatorOrder() == Qt::DescendingOrder) {
- return packet_num_ > other_expert->packetNum();
- } else {
- return packet_num_ < other_expert->packetNum();
- }
- }
-private:
- bool group_by_summary_;
- guint32 packet_num_;
- int group_;
- int severity_;
- int hf_id_;
- // Half-hearted attempt at conserving memory. If this isn't sufficient,
- // PacketListRecord interns column strings in a GStringChunk.
- QByteArray protocol_;
- QByteArray summary_;
- QByteArray info_;
-};
-
ExpertInfoDialog::ExpertInfoDialog(QWidget &parent, CaptureFile &capture_file) :
WiresharkDialog(parent, capture_file),
ui(new Ui::ExpertInfoDialog),
- need_show_hide_(false),
+ expert_info_model_(new ExpertInfoModel(capture_file)),
+ proxyModel_(new ExpertInfoProxyModel(this)),
display_filter_(QString())
{
ui->setupUi(this);
+ proxyModel_->setSourceModel(expert_info_model_);
+ ui->expertInfoTreeView->setModel(proxyModel_);
+
setWindowSubtitle(tr("Expert Information"));
// Clicking on an item jumps to its associated packet. Make the dialog
@@ -207,27 +66,14 @@ ExpertInfoDialog::ExpertInfoDialog(QWidget &parent, CaptureFile &capture_file) :
loadGeometry(dlg_width, parent.height());
int one_em = fontMetrics().height();
- ui->expertInfoTreeWidget->setColumnWidth(summary_col_, one_em * 25); // Arbitrary
-
- severity_actions_ = QList<QAction *>() << ui->actionShowError << ui->actionShowWarning
- << ui->actionShowNote << ui->actionShowChat
- << ui->actionShowComment;
- QList<int> severities = QList<int>() << PI_ERROR << PI_WARN << PI_NOTE << PI_CHAT << PI_COMMENT;
- QMenu *severity_menu = new QMenu(ui->severitiesPushButton);
-
- // It might be nice to color each menu item to match each severity. It
- // might also be nice if Qt supported that...
- foreach (QAction *sa, severity_actions_) {
- severity_menu->addAction(sa);
- sa->setData(QVariant(severities.takeFirst()));
- sa->setChecked(true);
- connect(sa, SIGNAL(toggled(bool)), this, SLOT(actionShowToggled()));
- }
- ui->severitiesPushButton->setMenu(severity_menu);
+ ui->expertInfoTreeView->setColumnWidth(ExpertInfoProxyModel::colProxySummary, one_em * 25); // Arbitrary
+
+ //Unfortunately this has to be done manually and not through .ui
+ ui->severitiesPushButton->setMenu(ui->menuShowExpert);
- ui->expertInfoTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(ui->expertInfoTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
- SLOT(showProtoHierMenu(QPoint)));
+ ui->expertInfoTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui->expertInfoTreeView, SIGNAL(customContextMenuRequested(QPoint)),
+ SLOT(showExpertInfoMenu(QPoint)));
QMenu *submenu;
@@ -260,6 +106,16 @@ ExpertInfoDialog::ExpertInfoDialog(QWidget &parent, CaptureFile &capture_file) :
connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
}
+ //Add collapse/expand all menu options
+ QAction *collapse = new QAction(tr("Collapse All"), this);
+ ctx_menu_.addAction(collapse);
+ connect(collapse, SIGNAL(triggered()), this, SLOT(collapseTree()));
+
+ QAction *expand = new QAction(tr("Expand All"), this);
+ ctx_menu_.addAction(expand);
+ connect(expand, SIGNAL(triggered()), this, SLOT(expandTree()));
+
+
connect(&cap_file_, SIGNAL(captureFileRetapStarted()),
this, SLOT(retapStarted()));
connect(&cap_file_, SIGNAL(captureFileRetapFinished()),
@@ -275,16 +131,7 @@ ExpertInfoDialog::~ExpertInfoDialog()
void ExpertInfoDialog::clearAllData()
{
- ui->expertInfoTreeWidget->clear();
- error_events_ = 0;
- warn_events_ = 0;
- note_events_ = 0;
- chat_events_ = 0;
- comment_events_ = 0;
-
- need_show_hide_ = false;
- ei_to_ti_.clear();
- gti_packets_.clear();
+ expert_info_model_->clear();
}
void ExpertInfoDialog::setDisplayFilter(const QString &display_filter)
@@ -293,6 +140,11 @@ void ExpertInfoDialog::setDisplayFilter(const QString &display_filter)
updateWidgets();
}
+ExpertInfoTreeView* ExpertInfoDialog::getExpertInfoView()
+{
+ return ui->expertInfoTreeView;
+}
+
void ExpertInfoDialog::retapPackets()
{
if (file_closed_) return;
@@ -301,196 +153,26 @@ void ExpertInfoDialog::retapPackets()
removeTapListeners();
if (!registerTapListener("expert",
- this,
- NULL,
+ expert_info_model_,
+ ui->limitCheckBox->isChecked() ? display_filter_.toUtf8().constData(): NULL,
TL_REQUIRES_COLUMNS,
- tapReset,
- tapPacket,
- tapDraw)) {
+ ExpertInfoModel::tapReset,
+ ExpertInfoModel::tapPacket,
+ ExpertInfoModel::tapDraw)) {
return;
}
- if (ui->limitCheckBox->isChecked()) {
- GString *error_string = set_tap_dfilter(this, display_filter_.toUtf8().constData());
- if (error_string) {
- QMessageBox::warning(this, tr("Endpoint expert failed to set filter"),
- error_string->str);
- g_string_free(error_string, TRUE);
- return;
- }
- }
-
cap_file_.retapPackets();
}
void ExpertInfoDialog::retapStarted()
{
ui->limitCheckBox->setEnabled(false);
+ ui->groupBySummaryCheckBox->setEnabled(false);
}
void ExpertInfoDialog::retapFinished()
{
- ui->limitCheckBox->setEnabled(! file_closed_ && ! display_filter_.isEmpty());
- addPacketTreeItems();
- for (int i = 0; i < ui->expertInfoTreeWidget->topLevelItemCount(); i++) {
- QTreeWidgetItem *group_ti = ui->expertInfoTreeWidget->topLevelItem(i);
- if (group_ti->childCount() <= auto_expand_threshold_) {
- group_ti->setExpanded(true);
- }
- }
-}
-
-void ExpertInfoDialog::addExpertInfo(ExpertPacketTreeWidgetItem *packet_ti)
-{
- if (!packet_ti) return;
-
- QTreeWidgetItem *group_ti;
-
- group_ti = ensureGroupTreeWidgetItem(packet_ti);
- gti_packets_[group_ti] << packet_ti;
-
- // XXX Use plain colors until our users demand to be blinded.
-// if (background.isValid()) {
-// packet_ti->setBackground(0, background);
-// packet_ti->setForeground(0, ColorUtils::expert_color_foreground);
- // }
-
-}
-
-void ExpertInfoDialog::addExpertInfo(struct expert_info_s *expert_info)
-{
- if (!expert_info) return;
-
- ExpertPacketTreeWidgetItem *packet_ti = new ExpertPacketTreeWidgetItem(expert_info, &(cap_file_.capFile()->cinfo));
-
- addExpertInfo(packet_ti);
-}
-
-void ExpertInfoDialog::updateCounts()
-{
- for (int i = 0; i < ui->expertInfoTreeWidget->topLevelItemCount(); i++) {
- ExpertGroupTreeWidgetItem *group_ti = dynamic_cast<ExpertGroupTreeWidgetItem *>(ui->expertInfoTreeWidget->topLevelItem(i));
- if (group_ti) group_ti->updateCounts();
- }
-}
-
-void ExpertInfoDialog::tapReset(void *eid_ptr)
-{
- ExpertInfoDialog *eid = static_cast<ExpertInfoDialog *>(eid_ptr);
- if (!eid) return;
-
- eid->clearAllData();
-}
-
-gboolean ExpertInfoDialog::tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data)
-{
- ExpertInfoDialog *eid = static_cast<ExpertInfoDialog *>(eid_ptr);
- expert_info_t *expert_info = (expert_info_t *) data;
- gboolean draw_required = FALSE;
-
- if (!pinfo || !eid || !expert_info) return FALSE;
-
- eid->addExpertInfo(expert_info);
-
- switch(expert_info->severity) {
- case(PI_COMMENT):
- if (eid->comment_events_ < 1) draw_required = TRUE;
- eid->comment_events_++;
- break;
- case(PI_CHAT):
- if (eid->chat_events_ < 1) draw_required = TRUE;
- eid->chat_events_++;
- break;
- case(PI_NOTE):
- if (eid->note_events_ < 1) draw_required = TRUE;
- eid->note_events_++;
- break;
- case(PI_WARN):
- if (eid->warn_events_ < 1) draw_required = TRUE;
- eid->warn_events_++;
- break;
- case(PI_ERROR):
- if (eid->error_events_ < 1) draw_required = TRUE;
- eid->error_events_++;
- break;
- default:
- g_assert_not_reached();
- }
-
- return draw_required;
-}
-
-void ExpertInfoDialog::tapDraw(void *eid_ptr)
-{
- ExpertInfoDialog *eid = static_cast<ExpertInfoDialog *>(eid_ptr);
- if (!eid) return;
-
- eid->addPacketTreeItems();
-}
-
-QTreeWidgetItem *ExpertInfoDialog::ensureGroupTreeWidgetItem(ExpertPacketTreeWidgetItem *packet_ti)
-{
- if (!packet_ti) return NULL;
-
- QTreeWidgetItem *group_ti;
- QString key = packet_ti->groupKey(ui->groupBySummaryCheckBox->isChecked());
-
- if (ei_to_ti_.contains(key)) {
- group_ti = ei_to_ti_[key];
- } else {
- QString summary;
-
- if (ui->groupBySummaryCheckBox->isChecked()) summary = packet_ti->summary();
- group_ti = new ExpertGroupTreeWidgetItem(ui->expertInfoTreeWidget, packet_ti->severity(), summary, packet_ti->group(), packet_ti->protocol());
-
- QColor background;
- switch(packet_ti->severity()) {
- case(PI_COMMENT):
- background = ColorUtils::expert_color_comment;
- break;
- case(PI_CHAT):
- background = ColorUtils::expert_color_chat;
- break;
- case(PI_NOTE):
- background = ColorUtils::expert_color_note;
- break;
- case(PI_WARN):
- background = ColorUtils::expert_color_warn;
- break;
- case(PI_ERROR):
- background = ColorUtils::expert_color_error;
- break;
- default:
- break;
- }
- if (background.isValid()) {
- for (int i = 0; i < ui->expertInfoTreeWidget->columnCount(); i++) {
- group_ti->setBackground(i, background);
- group_ti->setForeground(i, ColorUtils::expert_color_foreground);
- }
- }
- ei_to_ti_[key] = group_ti;
- gti_packets_[group_ti] = QList<QTreeWidgetItem *>();
- }
-
- return group_ti;
-}
-
-void ExpertInfoDialog::addPacketTreeItems()
-{
- setUpdatesEnabled(false);
- // Adding a list of ExpertPacketTreeWidgetItems is much faster than
- // adding them individually. We still add ExpertGroupTreeWidgetItems
- // individually since that gives us a nice progress indicator.
- for (int i = 0; i < ui->expertInfoTreeWidget->topLevelItemCount(); i++) {
- QTreeWidgetItem *group_ti = ui->expertInfoTreeWidget->topLevelItem(i);
- if (gti_packets_.contains(group_ti)) {
- group_ti->addChildren(gti_packets_[group_ti]);
- gti_packets_[group_ti].clear();
- }
- }
- setUpdatesEnabled(true);
-
updateWidgets();
}
@@ -498,37 +180,11 @@ void ExpertInfoDialog::updateWidgets()
{
ui->limitCheckBox->setEnabled(! file_closed_ && ! display_filter_.isEmpty());
- ui->actionShowError->setEnabled(error_events_ > 0);
- ui->actionShowWarning->setEnabled(warn_events_ > 0);
- ui->actionShowNote->setEnabled(note_events_ > 0);
- ui->actionShowChat->setEnabled(chat_events_ > 0);
- ui->actionShowComment->setEnabled(comment_events_ > 0);
-
- if (need_show_hide_) {
- for (int i = 0; i < ui->expertInfoTreeWidget->topLevelItemCount(); i++) {
- QTreeWidgetItem *group_ti = ui->expertInfoTreeWidget->topLevelItem(i);
- switch (group_ti->data(severity_col_, Qt::UserRole).value<int>()) {
- case PI_ERROR:
- group_ti->setHidden(! ui->actionShowError->isChecked());
- break;
- case PI_WARN:
- group_ti->setHidden(! ui->actionShowWarning->isChecked());
- break;
- case PI_NOTE:
- group_ti->setHidden(! ui->actionShowNote->isChecked());
- break;
- case PI_CHAT:
- group_ti->setHidden(! ui->actionShowChat->isChecked());
- break;
- case PI_COMMENT:
- group_ti->setHidden(! ui->actionShowComment->isChecked());
- break;
- default:
- break;
- }
- }
- }
- updateCounts();
+ ui->actionShowError->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityError) > 0);
+ ui->actionShowWarning->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityWarn) > 0);
+ ui->actionShowNote->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityNote) > 0);
+ ui->actionShowChat->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityChat) > 0);
+ ui->actionShowComment->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityComment) > 0);
QString tooltip;
QString hint;
@@ -549,19 +205,47 @@ void ExpertInfoDialog::updateWidgets()
hint.append("</i></small>");
ui->hintLabel->setText(hint);
+ ui->groupBySummaryCheckBox->setEnabled(!file_closed_);
+}
+
+void ExpertInfoDialog::on_actionShowError_toggled(bool checked)
+{
+ proxyModel_->setSeverityFilter(PI_ERROR, !checked);
+ updateWidgets();
+}
+
+void ExpertInfoDialog::on_actionShowWarning_toggled(bool checked)
+{
+ proxyModel_->setSeverityFilter(PI_WARN, !checked);
+ updateWidgets();
+}
+
+void ExpertInfoDialog::on_actionShowNote_toggled(bool checked)
+{
+ proxyModel_->setSeverityFilter(PI_NOTE, !checked);
+ updateWidgets();
+}
+
+void ExpertInfoDialog::on_actionShowChat_toggled(bool checked)
+{
+ proxyModel_->setSeverityFilter(PI_CHAT, !checked);
+ updateWidgets();
}
-void ExpertInfoDialog::actionShowToggled()
+void ExpertInfoDialog::on_actionShowComment_toggled(bool checked)
{
- need_show_hide_ = true;
+ proxyModel_->setSeverityFilter(PI_COMMENT, !checked);
updateWidgets();
}
-void ExpertInfoDialog::showProtoHierMenu(QPoint pos)
+
+void ExpertInfoDialog::showExpertInfoMenu(QPoint pos)
{
bool enable = true;
- ExpertPacketTreeWidgetItem *packet_ti = dynamic_cast<ExpertPacketTreeWidgetItem *>(ui->expertInfoTreeWidget->currentItem());
- if (!packet_ti || packet_ti->hfId() < 0) {
+ QModelIndex expertIndex = ui->expertInfoTreeView->indexAt(pos);
+
+ if (!expertIndex.isValid() ||
+ (expert_info_model_->data(expertIndex.sibling(expertIndex.row(), ExpertInfoModel::colHf), Qt::DisplayRole).toInt() < 0)) {
enable = false;
}
@@ -577,30 +261,32 @@ void ExpertInfoDialog::showProtoHierMenu(QPoint pos)
action->setEnabled(action_enable);
}
- ctx_menu_.popup(ui->expertInfoTreeWidget->viewport()->mapToGlobal(pos));
+ ctx_menu_.popup(ui->expertInfoTreeView->viewport()->mapToGlobal(pos));
}
void ExpertInfoDialog::filterActionTriggered()
{
- ExpertPacketTreeWidgetItem *packet_ti = dynamic_cast<ExpertPacketTreeWidgetItem *>(ui->expertInfoTreeWidget->currentItem());
+ QModelIndex packetIndex = ui->expertInfoTreeView->currentIndex();
FilterAction *fa = qobject_cast<FilterAction *>(QObject::sender());
- if (!fa || !packet_ti) {
+ if (!fa || !packetIndex.isValid()) {
return;
}
- int hf_index = packet_ti->hfId();
+ QModelIndex modelIndex = proxyModel_->mapToSource(packetIndex);
+ int hf_index = expert_info_model_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colHf), Qt::DisplayRole).toInt();
+
if (hf_index > -1) {
QString filter_string;
if (fa->action() == FilterAction::ActionWebLookup) {
filter_string = QString("%1 %2")
- .arg(packet_ti->protocol())
- .arg(packet_ti->summary());
+ .arg(expert_info_model_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colProtocol), Qt::DisplayRole).toString())
+ .arg(expert_info_model_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colSummary), Qt::DisplayRole).toString());
} else if (fa->action() == FilterAction::ActionCopy) {
filter_string = QString("%1 %2: %3")
- .arg(packet_ti->packetNum())
- .arg(packet_ti->protocol())
- .arg(packet_ti->summary());
+ .arg(expert_info_model_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colPacket), Qt::DisplayRole).toUInt())
+ .arg(expert_info_model_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colProtocol), Qt::DisplayRole).toString())
+ .arg(expert_info_model_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colSummary), Qt::DisplayRole).toString());
} else {
filter_string = proto_registrar_get_abbrev(hf_index);
}
@@ -611,26 +297,14 @@ void ExpertInfoDialog::filterActionTriggered()
}
}
-void ExpertInfoDialog::captureFileClosing()
+void ExpertInfoDialog::collapseTree()
{
- WiresharkDialog::captureFileClosing();
+ ui->expertInfoTreeView->collapseAll();
}
-void ExpertInfoDialog::on_expertInfoTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
+void ExpertInfoDialog::expandTree()
{
- QString first_col_title = tr("Severity");
- if (current && current->type() == packet_type_) {
- first_col_title = tr("Packet");
- }
- ui->expertInfoTreeWidget->headerItem()->setText(severity_col_, first_col_title);
-
- // Ignore top-level items.
- if (!current || !current->parent() || file_closed_) return;
-
- ExpertPacketTreeWidgetItem *packet_ti = dynamic_cast<ExpertPacketTreeWidgetItem *>(current);
- if (!packet_ti) return;
-
- emit goToPacket(packet_ti->packetNum(), packet_ti->hfId());
+ ui->expertInfoTreeView->expandAll();
}
void ExpertInfoDialog::on_limitCheckBox_toggled(bool)
@@ -640,56 +314,13 @@ void ExpertInfoDialog::on_limitCheckBox_toggled(bool)
void ExpertInfoDialog::on_groupBySummaryCheckBox_toggled(bool)
{
- QList<QTreeWidgetItem *> pending_items;
- QList<QTreeWidgetItem *> group_items = ui->expertInfoTreeWidget->invisibleRootItem()->takeChildren();
-
- foreach (QList<QTreeWidgetItem *> gti_list, gti_packets_.values()) {
- pending_items.append(gti_list);
- }
- gti_packets_.clear();
- ei_to_ti_.clear();
-
- foreach (QTreeWidgetItem *ti, pending_items) {
- ExpertPacketTreeWidgetItem *packet_ti = dynamic_cast<ExpertPacketTreeWidgetItem *>(ti);
- addExpertInfo(packet_ti);
- }
- addPacketTreeItems();
-
- foreach (QTreeWidgetItem *gti, group_items) {
- QList<QTreeWidgetItem *> packet_items = gti->takeChildren();
- foreach (QTreeWidgetItem *ti, packet_items) {
- ExpertPacketTreeWidgetItem *packet_ti = dynamic_cast<ExpertPacketTreeWidgetItem *>(ti);
- addExpertInfo(packet_ti);
- }
- delete gti;
- addPacketTreeItems();
- }
-
- retapFinished(); // Expands tree items.
+ expert_info_model_->setGroupBySummary(ui->groupBySummaryCheckBox->isChecked());
}
// Show child (packet list) items that match the contents of searchLineEdit.
void ExpertInfoDialog::on_searchLineEdit_textChanged(const QString &search_re)
{
- QTreeWidgetItemIterator it(ui->expertInfoTreeWidget,
- ui->groupBySummaryCheckBox->isChecked()
- ? QTreeWidgetItemIterator::HasChildren
- : QTreeWidgetItemIterator::NoChildren);
- QRegExp regex(search_re, Qt::CaseInsensitive);
-
- while (*it) {
- bool hidden = true;
- // XXX Check other columns as well?
- if (search_re.isEmpty() || (*it)->text(summary_col_).contains(regex)) {
- hidden = false;
- }
- (*it)->setHidden(hidden);
- ++it;
- }
-
- if (!ui->groupBySummaryCheckBox->isChecked()) {
- updateCounts();
- }
+ proxyModel_->setSummaryFilter(search_re);
}
void ExpertInfoDialog::on_buttonBox_helpRequested()
diff --git a/ui/qt/expert_info_dialog.h b/ui/qt/expert_info_dialog.h
index 2128b78c8f..e3fa1b5870 100644
--- a/ui/qt/expert_info_dialog.h
+++ b/ui/qt/expert_info_dialog.h
@@ -28,20 +28,16 @@
#include "filter_action.h"
#include "wireshark_dialog.h"
+#include <ui/qt/models/expert_info_model.h>
+#include <ui/qt/models/expert_info_proxy_model.h>
+#include <ui/qt/widgets/expert_info_view.h>
#include <QMenu>
-#include <QTreeWidgetItem>
-
-struct epan_dissect;
-struct expert_info_s;
-struct _packet_info;
namespace Ui {
class ExpertInfoDialog;
}
-class ExpertPacketTreeWidgetItem;
-
class ExpertInfoDialog : public WiresharkDialog
{
Q_OBJECT
@@ -53,44 +49,21 @@ public:
void clearAllData();
void setDisplayFilter(const QString &display_filter = QString());
+ ExpertInfoTreeView* getExpertInfoView();
+
signals:
- void goToPacket(int packet_num, int hf_id);
void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type);
private:
Ui::ExpertInfoDialog *ui;
- int comment_events_;
-// int disp_events;
- int chat_events_;
- int note_events_;
- int warn_events_;
- int error_events_;
-
- bool need_show_hide_;
+ ExpertInfoModel* expert_info_model_;
+ ExpertInfoProxyModel* proxyModel_;
QMenu ctx_menu_;
- QHash<QString, QTreeWidgetItem*> ei_to_ti_;
- QHash<QTreeWidgetItem*, QList<QTreeWidgetItem *> > gti_packets_;
- QList<QAction *> severity_actions_;
-
QString display_filter_;
- void addExpertInfo(ExpertPacketTreeWidgetItem *packet_ti);
- // Called from tapPacket
- void addExpertInfo(struct expert_info_s *expert_info);
- // Called from tapDraw
- void updateCounts();
-
- // Callbacks for register_tap_listener
- static void tapReset(void *eid_ptr);
- static gboolean tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data);
- static void tapDraw(void *eid_ptr);
-
- QTreeWidgetItem *ensureGroupTreeWidgetItem(ExpertPacketTreeWidgetItem *packet_ti);
- void addPacketTreeItems();
-
private slots:
void retapPackets();
void retapStarted();
@@ -98,12 +71,17 @@ private slots:
void updateWidgets();
- void actionShowToggled();
- void showProtoHierMenu(QPoint pos);
+ void on_actionShowError_toggled(bool checked);
+ void on_actionShowWarning_toggled(bool checked);
+ void on_actionShowNote_toggled(bool checked);
+ void on_actionShowChat_toggled(bool checked);
+ void on_actionShowComment_toggled(bool checked);
+
+ void showExpertInfoMenu(QPoint pos);
void filterActionTriggered();
- void captureFileClosing();
+ void collapseTree();
+ void expandTree();
- void on_expertInfoTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *);
void on_limitCheckBox_toggled(bool);
void on_groupBySummaryCheckBox_toggled(bool);
void on_searchLineEdit_textChanged(const QString &search_re);
diff --git a/ui/qt/expert_info_dialog.ui b/ui/qt/expert_info_dialog.ui
index 1e4074dcb8..ade236944d 100644
--- a/ui/qt/expert_info_dialog.ui
+++ b/ui/qt/expert_info_dialog.ui
@@ -15,38 +15,13 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QTreeWidget" name="expertInfoTreeWidget">
+ <widget class="ExpertInfoTreeView" name="expertInfoTreeView">
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
- <column>
- <property name="text">
- <string>Severity</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Summary</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Group</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Protocol</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Count</string>
- </property>
- </column>
</widget>
</item>
<item>
@@ -136,6 +111,13 @@
<property name="text">
<string>Show…</string>
</property>
+ <widget class="QMenu" name="menuShowExpert">
+ <addaction name="actionShowError"/>
+ <addaction name="actionShowWarning"/>
+ <addaction name="actionShowChat"/>
+ <addaction name="actionShowNote"/>
+ <addaction name="actionShowComment"/>
+ </widget>
</widget>
</item>
</layout>
@@ -155,6 +137,9 @@
<property name="checkable">
<bool>true</bool>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
<property name="text">
<string>Error</string>
</property>
@@ -166,6 +151,9 @@
<property name="checkable">
<bool>true</bool>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
<property name="text">
<string>Warning</string>
</property>
@@ -177,6 +165,9 @@
<property name="checkable">
<bool>true</bool>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
<property name="text">
<string>Note</string>
</property>
@@ -188,6 +179,9 @@
<property name="checkable">
<bool>true</bool>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
<property name="text">
<string>Chat</string>
</property>
@@ -199,6 +193,9 @@
<property name="checkable">
<bool>true</bool>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
<property name="text">
<string>Comment</string>
</property>
@@ -213,6 +210,11 @@
<extends>QLabel</extends>
<header>widgets/elided_label.h</header>
</customwidget>
+ <customwidget>
+ <class>ExpertInfoTreeView</class>
+ <extends>QTreeView</extends>
+ <header>widgets/expert_info_view.h</header>
+ </customwidget>
</customwidgets>
<resources/>
<connections>
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index 523ad5ee45..70f76614ed 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -3007,7 +3007,7 @@ void MainWindow::statCommandExpertInfo(const char *, void *)
expert_dialog->setDisplayFilter(df_edit->text());
- connect(expert_dialog, SIGNAL(goToPacket(int, int)),
+ connect(expert_dialog->getExpertInfoView(), SIGNAL(goToPacket(int, int)),
packet_list_, SLOT(goToPacket(int, int)));
connect(expert_dialog, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)),
this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)));
diff --git a/ui/qt/models/expert_info_model.cpp b/ui/qt/models/expert_info_model.cpp
new file mode 100644
index 0000000000..326a369290
--- /dev/null
+++ b/ui/qt/models/expert_info_model.cpp
@@ -0,0 +1,406 @@
+/* expert_info_model.cpp
+ * Data model for Expert Info tap data.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "expert_info_model.h"
+#include <ui/qt/utils/color_utils.h>
+
+#include "file.h"
+
+ExpertPacketItem::ExpertPacketItem(expert_info_t& expert_info, column_info *cinfo, ExpertPacketItem* parent) :
+ packet_num_(expert_info.packet_num),
+ group_(expert_info.group),
+ severity_(expert_info.severity),
+ hf_id_(expert_info.hf_index),
+ protocol_(expert_info.protocol),
+ summary_(expert_info.summary),
+ parentItem_(parent)
+{
+ if (cinfo) {
+ info_ = col_get_text(cinfo, COL_INFO);
+ }
+}
+
+ExpertPacketItem::~ExpertPacketItem()
+{
+}
+
+QString ExpertPacketItem::groupKey(bool group_by_summary, int severity, int group, QString protocol, int expert_hf)
+{
+ QString key = QString("%1|%2|%3")
+ .arg(severity)
+ .arg(group)
+ .arg(protocol);
+ if (group_by_summary) {
+ key += QString("|%1").arg(expert_hf);
+ }
+ return key;
+}
+
+QString ExpertPacketItem::groupKey(bool group_by_summary) {
+ return groupKey(group_by_summary, severity_, group_, protocol_, hf_id_);
+}
+
+void ExpertPacketItem::appendChild(ExpertPacketItem* child, QString hash)
+{
+ childItems_.append(child);
+ hashChild_[hash] = child;
+}
+
+ExpertPacketItem* ExpertPacketItem::child(int row)
+{
+ return childItems_.value(row);
+}
+
+ExpertPacketItem* ExpertPacketItem::child(QString hash)
+{
+ return hashChild_[hash];
+}
+
+int ExpertPacketItem::childCount() const
+{
+ return childItems_.count();
+}
+
+int ExpertPacketItem::row() const
+{
+ if (parentItem_)
+ return parentItem_->childItems_.indexOf(const_cast<ExpertPacketItem*>(this));
+
+ return 0;
+}
+
+ExpertPacketItem* ExpertPacketItem::parentItem()
+{
+ return parentItem_;
+}
+
+
+
+
+ExpertInfoModel::ExpertInfoModel(CaptureFile& capture_file, QObject *parent) :
+ QAbstractItemModel(parent),
+ capture_file_(capture_file),
+ group_by_summary_(true),
+ root_(createRootItem())
+{
+}
+
+void ExpertInfoModel::clear()
+{
+ emit beginResetModel();
+
+ eventCounts_.clear();
+ delete root_;
+ root_ = createRootItem();
+
+ emit endResetModel();
+}
+
+ExpertPacketItem* ExpertInfoModel::createRootItem()
+{
+ static const char* rootName = "ROOT";
+ static expert_info_t root_expert = { 0, -1, -1, -1, rootName, (gchar*)rootName, NULL };
+
+ return new ExpertPacketItem(root_expert, NULL, NULL);
+}
+
+
+
+int ExpertInfoModel::numEvents(enum ExpertSeverity severity)
+{
+ return eventCounts_[severity];
+}
+
+QModelIndex ExpertInfoModel::index(int row, int column, const QModelIndex& parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ ExpertPacketItem *parent_item, *child_item;
+
+ if (!parent.isValid())
+ parent_item = root_;
+ else
+ parent_item = static_cast<ExpertPacketItem*>(parent.internalPointer());
+
+ Q_ASSERT(parent_item);
+ if (group_by_summary_) {
+ //don't allow group layer
+ if (parent_item == root_) {
+ int row_count = 0;
+ ExpertPacketItem *grandchild_item;
+
+ for (int subrow = 0; subrow < parent_item->childCount(); subrow++) {
+ child_item = parent_item->child(subrow);
+ //summary children are always stored in first child of group
+ grandchild_item = child_item->child(0);
+
+ if (row_count+grandchild_item->childCount() > row) {
+ return createIndex(row, column, grandchild_item->child(row-row_count));
+ }
+ row_count += grandchild_item->childCount();
+ }
+
+ //shouldn't happen
+ return QModelIndex();
+ }
+
+ int root_level = 0;
+ ExpertPacketItem *item = parent_item;
+ while (item != root_)
+ {
+ root_level++;
+ item = item->parentItem();
+ }
+
+ if (root_level == 3) {
+ child_item = parent_item->child(row);
+ if (child_item) {
+ return createIndex(row, column, child_item);
+ }
+ }
+
+ } else {
+ child_item = parent_item->child(row);
+ if (child_item) {
+ //only allow 2 levels deep
+ if (((parent_item == root_) || (parent_item->parentItem() == root_)))
+ return createIndex(row, column, child_item);
+ }
+ }
+ return QModelIndex();
+}
+
+QModelIndex ExpertInfoModel::parent(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ ExpertPacketItem *item = static_cast<ExpertPacketItem*>(index.internalPointer());
+ ExpertPacketItem *parent_item = item->parentItem();
+
+ if (group_by_summary_)
+ {
+ //don't allow group layer
+ int root_level = 0;
+ item = parent_item;
+ while ((item != root_) && (item != NULL))
+ {
+ root_level++;
+ item = item->parentItem();
+ }
+
+ if (root_level == 3)
+ return createIndex(parent_item->row(), 0, parent_item);
+
+ } else {
+ if (parent_item == root_)
+ return QModelIndex();
+
+ return createIndex(parent_item->row(), 0, parent_item);
+ }
+
+ return QModelIndex();
+}
+
+#if 0
+Qt::ItemFlags ExpertInfoModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+
+ ExpertPacketItem* item = static_cast<ExpertPacketItem*>(index.internalPointer());
+ Qt::ItemFlags flags = QAbstractTableModel::flags(index);
+
+ //collapse???
+ return flags;
+}
+#endif
+
+QVariant ExpertInfoModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || role != Qt::DisplayRole)
+ return QVariant();
+
+ ExpertPacketItem* item = static_cast<ExpertPacketItem*>(index.internalPointer());
+ if (item == NULL)
+ return QVariant();
+
+ switch ((enum ExpertColumn)index.column()) {
+ case colSeverity:
+ return QString(val_to_str_const(item->severity(), expert_severity_vals, "Unknown"));
+ case colSummary:
+ if (index.parent().isValid())
+ {
+ if (group_by_summary_)
+ return item->colInfo().simplified();
+
+ return item->summary().simplified();
+ }
+ else
+ {
+ if (group_by_summary_)
+ return item->summary().simplified();
+ }
+ return QVariant();
+ case colGroup:
+ return QString(val_to_str_const(item->group(), expert_group_vals, "Unknown"));
+ case colProtocol:
+ return item->protocol();
+ case colCount:
+ if (!index.parent().isValid())
+ {
+ return item->childCount();
+ }
+ break;
+ case colPacket:
+ return item->packetNum();
+ case colHf:
+ return item->hfId();
+ default:
+ break;
+ }
+
+ return QVariant();
+}
+
+//GUI helpers
+void ExpertInfoModel::setGroupBySummary(bool group_by_summary)
+{
+ emit beginResetModel();
+ group_by_summary_ = group_by_summary;
+ emit endResetModel();
+}
+
+int ExpertInfoModel::rowCount(const QModelIndex &parent) const
+{
+ ExpertPacketItem *parent_item;
+ if (parent.column() > 0)
+ return 0;
+
+ if (!parent.isValid())
+ parent_item = root_;
+ else
+ parent_item = static_cast<ExpertPacketItem*>(parent.internalPointer());
+
+ if (group_by_summary_) {
+ int row_count = 0;
+
+ //don't allow group layer
+ if (parent_item == root_) {
+ ExpertPacketItem *child_item, *grandchild_item;
+
+ for (int row = 0; row < parent_item->childCount(); row++) {
+ child_item = parent_item->child(row);
+ grandchild_item = child_item->child(0);
+ row_count += grandchild_item->childCount();
+ }
+
+ return row_count;
+ }
+
+ return parent_item->childCount();
+
+ } else {
+ //only allow 2 levels deep
+ if ((parent_item == root_) || (parent_item->parentItem() == root_))
+ return parent_item->childCount();
+ }
+
+ return 0;
+}
+
+int ExpertInfoModel::columnCount(const QModelIndex& ) const
+{
+ return colLast;
+}
+
+void ExpertInfoModel::addExpertInfo(struct expert_info_s& expert_info)
+{
+ QString groupKey = ExpertPacketItem::groupKey(FALSE, expert_info.severity, expert_info.group, QString(expert_info.protocol), expert_info.hf_index);
+ QString summaryKey = ExpertPacketItem::groupKey(TRUE, expert_info.severity, expert_info.group, QString(expert_info.protocol), expert_info.hf_index);
+
+ ExpertPacketItem* expert_root = root_->child(groupKey);
+ if (expert_root == NULL) {
+ ExpertPacketItem *new_item = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), root_);
+
+ root_->appendChild(new_item, groupKey);
+
+ expert_root = new_item;
+ }
+
+ ExpertPacketItem *expert = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), expert_root);
+ expert_root->appendChild(expert, groupKey);
+
+ //add the summary children off of the first child of the root children
+ ExpertPacketItem* summary_root = expert_root->child(0);
+
+ //make a summary child
+ ExpertPacketItem* expert_summary_root = summary_root->child(summaryKey);
+ if (expert_summary_root == NULL) {
+ ExpertPacketItem *new_summary = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), summary_root);
+
+ summary_root->appendChild(new_summary, summaryKey);
+ expert_summary_root = new_summary;
+ }
+
+ ExpertPacketItem *expert_summary = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), expert_summary_root);
+ expert_summary_root->appendChild(expert_summary, summaryKey);
+}
+
+void ExpertInfoModel::tapReset(void *eid_ptr)
+{
+ ExpertInfoModel *model = static_cast<ExpertInfoModel*>(eid_ptr);
+ if (!model)
+ return;
+
+ model->clear();
+}
+
+gboolean ExpertInfoModel::tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data)
+{
+ ExpertInfoModel *model = static_cast<ExpertInfoModel*>(eid_ptr);
+ expert_info_t *expert_info = (expert_info_t *) data;
+ gboolean draw_required = FALSE;
+
+ if (!pinfo || !model || !expert_info)
+ return FALSE;
+
+ model->addExpertInfo(*expert_info);
+
+ if (model->numEvents((enum ExpertSeverity)expert_info->severity) < 1)
+ draw_required = TRUE;
+
+ model->eventCounts_[(enum ExpertSeverity)expert_info->severity]++;
+
+ return draw_required;
+}
+
+void ExpertInfoModel::tapDraw(void *eid_ptr)
+{
+ ExpertInfoModel *model = static_cast<ExpertInfoModel*>(eid_ptr);
+ if (!model)
+ return;
+
+ emit model->beginResetModel();
+ emit model->endResetModel();
+}
+
+/* * 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:
+ */
diff --git a/ui/qt/models/expert_info_model.h b/ui/qt/models/expert_info_model.h
new file mode 100644
index 0000000000..471cd6700a
--- /dev/null
+++ b/ui/qt/models/expert_info_model.h
@@ -0,0 +1,140 @@
+/* expert_info_model.h
+ * Data model for Expert Info tap data.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef EXPERT_INFO_MODEL_H
+#define EXPERT_INFO_MODEL_H
+
+#include <config.h>
+
+#include <QAbstractItemModel>
+#include <QList>
+#include <QMap>
+
+#include <ui/qt/capture_file.h>
+
+#include <epan/expert.h>
+#include <epan/column-utils.h>
+
+class ExpertPacketItem
+{
+public:
+ ExpertPacketItem(expert_info_t& expert_info, column_info *cinfo, ExpertPacketItem* parent);
+ virtual ~ExpertPacketItem();
+
+ unsigned int packetNum() const { return packet_num_; }
+ int group() const { return group_; }
+ int severity() const { return severity_; }
+ int hfId() const { return hf_id_; }
+ QString protocol() const { return protocol_; }
+ QString summary() const { return summary_; }
+ QString colInfo() const { return info_; }
+
+ static QString groupKey(bool group_by_summary, int severity, int group, QString protocol, int expert_hf);
+ QString groupKey(bool group_by_summary);
+
+ void appendChild(ExpertPacketItem* child, QString hash);
+ ExpertPacketItem* child(int row);
+ ExpertPacketItem* child(QString hash);
+ int childCount() const;
+ int row() const;
+ ExpertPacketItem* parentItem();
+
+private:
+ unsigned int packet_num_;
+ int group_;
+ int severity_;
+ int hf_id_;
+ // Half-hearted attempt at conserving memory. If this isn't sufficient,
+ // PacketListRecord interns column strings in a GStringChunk.
+ QByteArray protocol_;
+ QByteArray summary_;
+ QByteArray info_;
+
+ QList<ExpertPacketItem*> childItems_;
+ ExpertPacketItem* parentItem_;
+ QHash<QString, ExpertPacketItem*> hashChild_; //optimization for insertion
+};
+
+class ExpertInfoModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ ExpertInfoModel(CaptureFile& capture_file, QObject *parent = 0);
+
+ enum ExpertColumn {
+ colSeverity = 0,
+ colSummary,
+ colGroup,
+ colProtocol,
+ colCount,
+ colPacket,
+ colHf,
+ colLast
+ };
+
+ enum ExpertSeverity {
+ severityError = PI_ERROR,
+ severityWarn = PI_WARN,
+ severityNote = PI_NOTE,
+ severityChat = PI_CHAT,
+ severityComment = PI_COMMENT
+ };
+
+ QModelIndex index(int row, int column,
+ const QModelIndex & = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &) const;
+#if 0
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+#endif
+ QVariant data(const QModelIndex &index, int role) const;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ int numEvents(enum ExpertSeverity severity);
+
+ void clear();
+
+ //GUI helpers
+ void setGroupBySummary(bool group_by_summary);
+
+ // Called from tapPacket
+ void addExpertInfo(struct expert_info_s& expert_info);
+
+ // Callbacks for register_tap_listener
+ static void tapReset(void *eid_ptr);
+ static gboolean tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data);
+ static void tapDraw(void *eid_ptr);
+
+private:
+ CaptureFile& capture_file_;
+
+ ExpertPacketItem* createRootItem();
+
+ bool group_by_summary_;
+ ExpertPacketItem* root_;
+
+ QHash<enum ExpertSeverity, int> eventCounts_;
+};
+#endif // EXPERT_INFO_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:
+ */
diff --git a/ui/qt/models/expert_info_proxy_model.cpp b/ui/qt/models/expert_info_proxy_model.cpp
new file mode 100644
index 0000000000..68a1faf11f
--- /dev/null
+++ b/ui/qt/models/expert_info_proxy_model.cpp
@@ -0,0 +1,278 @@
+/* expert_info_model.cpp
+ * Data model for Expert Info tap data.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <ui/qt/models/expert_info_model.h>
+#include <ui/qt/models/expert_info_proxy_model.h>
+#include <ui/qt/utils/color_utils.h>
+
+ExpertInfoProxyModel::ExpertInfoProxyModel(QObject *parent) : QSortFilterProxyModel(parent),
+ severityMode_(Group)
+{
+}
+
+bool ExpertInfoProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ ExpertPacketItem *left_item,
+ *right_item;
+ QString leftStr, rightStr;
+ bool checkPacketNumber = false;
+ int compare_ret;
+
+ if (source_left.parent().isValid() && source_right.parent().isValid()) {
+ left_item = static_cast<ExpertPacketItem*>(source_left.parent().internalPointer());
+ right_item = static_cast<ExpertPacketItem*>(source_right.parent().internalPointer());
+ } else {
+ left_item = static_cast<ExpertPacketItem*>(source_left.internalPointer()),
+ right_item = static_cast<ExpertPacketItem*>(source_right.internalPointer());
+ }
+
+ if ((left_item != NULL) && (right_item != NULL)) {
+ switch (source_left.column())
+ {
+ case colProxySeverity:
+ if (left_item->severity() != right_item->severity()) {
+ return (left_item->severity() < right_item->severity());
+ }
+
+ checkPacketNumber = true;
+ break;
+ case colProxySummary:
+ compare_ret = left_item->summary().compare(right_item->summary());
+ if (compare_ret < 0)
+ return true;
+ if (compare_ret > 0)
+ return false;
+
+ checkPacketNumber = true;
+ break;
+ case colProxyGroup:
+ if (left_item->group() != right_item->group()) {
+ return (left_item->group() < right_item->group());
+ }
+
+ checkPacketNumber = true;
+ break;
+ case colProxyProtocol:
+ compare_ret = left_item->protocol().compare(right_item->protocol());
+ if (compare_ret < 0)
+ return true;
+ if (compare_ret > 0)
+ return false;
+
+ checkPacketNumber = true;
+ break;
+ case colProxyCount:
+ break;
+ default:
+ break;
+ }
+
+ if (checkPacketNumber) {
+ return (left_item->packetNum() < right_item->packetNum());
+ }
+ }
+
+ // fallback to string cmp on other fields
+ return QSortFilterProxyModel::lessThan(source_left, source_right);
+}
+
+QVariant ExpertInfoProxyModel::data(const QModelIndex &proxy_index, int role) const
+{
+ QModelIndex source_index;
+
+ switch (role)
+ {
+ case Qt::BackgroundRole:
+ {
+ source_index = mapToSource(proxy_index);
+
+ //only color base row
+ if (!source_index.isValid() || source_index.parent().isValid())
+ return QVariant();
+
+ ExpertPacketItem* item = static_cast<ExpertPacketItem*>(source_index.internalPointer());
+ if (item == NULL)
+ return QVariant();
+
+ // provide background color for groups
+ switch(item->severity()) {
+ case(PI_COMMENT):
+ return QBrush(ColorUtils::expert_color_comment);
+ case(PI_CHAT):
+ return QBrush(ColorUtils::expert_color_chat);
+ case(PI_NOTE):
+ return QBrush(ColorUtils::expert_color_note);
+ case(PI_WARN):
+ return QBrush(ColorUtils::expert_color_warn);
+ case(PI_ERROR):
+ return QBrush(ColorUtils::expert_color_error);
+ }
+ }
+ break;
+ case Qt::ForegroundRole:
+ // XXX Use plain colors until our users demand to be blinded.
+ return QBrush(ColorUtils::expert_color_foreground);
+ case Qt::TextAlignmentRole:
+ switch (proxy_index.column())
+ {
+ case colProxySeverity:
+ //packet number should be right aligned
+ if (source_index.parent().isValid())
+ return Qt::AlignRight;
+ break;
+ case colProxyCount:
+ return Qt::AlignRight;
+ default:
+ break;
+ }
+ return Qt::AlignLeft;
+
+ case Qt::DisplayRole:
+ source_index = mapToSource(proxy_index);
+
+ switch (proxy_index.column())
+ {
+ case colProxySeverity:
+ if (source_index.parent().isValid())
+ return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colPacket), role);
+
+ return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colSeverity), role);
+ case colProxySummary:
+ return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colSummary), role);
+ case colProxyGroup:
+ return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colGroup), role);
+ case colProxyProtocol:
+ return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colProtocol), role);
+ case colProxyCount:
+ //only show counts for parent
+ if (!source_index.parent().isValid()) {
+ //because of potential filtering, count is computed manually
+ unsigned int count = 0;
+ ExpertPacketItem *child_item,
+ *item = static_cast<ExpertPacketItem*>(source_index.internalPointer());
+ for (int row = 0; row < item->childCount(); row++) {
+ child_item = item->child(row);
+ if (child_item == NULL)
+ continue;
+ if (filterAcceptItem(*child_item))
+ count++;
+ }
+
+ return count;
+ }
+ }
+ break;
+ }
+
+ return QSortFilterProxyModel::data(proxy_index, role);
+}
+
+QVariant ExpertInfoProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+
+ switch ((enum ExpertProxyColumn)section) {
+ case colProxySeverity:
+ if (severityMode_ == Packet)
+ return tr("Packet");
+ else
+ return tr("Severity");
+ case colProxySummary:
+ return tr("Summary");
+ case colProxyGroup:
+ return tr("Group");
+ case colProxyProtocol:
+ return tr("Protocol");
+ case colProxyCount:
+ return tr("Count");
+ default:
+ break;
+ }
+ }
+ return QVariant();
+}
+
+int ExpertInfoProxyModel::columnCount(const QModelIndex& ) const
+{
+ return colProxyLast;
+}
+
+bool ExpertInfoProxyModel::filterAcceptItem(ExpertPacketItem& item) const
+{
+ if (hidden_severities_.contains(item.severity()))
+ return false;
+
+ if (!textFilter_.isEmpty()) {
+ QRegExp regex(textFilter_, Qt::CaseInsensitive);
+
+ if (item.protocol().contains(regex))
+ return true;
+
+ if (item.summary().contains(regex))
+ return true;
+
+ if (item.colInfo().contains(regex))
+ return true;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ExpertInfoProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ QModelIndex severityIdx = sourceModel()->index(sourceRow, ExpertInfoModel::colSeverity, sourceParent);
+ ExpertPacketItem* item = static_cast<ExpertPacketItem*>(severityIdx.internalPointer());
+ if (item == NULL)
+ return true;
+
+ return filterAcceptItem(*item);
+}
+
+//GUI helpers
+void ExpertInfoProxyModel::setSeverityMode(enum SeverityMode mode)
+{
+ severityMode_ = mode;
+ emit headerDataChanged(Qt::Vertical, 0, 1);
+}
+
+void ExpertInfoProxyModel::setSeverityFilter(int severity, bool hide)
+{
+ if (hide)
+ {
+ hidden_severities_ << severity;
+ }
+ else
+ {
+ hidden_severities_.removeOne(severity);
+ }
+
+ invalidateFilter();
+}
+
+void ExpertInfoProxyModel::setSummaryFilter(const QString &filter)
+{
+ textFilter_ = filter;
+ invalidateFilter();
+}
+
+
+/* * 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:
+ */
diff --git a/ui/qt/models/expert_info_proxy_model.h b/ui/qt/models/expert_info_proxy_model.h
new file mode 100644
index 0000000000..3926e0e9e5
--- /dev/null
+++ b/ui/qt/models/expert_info_proxy_model.h
@@ -0,0 +1,73 @@
+/* expert_info_model.h
+ * Data model for Expert Info tap data.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef EXPERT_INFO_PROXY_MODEL_H
+#define EXPERT_INFO_PROXY_MODEL_H
+
+#include <config.h>
+
+#include <QSortFilterProxyModel>
+
+class ExpertPacketItem;
+
+class ExpertInfoProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ ExpertInfoProxyModel(QObject *parent = 0);
+
+ enum SeverityMode { Group, Packet };
+ enum ExpertProxyColumn {
+ colProxySeverity = 0,
+ colProxySummary,
+ colProxyGroup,
+ colProxyProtocol,
+ colProxyCount,
+ colProxyLast
+ };
+
+ QVariant data(const QModelIndex &index, int role) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
+
+ //GUI helpers
+ void setSeverityMode(enum SeverityMode);
+ void setSeverityFilter(int severity, bool hide);
+ void setSummaryFilter(const QString &filter);
+
+protected:
+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
+ bool filterAcceptItem(ExpertPacketItem& item) const;
+
+ enum SeverityMode severityMode_;
+ QList<int> hidden_severities_;
+
+ QString textFilter_;
+
+};
+
+#endif // EXPERT_INFO_PROXY_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:
+ */
diff --git a/ui/qt/widgets/expert_info_view.cpp b/ui/qt/widgets/expert_info_view.cpp
new file mode 100644
index 0000000000..1436ee23a4
--- /dev/null
+++ b/ui/qt/widgets/expert_info_view.cpp
@@ -0,0 +1,55 @@
+/* expert_info_view.cpp
+ * Tree view of Expert Info data.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "expert_info_view.h"
+#include <ui/qt/models/expert_info_model.h>
+#include <ui/qt/models/expert_info_proxy_model.h>
+
+#include <QHeaderView>
+
+ExpertInfoTreeView::ExpertInfoTreeView(QWidget *parent) : QTreeView(parent)
+{
+}
+
+void ExpertInfoTreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+ if (current.isValid())
+ {
+ if (current.parent().isValid()) {
+ ((ExpertInfoProxyModel*)model())->setSeverityMode(ExpertInfoProxyModel::Packet);
+ } else {
+ ((ExpertInfoProxyModel*)model())->setSeverityMode(ExpertInfoProxyModel::Group);
+ }
+
+ QModelIndex model_index = ((ExpertInfoProxyModel*)model())->mapToSource(current);
+
+ if (model_index.parent().isValid()) {
+ ExpertPacketItem* currentItem = static_cast<ExpertPacketItem*>(model_index.internalPointer());
+ if (currentItem != NULL)
+ {
+ emit goToPacket(currentItem->packetNum(), currentItem->hfId());
+ }
+ }
+ }
+
+ QTreeView::currentChanged(current, previous);
+}
+
+/* * 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:
+ */
diff --git a/ui/qt/widgets/expert_info_view.h b/ui/qt/widgets/expert_info_view.h
new file mode 100644
index 0000000000..7662cf330d
--- /dev/null
+++ b/ui/qt/widgets/expert_info_view.h
@@ -0,0 +1,42 @@
+/* expert_info_view.h
+ * Tree view of Expert Info data.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef EXPERT_INFO_VIEW_H
+#define EXPERT_INFO_VIEW_H
+
+#include <config.h>
+#include <QTreeView>
+
+class ExpertInfoTreeView : public QTreeView
+{
+ Q_OBJECT
+public:
+ ExpertInfoTreeView(QWidget *parent = 0);
+
+signals:
+ void goToPacket(int packet_num, int hf_id);
+
+protected slots:
+ void currentChanged(const QModelIndex &current, const QModelIndex &previous);
+};
+#endif // EXPERT_INFO_VIEW_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:
+ */