summaryrefslogtreecommitdiffstats
path: root/ui/qt/sequence_dialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ui/qt/sequence_dialog.cpp')
-rw-r--r--ui/qt/sequence_dialog.cpp228
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()