aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Mann <mmann78@netscape.net>2017-06-28 20:00:38 -0400
committerRoland Knall <rknall@gmail.com>2017-12-13 16:07:07 +0000
commitb0112e60ada68646b3e304f73f8321f5fb1ddc01 (patch)
tree4b7e3e2503ba90cbfda0c10ccddb783afe43143b
parentf945b85391976bc59da4b542fe84cb58033bd22e (diff)
Add a model to use for Expert Info dialog.
With the model (and proxy), the following features were added/fixed. 1. Expert severities can be filtered by type 2. Search filter expanded to include summary, protocol and column info 3. Expert info starts with all items collapsed. 4. Context menus for collapse/expand all Bug: 11753 Bug: 13831 Bug: 13842 Change-Id: I8e89c7be441e1f08e18915ef8805609e5c5d0bd1 Reviewed-on: https://code.wireshark.org/review/22458 Reviewed-by: Michael Mann <mmann78@netscape.net> Petri-Dish: Michael Mann <mmann78@netscape.net> Tested-by: Petri Dish Buildbot Reviewed-by: Roland Knall <rknall@gmail.com>
-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 4934fd4..bb8d027 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 b739654..b9d21eb 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 b099e56..84ecd0c 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 2128b78..e3fa1b5 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 1e4074d..ade2369 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 523ad5e..70f7661 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 0000000..326a369
--- /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 0000000..471cd67
--- /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 0000000..68a1faf
--- /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 0000000..3926e0e
--- /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 0000000..1436ee2
--- /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 0000000..7662cf3
--- /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:
+ */