summaryrefslogtreecommitdiff
path: root/ui/qt
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2015-01-14 17:25:56 -0800
committerGerald Combs <gerald@wireshark.org>2015-01-30 06:48:32 +0000
commit2bf7878e8a7455fe656bb07e9a7d42e6ac4d87fd (patch)
tree3a0c99831311c43017d1d9b3336856e4a956c353 /ui/qt
parent6824cee6c4b5f7c00b9dc4e9013aaa936b18b739 (diff)
downloadwireshark-2bf7878e8a7455fe656bb07e9a7d42e6ac4d87fd.tar.gz
Qt: Add the RTP Streams dialog.
Add keyboard shortcuts. Note that not all of the buttons made it from GTK+. Add a "Go to setup frame" option. Move rtp_streams.c from ui/gtk to ui. Add a help URL for RTP analysis (which needs to be split into streams + analysis). Fix RTP stream packet marking. Change-Id: Ifb8192ff701a933422509233d76461a46e459f4f Reviewed-on: https://code.wireshark.org/review/6852 Petri-Dish: Gerald Combs <gerald@wireshark.org> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Gerald Combs <gerald@wireshark.org>
Diffstat (limited to 'ui/qt')
-rw-r--r--ui/qt/CMakeLists.txt5
-rw-r--r--ui/qt/Makefile.am2
-rw-r--r--ui/qt/Makefile.common4
-rw-r--r--ui/qt/Wireshark.pro3
-rw-r--r--ui/qt/main_window.h1
-rw-r--r--ui/qt/main_window.ui12
-rw-r--r--ui/qt/main_window_slots.cpp13
-rw-r--r--ui/qt/packet_list.cpp1
-rw-r--r--ui/qt/rtp_stream_dialog.cpp636
-rw-r--r--ui/qt/rtp_stream_dialog.h105
-rw-r--r--ui/qt/rtp_stream_dialog.ui246
-rw-r--r--ui/qt/syntax_line_edit.cpp6
-rw-r--r--ui/qt/traffic_table_dialog.cpp3
-rw-r--r--ui/qt/wireshark_dialog.h1
14 files changed, 1033 insertions, 5 deletions
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index 1e20e8d2f1..822d81ad90 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -82,6 +82,7 @@ set(WIRESHARK_QT_HEADERS
proto_tree.h
qcustomplot.h
recent_file_status.h
+ rtp_stream_dialog.h
sctp_all_assocs_dialog.h
sctp_assoc_analyse_dialog.h
sctp_chunk_statistics_dialog.h
@@ -112,7 +113,7 @@ if(HAVE_PCAP_REMOTE)
)
endif()
-file(GLOB EXTA_QT_HEADERS
+file(GLOB EXTRA_QT_HEADERS
packet_list_record.h
qt_ui_utils.h
related_packet_delegate.h
@@ -181,6 +182,7 @@ set(WIRESHARK_QT_SRC
qt_ui_utils.cpp
recent_file_status.cpp
related_packet_delegate.cpp
+ rtp_stream_dialog.cpp
sctp_all_assocs_dialog.cpp
sctp_assoc_analyse_dialog.cpp
sctp_chunk_statistics_dialog.cpp
@@ -261,6 +263,7 @@ set(WIRESHARK_QT_UI
preferences_dialog.ui
print_dialog.ui
profile_dialog.ui
+ rtp_stream_dialog.ui
sctp_all_assocs_dialog.ui
sctp_assoc_analyse_dialog.ui
sctp_chunk_statistics_dialog.ui
diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am
index d7f4b4f96c..07879dc839 100644
--- a/ui/qt/Makefile.am
+++ b/ui/qt/Makefile.am
@@ -188,6 +188,8 @@ remote_capture_dialog.cpp remote_capture_dialog.h: ui_remote_capture_dialog.h
remote_settings_dialog.cpp remote_settings_dialog.h: ui_remote_settings_dialog.h
+rtp_stream_dialog.cpp rtp_stream_dialog.h: ui_rtp_stream_dialog.h
+
search_frame.cpp search_frame.h: ui_search_frame.h
sequence_dialog.cpp sequence_dialog.h: ui_sequence_dialog.h
diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common
index 4c17bb63cb..93a9ca25a6 100644
--- a/ui/qt/Makefile.common
+++ b/ui/qt/Makefile.common
@@ -63,6 +63,7 @@ NODIST_GENERATED_HEADER_FILES = \
ui_profile_dialog.h \
ui_remote_capture_dialog.h \
ui_remote_settings_dialog.h \
+ ui_rtp_stream_dialog.h \
ui_sctp_all_assocs_dialog.h \
ui_sctp_assoc_analyse_dialog.h \
ui_sctp_chunk_statistics_dialog.h \
@@ -177,6 +178,7 @@ MOC_HDRS = \
remote_capture_dialog.h \
remote_settings_dialog.h \
search_frame.h \
+ rtp_stream_dialog.h \
sctp_all_assocs_dialog.h \
sctp_assoc_analyse_dialog.h \
sctp_chunk_statistics_dialog.h \
@@ -237,6 +239,7 @@ UI_FILES = \
profile_dialog.ui \
remote_capture_dialog.ui \
remote_settings_dialog.ui \
+ rtp_stream_dialog.ui \
sctp_all_assocs_dialog.ui \
sctp_assoc_analyse_dialog.ui \
sctp_chunk_statistics_dialog.ui \
@@ -376,6 +379,7 @@ WIRESHARK_QT_SRC = \
related_packet_delegate.cpp \
remote_capture_dialog.cpp \
remote_settings_dialog.cpp \
+ rtp_stream_dialog.cpp \
sctp_all_assocs_dialog.cpp \
sctp_assoc_analyse_dialog.cpp \
sctp_chunk_statistics_dialog.cpp \
diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro
index b169212c29..aa49c0481a 100644
--- a/ui/qt/Wireshark.pro
+++ b/ui/qt/Wireshark.pro
@@ -239,6 +239,7 @@ FORMS += \
profile_dialog.ui \
remote_capture_dialog.ui \
remote_settings_dialog.ui \
+ rtp_stream_dialog.ui \
sctp_all_assocs_dialog.ui \
sctp_assoc_analyse_dialog.ui \
sctp_chunk_statistics_dialog.ui \
@@ -292,6 +293,7 @@ HEADERS += $$HEADERS_WS_C \
profile_dialog.h \
remote_capture_dialog.h \
remote_settings_dialog.h \
+ rtp_stream_dialog.h \
sctp_all_assocs_dialog.h \
sctp_assoc_analyse_dialog.h \
sctp_chunk_statistics_dialog.h \
@@ -654,6 +656,7 @@ SOURCES += \
related_packet_delegate.cpp \
remote_capture_dialog.cpp \
remote_settings_dialog.cpp \
+ rtp_stream_dialog.cpp \
sctp_all_assocs_dialog.cpp \
sctp_assoc_analyse_dialog.cpp \
sctp_chunk_statistics_dialog.cpp \
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index d1fedbeaa1..28dd1af301 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -428,6 +428,7 @@ private slots:
void openVoipCallsDialog(bool all_flows = false);
void on_actionTelephonyVoipCalls_triggered();
void on_actionTelephonyISUPMessages_triggered();
+ void on_actionTelephonyRTPStreams_triggered();
void on_actionTelephonyRTSPPacketCounter_triggered();
void on_actionTelephonySMPPOperations_triggered();
void on_actionTelephonyUCPMessages_triggered();
diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui
index 0e1888faed..b03092f5df 100644
--- a/ui/qt/main_window.ui
+++ b/ui/qt/main_window.ui
@@ -442,8 +442,15 @@
</property>
<addaction name="actionTelephonyRTSPPacketCounter"/>
</widget>
+ <widget class="QMenu" name="menuRTP">
+ <property name="title">
+ <string>RTP</string>
+ </property>
+ <addaction name="actionTelephonyRTPStreams"/>
+ </widget>
<addaction name="actionTelephonyVoipCalls"/>
<addaction name="actionTelephonyISUPMessages"/>
+ <addaction name="menuRTP"/>
<addaction name="menuRTSP"/>
<addaction name="actionTelephonySMPPOperations"/>
<addaction name="actionTelephonyUCPMessages"/>
@@ -2213,6 +2220,11 @@
<string>SIP Flows</string>
</property>
</action>
+ <action name="actionTelephonyRTPStreams">
+ <property name="text">
+ <string>RTP Streams</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 dad5df9943..1df80ab575 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -88,6 +88,7 @@
#include "print_dialog.h"
#include "profile_dialog.h"
#include "qt_ui_utils.h"
+#include "rtp_stream_dialog.h"
#include "sctp_all_assocs_dialog.h"
#include "sctp_assoc_analyse_dialog.h"
#include "sctp_graph_dialog.h"
@@ -2468,6 +2469,18 @@ void MainWindow::on_actionTelephonyISUPMessages_triggered()
openStatisticsTreeDialog("isup_msg");
}
+void MainWindow::on_actionTelephonyRTPStreams_triggered()
+{
+ RtpStreamDialog *rtp_stream_dialog = new RtpStreamDialog(*this, capture_file_);
+ connect(rtp_stream_dialog, SIGNAL(packetsMarked()),
+ packet_list_, SLOT(redrawVisiblePackets()));
+ connect(rtp_stream_dialog, SIGNAL(goToPacket(int)),
+ packet_list_, SLOT(goToPacket(int)));
+ connect(rtp_stream_dialog, SIGNAL(updateFilter(QString&, bool)),
+ this, SLOT(filterPackets(QString&, bool)));
+ rtp_stream_dialog->show();
+}
+
void MainWindow::on_actionTelephonyRTSPPacketCounter_triggered()
{
openStatisticsTreeDialog("rtsp");
diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp
index 57e2acc76a..6b465186a1 100644
--- a/ui/qt/packet_list.cpp
+++ b/ui/qt/packet_list.cpp
@@ -982,7 +982,6 @@ void PacketList::addRelatedFrame(int related_frame)
related_packet_delegate_.addRelatedFrame(related_frame);
}
-#include <QDebug>
void PacketList::showHeaderMenu(QPoint pos)
{
header_ctx_column_ = header()->logicalIndexAt(pos);
diff --git a/ui/qt/rtp_stream_dialog.cpp b/ui/qt/rtp_stream_dialog.cpp
new file mode 100644
index 0000000000..7a502d5585
--- /dev/null
+++ b/ui/qt/rtp_stream_dialog.cpp
@@ -0,0 +1,636 @@
+/* rtp_stream_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 "rtp_stream_dialog.h"
+#include "ui_rtp_stream_dialog.h"
+
+#include "file.h"
+
+#include "epan/addr_resolv.h"
+#include <epan/rtp_pt.h>
+
+#include "ui/utf8_entities.h"
+
+#include "qt_ui_utils.h"
+#include "wireshark_application.h"
+
+#include <QAction>
+#include <QClipboard>
+#include <QFileDialog>
+#include <QKeyEvent>
+#include <QPushButton>
+#include <QTextStream>
+#include <QTreeWidgetItem>
+#include <QTreeWidgetItemIterator>
+
+#include "tango_colors.h"
+
+/*
+ * @file RTP stream dialog
+ *
+ * Displays a list of RTP streams with the following information:
+ * - UDP 4-tuple
+ * - SSRC
+ * - Payload type
+ * - Stats: Packets, lost, max delta, max jitter, mean jitter
+ * - Problems
+ *
+ * Finds reverse streams
+ * "Save As" rtpdump
+ * Mark packets
+ * Go to the setup frame
+ * Prepare filter
+ * Copy As CSV and YAML
+ * Analyze
+ */
+
+// To do:
+// - Add more statistics to the hint text (e.g. lost packets).
+// - Add more statistics to the main list (e.g. stream duration)
+
+const int src_addr_col_ = 0;
+const int src_port_col_ = 1;
+const int dst_addr_col_ = 2;
+const int dst_port_col_ = 3;
+const int ssrc_col_ = 4;
+const int payload_col_ = 5;
+const int packets_col_ = 6;
+const int lost_col_ = 7;
+const int max_delta_col_ = 8;
+const int max_jitter_col_ = 9;
+const int mean_jitter_col_ = 10;
+const int status_col_ = 11;
+
+Q_DECLARE_METATYPE(rtp_stream_info_t*)
+
+class RtpStreamTreeWidgetItem : public QTreeWidgetItem
+{
+public:
+ RtpStreamTreeWidgetItem(QTreeWidget *tree, rtp_stream_info_t *stream_info) : QTreeWidgetItem(tree) {
+ setData(0, Qt::UserRole, qVariantFromValue(stream_info));
+ drawData();
+ }
+
+ void drawData() {
+ rtp_stream_info_t *stream_info = data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (!stream_info) {
+ return;
+ }
+ setText(src_addr_col_, address_to_display_qstring(&stream_info->src_addr));
+ setText(src_port_col_, QString::number(stream_info->src_port));
+ setText(dst_addr_col_, address_to_display_qstring(&stream_info->dest_addr));
+ setText(dst_port_col_, QString::number(stream_info->dest_port));
+ setText(ssrc_col_, QString("0x%1").arg(stream_info->ssrc, 0, 16));
+
+ if (stream_info->payload_type_name != NULL) {
+ setText(payload_col_, stream_info->payload_type_name);
+ } else {
+ setText(payload_col_, val_to_str_ext(stream_info->payload_type,
+ &rtp_payload_type_short_vals_ext,
+ "Unknown (%u)"));
+ }
+
+ setText(packets_col_, QString::number(stream_info->packet_count));
+
+ guint32 expected;
+ double pct_loss;
+ expected = (stream_info->rtp_stats.stop_seq_nr + stream_info->rtp_stats.cycles*65536)
+ - stream_info->rtp_stats.start_seq_nr + 1;
+ lost_ = expected - stream_info->rtp_stats.total_nr;
+ if (expected) {
+ pct_loss = (double)(lost_*100.0)/(double)expected;
+ } else {
+ pct_loss = 0;
+ }
+
+ setText(lost_col_, QObject::tr("%1 (%L2%)").arg(lost_).arg(QString::number(pct_loss, 'f', 1)));
+ setText(max_delta_col_, QString::number(stream_info->rtp_stats.max_delta, 'f', 3)); // This is RTP. Do we need nanoseconds?
+ setText(max_jitter_col_, QString::number(stream_info->rtp_stats.max_jitter, 'f', 3));
+ setText(mean_jitter_col_, QString::number(stream_info->rtp_stats.mean_jitter, 'f', 3));
+
+ if (stream_info->problem) {
+ setText(status_col_, UTF8_BULLET);
+ setTextAlignment(status_col_, Qt::AlignCenter);
+ for (int i = 0; i < columnCount(); i++) {
+ setBackgroundColor(i, ws_css_warn_background);
+ setTextColor(i, ws_css_warn_text);
+ }
+ }
+ }
+ // Return a QString, int, double, or invalid QVariant representing the raw column data.
+ QVariant colData(int col) const {
+ rtp_stream_info_t *stream_info = data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (!stream_info) {
+ return QVariant();
+ }
+
+ switch(col) {
+ case src_addr_col_:
+ case dst_addr_col_:
+ case payload_col_: // XXX Return numeric value?
+ return text(col);
+ case src_port_col_:
+ return stream_info->src_port;
+ case dst_port_col_:
+ return stream_info->dest_port;
+ case ssrc_col_:
+ return stream_info->ssrc;
+ case packets_col_:
+ return stream_info->packet_count;
+ case lost_col_:
+ return lost_;
+ case max_delta_col_:
+ return stream_info->rtp_stats.max_delta;
+ case max_jitter_col_:
+ return stream_info->rtp_stats.max_jitter;
+ case mean_jitter_col_:
+ return stream_info->rtp_stats.mean_jitter;
+ case status_col_:
+ return stream_info->problem ? "Problem" : "";
+ default:
+ break;
+ }
+ return QVariant();
+ }
+
+ bool operator< (const QTreeWidgetItem &other) const
+ {
+ rtp_stream_info_t *this_stream_info = data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ rtp_stream_info_t *other_stream_info = other.data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (!this_stream_info || !other_stream_info) {
+ return false;
+ }
+ const RtpStreamTreeWidgetItem &other_rstwi = dynamic_cast<const RtpStreamTreeWidgetItem&>(other);
+
+ switch (treeWidget()->sortColumn()) {
+ case src_addr_col_:
+ return cmp_address(&(this_stream_info->src_addr), &(other_stream_info->src_addr)) < 0;
+ case src_port_col_:
+ return this_stream_info->src_port < other_stream_info->src_port;
+ case dst_addr_col_:
+ return cmp_address(&(this_stream_info->dest_addr), &(other_stream_info->dest_addr)) < 0;
+ case dst_port_col_:
+ return this_stream_info->dest_port < other_stream_info->dest_port;
+ case ssrc_col_:
+ return this_stream_info->ssrc < other_stream_info->ssrc;
+ case payload_col_:
+ return this_stream_info->payload_type < other_stream_info->payload_type; // XXX Compare payload_type_name instead?
+ case packets_col_:
+ return this_stream_info->packet_count < other_stream_info->packet_count;
+ case lost_col_:
+ return lost_ < other_rstwi.lost_;
+ case max_delta_col_:
+ return this_stream_info->rtp_stats.max_delta < other_stream_info->rtp_stats.max_delta;
+ case max_jitter_col_:
+ return this_stream_info->rtp_stats.max_jitter < other_stream_info->rtp_stats.max_jitter;
+ case mean_jitter_col_:
+ return this_stream_info->rtp_stats.mean_jitter < other_stream_info->rtp_stats.mean_jitter;
+ default:
+ break;
+ }
+
+ // Fall back to string comparison
+ return QTreeWidgetItem::operator <(other);
+ }
+
+private:
+ guint32 lost_;
+};
+
+RtpStreamDialog::RtpStreamDialog(QWidget &parent, CaptureFile &cf) :
+ WiresharkDialog(parent, cf),
+ ui(new Ui::RtpStreamDialog),
+ need_redraw_(false)
+{
+ ui->setupUi(this);
+ setWindowSubtitle(tr("RTP Streams"));
+ ui->streamTreeWidget->installEventFilter(this);
+
+ // XXX Use recent settings instead
+ resize(parent.width() * 4 / 5, parent.height() * 2 / 3);
+
+ ctx_menu_.addAction(ui->actionSelectNone);
+ ctx_menu_.addAction(ui->actionFindReverse);
+ ctx_menu_.addAction(ui->actionGoToSetup);
+ ctx_menu_.addAction(ui->actionMarkPackets);
+ ctx_menu_.addAction(ui->actionPrepareFilter);
+ ctx_menu_.addAction(ui->actionExportAsRtpDump);
+ ctx_menu_.addAction(ui->actionCopyAsCsv);
+ ctx_menu_.addAction(ui->actionCopyAsYaml);
+ ctx_menu_.addAction(ui->actionAnalyze);
+ ui->streamTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui->streamTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
+ SLOT(showStreamMenu(QPoint)));
+
+ // Some GTK+ buttons have been left out intentionally in order to
+ // reduce clutter. Do you have a strong and informed opinion about
+ // this? Perhaps you should volunteer to maintain this code!
+ find_reverse_button_ = ui->buttonBox->addButton(ui->actionFindReverse->text(), QDialogButtonBox::ApplyRole);
+ find_reverse_button_->setToolTip(ui->actionFindReverse->toolTip());
+ prepare_button_ = ui->buttonBox->addButton(ui->actionPrepareFilter->text(), QDialogButtonBox::ApplyRole);
+ prepare_button_->setToolTip(ui->actionPrepareFilter->toolTip());
+ export_button_ = ui->buttonBox->addButton(tr("Export..."), QDialogButtonBox::ApplyRole);
+ export_button_->setToolTip(ui->actionExportAsRtpDump->toolTip());
+ copy_button_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ApplyRole);
+ analyze_button_ = ui->buttonBox->addButton(ui->actionAnalyze->text(), QDialogButtonBox::ApplyRole);
+ analyze_button_->setToolTip(ui->actionAnalyze->toolTip());
+
+ QMenu *copy_menu = new QMenu();
+ QAction *ca;
+ ca = copy_menu->addAction(tr("as CSV"));
+ ca->setToolTip(ui->actionCopyAsCsv->toolTip());
+ connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsCsv_triggered()));
+ ca = copy_menu->addAction(tr("as YAML"));
+ ca->setToolTip(ui->actionCopyAsYaml->toolTip());
+ connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsYaml_triggered()));
+ copy_button_->setMenu(copy_menu);
+
+ /* Register the tap listener */
+ memset(&tapinfo_, 0, sizeof(rtpstream_tapinfo_t));
+ tapinfo_.tap_draw = tapDraw;
+ tapinfo_.tap_mark_packet = tapMarkPacket;
+ tapinfo_.tap_data = this;
+ tapinfo_.mode = TAP_ANALYSE;
+
+ register_tap_listener_rtp_stream(&tapinfo_);
+ /* Scan for RTP streams (redissect all packets) */
+ rtpstream_scan(&tapinfo_, cf.capFile());
+
+ updateWidgets();
+}
+
+RtpStreamDialog::~RtpStreamDialog()
+{
+ delete ui;
+ remove_tap_listener_rtp_stream(&tapinfo_);
+}
+
+bool RtpStreamDialog::eventFilter(QObject *obj, QEvent *event)
+{
+ Q_UNUSED(obj)
+ if (ui->streamTreeWidget->hasFocus() && event->type() == QEvent::KeyPress) {
+ QKeyEvent &keyEvent = static_cast<QKeyEvent&>(*event);
+ switch(keyEvent.key()) {
+ case Qt::Key_G:
+ on_actionGoToSetup_triggered();
+ return true;
+ case Qt::Key_M:
+ on_actionMarkPackets_triggered();
+ return true;
+ case Qt::Key_P:
+ on_actionPrepareFilter_triggered();
+ return true;
+ case Qt::Key_R:
+ on_actionFindReverse_triggered();
+ return true;
+ case Qt::Key_A:
+ // XXX "Shift+Ctrl+A" is a fairly standard shortcut for "select none".
+ // However, the main window uses this for displaying the profile dialog.
+// if (keyEvent.modifiers() == (Qt::ControlModifier | Qt::ShiftModifier))
+// on_actionSelectNone_triggered();
+// return true;
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+void RtpStreamDialog::tapDraw(void *tapinfo_ptr)
+{
+ rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *) tapinfo_ptr;
+
+ RtpStreamDialog *rtp_stream_dialog = static_cast<RtpStreamDialog *>(tapinfo->tap_data);
+ if (rtp_stream_dialog) {
+ rtp_stream_dialog->updateStreams();
+ }
+}
+
+void RtpStreamDialog::tapMarkPacket(rtpstream_tapinfo_t *tapinfo, frame_data *fd)
+{
+ if (!tapinfo) return;
+
+ RtpStreamDialog *rtp_stream_dialog = static_cast<RtpStreamDialog *>(tapinfo->tap_data);
+ if (rtp_stream_dialog) {
+ rtp_stream_dialog->need_redraw_ = true;
+ cf_mark_frame(rtp_stream_dialog->cap_file_.capFile(), fd);
+ rtp_stream_dialog->need_redraw_ = true;
+ }
+}
+
+void RtpStreamDialog::updateStreams()
+{
+ GList *cur_stream = g_list_nth(tapinfo_.strinfo_list, ui->streamTreeWidget->topLevelItemCount());
+
+ // Add any missing items
+ while (cur_stream && cur_stream->data) {
+ rtp_stream_info_t *stream_info = (rtp_stream_info_t*) cur_stream->data;
+ new RtpStreamTreeWidgetItem(ui->streamTreeWidget, stream_info);
+ cur_stream = g_list_next(cur_stream);
+ }
+
+ // Recalculate values
+ QTreeWidgetItemIterator iter(ui->streamTreeWidget);
+ while (*iter) {
+ RtpStreamTreeWidgetItem *rsti = static_cast<RtpStreamTreeWidgetItem*>(*iter);
+ rsti->drawData();
+ ++iter;
+ }
+
+ // Resize columns
+ for (int i = 0; i < ui->streamTreeWidget->columnCount(); i++) {
+ ui->streamTreeWidget->resizeColumnToContents(i);
+ }
+
+ ui->streamTreeWidget->setSortingEnabled(true);
+
+ updateWidgets();
+
+ if (need_redraw_) {
+ emit packetsMarked();
+ need_redraw_ = false;
+ }
+}
+
+void RtpStreamDialog::updateWidgets()
+{
+ bool selected = ui->streamTreeWidget->selectedItems().count() > 0;
+
+ QString hint = "<small><i>";
+ hint += tr("%1 streams").arg(ui->streamTreeWidget->topLevelItemCount());
+
+ if (selected) {
+ int tot_packets = 0;
+ foreach(QTreeWidgetItem *ti, ui->streamTreeWidget->selectedItems()) {
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ tot_packets += stream_info->packet_count;
+ }
+ }
+ hint += tr(", %1 selected, %2 total packets")
+ .arg(ui->streamTreeWidget->selectedItems().count())
+ .arg(tot_packets);
+ }
+
+ hint += ". Right-click for more options.";
+ hint += "</i></small>";
+ ui->hintLabel->setText(hint);
+
+ bool enable = selected && !file_closed_;
+ bool has_data = ui->streamTreeWidget->topLevelItemCount() > 0;
+
+ find_reverse_button_->setEnabled(enable);
+ prepare_button_->setEnabled(enable);
+ export_button_->setEnabled(enable);
+ copy_button_->setEnabled(has_data);
+ analyze_button_->setEnabled(false); // XXX No dialog
+
+ ui->actionFindReverse->setEnabled(enable);
+ ui->actionGoToSetup->setEnabled(enable);
+ ui->actionMarkPackets->setEnabled(enable);
+ ui->actionPrepareFilter->setEnabled(enable);
+ ui->actionExportAsRtpDump->setEnabled(enable);
+ ui->actionCopyAsCsv->setEnabled(has_data);
+ ui->actionCopyAsYaml->setEnabled(has_data);
+ ui->actionAnalyze->setEnabled(false); // XXX No dialog
+}
+
+QList<QVariant> RtpStreamDialog::streamRowData(int row) const
+{
+ QList<QVariant> row_data;
+
+ if (row >= ui->streamTreeWidget->topLevelItemCount()) {
+ return row_data;
+ }
+
+ for (int col = 0; col < ui->streamTreeWidget->columnCount(); col++) {
+ if (row < 0) {
+ row_data << ui->streamTreeWidget->headerItem()->text(col);
+ } else {
+ RtpStreamTreeWidgetItem *rsti = static_cast<RtpStreamTreeWidgetItem*>(ui->streamTreeWidget->topLevelItem(row));
+ if (rsti) {
+ row_data << rsti->colData(col);
+ }
+ }
+ }
+ return row_data;
+}
+
+void RtpStreamDialog::captureFileClosing()
+{
+ remove_tap_listener_rtp_stream(&tapinfo_);
+ WiresharkDialog::captureFileClosing();
+}
+
+void RtpStreamDialog::showStreamMenu(QPoint pos)
+{
+ ctx_menu_.popup(ui->streamTreeWidget->viewport()->mapToGlobal(pos));
+}
+
+void RtpStreamDialog::on_actionAnalyze_triggered()
+{
+
+}
+
+void RtpStreamDialog::on_actionCopyAsCsv_triggered()
+{
+ QString csv;
+ QTextStream stream(&csv, QIODevice::Text);
+ for (int row = -1; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
+ QStringList rdsl;
+ foreach (QVariant v, streamRowData(row)) {
+ if (!v.isValid()) {
+ rdsl << "\"\"";
+ } else if ((int) v.type() == (int) QMetaType::QString) {
+ rdsl << QString("\"%1\"").arg(v.toString());
+ } else {
+ rdsl << v.toString();
+ }
+ }
+ stream << rdsl.join(",") << endl;
+ }
+ wsApp->clipboard()->setText(stream.readAll());
+}
+
+void RtpStreamDialog::on_actionCopyAsYaml_triggered()
+{
+ QString yaml;
+ QTextStream stream(&yaml, QIODevice::Text);
+ stream << "---" << endl;
+ for (int row = -1; row < ui->streamTreeWidget->topLevelItemCount(); row ++) {
+ stream << "-" << endl;
+ foreach (QVariant v, streamRowData(row)) {
+ stream << " - " << v.toString() << endl;
+ }
+ }
+ wsApp->clipboard()->setText(stream.readAll());
+}
+
+void RtpStreamDialog::on_actionExportAsRtpDump_triggered()
+{
+ if (file_closed_ || ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ // XXX If the user selected multiple frames is this the one we actually want?
+ QTreeWidgetItem *ti = ui->streamTreeWidget->selectedItems()[0];
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ QString file_name;
+ QDir path(wsApp->lastOpenDir());
+ QString save_file = path.canonicalPath() + "/" + cap_file_.fileTitle();
+ QString extension;
+ file_name = QFileDialog::getSaveFileName(this, wsApp->windowTitleString(tr("Save RTPDump As" UTF8_HORIZONTAL_ELLIPSIS)),
+ save_file, "RTPDump Format (*.rtpdump)", &extension);
+
+ if (file_name.length() > 0) {
+ gchar *dest_file = qstring_strdup(file_name);
+ gboolean save_ok = rtpstream_save(&tapinfo_, cap_file_.capFile(), stream_info, dest_file);
+ g_free(dest_file);
+ // else error dialog?
+ if (save_ok) {
+ path = QDir(file_name);
+ wsApp->setLastOpenDir(path.canonicalPath().toUtf8().constData());
+ }
+ }
+
+ }
+}
+
+void RtpStreamDialog::on_actionFindReverse_triggered()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ // Gather up our selected streams...
+ QList<rtp_stream_info_t *> selected_streams;
+ foreach(QTreeWidgetItem *ti, ui->streamTreeWidget->selectedItems()) {
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ selected_streams << stream_info;
+ }
+ }
+
+ // ...and compare them to our unselected streams.
+ QTreeWidgetItemIterator iter(ui->streamTreeWidget, QTreeWidgetItemIterator::Unselected);
+ while (*iter) {
+ rtp_stream_info_t *stream = (*iter)->data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (stream) {
+ foreach (rtp_stream_info_t *fwd_stream, selected_streams) {
+ if (rtp_stream_info_is_reverse(fwd_stream, stream)) {
+ (*iter)->setSelected(true);
+ }
+ }
+ }
+ ++iter;
+ }
+}
+
+void RtpStreamDialog::on_actionGoToSetup_triggered()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+ // XXX If the user selected multiple frames is this the one we actually want?
+ QTreeWidgetItem *ti = ui->streamTreeWidget->selectedItems()[0];
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ emit goToPacket(stream_info->setup_frame_number);
+ }
+}
+
+void RtpStreamDialog::on_actionMarkPackets_triggered()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+ rtp_stream_info_t *stream_a, *stream_b = NULL;
+
+ stream_a = ui->streamTreeWidget->selectedItems()[0]->data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (ui->streamTreeWidget->selectedItems().count() > 1)
+ stream_b = ui->streamTreeWidget->selectedItems()[1]->data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+
+ if (stream_a == NULL && stream_b == NULL) return;
+
+ // XXX Mark the setup frame as well?
+ need_redraw_ = false;
+ rtpstream_mark(&tapinfo_, cap_file_.capFile(), stream_a, stream_b);
+ updateWidgets();
+}
+
+void RtpStreamDialog::on_actionPrepareFilter_triggered()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ // Gather up our selected streams...
+ QStringList stream_filters;
+ foreach(QTreeWidgetItem *ti, ui->streamTreeWidget->selectedItems()) {
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ QString ip_proto = stream_info->src_addr.type == AT_IPv6 ? "ipv6" : "ip";
+ stream_filters << QString("(%1.src==%2 && udp.srcport==%3 && %1.dst==%4 && udp.dstport==%5 && rtp.ssrc==0x%6)")
+ .arg(ip_proto) // %1
+ .arg(address_to_qstring(&stream_info->src_addr)) // %2
+ .arg(stream_info->src_port) // %3
+ .arg(address_to_qstring(&stream_info->dest_addr)) // %4
+ .arg(stream_info->dest_port) // %5
+ .arg(stream_info->ssrc, 0, 16);
+ }
+ }
+ if (stream_filters.length() > 0) {
+ QString filter = stream_filters.join(" || ");
+ remove_tap_listener_rtp_stream(&tapinfo_);
+ emit updateFilter(filter);
+ }
+}
+
+void RtpStreamDialog::on_actionSelectNone_triggered()
+{
+ ui->streamTreeWidget->clearSelection();
+}
+
+void RtpStreamDialog::on_streamTreeWidget_itemSelectionChanged()
+{
+ updateWidgets();
+}
+
+void RtpStreamDialog::on_buttonBox_clicked(QAbstractButton *button)
+{
+ if (button == prepare_button_) {
+ on_actionPrepareFilter_triggered();
+ } else if (button == export_button_) {
+ on_actionExportAsRtpDump_triggered();
+ } else if (button == analyze_button_) {
+
+ }
+}
+
+void RtpStreamDialog::on_buttonBox_helpRequested()
+{
+ wsApp->helpTopicAction(HELP_RTP_ANALYSIS_DIALOG);
+}
+
+/*
+ * 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/rtp_stream_dialog.h b/ui/qt/rtp_stream_dialog.h
new file mode 100644
index 0000000000..1615d5453e
--- /dev/null
+++ b/ui/qt/rtp_stream_dialog.h
@@ -0,0 +1,105 @@
+/* rtp_stream_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 RTP_STREAM_DIALOG_H
+#define RTP_STREAM_DIALOG_H
+
+#include "wireshark_dialog.h"
+
+#include "ui/rtp_stream.h"
+
+#include <QAbstractButton>
+#include <QMenu>
+
+namespace Ui {
+class RtpStreamDialog;
+}
+
+class RtpStreamDialog : public WiresharkDialog
+{
+ Q_OBJECT
+
+public:
+ explicit RtpStreamDialog(QWidget &parent, CaptureFile &cf);
+ ~RtpStreamDialog();
+
+signals:
+ // Tells the packet list to redraw. An alternative might be to add a
+ // cf_packet_marked callback to file.[ch] but that's synchronous and
+ // might incur too much overhead.
+ void packetsMarked();
+ void updateFilter(QString &filter, bool force = false);
+ void goToPacket(int packet_num);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event);
+
+private:
+ Ui::RtpStreamDialog *ui;
+ rtpstream_tapinfo_t tapinfo_;
+ QPushButton *find_reverse_button_;
+ QPushButton *prepare_button_;
+ QPushButton *export_button_;
+ QPushButton *copy_button_;
+ QPushButton *analyze_button_;
+ QMenu ctx_menu_;
+ bool need_redraw_;
+
+ static void tapDraw(void *tapinfo_ptr);
+ static void tapMarkPacket(rtpstream_tapinfo_t *tapinfo, frame_data *fd);
+
+ void updateStreams();
+ void updateWidgets();
+
+ QList<QVariant> streamRowData(int row) const;
+
+
+private slots:
+ void captureFileClosing();
+ void showStreamMenu(QPoint pos);
+ void on_actionCopyAsCsv_triggered();
+ void on_actionCopyAsYaml_triggered();
+ void on_actionFindReverse_triggered();
+ void on_actionGoToSetup_triggered();
+ void on_actionMarkPackets_triggered();
+ void on_actionPrepareFilter_triggered();
+ void on_actionSelectNone_triggered();
+ void on_streamTreeWidget_itemSelectionChanged();
+ void on_buttonBox_helpRequested();
+ void on_buttonBox_clicked(QAbstractButton *button);
+ void on_actionExportAsRtpDump_triggered();
+ void on_actionAnalyze_triggered();
+};
+
+#endif // RTP_STREAM_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/rtp_stream_dialog.ui b/ui/qt/rtp_stream_dialog.ui
new file mode 100644
index 0000000000..03573aa0a0
--- /dev/null
+++ b/ui/qt/rtp_stream_dialog.ui
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RtpStreamDialog</class>
+ <widget class="QDialog" name="RtpStreamDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>460</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTreeWidget" name="streamTreeWidget">
+ <property name="selectionMode">
+ <enum>QAbstractItemView::MultiSelection</enum>
+ </property>
+ <property name="textElideMode">
+ <enum>Qt::ElideMiddle</enum>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="expandsOnDoubleClick">
+ <bool>false</bool>
+ </property>
+ <attribute name="headerDefaultSectionSize">
+ <number>50</number>
+ </attribute>
+ <column>
+ <property name="text">
+ <string>Source Address</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Source Port</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Destination Address</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Destination Port</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>SSRC</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Payload</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Packets</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Lost</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Max Delta (ms)</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Max Jitter</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Mean Jitter</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="hintLabel">
+ <property name="text">
+ <string>&lt;small&gt;&lt;i&gt;A hint.&lt;/i&gt;&lt;/small&gt;</string>
+ </property>
+ </widget>
+ </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="actionFindReverse">
+ <property name="text">
+ <string>Find Reverse</string>
+ </property>
+ <property name="toolTip">
+ <string>Find the reverse stream matching the selected forward stream.</string>
+ </property>
+ <property name="shortcut">
+ <string>R</string>
+ </property>
+ </action>
+ <action name="actionMarkPackets">
+ <property name="text">
+ <string>Mark Packets</string>
+ </property>
+ <property name="toolTip">
+ <string>Mark the packets of the selected stream(s).</string>
+ </property>
+ <property name="shortcut">
+ <string>M</string>
+ </property>
+ </action>
+ <action name="actionSelectNone">
+ <property name="text">
+ <string>Select None</string>
+ </property>
+ <property name="toolTip">
+ <string>Undo stream selection.</string>
+ </property>
+ </action>
+ <action name="actionGoToSetup">
+ <property name="text">
+ <string>Go To Setup</string>
+ </property>
+ <property name="toolTip">
+ <string>Go to the setup packet for this stream.</string>
+ </property>
+ <property name="shortcut">
+ <string>G</string>
+ </property>
+ </action>
+ <action name="actionPrepareFilter">
+ <property name="text">
+ <string>Prepare Filter</string>
+ </property>
+ <property name="toolTip">
+ <string>Prepare a filter matching the selected stream(s).</string>
+ </property>
+ <property name="shortcut">
+ <string>P</string>
+ </property>
+ </action>
+ <action name="actionExportAsRtpDump">
+ <property name="text">
+ <string>Export As RTPDump</string>
+ </property>
+ <property name="toolTip">
+ <string>Export the stream payload as rtpdump</string>
+ </property>
+ <property name="shortcut">
+ <string>E</string>
+ </property>
+ </action>
+ <action name="actionAnalyze">
+ <property name="text">
+ <string>Analyze</string>
+ </property>
+ <property name="toolTip">
+ <string>Open the analysis window for the selected stream(s)</string>
+ </property>
+ </action>
+ <action name="actionCopyAsCsv">
+ <property name="text">
+ <string>Copy as CSV</string>
+ </property>
+ <property name="toolTip">
+ <string>Copy stream list as CSV.</string>
+ </property>
+ </action>
+ <action name="actionCopyAsYaml">
+ <property name="text">
+ <string>Copy as YAML</string>
+ </property>
+ <property name="toolTip">
+ <string>Copy stream list as YAML.</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>RtpStreamDialog</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>RtpStreamDialog</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/syntax_line_edit.cpp b/ui/qt/syntax_line_edit.cpp
index 483bdcb7ce..867c01d38a 100644
--- a/ui/qt/syntax_line_edit.cpp
+++ b/ui/qt/syntax_line_edit.cpp
@@ -59,9 +59,9 @@ void SyntaxLineEdit::setSyntaxState(SyntaxState state) {
.arg(Invalid)
.arg(Deprecated)
.arg("palette(text)") // Foreground
- .arg(ColorUtils::fromColorT(&prefs.gui_text_valid).name()) // Invalid
- .arg(ColorUtils::fromColorT(&prefs.gui_text_invalid).name()) // Deprecated
- .arg(ColorUtils::fromColorT(&prefs.gui_text_deprecated).name()) // Valid
+ .arg(ColorUtils::fromColorT(&prefs.gui_text_valid).name()) // Valid
+ .arg(ColorUtils::fromColorT(&prefs.gui_text_invalid).name()) // Invalid
+ .arg(ColorUtils::fromColorT(&prefs.gui_text_deprecated).name()) // VDeprecated
;
setStyleSheet(style_sheet_);
}
diff --git a/ui/qt/traffic_table_dialog.cpp b/ui/qt/traffic_table_dialog.cpp
index e4b1034c64..4e3865f215 100644
--- a/ui/qt/traffic_table_dialog.cpp
+++ b/ui/qt/traffic_table_dialog.cpp
@@ -44,6 +44,9 @@
#include <QTextStream>
#include <QToolButton>
+// To do:
+// - Add "copy" items to the menu.
+
// Bugs:
// - Name resolution doesn't do anything if its preference is disabled.
// - Columns don't resize correctly.
diff --git a/ui/qt/wireshark_dialog.h b/ui/qt/wireshark_dialog.h
index 95be7f05c6..36072a79ea 100644
--- a/ui/qt/wireshark_dialog.h
+++ b/ui/qt/wireshark_dialog.h
@@ -39,6 +39,7 @@ signals:
public slots:
protected:
+ virtual void keyPressEvent(QKeyEvent *event) { QDialog::keyPressEvent(event); }
void setWindowSubtitle(const QString &subtitle);
virtual void updateWidgets();