diff options
author | Gerald Combs <gerald@wireshark.org> | 2015-03-11 15:22:20 -0700 |
---|---|---|
committer | Anders Broman <a.broman58@gmail.com> | 2015-05-08 04:29:59 +0000 |
commit | f77e02ccc62c9283eefba416efbb2f94e460066e (patch) | |
tree | 40aa62ed6f8c03e22cdc636e2335f3b810810b65 /ui | |
parent | 3d7ff97e4fa9211a064acbac0ec0c025a164d69e (diff) |
Expert Info dialog.
Show all expert messages in a combined view. Group top-level items by a
(severity, group, protocol) tuple.
Let the user enable and disable messages via a check menu.
Add ProtoTree::goToField and expert_info_t.hf_index. Use them to jump to
what we hope is the afflicted item.
Enable the context menu only if the user has selected a packet item.
Add a free-form search field that matches expert summaries.
This differs from the GTK+ version but hopefully provides a smoother
workflow.
Bug: 10931
Change-Id: Ia12cb7c27cdea1634fa2798fb7e4c1b23bd16ad2
Reviewed-on: https://code.wireshark.org/review/8294
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'ui')
-rw-r--r-- | ui/gtk/expert_comp_dlg.c | 2 | ||||
-rw-r--r-- | ui/gtk/expert_comp_table.c | 2 | ||||
-rw-r--r-- | ui/qt/CMakeLists.txt | 3 | ||||
-rw-r--r-- | ui/qt/Makefile.am | 2 | ||||
-rw-r--r-- | ui/qt/Makefile.common | 4 | ||||
-rw-r--r-- | ui/qt/Wireshark.pro | 3 | ||||
-rw-r--r-- | ui/qt/color_utils.cpp | 8 | ||||
-rw-r--r-- | ui/qt/color_utils.h | 9 | ||||
-rw-r--r-- | ui/qt/display_filter_edit.h | 1 | ||||
-rw-r--r-- | ui/qt/expert_info_dialog.cpp | 581 | ||||
-rw-r--r-- | ui/qt/expert_info_dialog.h | 116 | ||||
-rw-r--r-- | ui/qt/expert_info_dialog.ui | 221 | ||||
-rw-r--r-- | ui/qt/main_status_bar.cpp | 5 | ||||
-rw-r--r-- | ui/qt/main_status_bar.h | 1 | ||||
-rw-r--r-- | ui/qt/main_window.cpp | 3 | ||||
-rw-r--r-- | ui/qt/main_window.h | 2 | ||||
-rw-r--r-- | ui/qt/main_window.ui | 10 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 28 | ||||
-rw-r--r-- | ui/qt/packet_list.cpp | 6 | ||||
-rw-r--r-- | ui/qt/packet_list.h | 1 | ||||
-rw-r--r-- | ui/qt/proto_tree.cpp | 41 | ||||
-rw-r--r-- | ui/qt/proto_tree.h | 1 |
22 files changed, 1031 insertions, 19 deletions
diff --git a/ui/gtk/expert_comp_dlg.c b/ui/gtk/expert_comp_dlg.c index 4406b83892..0b34b612f9 100644 --- a/ui/gtk/expert_comp_dlg.c +++ b/ui/gtk/expert_comp_dlg.c @@ -1005,7 +1005,7 @@ expert_comp_dlg_launch(void) static stat_tap_ui expert_comp_ui = { REGISTER_STAT_GROUP_GENERIC, NULL, - "expert_comp", + "expert", expert_comp_init, 0, NULL diff --git a/ui/gtk/expert_comp_table.c b/ui/gtk/expert_comp_table.c index 0eaf2418ea..7fb49d8c5b 100644 --- a/ui/gtk/expert_comp_table.c +++ b/ui/gtk/expert_comp_table.c @@ -318,7 +318,7 @@ error_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint call break; case ACTION_WEB_LOOKUP: /* Lookup expert string on internet. Default search via www.google.com */ - g_snprintf(str, sizeof(str), "http://www.google.com/search?hl=en&q=%s+'%s'", procedure->entries[0], procedure->entries[1]); + g_snprintf(str, sizeof(str), "https://www.google.com/search?hl=en&q=%s+'%s'", procedure->entries[0], procedure->entries[1]); browser_open_url(str); break; case ACTION_COPY: diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index e52e803185..821aaf1837 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -47,6 +47,7 @@ set(WIRESHARK_QT_HEADERS display_filter_edit.h elided_label.h endpoint_dialog.h + expert_info_dialog.h export_dissection_dialog.h export_object_dialog.h export_pdu_dialog.h @@ -157,6 +158,7 @@ set(WIRESHARK_QT_SRC display_filter_combo.cpp display_filter_edit.cpp elided_label.cpp + expert_info_dialog.cpp export_dissection_dialog.cpp export_object_dialog.cpp export_pdu_dialog.cpp @@ -262,6 +264,7 @@ set(WIRESHARK_QT_UI column_editor_frame.ui compiled_filter_output.ui decode_as_dialog.ui + expert_info_dialog.ui export_object_dialog.ui export_pdu_dialog.ui file_set_dialog.ui diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index 372ed6f675..d29a8235c3 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -140,6 +140,8 @@ compiled_filter_output.cpp compiled_filter_output.h: ui_compiled_filter_output.h decode_as_dialog.cpp decode_as_dialog.h: ui_decode_as_dialog.h +expert_info_dialog.cpp expert_info_dialog.h: ui_expert_info_dialog.h + export_object_dialog.cpp export_object_dialog.h: ui_export_object_dialog.h export_pdu_dialog.cpp export_pdu_dialog.h: ui_export_pdu_dialog.h diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index bd5da843d9..b9a2705c73 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -39,6 +39,7 @@ NODIST_GENERATED_HEADER_FILES = \ ui_column_editor_frame.h \ ui_compiled_filter_output.h \ ui_decode_as_dialog.h \ + ui_expert_info_dialog.h \ ui_export_object_dialog.h \ ui_export_pdu_dialog.h \ ui_extcap_options_dialog.h \ @@ -144,6 +145,7 @@ MOC_HDRS = \ display_filter_edit.h \ elided_label.h \ endpoint_dialog.h \ + expert_info_dialog.h \ export_dissection_dialog.h \ export_object_dialog.h \ export_pdu_dialog.h \ @@ -226,6 +228,7 @@ UI_FILES = \ column_editor_frame.ui \ compiled_filter_output.ui \ decode_as_dialog.ui \ + expert_info_dialog.ui \ export_object_dialog.ui \ export_pdu_dialog.ui \ extcap_options_dialog.ui \ @@ -347,6 +350,7 @@ WIRESHARK_QT_SRC = \ display_filter_edit.cpp \ elided_label.cpp \ endpoint_dialog.cpp \ + expert_info_dialog.cpp \ export_dissection_dialog.cpp \ export_object_dialog.cpp \ export_pdu_dialog.cpp \ diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index ec80b40281..6cfe6088db 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -215,6 +215,7 @@ FORMS += \ column_editor_frame.ui \ compiled_filter_output.ui \ decode_as_dialog.ui \ + expert_info_dialog.ui \ export_object_dialog.ui \ export_pdu_dialog.ui \ extcap_options_dialog.ui \ @@ -277,6 +278,7 @@ HEADERS += $$HEADERS_WS_C \ decode_as_dialog.h \ elided_label.h \ endpoint_dialog.h \ + expert_info_dialog.h \ export_dissection_dialog.h \ export_object_dialog.h \ export_pdu_dialog.h \ @@ -628,6 +630,7 @@ SOURCES += \ display_filter_edit.cpp \ elided_label.cpp \ endpoint_dialog.cpp \ + expert_info_dialog.cpp \ export_dissection_dialog.cpp \ export_object_dialog.cpp \ export_pdu_dialog.cpp \ diff --git a/ui/qt/color_utils.cpp b/ui/qt/color_utils.cpp index e6d7f31082..70babce6e8 100644 --- a/ui/qt/color_utils.cpp +++ b/ui/qt/color_utils.cpp @@ -45,6 +45,14 @@ initialize_color(color_t *color, guint16 red, guint16 green, guint16 blue) return TRUE; } +const QColor ColorUtils::expert_color_comment = QColor ( 0xb7, 0xf7, 0x74 ); /* Green */ +const QColor ColorUtils::expert_color_chat = QColor ( 0x80, 0xb7, 0xf7 ); /* Light blue */ +const QColor ColorUtils::expert_color_note = QColor ( 0xa0, 0xff, 0xff ); /* Bright turquoise */ +const QColor ColorUtils::expert_color_warn = QColor ( 0xf7, 0xf2, 0x53 ); /* Yellow */ +const QColor ColorUtils::expert_color_error = QColor ( 0xff, 0x5c, 0x5c ); /* Pale red */ +const QColor ColorUtils::expert_color_foreground = QColor ( 0x00, 0x00, 0x00 ); /* Black */ +const QColor ColorUtils::hidden_proto_item = QColor ( 0x44, 0x44, 0x44 ); /* Gray */ + ColorUtils::ColorUtils(QObject *parent) : QObject(parent) { diff --git a/ui/qt/color_utils.h b/ui/qt/color_utils.h index aa74adcfaf..6d5a872106 100644 --- a/ui/qt/color_utils.h +++ b/ui/qt/color_utils.h @@ -44,6 +44,15 @@ public: static QRgb alphaBlend(const QColor &color1, const QColor &color2, qreal alpha); static QRgb alphaBlend(const QBrush &brush1, const QBrush &brush2, qreal alpha); + // ...because they don't really fit anywhere else? + static const QColor expert_color_comment; /* green */ + static const QColor expert_color_chat; /* light blue */ + static const QColor expert_color_note; /* bright turquoise */ + static const QColor expert_color_warn; /* yellow */ + static const QColor expert_color_error; /* pale red */ + static const QColor expert_color_foreground; /* black */ + static const QColor hidden_proto_item; /* gray */ + signals: public slots: diff --git a/ui/qt/display_filter_edit.h b/ui/qt/display_filter_edit.h index 74f373a09f..1648b78bb0 100644 --- a/ui/qt/display_filter_edit.h +++ b/ui/qt/display_filter_edit.h @@ -61,7 +61,6 @@ signals: void pushFilterSyntaxWarning(const QString&); void filterPackets(QString& new_filter, bool force); void addBookmark(QString filter); - }; #endif // DISPLAYFILTEREDIT_H diff --git a/ui/qt/expert_info_dialog.cpp b/ui/qt/expert_info_dialog.cpp new file mode 100644 index 0000000000..98df85251f --- /dev/null +++ b/ui/qt/expert_info_dialog.cpp @@ -0,0 +1,581 @@ +/* expert_info_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "expert_info_dialog.h" +#include "ui_expert_info_dialog.h" + +#include "file.h" + +#include <epan/expert.h> +#include <epan/stat_tap_ui.h> +#include <epan/tap.h> + +#include "color_utils.h" +#include "wireshark_application.h" + +#include <QAction> +#include <QHash> +#include <QMenu> +#include <QMessageBox> +#include <QPushButton> +#include <QTreeWidgetItemIterator> + +// To do: +// - Test with custom expert levels (Preferences -> Protocols -> Expert). +// - Figure out why the expert level prefs are buried under "Protocols". +// - Test with large captures. Add a custom model if needed. +// - 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. + +const int severity_col_ = 0; +const int group_col_ = 1; +const int protocol_col_ = 2; +const int count_col_ = 3; + +const int packet_col_ = 0; + +const int group_type_ = 1000; +const int packet_type_ = 1001; + +const int auto_expand_threshold_ = 20; // Arbitrary + +class ExpertGroupTreeWidgetItem : public QTreeWidgetItem +{ +public: + ExpertGroupTreeWidgetItem(QTreeWidget *parent, int severity, 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")); + setText(group_col_, val_to_str_const(group, expert_group_vals, "Unknown")); + setText(protocol_col_, protocol); + setText(count_col_, "0"); + } + 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(QTreeWidgetItem *parent, expert_info_t *expert_info = NULL) : + QTreeWidgetItem (parent, packet_type_), + packet_num_(0), + hf_id_(-1) + { + if (expert_info) { + packet_num_ = expert_info->packet_num; + hf_id_ = expert_info->hf_index; + protocol_ = expert_info->protocol; + summary_ = expert_info->summary; + } + setFirstColumnSpanned(true); + setText(packet_col_, QString("%1: %2") + .arg(packet_num_) + .arg(summary_)); + } + guint32 packetNum() const { return packet_num_; } + int hfId() const { return hf_id_; } + QString protocol() const { return protocol_; } + QString summary() const { return summary_; } + 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: + guint32 packet_num_; + int hf_id_; + QString protocol_; + QString summary_; +}; + +ExpertInfoDialog::ExpertInfoDialog(QWidget &parent, CaptureFile &capture_file) : + WiresharkDialog(parent, capture_file), + ui(new Ui::ExpertInfoDialog), + need_show_hide_(false), + display_filter_(QString()) +{ + ui->setupUi(this); + + setWindowSubtitle(tr("Expert Information")); + + // Clicking on an item jumps to its associated packet. Make the dialog + // narrow so that we avoid obscuring the packet list. + // XXX Use recent settings instead + int dlg_width = parent.width() / 2; + if (dlg_width < width()) dlg_width = width(); + resize(dlg_width, parent.height()); + + 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(); + + // 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->expertInfoTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->expertInfoTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showProtoHierMenu(QPoint))); + + QMenu *submenu; + + FilterAction::Action cur_action = FilterAction::ActionApply; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + cur_action = FilterAction::ActionPrepare; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + FilterAction *fa; + QList<FilterAction::Action> extra_actions = + QList<FilterAction::Action>() << FilterAction::ActionFind + << FilterAction::ActionColorize + << FilterAction::ActionWebLookup + << FilterAction::ActionCopy; + + foreach (FilterAction::Action extra_action, extra_actions) { + fa = new FilterAction(&ctx_menu_, extra_action); + ctx_menu_.addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + setDisplayFilter(); + retapPackets(); + +} + +ExpertInfoDialog::~ExpertInfoDialog() +{ + delete ui; +} + +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(); +} + +void ExpertInfoDialog::setDisplayFilter(const QString &display_filter) +{ + display_filter_ = display_filter; + updateWidgets(); +} + +void ExpertInfoDialog::retapPackets() +{ + if (file_closed_) return; + + clearAllData(); + remove_tap_listener(this); + + GString *error_string = register_tap_listener("expert", + this, + NULL, + TL_REQUIRES_NOTHING, + tapReset, + tapPacket, + tapDraw); + if (error_string) { + QMessageBox::warning(this, tr("Endpoint expert failed to register tap listener"), + error_string->str); + g_string_free(error_string, TRUE); + return; + } + + if (ui->limitCheckBox->isChecked()) { + 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(); + + updateWidgets(); + + 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(struct expert_info_s *expert_info) +{ + if (!expert_info) return; + QTreeWidgetItem *group_ti; + + QString key = QString("%1|%2|%3") + .arg(expert_info->severity) + .arg(expert_info->group) + .arg(expert_info->protocol); + + QColor background; + switch(expert_info->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 (ei_to_ti_.contains(key)) { + group_ti = ei_to_ti_[key]; + } else { + group_ti = new ExpertGroupTreeWidgetItem(ui->expertInfoTreeWidget, expert_info->severity, expert_info->group, expert_info->protocol); + 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; + } + + new ExpertPacketTreeWidgetItem(group_ti, expert_info); + + // 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::updateCounts() +{ + for (int i = 0; i < ui->expertInfoTreeWidget->topLevelItemCount(); i++) { + QTreeWidgetItem *group_ti = ui->expertInfoTreeWidget->topLevelItem(i); + group_ti->setText(count_col_, QString::number(group_ti->childCount())); + } +} + +void ExpertInfoDialog::tapReset(void *eid_ptr) +{ + ExpertInfoDialog *eid = static_cast<ExpertInfoDialog *>(eid_ptr); + if (!eid) return; + + eid->clearAllData(); +} + +gboolean ExpertInfoDialog::tapPacket(void *eid_ptr, packet_info *pinfo, epan_dissect_t *, 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 0; + + 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->updateWidgets(); +} + +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(); + + QString tooltip; + QString hint; + + if (file_closed_) { + tooltip = tr("Capture file closed."); + hint = tr("Capture file closed."); + } else if (display_filter_.isEmpty()) { + tooltip = tr("No display filter"); + hint = tr("No display filter set."); + } else { + tooltip = tr("Limit information to \"%1\".").arg(display_filter_); + hint = tr("Display filter: \"%1\"").arg(display_filter_); + } + + ui->limitCheckBox->setToolTip(tooltip); + hint.prepend("<small><i>"); + hint.append("</i></small>"); + ui->hintLabel->setText(hint); + +} + +void ExpertInfoDialog::actionShowToggled() +{ + need_show_hide_ = true; + updateWidgets(); +} + +void ExpertInfoDialog::showProtoHierMenu(QPoint pos) +{ + bool enable = true; + ExpertPacketTreeWidgetItem *packet_ti = dynamic_cast<ExpertPacketTreeWidgetItem *>(ui->expertInfoTreeWidget->currentItem()); + if (!packet_ti || packet_ti->hfId() < 0) { + enable = false; + } + + foreach (QMenu *submenu, ctx_menu_.findChildren<QMenu*>()) { + submenu->setEnabled(enable && !file_closed_); + } + foreach (QAction *action, ctx_menu_.actions()) { + FilterAction *fa = qobject_cast<FilterAction *>(action); + bool action_enable = enable && !file_closed_; + if (fa && (fa->action() == FilterAction::ActionWebLookup || fa->action() == FilterAction::ActionCopy)) { + action_enable = enable; + } + action->setEnabled(action_enable); + } + + ctx_menu_.popup(ui->expertInfoTreeWidget->viewport()->mapToGlobal(pos)); +} + +void ExpertInfoDialog::filterActionTriggered() +{ + ExpertPacketTreeWidgetItem *packet_ti = dynamic_cast<ExpertPacketTreeWidgetItem *>(ui->expertInfoTreeWidget->currentItem()); + FilterAction *fa = qobject_cast<FilterAction *>(QObject::sender()); + + if (!fa || !packet_ti) { + return; + } + + int hf_index = packet_ti->hfId(); + 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()); + } else if (fa->action() == FilterAction::ActionCopy) { + filter_string = QString("%1 %2: %3") + .arg(packet_ti->packetNum()) + .arg(packet_ti->protocol()) + .arg(packet_ti->summary()); + } else { + filter_string = proto_registrar_get_abbrev(hf_index); + } + + if (! filter_string.isEmpty()) { + emit filterAction(filter_string, fa->action(), fa->actionType()); + } + } +} + +void ExpertInfoDialog::captureFileClosing() +{ + remove_tap_listener(this); + WiresharkDialog::captureFileClosing(); +} + +void ExpertInfoDialog::on_expertInfoTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *) +{ + // 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()); +} + +void ExpertInfoDialog::on_limitCheckBox_toggled(bool) +{ + retapPackets(); +} + +// Show child (packet list) items that match the contents of searchLineEdit. +void ExpertInfoDialog::on_searchLineEdit_textChanged(const QString &search_re) +{ + QTreeWidgetItemIterator it(ui->expertInfoTreeWidget, QTreeWidgetItemIterator::NoChildren); + QRegExp regex(search_re, Qt::CaseInsensitive); + while (*it) { + bool hidden = true; + if (search_re.isEmpty() || (*it)->text(packet_col_).contains(regex)) { + hidden = false; + } + (*it)->setHidden(hidden); + ++it; + } +} + +void ExpertInfoDialog::on_buttonBox_helpRequested() +{ + wsApp->helpTopicAction(HELP_EXPERT_INFO_DIALOG); +} + +// Stat command + args + +static void +expert_info_init(const char *, void*) { + wsApp->emitStatCommandSignal("ExpertInfo", NULL, NULL); +} + +static stat_tap_ui expert_info_stat_ui = { + REGISTER_STAT_GROUP_GENERIC, + NULL, + "expert", + expert_info_init, + 0, + NULL +}; + +extern "C" { +void +register_tap_listener_expert_info(void) +{ + register_stat_tap_ui(&expert_info_stat_ui, NULL); +} +} + +/* + * 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/expert_info_dialog.h b/ui/qt/expert_info_dialog.h new file mode 100644 index 0000000000..04fb211159 --- /dev/null +++ b/ui/qt/expert_info_dialog.h @@ -0,0 +1,116 @@ +/* expert_info_dialog.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef EXPERT_INFO_DIALOG_H +#define EXPERT_INFO_DIALOG_H + +#include "config.h" + +#include <glib.h> + +#include "epan/epan_dissect.h" + +#include "filter_action.h" +#include "wireshark_dialog.h" + +#include <QMenu> +#include <QTreeWidgetItem> + +struct expert_info_s; + +namespace Ui { +class ExpertInfoDialog; +} + +class ExpertInfoDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit ExpertInfoDialog(QWidget &parent, CaptureFile& capture_file); + ~ExpertInfoDialog(); + + void clearAllData(); + void setDisplayFilter(const QString &display_filter = QString()); + +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_; + + QMenu ctx_menu_; + + QHash<QString, QTreeWidgetItem*> ei_to_ti_; + QList<QAction *> severity_actions_; + + QString display_filter_; + + void retapPackets(); + + // 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, packet_info *pinfo, epan_dissect_t *, const void *data); + static void tapDraw(void *eid_ptr); + +private slots: + void updateWidgets(); + + void actionShowToggled(); + void showProtoHierMenu(QPoint pos); + void filterActionTriggered(); + void captureFileClosing(); + + void on_expertInfoTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *); + void on_limitCheckBox_toggled(bool); + void on_searchLineEdit_textChanged(const QString &search_re); + void on_buttonBox_helpRequested(); +}; + +#endif // EXPERT_INFO_DIALOG_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/expert_info_dialog.ui b/ui/qt/expert_info_dialog.ui new file mode 100644 index 0000000000..7b36744f9a --- /dev/null +++ b/ui/qt/expert_info_dialog.ui @@ -0,0 +1,221 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ExpertInfoDialog</class> + <widget class="QDialog" name="ExpertInfoDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>620</width> + <height>450</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTreeWidget" name="expertInfoTreeWidget"> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <column> + <property name="text"> + <string notr="true">Severity</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> + <widget class="ElidedLabel" name="hintLabel"> + <property name="text"> + <string><small><i>A hint.</i></small></string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0,1,0,0"> + <item> + <widget class="QCheckBox" name="limitCheckBox"> + <property name="text"> + <string>Limit to Display Filter</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>10</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="toolTip"> + <string>Search expert summaries.</string> + </property> + <property name="text"> + <string>Search:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="searchLineEdit"> + <property name="toolTip"> + <string>Search expert summaries.</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>10</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="severitiesPushButton"> + <property name="text"> + <string>Show...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close|QDialogButtonBox::Help</set> + </property> + </widget> + </item> + </layout> + <action name="actionShowError"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Error</string> + </property> + <property name="toolTip"> + <string>Show error packets.</string> + </property> + </action> + <action name="actionShowWarning"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Warning</string> + </property> + <property name="toolTip"> + <string>Show warning packets.</string> + </property> + </action> + <action name="actionShowNote"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Note</string> + </property> + <property name="toolTip"> + <string>Show note packets.</string> + </property> + </action> + <action name="actionShowChat"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Chat</string> + </property> + <property name="toolTip"> + <string>Show chat packets.</string> + </property> + </action> + <action name="actionShowComment"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Comment</string> + </property> + <property name="toolTip"> + <string>Show comment packets.</string> + </property> + </action> + </widget> + <customwidgets> + <customwidget> + <class>ElidedLabel</class> + <extends>QLabel</extends> + <header>elided_label.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ExpertInfoDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ExpertInfoDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/ui/qt/main_status_bar.cpp b/ui/qt/main_status_bar.cpp index e834f178b0..756bc63af8 100644 --- a/ui/qt/main_status_bar.cpp +++ b/ui/qt/main_status_bar.cpp @@ -125,6 +125,7 @@ MainStatusBar::MainStatusBar(QWidget *parent) : comment_label_.setText("<a href><img src=\":/comment/capture_comment_update.png\"></img></a>"); comment_label_.setToolTip(tr("Open the Capture File Properties dialog")); comment_label_.setEnabled(false); + connect(&expert_status_, SIGNAL(linkActivated(QString)), this, SIGNAL(showExpertInfo())); connect(&comment_label_, SIGNAL(linkActivated(QString)), this, SIGNAL(editCaptureComment())); info_progress_hb->setContentsMargins(0, 0, 0, 0); @@ -192,7 +193,7 @@ void MainStatusBar::hideExpert() { void MainStatusBar::expertUpdate() { // <img> won't load @2x versions in Qt versions earlier than 5.4. // We might have to switch to a QPushButton. - QString img_text = "<img src=\":/expert/expert_"; + QString img_text = "<a href><img src=\":/expert/expert_"; QString tt_text = tr(" is the highest expert info level"); switch(expert_get_highest_severity()) { @@ -221,7 +222,7 @@ void MainStatusBar::expertUpdate() { break; } - img_text.append(".png\"></img>"); + img_text.append(".png\"></img></a>"); expert_status_.setText(img_text); expert_status_.setToolTip(tt_text); expert_status_.show(); diff --git a/ui/qt/main_status_bar.h b/ui/qt/main_status_bar.h index 9d8cdefa90..1cb024a4d2 100644 --- a/ui/qt/main_status_bar.h +++ b/ui/qt/main_status_bar.h @@ -57,6 +57,7 @@ private: QAction *delete_action_; signals: + void showExpertInfo(); void editCaptureComment(); public slots: diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index 8948ec432d..8c1dc798fd 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -406,6 +406,9 @@ MainWindow::MainWindow(QWidget *parent) : connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)), main_ui_->statusBar, SLOT(pushByteStatus(const QString&))); + connect(main_ui_->statusBar, SIGNAL(showExpertInfo()), + this, SLOT(on_actionAnalyzeExpertInfo_triggered())); + connect(main_ui_->statusBar, SIGNAL(editCaptureComment()), this, SLOT(on_actionStatisticsCaptureFileProperties_triggered())); diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index 408a5ab0ab..e4becaf39e 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -375,6 +375,8 @@ private slots: void on_actionAnalyzeFollowTCPStream_triggered(); void on_actionAnalyzeFollowUDPStream_triggered(); void on_actionAnalyzeFollowSSLStream_triggered(); + void statCommandExpertInfo(const char *, void *); + void on_actionAnalyzeExpertInfo_triggered(); void on_actionHelpContents_triggered(); void on_actionHelpMPWireshark_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index c232c03316..deb03fd4bd 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -351,6 +351,8 @@ <addaction name="separator"/> <addaction name="menuSCTP"/> <addaction name="menuFollow"/> + <addaction name="separator"/> + <addaction name="actionAnalyzeExpertInfo"/> </widget> <widget class="QMenu" name="menuStatistics"> <property name="enabled"> @@ -2319,6 +2321,14 @@ <string>Automatically scroll to the last packet during a live capture.</string> </property> </action> + <action name="actionAnalyzeExpertInfo"> + <property name="text"> + <string>Expert Info</string> + </property> + <property name="toolTip"> + <string>Show expert notifications</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <customwidgets> diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index feb11fe7b2..4027880b15 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -79,7 +79,9 @@ #include "coloring_rules_dialog.h" #include "conversation_dialog.h" #include "decode_as_dialog.h" +#include "display_filter_edit.h" #include "endpoint_dialog.h" +#include "expert_info_dialog.h" #include "export_object_dialog.h" #include "export_pdu_dialog.h" #if HAVE_EXTCAP @@ -488,6 +490,12 @@ void MainWindow::filterAction(QString &action_filter, FilterAction::Action actio df_combo_box_->lineEdit()->setText(new_filter); df_combo_box_->lineEdit()->setFocus(); break; + case FilterAction::ActionWebLookup: + { + QString url = QString("https://www.google.com/search?q=") + new_filter; + QDesktopServices::openUrl(QUrl(url)); + break; + } case FilterAction::ActionCopy: wsApp->clipboard()->setText(new_filter); break; @@ -2315,6 +2323,26 @@ void MainWindow::on_actionSCTPFilterThisAssociation_triggered() } } +void MainWindow::statCommandExpertInfo(const char *, void *) +{ + ExpertInfoDialog *expert_dialog = new ExpertInfoDialog(*this, capture_file_); + const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit()); + + expert_dialog->setDisplayFilter(df_edit->text()); + + connect(expert_dialog, SIGNAL(goToPacket(int, int)), + packet_list_, SLOT(goToPacket(int, int))); + connect(expert_dialog, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), + this, SLOT(filterAction(QString&,FilterAction::Action,FilterAction::ActionType))); + + expert_dialog->show(); +} + +void MainWindow::on_actionAnalyzeExpertInfo_triggered() +{ + statCommandExpertInfo(NULL, NULL); +} + // Next / previous / first / last slots in packet_list diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index 7255cc506a..ec6c77779d 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -961,6 +961,12 @@ void PacketList::goToPacket(int packet) { } } +void PacketList::goToPacket(int packet, int hf_id) +{ + goToPacket(packet); + proto_tree_->goToField(hf_id); +} + void PacketList::markFrame() { int row = currentIndex().row(); diff --git a/ui/qt/packet_list.h b/ui/qt/packet_list.h index 0e9194cd6c..0c29db71d8 100644 --- a/ui/qt/packet_list.h +++ b/ui/qt/packet_list.h @@ -121,6 +121,7 @@ public slots: void goFirstPacket(); void goLastPacket(); void goToPacket(int packet); + void goToPacket(int packet, int hf_id); void markFrame(); void markAllDisplayedFrames(bool set); void ignoreFrame(); diff --git a/ui/qt/proto_tree.cpp b/ui/qt/proto_tree.cpp index ed07e0e36b..7af1956dbc 100644 --- a/ui/qt/proto_tree.cpp +++ b/ui/qt/proto_tree.cpp @@ -26,6 +26,8 @@ #include <epan/ftypes/ftypes.h> #include <epan/prefs.h> +#include "color_utils.h" + #include <QApplication> #include <QContextMenuEvent> #include <QDesktopServices> @@ -33,14 +35,6 @@ #include <QTreeWidgetItemIterator> #include <QUrl> -QColor expert_color_comment ( 0xb7, 0xf7, 0x74 ); /* Green */ -QColor expert_color_chat ( 0x80, 0xb7, 0xf7 ); /* light blue */ -QColor expert_color_note ( 0xa0, 0xff, 0xff ); /* bright turquoise */ -QColor expert_color_warn ( 0xf7, 0xf2, 0x53 ); /* yellow */ -QColor expert_color_error ( 0xff, 0x5c, 0x5c ); /* pale red */ -QColor expert_color_foreground ( 0x00, 0x00, 0x00 ); /* black */ -QColor hidden_proto_item ( 0x44, 0x44, 0x44 ); /* gray */ - /* Fill a single protocol tree item with its string value and set its color. */ static void proto_tree_draw_node(proto_node *node, gpointer data) @@ -117,24 +111,24 @@ proto_tree_draw_node(proto_node *node, gpointer data) if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) { switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) { case(PI_COMMENT): - item->setData(0, Qt::BackgroundRole, expert_color_comment); + item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_comment); break; case(PI_CHAT): - item->setData(0, Qt::BackgroundRole, expert_color_chat); + item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_chat); break; case(PI_NOTE): - item->setData(0, Qt::BackgroundRole, expert_color_note); + item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_note); break; case(PI_WARN): - item->setData(0, Qt::BackgroundRole, expert_color_warn); + item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_warn); break; case(PI_ERROR): - item->setData(0, Qt::BackgroundRole, expert_color_error); + item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_error); break; default: g_assert_not_reached(); } - item->setData(0, Qt::ForegroundRole, expert_color_foreground); + item->setData(0, Qt::ForegroundRole, ColorUtils::expert_color_foreground); } item->setText(0, label_ptr); @@ -302,6 +296,25 @@ void ProtoTree::emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_ emit relatedFrame(related_frame, framenum_type); } +// XXX We select the first match, which might not be the desired item. +void ProtoTree::goToField(int hf_id) +{ + if (hf_id < 0) return; + + QTreeWidgetItemIterator iter(this); + while (*iter) { + field_info *fi = (*iter)->data(0, Qt::UserRole).value<field_info *>(); + + if (fi && fi->hfinfo) { + if (fi->hfinfo->id == hf_id) { + setCurrentItem(*iter); + break; + } + } + iter++; + } +} + void ProtoTree::updateSelectionStatus(QTreeWidgetItem* item) { if (item) { diff --git a/ui/qt/proto_tree.h b/ui/qt/proto_tree.h index b883308b1b..92cd3e1291 100644 --- a/ui/qt/proto_tree.h +++ b/ui/qt/proto_tree.h @@ -38,6 +38,7 @@ public: explicit ProtoTree(QWidget *parent = 0); void fillProtocolTree(proto_tree *protocol_tree); void emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_type = FT_FRAMENUM_NONE); + void goToField(int hf_id); void clear(); protected: |