diff options
author | Jirka Novak <j.novak@netsystem.cz> | 2021-03-09 01:58:15 +0100 |
---|---|---|
committer | Wireshark GitLab Utility <gerald+gitlab-utility@wireshark.org> | 2021-03-09 16:49:09 +0000 |
commit | ce786ed26528f8f39f28608afffab132909da6eb (patch) | |
tree | 223f9fbdab518f95a61a370b79cc8442cb83cf4d /ui/qt | |
parent | 5d709459c4728a460888ee8602f3990f17e91318 (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.txt | 2 | ||||
-rw-r--r-- | ui/qt/rtp_player_dialog.cpp | 460 | ||||
-rw-r--r-- | ui/qt/rtp_player_dialog.h | 16 | ||||
-rw-r--r-- | ui/qt/rtp_player_dialog.ui | 112 | ||||
-rw-r--r-- | ui/qt/widgets/rtp_audio_graph.cpp | 102 | ||||
-rw-r--r-- | ui/qt/widgets/rtp_audio_graph.h | 53 |
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 @@ <tr><th>g</th><td>Go to packet under cursor</td></th> -<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> -<tr><th>t</th><td>Toggle capture / session time origin</td></th> -<tr><th>Space</th><td>Toggle crosshairs</td></th> - </tbody></table> </body></html></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: + */ |