diff options
-rw-r--r-- | ui/qt/CMakeLists.txt | 6 | ||||
-rw-r--r-- | ui/qt/Makefile.am | 6 | ||||
-rw-r--r-- | ui/qt/expert_info_dialog.cpp | 549 | ||||
-rw-r--r-- | ui/qt/expert_info_dialog.h | 54 | ||||
-rw-r--r-- | ui/qt/expert_info_dialog.ui | 54 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 2 | ||||
-rw-r--r-- | ui/qt/models/expert_info_model.cpp | 406 | ||||
-rw-r--r-- | ui/qt/models/expert_info_model.h | 140 | ||||
-rw-r--r-- | ui/qt/models/expert_info_proxy_model.cpp | 278 | ||||
-rw-r--r-- | ui/qt/models/expert_info_proxy_model.h | 73 | ||||
-rw-r--r-- | ui/qt/widgets/expert_info_view.cpp | 55 | ||||
-rw-r--r-- | ui/qt/widgets/expert_info_view.h | 42 |
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 ¤t, 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 ¤t, 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: + */ |