aboutsummaryrefslogtreecommitdiffstats
path: root/ui/qt
diff options
context:
space:
mode:
authorJirka Novak <j.novak@netsystem.cz>2021-03-09 01:58:15 +0100
committerWireshark GitLab Utility <gerald+gitlab-utility@wireshark.org>2021-03-09 16:49:09 +0000
commitce786ed26528f8f39f28608afffab132909da6eb (patch)
tree223f9fbdab518f95a61a370b79cc8442cb83cf4d /ui/qt
parent5d709459c4728a460888ee8602f3990f17e91318 (diff)
Rtp player: It is possible to select multiple rows in stream's list and graph
Changes: - It is possible to select multiple streams in list and in graph - Select All/None/Invert implemented in list of streams and in graph - Indication of "Selected" stream redesigned in graph - Mouse hovering shows related row/wave - All operations adapted to multiselection
Diffstat (limited to 'ui/qt')
-rw-r--r--ui/qt/CMakeLists.txt2
-rw-r--r--ui/qt/rtp_player_dialog.cpp460
-rw-r--r--ui/qt/rtp_player_dialog.h16
-rw-r--r--ui/qt/rtp_player_dialog.ui112
-rw-r--r--ui/qt/widgets/rtp_audio_graph.cpp102
-rw-r--r--ui/qt/widgets/rtp_audio_graph.h53
6 files changed, 538 insertions, 207 deletions
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index f703d0119a..4d5d034d52 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -37,6 +37,7 @@ set(WIRESHARK_WIDGET_HEADERS
widgets/packet_list_header.h
widgets/profile_tree_view.h
widgets/range_syntax_lineedit.h
+ widgets/rtp_audio_graph.h
widgets/splash_overlay.h
widgets/stock_icon_tool_button.h
widgets/syntax_line_edit.h
@@ -280,6 +281,7 @@ set(WIRESHARK_WIDGET_SRCS
widgets/pref_module_view.cpp
widgets/profile_tree_view.cpp
widgets/range_syntax_lineedit.cpp
+ widgets/rtp_audio_graph.cpp
widgets/splash_overlay.cpp
widgets/stock_icon_tool_button.cpp
widgets/syntax_line_edit.cpp
diff --git a/ui/qt/rtp_player_dialog.cpp b/ui/qt/rtp_player_dialog.cpp
index fa7b3df0f1..88c3996884 100644
--- a/ui/qt/rtp_player_dialog.cpp
+++ b/ui/qt/rtp_player_dialog.cpp
@@ -23,6 +23,7 @@
#include <ui/qt/utils/qt_ui_utils.h>
#include "rtp_audio_stream.h"
#include <ui/qt/utils/tango_colors.h>
+#include <widgets/rtp_audio_graph.h>
#include <QAudio>
#include <QAudioDeviceInfo>
@@ -93,10 +94,31 @@ enum {
graph_silence_data_col_ = first_pkt_col_, // QCPGraph (silence)
};
+class RtpPlayerTreeWidgetItem : public QTreeWidgetItem
+{
+public:
+ RtpPlayerTreeWidgetItem(QTreeWidget *tree) :
+ QTreeWidgetItem(tree)
+ {
+ }
-#ifdef QT_MULTIMEDIA_LIB
-static const double wf_graph_normal_width_ = 0.5;
-#endif
+ bool operator< (const QTreeWidgetItem &other) const
+ {
+ // Handle numeric sorting
+ switch (treeWidget()->sortColumn()) {
+ case src_port_col_:
+ case dst_port_col_:
+ case first_pkt_col_:
+ case num_pkts_col_:
+ case sample_rate_col_:
+ return text(treeWidget()->sortColumn()).toULong() < other.text(treeWidget()->sortColumn()).toULong();
+ default:
+ // Fall back to string comparison
+ return QTreeWidgetItem::operator <(other);
+ break;
+ }
+ }
+};
RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf) :
WiresharkDialog(parent, cf)
@@ -112,16 +134,21 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf) :
, datetime_ticker_(new QCPAxisTickerDateTime)
, stereo_available_(false)
, marker_stream_(0)
+ , last_ti_(0)
, listener_removed_(false)
{
ui->setupUi(this);
- setWindowTitle(wsApp->windowTitleString(tr("RTP Player")));
loadGeometry(parent.width(), parent.height());
+ setWindowTitle(wsApp->windowTitleString(tr("RTP Player")));
+ ui->streamTreeWidget->installEventFilter(this);
+ ui->audioPlot->installEventFilter(this);
#ifdef QT_MULTIMEDIA_LIB
ui->splitter->setStretchFactor(0, 3);
ui->splitter->setStretchFactor(1, 1);
+ ui->streamTreeWidget->sortByColumn(first_pkt_col_, Qt::AscendingOrder);
+
graph_ctx_menu_ = new QMenu(this);
graph_ctx_menu_->addAction(ui->actionZoomIn);
@@ -134,14 +161,16 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf) :
graph_ctx_menu_->addAction(ui->actionMoveLeft1);
graph_ctx_menu_->addSeparator();
graph_ctx_menu_->addAction(ui->actionGoToPacket);
- graph_ctx_menu_->addSeparator();
- graph_ctx_menu_->addAction(ui->actionDragZoom);
- graph_ctx_menu_->addAction(ui->actionToggleTimeOrigin);
-// graph_ctx_menu_->addAction(ui->actionCrosshairs);
set_action_shortcuts_visible_in_context_menu(graph_ctx_menu_->actions());
+ ui->streamTreeWidget->setMouseTracking(true);
+ connect(ui->streamTreeWidget, SIGNAL(itemEntered(QTreeWidgetItem *, int)),
+ this, SLOT(itemEntered(QTreeWidgetItem *, int)));
+
connect(ui->audioPlot, SIGNAL(mouseMove(QMouseEvent*)),
- this, SLOT(updateHintLabel()));
+ this, SLOT(mouseMovePlot(QMouseEvent*)));
+ connect(ui->audioPlot, SIGNAL(mouseMove(QMouseEvent*)),
+ this, SLOT(mouseMovePlot(QMouseEvent*)));
connect(ui->audioPlot, SIGNAL(mousePress(QMouseEvent*)),
this, SLOT(graphClicked(QMouseEvent*)));
connect(ui->audioPlot, SIGNAL(mouseDoubleClick(QMouseEvent*)),
@@ -204,15 +233,22 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf) :
QCP::iRangeDrag |
QCP::iRangeZoom
);
- ui->audioPlot->setFocus();
graph_ctx_menu_->addSeparator();
list_ctx_menu_ = new QMenu(this);
+ QMenu *selection_menu1 = list_ctx_menu_->addMenu(tr("Select"));
+ QMenu *selection_menu2 = graph_ctx_menu_->addMenu(tr("Select"));
+ selection_menu1->addAction(ui->actionSelectAll);
+ selection_menu2->addAction(ui->actionSelectAll);
+ selection_menu1->addAction(ui->actionSelectNone);
+ selection_menu2->addAction(ui->actionSelectNone);
+ selection_menu1->addAction(ui->actionSelectInvert);
+ selection_menu2->addAction(ui->actionSelectInvert);
QMenu *audio_routing_menu1 = list_ctx_menu_->addMenu(tr("Audio Routing"));
QMenu *audio_routing_menu2 = graph_ctx_menu_->addMenu(tr("Audio Routing"));
// All AudioRouting actions are in menu, some of them are disabled later
- audio_routing_menu1->addAction(ui->actionAudioRoutingM);
- audio_routing_menu2->addAction(ui->actionAudioRoutingM);
+ audio_routing_menu1->addAction(ui->actionAudioRoutingI);
+ audio_routing_menu2->addAction(ui->actionAudioRoutingI);
audio_routing_menu1->addAction(ui->actionAudioRoutingP);
audio_routing_menu2->addAction(ui->actionAudioRoutingP);
audio_routing_menu1->addAction(ui->actionAudioRoutingL);
@@ -221,8 +257,6 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf) :
audio_routing_menu2->addAction(ui->actionAudioRoutingLR);
audio_routing_menu1->addAction(ui->actionAudioRoutingR);
audio_routing_menu2->addAction(ui->actionAudioRoutingR);
- audio_routing_menu1->addAction(ui->actionAudioRoutingI);
- audio_routing_menu2->addAction(ui->actionAudioRoutingI);
list_ctx_menu_->addAction(ui->actionRemoveStream);
graph_ctx_menu_->addAction(ui->actionRemoveStream);
set_action_shortcuts_visible_in_context_menu(list_ctx_menu_->actions());
@@ -379,19 +413,11 @@ void RtpPlayerDialog::createPlot(bool rescale_axes)
ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant());
// Waveform
- QCPGraph *audio_graph = ui->audioPlot->addGraph();
- QPen wf_pen(audio_stream->color());
- wf_pen.setWidthF(wf_graph_normal_width_);
- if (audio_routing.isMuted()) {
- // Indicate that audio will not be hearable
- wf_pen.setStyle(Qt::DotLine);
- }
- audio_graph->setPen(wf_pen);
- audio_graph->setSelectable(QCP::stNone);
+ RtpAudioGraph *audio_graph = new RtpAudioGraph(ui->audioPlot, audio_stream->color());
+ audio_graph->setMuted(audio_routing.isMuted());
audio_graph->setData(audio_stream->visualTimestamps(relative_timestamps), audio_stream->visualSamples(y_offset));
- audio_graph->removeFromLegend();
- ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(audio_graph));
- RTP_STREAM_DEBUG("Plotting %s, %d samples", ti->text(src_addr_col_).toUtf8().constData(), audio_graph->data()->size());
+ ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant::fromValue<RtpAudioGraph *>(audio_graph));
+ //RTP_STREAM_DEBUG("Plotting %s, %d samples", ti->text(src_addr_col_).toUtf8().constData(), audio_graph->wave->data()->size());
QString span_str;
if (ui->todCheckBox->isChecked()) {
@@ -507,7 +533,7 @@ void RtpPlayerDialog::addRtpStream(rtpstream_info_t *rtpstream)
audio_stream = new RtpAudioStream(this, rtpstream, stereo_available_);
audio_stream->setColor(ColorUtils::graphColor(tli_count));
- QTreeWidgetItem *ti = new QTreeWidgetItem(ui->streamTreeWidget);
+ QTreeWidgetItem *ti = new RtpPlayerTreeWidgetItem(ui->streamTreeWidget);
ti->setText(src_addr_col_, address_to_qstring(&rtpstream->id.src_addr));
ti->setText(src_port_col_, QString::number(rtpstream->id.src_port));
ti->setText(dst_addr_col_, address_to_qstring(&rtpstream->id.dst_addr));
@@ -566,62 +592,83 @@ void RtpPlayerDialog::showEvent(QShowEvent *)
ui->splitter->setSizes(split_sizes);
}
-void RtpPlayerDialog::keyPressEvent(QKeyEvent *event)
-{
- int pan_secs = event->modifiers() & Qt::ShiftModifier ? 1 : 10;
-
- switch(event->key()) {
- case Qt::Key_Minus:
- case Qt::Key_Underscore: // Shifted minus on U.S. keyboards
- case Qt::Key_O: // GTK+
- case Qt::Key_R:
- on_actionZoomOut_triggered();
- break;
- case Qt::Key_Plus:
- case Qt::Key_Equal: // Unshifted plus on U.S. keyboards
- case Qt::Key_I: // GTK+
- on_actionZoomIn_triggered();
- break;
-
- case Qt::Key_Right:
- case Qt::Key_L:
- panXAxis(pan_secs);
- break;
- case Qt::Key_Left:
- case Qt::Key_H:
- panXAxis(-1 * pan_secs);
- break;
-
- case Qt::Key_Space:
-// toggleTracerStyle();
- break;
-
- case Qt::Key_0:
- case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards
- case Qt::Key_Home:
- on_actionReset_triggered();
- break;
-
- case Qt::Key_G:
- on_actionGoToPacket_triggered();
- break;
- case Qt::Key_T:
-// on_actionToggleTimeOrigin_triggered();
- break;
- case Qt::Key_Z:
-// on_actionDragZoom_triggered();
- break;
+bool RtpPlayerDialog::eventFilter(QObject *, QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent &keyEvent = static_cast<QKeyEvent&>(*event);
+ int pan_secs = keyEvent.modifiers() & Qt::ShiftModifier ? 1 : 10;
+
+ switch(keyEvent.key()) {
+ case Qt::Key_Minus:
+ case Qt::Key_Underscore: // Shifted minus on U.S. keyboards
+ case Qt::Key_O: // GTK+
+ case Qt::Key_R:
+ on_actionZoomOut_triggered();
+ return true;
+ case Qt::Key_Plus:
+ case Qt::Key_Equal: // Unshifted plus on U.S. keyboards
+ case Qt::Key_I: // GTK+
+ if (keyEvent.modifiers() == Qt::ControlModifier) {
+ invertSelection();
+ return true;
+ } else {
+ on_actionZoomIn_triggered();
+ return true;
+ }
+ break;
+ case Qt::Key_Right:
+ case Qt::Key_L:
+ panXAxis(pan_secs);
+ return true;
+ case Qt::Key_Left:
+ case Qt::Key_H:
+ panXAxis(-1 * pan_secs);
+ return true;
+ case Qt::Key_0:
+ case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards
+ on_actionReset_triggered();
+ return true;
+ case Qt::Key_G:
+ on_actionGoToPacket_triggered();
+ return true;
+ case Qt::Key_A:
+ if (keyEvent.modifiers() == Qt::ControlModifier) {
+ ui->streamTreeWidget->selectAll();
+ return true;
+ } else if (keyEvent.modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) {
+ ui->streamTreeWidget->clearSelection();
+ return true;
+ }
+ break;
+ case Qt::Key_M:
+ on_actionAudioRoutingI_triggered();
+ return true;
+ case Qt::Key_Delete:
+ on_actionRemoveStream_triggered();
+ return true;
+ case Qt::Key_X:
+ if (keyEvent.modifiers() == Qt::ControlModifier) {
+ on_actionRemoveStream_triggered();
+ return true;
+ }
+ break;
+ // Route keys to QTreeWidget
+ case Qt::Key_Down:
+ case Qt::Key_Up:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ ui->streamTreeWidget->setFocus();
+ break;
+ }
}
- QDialog::keyPressEvent(event);
+ return false;
}
void RtpPlayerDialog::contextMenuEvent(QContextMenuEvent *event)
{
- QTreeWidgetItem *ti = ui->streamTreeWidget->currentItem();
- if (!ti)
- return;
-
list_ctx_menu_->exec(event->globalPos());
}
@@ -673,13 +720,64 @@ void RtpPlayerDialog::updateWidgets()
ui->audioPlot->replot();
}
+void RtpPlayerDialog::handleItemHighlight(QTreeWidgetItem *ti, bool scroll)
+{
+ if (ti) {
+ if (ti != last_ti_) {
+ if (last_ti_) {
+ highlightItem(last_ti_, false);
+ }
+ highlightItem(ti, true);
+
+ if (scroll)
+ ui->streamTreeWidget->scrollToItem(ti, QAbstractItemView::EnsureVisible);
+ ui->audioPlot->replot();
+ last_ti_ = ti;
+ }
+ } else {
+ if (last_ti_) {
+ highlightItem(last_ti_, false);
+ ui->audioPlot->replot();
+ last_ti_ = NULL;
+ }
+ }
+}
+
+void RtpPlayerDialog::highlightItem(QTreeWidgetItem *ti, bool highlight)
+{
+ QFont font;
+ RtpAudioGraph *audio_graph;
+
+ font.setBold(highlight);
+ for(int i=0; i<ui->streamTreeWidget->columnCount(); i++) {
+ ti->setFont(i, font);
+ }
+
+ audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
+ if (audio_graph) {
+ audio_graph->setHighlight(highlight);
+ }
+}
+
+void RtpPlayerDialog::itemEntered(QTreeWidgetItem *item, int column _U_)
+{
+ handleItemHighlight(item, false);
+}
+
+void RtpPlayerDialog::mouseMovePlot(QMouseEvent *event)
+{
+ updateHintLabel();
+
+ QTreeWidgetItem *ti = findItemByCoords(event->pos());
+ handleItemHighlight(ti, true);
+}
+
void RtpPlayerDialog::graphClicked(QMouseEvent *event)
{
updateWidgets();
if (event->button() == Qt::RightButton) {
graph_ctx_menu_->exec(event->globalPos());
}
- ui->audioPlot->setFocus();
}
void RtpPlayerDialog::graphDoubleClicked(QMouseEvent *event)
@@ -687,26 +785,50 @@ void RtpPlayerDialog::graphDoubleClicked(QMouseEvent *event)
updateWidgets();
if (event->button() == Qt::LeftButton) {
// Move start play line
- double ts = ui->audioPlot->xAxis->pixelToCoord(ui->audioPlot->mapFromGlobal(QCursor::pos()).x());
+ double ts = ui->audioPlot->xAxis->pixelToCoord(event->pos().x());
setStartPlayMarker(ts);
drawStartPlayMarker();
ui->audioPlot->replot();
}
- ui->audioPlot->setFocus();
}
-void RtpPlayerDialog::plotClicked(QCPAbstractPlottable *plottable, int dataIndex _U_, QMouseEvent *event _U_)
+void RtpPlayerDialog::plotClicked(QCPAbstractPlottable *plottable _U_, int dataIndex _U_, QMouseEvent *event)
+{
+ // Delivered plottable very often points to different element than a mouse
+ // so we find right one by mouse coordinates
+ QTreeWidgetItem *ti = findItemByCoords(event->pos());
+ if (ti) {
+ if (event->modifiers() == Qt::NoModifier) {
+ ti->setSelected(true);
+ } else if (event->modifiers() == Qt::ControlModifier) {
+ ti->setSelected(!ti->isSelected());
+ }
+ }
+}
+
+QTreeWidgetItem *RtpPlayerDialog::findItemByCoords(QPoint point)
+{
+ QCPAbstractPlottable *plottable=ui->audioPlot->plottableAt(point);
+ if (plottable) {
+ return findItem(plottable);
+ }
+
+ return NULL;
+}
+
+QTreeWidgetItem *RtpPlayerDialog::findItem(QCPAbstractPlottable *plottable)
{
- ui->streamTreeWidget->clearSelection();
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
- QCPGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<QCPGraph*>();
- if (plottable == audio_graph) {
- ui->streamTreeWidget->setCurrentItem(ti);
+ RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
+ if (audio_graph->isMyPlottable(plottable)) {
+ return ti;
}
}
+
+ return NULL;
}
void RtpPlayerDialog::updateHintLabel()
@@ -1006,20 +1128,12 @@ void RtpPlayerDialog::on_streamTreeWidget_itemSelectionChanged()
{
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
- QCPGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<QCPGraph*>();
+ RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
if (audio_graph) {
- audio_graph->setSelection(ti->isSelected() ? QCPDataSelection(QCPDataRange()) : QCPDataSelection());
- QPen p = audio_graph->pen();
- if (ti->isSelected()) {
- p.setWidthF(wf_graph_normal_width_*2);
- } else {
- p.setWidthF(wf_graph_normal_width_);
- }
- audio_graph->setPen(p);
+ audio_graph->setSelected(ti->isSelected());
}
}
ui->audioPlot->replot();
- ui->audioPlot->setFocus();
}
// Change channel audio routing if double clicked channel column
@@ -1038,53 +1152,56 @@ void RtpPlayerDialog::on_streamTreeWidget_itemDoubleClicked(QTreeWidgetItem *ite
void RtpPlayerDialog::on_actionRemoveStream_triggered()
{
- QTreeWidgetItem *ti = ui->streamTreeWidget->currentItem();
- if (!ti)
- return;
+ QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
- RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
- if (audio_stream) {
- ti->setData(stream_data_col_, Qt::UserRole, QVariant());
- delete audio_stream;
+ if (last_ti_) {
+ highlightItem(last_ti_, false);
+ last_ti_ = NULL;
}
+ for(int i = 0; i<items.count(); i++ ) {
- QCPGraph *graph;
- graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<QCPGraph*>();
- if (graph) {
- ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant());
- ui->audioPlot->removeGraph(graph);
- ui->audioPlot->replot();
- }
+ QTreeWidgetItem *ti = items[i];
- graph = ti->data(graph_sequence_data_col_, Qt::UserRole).value<QCPGraph*>();
- if (graph) {
- ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant());
- ui->audioPlot->removeGraph(graph);
- ui->audioPlot->replot();
- }
+ RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
+ if (audio_stream) {
+ ti->setData(stream_data_col_, Qt::UserRole, QVariant());
+ delete audio_stream;
+ }
- graph = ti->data(graph_jitter_data_col_, Qt::UserRole).value<QCPGraph*>();
- if (graph) {
- ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant());
- ui->audioPlot->removeGraph(graph);
- ui->audioPlot->replot();
- }
+ RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
+ if (audio_graph) {
+ ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant());
+ audio_graph->remove(ui->audioPlot);
+ }
- graph = ti->data(graph_timestamp_data_col_, Qt::UserRole).value<QCPGraph*>();
- if (graph) {
- ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant());
- ui->audioPlot->removeGraph(graph);
- ui->audioPlot->replot();
- }
+ QCPGraph *graph;
+ graph = ti->data(graph_sequence_data_col_, Qt::UserRole).value<QCPGraph*>();
+ if (graph) {
+ ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant());
+ ui->audioPlot->removeGraph(graph);
+ }
- graph = ti->data(graph_silence_data_col_, Qt::UserRole).value<QCPGraph*>();
- if (graph) {
- ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant());
- ui->audioPlot->removeGraph(graph);
- ui->audioPlot->replot();
- }
+ graph = ti->data(graph_jitter_data_col_, Qt::UserRole).value<QCPGraph*>();
+ if (graph) {
+ ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant());
+ ui->audioPlot->removeGraph(graph);
+ }
- delete ti;
+ graph = ti->data(graph_timestamp_data_col_, Qt::UserRole).value<QCPGraph*>();
+ if (graph) {
+ ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant());
+ ui->audioPlot->removeGraph(graph);
+ }
+
+ graph = ti->data(graph_silence_data_col_, Qt::UserRole).value<QCPGraph*>();
+ if (graph) {
+ ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant());
+ ui->audioPlot->removeGraph(graph);
+ }
+
+ delete ti;
+ }
+ ui->audioPlot->replot();
updateWidgets();
}
@@ -1105,18 +1222,11 @@ void RtpPlayerDialog::changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting
audio_stream->setAudioRouting(audio_routing);
- QCPGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<QCPGraph*>();
+ RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
if (audio_graph) {
- audio_graph->setSelection(ti->isSelected() ? QCPDataSelection(QCPDataRange()) : QCPDataSelection());
- QPen p = audio_graph->pen();
- if (audio_routing.isMuted()) {
- // Indicate that audio will not be hearable
- p.setStyle(Qt::DotLine);
- } else {
- p.setStyle(Qt::SolidLine);
- }
- audio_graph->setPen(p);
+ audio_graph->setSelected(ti->isSelected());
+ audio_graph->setMuted(audio_routing.isMuted());
ui->audioPlot->replot();
}
}
@@ -1124,8 +1234,13 @@ void RtpPlayerDialog::changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting
// Find current item and apply change on it
void RtpPlayerDialog::changeAudioRouting(AudioRouting new_audio_routing)
{
- QTreeWidgetItem *ti = ui->streamTreeWidget->currentItem();
- changeAudioRoutingOnItem(ti, new_audio_routing);
+ QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
+
+ for(int i = 0; i<items.count(); i++ ) {
+
+ QTreeWidgetItem *ti = items[i];
+ changeAudioRoutingOnItem(ti, new_audio_routing);
+ }
}
// Invert mute/unmute on item
@@ -1147,11 +1262,18 @@ void RtpPlayerDialog::invertAudioMutingOnItem(QTreeWidgetItem *ti)
}
}
-void RtpPlayerDialog::on_actionAudioRoutingM_triggered()
+/*
+void RtpPlayerDialog::on_aactionAudioRoutingMctionAudioRoutingM_triggered()
{
- QTreeWidgetItem *ti = ui->streamTreeWidget->currentItem();
- invertAudioMutingOnItem(ti);
+ QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
+
+ for(int i = 0; i<items.count(); i++ ) {
+
+ QTreeWidgetItem *ti = items[i];
+ changeAudioRoutingOnItem(ti, AudioRouting(AUDIO_MUTED, channel_any));
+ }
}
+*/
void RtpPlayerDialog::on_actionAudioRoutingP_triggered()
{
@@ -1175,8 +1297,11 @@ void RtpPlayerDialog::on_actionAudioRoutingR_triggered()
void RtpPlayerDialog::on_actionAudioRoutingI_triggered()
{
- for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
- QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
+ QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
+
+ for(int i = 0; i<items.count(); i++ ) {
+
+ QTreeWidgetItem *ti = items[i];
invertAudioMutingOnItem(ti);
}
}
@@ -1198,22 +1323,24 @@ const QString RtpPlayerDialog::getFormatedTime(double f_time)
const QString RtpPlayerDialog::getFormatedHoveredTime()
{
- QTreeWidgetItem *ti = ui->streamTreeWidget->currentItem();
+ QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos());
+ QTreeWidgetItem *ti = findItemByCoords(pos);
if (!ti) return tr("Unknown");
- double ts = ui->audioPlot->xAxis->pixelToCoord(ui->audioPlot->mapFromGlobal(QCursor::pos()).x());
+ double ts = ui->audioPlot->xAxis->pixelToCoord(ui->audioPlot->mapFromGlobal(pos).x());
return getFormatedTime(ts);
}
int RtpPlayerDialog::getHoveredPacket()
{
- QTreeWidgetItem *ti = ui->streamTreeWidget->currentItem();
+ QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos());
+ QTreeWidgetItem *ti = findItemByCoords(pos);
if (!ti) return 0;
RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
- double ts = ui->audioPlot->xAxis->pixelToCoord(ui->audioPlot->mapFromGlobal(QCursor::pos()).x());
+ double ts = ui->audioPlot->xAxis->pixelToCoord(ui->audioPlot->mapFromGlobal(pos).x());
return audio_stream->nearestPacket(ts, !ui->todCheckBox->isChecked());
}
@@ -1352,6 +1479,29 @@ bool RtpPlayerDialog::isStereoAvailable()
return false;
}
+void RtpPlayerDialog::invertSelection()
+{
+ for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
+ QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
+ ti->setSelected(!ti->isSelected());
+ }
+}
+
+void RtpPlayerDialog::on_actionSelectAll_triggered()
+{
+ ui->streamTreeWidget->selectAll();
+}
+
+void RtpPlayerDialog::on_actionSelectInvert_triggered()
+{
+ invertSelection();
+}
+
+void RtpPlayerDialog::on_actionSelectNone_triggered()
+{
+ ui->streamTreeWidget->clearSelection();
+}
+
#if 0
// This also serves as a title in RtpAudioFrame.
static const QString stream_key_tmpl_ = "%1:%2 " UTF8_RIGHTWARDS_ARROW " %3:%4 0x%5";
diff --git a/ui/qt/rtp_player_dialog.h b/ui/qt/rtp_player_dialog.h
index c9d8f659cb..1983cf2030 100644
--- a/ui/qt/rtp_player_dialog.h
+++ b/ui/qt/rtp_player_dialog.h
@@ -77,8 +77,8 @@ signals:
protected:
virtual void showEvent(QShowEvent *);
- virtual void keyPressEvent(QKeyEvent *event);
void contextMenuEvent(QContextMenuEvent *event);
+ bool eventFilter(QObject *obj, QEvent *event);
private slots:
/** Retap the capture file, adding RTP packets that match the
@@ -90,6 +90,8 @@ private slots:
void rescanPackets(bool rescale_axes = false);
void createPlot(bool rescale_axes = false);
void updateWidgets();
+ void itemEntered(QTreeWidgetItem *item, int column);
+ void mouseMovePlot(QMouseEvent *event);
void graphClicked(QMouseEvent *event);
void graphDoubleClicked(QMouseEvent *event);
void plotClicked(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event);
@@ -114,7 +116,7 @@ private slots:
void on_actionMoveRight1_triggered();
void on_actionGoToPacket_triggered();
void on_actionRemoveStream_triggered();
- void on_actionAudioRoutingM_triggered();
+ //void on_actionAudioRoutingM_triggered();
void on_actionAudioRoutingP_triggered();
void on_actionAudioRoutingL_triggered();
void on_actionAudioRoutingLR_triggered();
@@ -127,6 +129,9 @@ private slots:
void on_timingComboBox_currentIndexChanged(int);
void on_todCheckBox_toggled(bool checked);
void on_buttonBox_helpRequested();
+ void on_actionSelectAll_triggered();
+ void on_actionSelectInvert_triggered();
+ void on_actionSelectNone_triggered();
void outputNotify();
private:
@@ -147,7 +152,7 @@ private:
bool stereo_available_;
QList<RtpAudioStream *> playing_streams_;
QAudioOutput *marker_stream_;
-
+ QTreeWidgetItem *last_ti_;
bool listener_removed_;
// const QString streamKey(const rtpstream_info_t *rtpstream);
@@ -173,6 +178,11 @@ private:
bool isStereoAvailable();
QAudioOutput *getSilenceAudioOutput();
QAudioDeviceInfo getCurrentDeviceInfo();
+ QTreeWidgetItem *findItemByCoords(QPoint point);
+ QTreeWidgetItem *findItem(QCPAbstractPlottable *plottable);
+ void handleItemHighlight(QTreeWidgetItem *ti, bool scroll);
+ void highlightItem(QTreeWidgetItem *ti, bool highlight);
+ void invertSelection();
#else // QT_MULTIMEDIA_LIB
private:
diff --git a/ui/qt/rtp_player_dialog.ui b/ui/qt/rtp_player_dialog.ui
index c3f8e294ad..9f29bc608a 100644
--- a/ui/qt/rtp_player_dialog.ui
+++ b/ui/qt/rtp_player_dialog.ui
@@ -21,6 +21,9 @@
</property>
<widget class="QCustomPlot" name="audioPlot" native="true"/>
<widget class="QTreeWidget" name="streamTreeWidget">
+ <property name="selectionMode">
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
@@ -30,6 +33,12 @@
<property name="itemsExpandable">
<bool>false</bool>
</property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="expandsOnDoubleClick">
+ <bool>false</bool>
+ </property>
<column>
<property name="text">
<string>Play</string>
@@ -107,10 +116,6 @@
&lt;tr&gt;&lt;th&gt;g&lt;/th&gt;&lt;td&gt;Go to packet under cursor&lt;/td&gt;&lt;/th&gt;
-&lt;tr&gt;&lt;th&gt;z&lt;/th&gt;&lt;td&gt;Toggle mouse drag / zoom&lt;/td&gt;&lt;/th&gt;
-&lt;tr&gt;&lt;th&gt;t&lt;/th&gt;&lt;td&gt;Toggle capture / session time origin&lt;/td&gt;&lt;/th&gt;
-&lt;tr&gt;&lt;th&gt;Space&lt;/th&gt;&lt;td&gt;Toggle crosshairs&lt;/td&gt;&lt;/th&gt;
-
&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;&lt;/html&gt;</string>
</property>
@@ -327,7 +332,7 @@
<string>Reset the graph to its initial state.</string>
</property>
<property name="shortcut">
- <string>0</string>
+ <string notr="true">0</string>
</property>
</action>
<action name="actionZoomIn">
@@ -338,7 +343,7 @@
<string>Zoom In</string>
</property>
<property name="shortcut">
- <string>+</string>
+ <string notr="true">+</string>
</property>
</action>
<action name="actionZoomOut">
@@ -349,7 +354,7 @@
<string>Zoom Out</string>
</property>
<property name="shortcut">
- <string>-</string>
+ <string notr="true">-</string>
</property>
</action>
<action name="actionMoveLeft10">
@@ -360,7 +365,7 @@
<string>Move Left 10 Pixels</string>
</property>
<property name="shortcut">
- <string>Left</string>
+ <string notr="true">Left</string>
</property>
</action>
<action name="actionMoveRight10">
@@ -371,7 +376,7 @@
<string>Move Right 10 Pixels</string>
</property>
<property name="shortcut">
- <string>Right</string>
+ <string notr="true">Right</string>
</property>
</action>
<action name="actionMoveLeft1">
@@ -382,7 +387,7 @@
<string>Move Left 1 Pixels</string>
</property>
<property name="shortcut">
- <string>Shift+Left</string>
+ <string notr="true">Shift+Left</string>
</property>
</action>
<action name="actionMoveRight1">
@@ -393,7 +398,7 @@
<string>Move Right 1 Pixels</string>
</property>
<property name="shortcut">
- <string>Shift+Right</string>
+ <string notr="true">Shift+Right</string>
</property>
</action>
<action name="actionGoToPacket">
@@ -404,96 +409,105 @@
<string>Go to packet currently under the cursor</string>
</property>
<property name="shortcut">
- <string>G</string>
+ <string notr="true">G</string>
</property>
</action>
- <action name="actionDragZoom">
+ <action name="actionAudioRoutingM">
<property name="text">
- <string>Drag / Zoom</string>
+ <string>Mute/Unmute</string>
</property>
<property name="toolTip">
- <string>Toggle mouse drag / zoom behavior</string>
+ <string>Mute or unmute selected streams</string>
</property>
<property name="shortcut">
- <string>Z</string>
+ <string notr="true">M</string>
</property>
</action>
- <action name="actionToggleTimeOrigin">
+ <action name="actionAudioRoutingP">
<property name="text">
- <string>Capture / Session Time Origin</string>
+ <string>Play</string>
</property>
<property name="toolTip">
- <string>Toggle capture / session time origin</string>
- </property>
- <property name="shortcut">
- <string>T</string>
+ <string>Play the stream</string>
</property>
</action>
- <action name="actionCrosshairs">
+ <action name="actionAudioRoutingL">
<property name="text">
- <string>Crosshairs</string>
+ <string>To Left</string>
</property>
<property name="toolTip">
- <string>Toggle crosshairs</string>
- </property>
- <property name="shortcut">
- <string>Space</string>
+ <string>Route audio to left channel of selected streams</string>
</property>
</action>
- <action name="actionAudioRoutingM">
+ <action name="actionAudioRoutingLR">
<property name="text">
- <string>Mute/Unmute</string>
+ <string>Left + Right</string>
</property>
<property name="toolTip">
- <string>Mute or unmute the stream</string>
+ <string>Route audio to left and right channel of selected streams</string>
</property>
</action>
- <action name="actionAudioRoutingP">
+ <action name="actionAudioRoutingR">
<property name="text">
- <string>Play</string>
+ <string>To Right</string>
</property>
<property name="toolTip">
- <string>Play the stream</string>
+ <string>Route audio to right channel of selected streams</string>
</property>
</action>
- <action name="actionAudioRoutingL">
+ <action name="actionAudioRoutingI">
<property name="text">
- <string>To Left</string>
+ <string>Invert Muting</string>
</property>
<property name="toolTip">
- <string>Route audio to left channel</string>
+ <string>Invert muting of selected streams</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">M</string>
</property>
</action>
- <action name="actionAudioRoutingLR">
+ <action name="actionRemoveStream">
<property name="text">
- <string>Left + Right</string>
+ <string>Remove Streams</string>
</property>
<property name="toolTip">
- <string>Route audio to left and right channel</string>
+ <string>Remove selected streams from the list</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">Delete</string>
</property>
</action>
- <action name="actionAudioRoutingR">
+ <action name="actionSelectAll">
<property name="text">
- <string>To Right</string>
+ <string>All</string>
</property>
<property name="toolTip">
- <string>Route audio to right channel</string>
+ <string>Select all</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">Ctrl+A</string>
</property>
</action>
- <action name="actionAudioRoutingI">
+ <action name="actionSelectNone">
<property name="text">
- <string>Invert Muting</string>
+ <string>None</string>
</property>
<property name="toolTip">
- <string>Invert muting of all channels</string>
+ <string>Clear selection</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">Ctrl+Shift+A</string>
</property>
</action>
- <action name="actionRemoveStream">
+ <action name="actionSelectInvert">
<property name="text">
- <string>Remove Stream</string>
+ <string>Invert</string>
</property>
<property name="toolTip">
- <string>Remove stream from the list</string>
+ <string>Invert selection</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">Ctrl+I</string>
</property>
</action>
</widget>
diff --git a/ui/qt/widgets/rtp_audio_graph.cpp b/ui/qt/widgets/rtp_audio_graph.cpp
new file mode 100644
index 0000000000..ca47df5ea3
--- /dev/null
+++ b/ui/qt/widgets/rtp_audio_graph.cpp
@@ -0,0 +1,102 @@
+/* rtp_audio_graph.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "rtp_audio_graph.h"
+
+#include <glib.h>
+
+#include <epan/prefs.h>
+#include <ui/qt/utils/color_utils.h>
+
+static const double wf_graph_normal_width_ = 0.5;
+
+RtpAudioGraph::RtpAudioGraph(QCustomPlot *audio_plot, QRgb color)
+{
+ QPen p;
+ QPalette sel_pal;
+
+ color_ = color;
+ wave_ = audio_plot->addGraph();
+ p = QPen(wave_->pen());
+ p.setColor(color_);
+ p.setWidthF(wf_graph_normal_width_);
+ wave_->setPen(p);
+ wave_->setSelectable(QCP::stNone);
+ wave_->removeFromLegend();
+ selection_color_ = sel_pal.color(QPalette::Highlight);
+}
+
+// Indicate that audio will not be hearable
+void RtpAudioGraph::setMuted(bool isMuted)
+{
+ QPen p = wave_->pen();
+ if (isMuted) {
+ p.setStyle(Qt::DotLine);
+ } else {
+ p.setStyle(Qt::SolidLine);
+ }
+ wave_->setPen(p);
+}
+
+void RtpAudioGraph::setHighlight(bool isHighlighted)
+{
+ wave_->setSelection(isHighlighted ? QCPDataSelection(QCPDataRange()) : QCPDataSelection());
+ QPen p = wave_->pen();
+ if (isHighlighted) {
+ p.setWidthF(wf_graph_normal_width_*2);
+ } else {
+ p.setWidthF(wf_graph_normal_width_);
+ }
+ wave_->setPen(p);
+}
+
+void RtpAudioGraph::setSelected(bool isSelected)
+{
+ wave_->setSelection(isSelected ? QCPDataSelection(QCPDataRange()) : QCPDataSelection());
+ QPen p = wave_->pen();
+ if (isSelected) {
+ p.setColor(selection_color_);
+ } else {
+ p.setColor(color_);
+ }
+ wave_->setPen(p);
+}
+
+void RtpAudioGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
+{
+ wave_->setData(keys, values, alreadySorted);
+}
+
+void RtpAudioGraph::remove(QCustomPlot *audioPlot)
+{
+ audioPlot->removeGraph(wave_);
+}
+
+bool RtpAudioGraph::isMyPlottable(QCPAbstractPlottable *plottable)
+{
+ if (plottable == wave_) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+/*
+ * 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/rtp_audio_graph.h b/ui/qt/widgets/rtp_audio_graph.h
new file mode 100644
index 0000000000..e48bb5211a
--- /dev/null
+++ b/ui/qt/widgets/rtp_audio_graph.h
@@ -0,0 +1,53 @@
+/* rtp_audio_graph.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RTP_AUDIO_GRAPH_H
+#define RTP_AUDIO_GRAPH_H
+
+#include "config.h"
+
+#include <ui/qt/widgets/qcustomplot.h>
+
+//class QCPItemStraightLine;
+//class QCPAxisTicker;
+//class QCPAxisTickerDateTime;
+
+class RtpAudioGraph : public QObject
+{
+ Q_OBJECT
+public:
+ explicit RtpAudioGraph(QCustomPlot *audioPlot, QRgb color);
+ void setMuted(bool isMuted);
+ void setHighlight(bool isHighlighted);
+ void setSelected(bool isSelected);
+ void setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);
+ void remove(QCustomPlot *audioPlot);
+ bool isMyPlottable(QCPAbstractPlottable *plottable);
+
+
+private:
+ QCPGraph *wave_;
+ QRgb color_;
+ QColor selection_color_;
+};
+
+#endif // RTP_AUDIO_GRAPH_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:
+ */