diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:14:53 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:14:53 +0000 |
commit | a86c5f7cae7ec9a3398300555a0b644689d946a1 (patch) | |
tree | 39fe4b107c71174fd1e8a8ceb9a4d2aa14116248 /ui/qt/sequence_dialog.cpp | |
parent | Releasing progress-linux version 4.2.6-1~progress7.99u1. (diff) | |
download | wireshark-a86c5f7cae7ec9a3398300555a0b644689d946a1.tar.xz wireshark-a86c5f7cae7ec9a3398300555a0b644689d946a1.zip |
Merging upstream version 4.4.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ui/qt/sequence_dialog.cpp')
-rw-r--r-- | ui/qt/sequence_dialog.cpp | 228 |
1 files changed, 180 insertions, 48 deletions
diff --git a/ui/qt/sequence_dialog.cpp b/ui/qt/sequence_dialog.cpp index e5fb7b87..8a541979 100644 --- a/ui/qt/sequence_dialog.cpp +++ b/ui/qt/sequence_dialog.cpp @@ -35,27 +35,20 @@ #include <QPoint> // To do: -// - Resize or show + hide the Time and Comment axes, possibly via one of -// the following: -// - Split the time, diagram, and comment sections into three separate -// widgets inside a QSplitter. This would resemble the GTK+ UI, but we'd -// have to coordinate between the three and we'd lose time and comment -// values in PDF and PNG exports. -// - Add separate controls for the width and/or visibility of the Time and -// Comment columns. -// - Fake a splitter widget by catching mouse events in the plot area. -// Drawing a QCPItemLine or QCPItemPixmap over each Y axis might make -// this easier. +// - Resize the Time and Comment axis as well? // - For general flows, let the user show columns other than COL_INFO. +// (#12549) // - Add UTF8 to text dump // - Save to XMI? https://www.spinellis.gr/umlgraph/ -// - Time: abs vs delta +// - Save to SVG? https://www.qcustomplot.com/index.php/support/forum/1677 +// - Time: abs vs delta (XXX - This is currently achieved by changing +// View->Time Display Format before opening the dialog.) // - Hide nodes -// - Clickable time + comments? +// - Clickable time + comments? (XXX - Clicking on them selects the item for +// the row, is there anything else?) // - Incorporate packet comments? // - Change line_style to seq_type (i.e. draw ACKs dashed) // - Create WSGraph subclasses with common behavior. -// - Help button and text static const double min_top_ = -1.0; static const double min_left_ = -0.5; @@ -73,6 +66,8 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i num_items_(0), packet_num_(0), sequence_w_(1), + axis_pressed_(false), + current_rtp_sai_hovered_(nullptr), voipFeaturesEnabled(voipFeatures) { QAction *action; @@ -86,6 +81,7 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i if (!info_) { info_ = new SequenceInfo(sequence_analysis_info_new()); info_->sainfo()->name = "any"; + info_->sainfo()->any_addr = true; } else { info_->ref(); sequence_analysis_free_nodes(info_->sainfo()); @@ -99,16 +95,33 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i //sp->axisRect()->setRangeDragAxes(sp->xAxis2, sp->yAxis); //sp->setInteractions(QCP::iRangeDrag); + sp->setInteraction(QCP::iSelectAxes, true); + sp->xAxis->setSelectableParts(QCPAxis::spNone); + sp->xAxis2->setSelectableParts(QCPAxis::spNone); + sp->yAxis->setSelectableParts(QCPAxis::spNone); + sp->yAxis2->setSelectableParts(QCPAxis::spAxis); + sp->xAxis->setVisible(false); sp->xAxis->setPadding(0); sp->xAxis->setLabelPadding(0); sp->xAxis->setTickLabelPadding(0); + // Light border for the diagram QPen base_pen(ColorUtils::alphaBlend(palette().text(), palette().base(), 0.25)); base_pen.setWidthF(0.5); sp->xAxis2->setBasePen(base_pen); sp->yAxis->setBasePen(base_pen); sp->yAxis2->setBasePen(base_pen); + // Keep the border the same if/when the axis is selected, instead of blue + sp->xAxis2->setSelectedBasePen(base_pen); + sp->yAxis->setSelectedBasePen(base_pen); + sp->yAxis2->setSelectedBasePen(base_pen); + + /* QCP documentation for setTicks() says "setting show to false does not imply + * that tick labels are invisible, too." In practice it seems to make them + * invisible, though, so set the length to 0. + */ + sp->yAxis2->setTickLength(0); sp->xAxis2->setVisible(true); sp->yAxis2->setVisible(true); @@ -162,7 +175,18 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i action->setEnabled(false); set_action_shortcuts_visible_in_context_menu(ctx_menu_.actions()); - ui->addressComboBox->setCurrentIndex(0); + sp->setContextMenuPolicy(Qt::CustomContextMenu); + connect(sp, &QCustomPlot::customContextMenuRequested, this, &SequenceDialog::showContextMenu); + + ui->addressComboBox->addItem(tr("Any"), QVariant(true)); + ui->addressComboBox->addItem(tr("Network"), QVariant(false)); + ui->addressComboBox->setCurrentIndex(ui->addressComboBox->findData(QVariant(true))); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + connect(ui->addressComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SequenceDialog::addressChanged); +#else + connect(ui->addressComboBox, &QComboBox::currentIndexChanged, this, &SequenceDialog::addressChanged); +#endif sequence_items_t item_data; @@ -198,13 +222,20 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i loadGeometry(parent.width(), parent.height() * 4 / 5); + if (cf.isValid() && cf.displayFilter().length() > 0) { + ui->displayFilterCheckBox->setChecked(true); + } + + connect(ui->displayFilterCheckBox, &QCheckBox::toggled, this, &SequenceDialog::displayFilterCheckBoxToggled); connect(ui->horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(hScrollBarChanged(int))); connect(ui->verticalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(vScrollBarChanged(int))); connect(sp->xAxis2, SIGNAL(rangeChanged(QCPRange)), this, SLOT(xAxisChanged(QCPRange))); connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(yAxisChanged(QCPRange))); - connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(diagramClicked(QMouseEvent*))); - connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); - connect(sp, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheeled(QWheelEvent*))); + connect(sp, &QCustomPlot::mousePress, this, &SequenceDialog::diagramClicked); + connect(sp, &QCustomPlot::mouseRelease, this, &SequenceDialog::mouseReleased); + connect(sp, &QCustomPlot::mouseMove, this, &SequenceDialog::mouseMoved); + connect(sp, &QCustomPlot::mouseWheel, this, &SequenceDialog::mouseWheeled); + connect(sp, &QCustomPlot::axisDoubleClick, this, &SequenceDialog::axisDoubleClicked); connect(sp, &QCustomPlot::afterLayout, this, &SequenceDialog::layoutAxisLabels); } @@ -227,6 +258,23 @@ void SequenceDialog::updateWidgets() WiresharkDialog::updateWidgets(); } +bool SequenceDialog::event(QEvent *event) +{ + if (event->type() == QEvent::ToolTip) { + QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event); + seq_analysis_item_t *sai = seq_diagram_->itemForPosY(helpEvent->pos().y()); + if (sai && seq_diagram_->inComment(helpEvent->pos()) && (sai->comment != seq_diagram_->elidedComment(sai->comment))) { + QToolTip::showText(helpEvent->globalPos(), sai->comment); + } else { + QToolTip::hideText(); + event->ignore(); + } + + return true; + } + return QWidget::event(event); +} + void SequenceDialog::showEvent(QShowEvent *) { QTimer::singleShot(0, this, SLOT(fillDiagram())); @@ -314,7 +362,7 @@ void SequenceDialog::hScrollBarChanged(int value) { if (qAbs(ui->sequencePlot->xAxis2->range().center()-value/100.0) > 0.01) { ui->sequencePlot->xAxis2->setRange(value/100.0, ui->sequencePlot->xAxis2->range().size(), Qt::AlignCenter); - ui->sequencePlot->replot(); + ui->sequencePlot->replot(QCustomPlot::rpQueuedReplot); } } @@ -322,7 +370,7 @@ void SequenceDialog::vScrollBarChanged(int value) { if (qAbs(ui->sequencePlot->yAxis->range().center()-value/100.0) > 0.01) { ui->sequencePlot->yAxis->setRange(value/100.0, ui->sequencePlot->yAxis->range().size(), Qt::AlignCenter); - ui->sequencePlot->replot(); + ui->sequencePlot->replot(QCustomPlot::rpQueuedReplot); } } @@ -338,10 +386,20 @@ void SequenceDialog::yAxisChanged(QCPRange range) ui->verticalScrollBar->setPageStep(qRound(qreal(range.size()*100.0))); } +void SequenceDialog::showContextMenu(const QPoint &pos) +{ + ctx_menu_.popup(ui->sequencePlot->mapToGlobal(pos)); +} + void SequenceDialog::diagramClicked(QMouseEvent *event) { current_rtp_sai_selected_ = NULL; if (event) { + QCPAxis *yAxis2 = ui->sequencePlot->yAxis2; + if (std::abs(event->pos().x() - yAxis2->axisRect()->right()) < 5) { + yAxis2->setSelectedParts(QCPAxis::spAxis); + axis_pressed_ = true; + } seq_analysis_item_t *sai = seq_diagram_->itemForPosY(event->pos().y()); if (voipFeaturesEnabled) { ui->actionSelectRtpStreams->setEnabled(false); @@ -361,13 +419,6 @@ void SequenceDialog::diagramClicked(QMouseEvent *event) case Qt::LeftButton: on_actionGoToPacket_triggered(); break; - case Qt::RightButton: -#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) - ctx_menu_.popup(event->globalPosition().toPoint()); -#else - ctx_menu_.popup(event->globalPos()); -#endif - break; default: break; } @@ -375,12 +426,47 @@ void SequenceDialog::diagramClicked(QMouseEvent *event) } +void SequenceDialog::axisDoubleClicked(QCPAxis *axis, QCPAxis::SelectablePart, QMouseEvent*) +{ + if (axis == ui->sequencePlot->yAxis2) { + QCP::MarginSides autoMargins = axis->axisRect()->autoMargins(); + axis->axisRect()->setAutoMargins(autoMargins | QCP::msRight); + ui->sequencePlot->replot(); + axis->axisRect()->setAutoMargins(autoMargins); + ui->sequencePlot->replot(); + } +} + +void SequenceDialog::mouseReleased(QMouseEvent*) +{ + QCustomPlot *sp = ui->sequencePlot; + sp->yAxis2->setSelectedParts(QCPAxis::spNone); + axis_pressed_ = false; + sp->replot(QCustomPlot::rpQueuedReplot); +} + void SequenceDialog::mouseMoved(QMouseEvent *event) { current_rtp_sai_hovered_ = NULL; packet_num_ = 0; QString hint; + Qt::CursorShape shape = Qt::ArrowCursor; if (event) { + QCPAxis *yAxis2 = ui->sequencePlot->yAxis2; + // For some reason we need this extra bool and can't rely just on + // yAxis2->selectedParts().testFlag(QCPAxis::spAxis) + if (axis_pressed_) { + int x = qMax(event->pos().x(), yAxis2->axisRect()->left()); + QMargins margins = yAxis2->axisRect()->margins(); + margins += QMargins(0, 0, yAxis2->axisRect()->right() - x, 0); + yAxis2->axisRect()->setMargins(margins); + shape = Qt::SplitHCursor; + ui->sequencePlot->replot(QCustomPlot::rpQueuedReplot); + } else { + if (std::abs(event->pos().x() - yAxis2->axisRect()->right()) < 5) { + shape = Qt::SplitHCursor; + } + } seq_analysis_item_t *sai = seq_diagram_->itemForPosY(event->pos().y()); if (sai) { if (GA_INFO_TYPE_RTP == sai->info_type) { @@ -393,6 +479,10 @@ void SequenceDialog::mouseMoved(QMouseEvent *event) } } + if (ui->sequencePlot->cursor().shape() != shape) { + ui->sequencePlot->setCursor(QCursor(shape)); + } + if (hint.isEmpty()) { if (!info_->sainfo()) { hint += tr("No data"); @@ -456,14 +546,55 @@ void SequenceDialog::exportDiagram() if (file_name.length() > 0) { bool save_ok = false; + // The QCustomPlot save functions take a width and a height, measured + // in pixels (for the entire viewport). + // In order to display the whole graph, we have to change the axes + // and scale up the width and height appropriately so that the text + // has the proper spacing. (Using the scale factor in some of the + // image writing functions makes the text illegible.) + // If we set the axes back to their old value without replotting, + // there's no visual effects from doing this. + QCustomPlot *sp = ui->sequencePlot; + QCPRange old_yrange = sp->yAxis->range(); + QCPRange old_xrange = sp->xAxis2->range(); + // For the horizontal aspect, we'll display all the nodes. + // Nodes can excluded by filtering (hiding nodes is in the todo list.) + // Use the current width of a node as determined by the user zooming + // with Key_Plus and Key_Minus, multiply that by the number of nodes, + // and add in the margin from the Time and Comment columns. + // MAX_NUM_NODES is 40, which results in a manageable 8802 pixel width + // at the default zoom level on my Linux box. + // (If the user has zoomed in unreasonably, that's on the user.) + int hmargin = sp->axisRect()->outerRect().width() - sp->axisRect()->width(); + double nodeSize = (sp->axisRect()->width()) / old_xrange.size(); + // For the vertical aspect, we need to put a bound on the number of + // pixels or items we'll put in an image, as it can get far too large. + // (JPEG only supports 16 bit aspect sizes, PNG supports 31 bit but + // many viewers don't.) + int vmargin = sp->axisRect()->outerRect().height() - sp->axisRect()->height(); + // 1000 items is a little over 27000 pixels in height on my machine. + // XXX - Should this pref be pixels instead of items? + //int max_pixel = 24576; + //double range_span = ((max_pixel - vmargin) / (one_em_ * 1.5)); + double range_span = prefs.flow_graph_max_export_items; + // Start at the current top item, and QCPRange::bounded does what + // we want, with margins of 1.0 on top and bottom. + QCPRange new_yrange(old_yrange.lower, old_yrange.lower + range_span); + new_yrange = new_yrange.bounded(min_top_, num_items_); + sp->yAxis->setRange(new_yrange); + // margins of 0.5 on left and right for port number, etc. + sp->xAxis2->setRange(min_left_, info_->sainfo()->num_nodes - 0.5); + // As seen in resetAxes(), we have an item take ~ 1.5*one_em_ pixels. + int ySize = new_yrange.size() * (one_em_ * 1.5) + vmargin; + int xSize = (nodeSize * info_->sainfo()->num_nodes) + hmargin; if (extension.compare(pdf_filter) == 0) { - save_ok = ui->sequencePlot->savePdf(file_name); + save_ok = ui->sequencePlot->savePdf(file_name, xSize, ySize); } else if (extension.compare(png_filter) == 0) { - save_ok = ui->sequencePlot->savePng(file_name); + save_ok = ui->sequencePlot->savePng(file_name, xSize, ySize); } else if (extension.compare(bmp_filter) == 0) { - save_ok = ui->sequencePlot->saveBmp(file_name); + save_ok = ui->sequencePlot->saveBmp(file_name, xSize, ySize); } else if (extension.compare(jpeg_filter) == 0) { - save_ok = ui->sequencePlot->saveJpg(file_name); + save_ok = ui->sequencePlot->saveJpg(file_name, xSize, ySize); } else if (extension.compare(ascii_filter) == 0 && !file_closed_ && info_->sainfo()) { FILE *outfile = ws_fopen(file_name.toUtf8().constData(), "w"); if (outfile != NULL) { @@ -474,11 +605,13 @@ void SequenceDialog::exportDiagram() save_ok = false; } } + sp->yAxis->setRange(old_yrange); + sp->xAxis2->setRange(old_xrange); // else error dialog? if (save_ok) { mainApp->setLastOpenDirFromFilename(file_name); } else { - open_failure_alert_box(file_name.toUtf8().constData(), errno, TRUE); + open_failure_alert_box(file_name.toUtf8().constData(), errno, true); } } } @@ -547,7 +680,7 @@ void SequenceDialog::panAxes(int x_pixels, int y_pixels) } if (sp->yAxis->rangeReversed()) { - // For reversed axes, lower still references the mathemathetically + // For reversed axes, lower still references the mathematically // smaller number than upper, so reverse the direction. y_pixels = -y_pixels; } @@ -560,11 +693,11 @@ void SequenceDialog::panAxes(int x_pixels, int y_pixels) if (h_pan && !(sp->xAxis2->range().contains(min_left_) && sp->xAxis2->range().contains(info_->sainfo()->num_nodes - 0.5))) { sp->xAxis2->moveRange(h_pan); - sp->replot(); + sp->replot(QCustomPlot::rpQueuedReplot); } if (v_pan && !(sp->yAxis->range().contains(min_top_) && sp->yAxis->range().contains(num_items_))) { sp->yAxis->moveRange(v_pan); - sp->replot(); + sp->replot(QCustomPlot::rpQueuedReplot); } } @@ -588,12 +721,12 @@ void SequenceDialog::resetAxes(bool keep_lower) sp->yAxis->setRange(top_pos, range_span + top_pos); double rmin = sp->xAxis2->range().size() / 2; - ui->horizontalScrollBar->setRange((rmin - 0.5) * 100, (info_->sainfo()->num_nodes - 0.5 - rmin) * 100); + ui->horizontalScrollBar->setRange((rmin + min_left_) * 100, (info_->sainfo()->num_nodes - 0.5 - rmin) * 100); xAxisChanged(sp->xAxis2->range()); ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->minimum()); // Shouldn't be needed. rmin = (sp->yAxis->range().size() / 2); - ui->verticalScrollBar->setRange((rmin - 1.0) * 100, (num_items_ - 0.5 - rmin) * 100); + ui->verticalScrollBar->setRange((rmin + min_top_) * 100, (num_items_ - 0.5 - rmin) * 100); yAxisChanged(sp->yAxis->range()); sp->replot(QCustomPlot::rpQueuedReplot); @@ -634,7 +767,7 @@ void SequenceDialog::resetView() void SequenceDialog::on_actionGoToPacket_triggered() { if (!file_closed_ && packet_num_ > 0) { - cf_goto_frame(cap_file_.capFile(), packet_num_); + cf_goto_frame(cap_file_.capFile(), packet_num_, false); seq_diagram_->setSelectedPacket(packet_num_); } } @@ -685,12 +818,12 @@ void SequenceDialog::goToAdjacentPacket(bool next) } sp->yAxis->moveRange(range_offset); } - cf_goto_frame(cap_file_.capFile(), adjacent_packet); + cf_goto_frame(cap_file_.capFile(), adjacent_packet, false); seq_diagram_->setSelectedPacket(adjacent_packet); } } -void SequenceDialog::on_displayFilterCheckBox_toggled(bool) +void SequenceDialog::displayFilterCheckBoxToggled(bool) { fillDiagram(); } @@ -706,16 +839,15 @@ void SequenceDialog::on_flowComboBox_activated(int index) fillDiagram(); } -void SequenceDialog::on_addressComboBox_activated(int index) +void SequenceDialog::addressChanged(int) { if (!info_->sainfo()) return; - if (index == 0) { - info_->sainfo()->any_addr = TRUE; - } else { - info_->sainfo()->any_addr = FALSE; + QVariant data = ui->addressComboBox->currentData(); + if (data.isValid()) { + info_->sainfo()->any_addr = data.toBool(); + fillDiagram(); } - fillDiagram(); } void SequenceDialog::on_actionMoveRight10_triggered() @@ -825,7 +957,7 @@ bool SequenceDialog::addFlowSequenceItem(const void* key, void *value, void *use /* XXX - Although "voip" isn't a registered name yet, it appears to have special handling that will be done outside of registered data */ if (strcmp(name, "voip") == 0) - return FALSE; + return false; item_data->flow->addItem(sequence_analysis_get_ui_name(analysis), VariantPointer<register_analysis_t>::asQVariant(analysis)); @@ -834,7 +966,7 @@ bool SequenceDialog::addFlowSequenceItem(const void* key, void *value, void *use item_data->curr_index++; - return FALSE; + return false; } QVector<rtpstream_id_t *>SequenceDialog::getSelectedRtpIds() |