diff options
author | Gerald Combs <gerald@wireshark.org> | 2013-09-09 01:40:06 +0000 |
---|---|---|
committer | Gerald Combs <gerald@wireshark.org> | 2013-09-09 01:40:06 +0000 |
commit | 829c1ed25ec2734d9c6fec6d8cbcc2b6e69ff6f1 (patch) | |
tree | a33d1faa4ba754b7a9c831c61fd8dff5bd8b1130 /ui/qt | |
parent | 077d252fd92c4a41e3d5396e2f869c6804ce86ef (diff) |
Add the TCP window scaling graph. Add zoom selections.
Rename some methods. Add the ability to toggle time and sequence number
origins. Add more keyboard shortcuts. Comment out abs_secs abs_usecs in
the segment struct since it looks like we aren't using them. Make sure
we stay in the same TCP stream.
svn path=/trunk/; revision=51856
Diffstat (limited to 'ui/qt')
-rw-r--r-- | ui/qt/main_window.h | 1 | ||||
-rw-r--r-- | ui/qt/main_window.ui | 9 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 5 | ||||
-rw-r--r-- | ui/qt/tcp_stream_dialog.cpp | 387 | ||||
-rw-r--r-- | ui/qt/tcp_stream_dialog.h | 24 | ||||
-rw-r--r-- | ui/qt/tcp_stream_dialog.ui | 53 |
6 files changed, 401 insertions, 78 deletions
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index d8c3938005..bba37468ff 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -303,6 +303,7 @@ private slots: void on_actionStatisticsTcpStreamStevens_triggered(); void on_actionStatisticsTcpStreamThroughput_triggered(); void on_actionStatisticsTcpStreamRoundTripTime_triggered(); + void on_actionStatisticsTcpStreamWindowScaling_triggered(); }; diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 366203bca9..3998565593 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -322,6 +322,7 @@ <addaction name="actionStatisticsTcpStreamStevens"/> <addaction name="actionStatisticsTcpStreamThroughput"/> <addaction name="actionStatisticsTcpStreamRoundTripTime"/> + <addaction name="actionStatisticsTcpStreamWindowScaling"/> </widget> <addaction name="actionSummary"/> <addaction name="actionProtocol_Hierarchy"/> @@ -1274,6 +1275,14 @@ <string>TCP round trip time</string> </property> </action> + <action name="actionStatisticsTcpStreamWindowScaling"> + <property name="text"> + <string>Window Scaling</string> + </property> + <property name="toolTip"> + <string>TCP window scaling</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <customwidgets> diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 9ed79afb63..3dda0dca78 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -1717,6 +1717,11 @@ void MainWindow::on_actionStatisticsTcpStreamRoundTripTime_triggered() openTcpStreamDialog(GRAPH_RTT); } +void MainWindow::on_actionStatisticsTcpStreamWindowScaling_triggered() +{ + openTcpStreamDialog(GRAPH_WSCALE); +} + // Help Menu void MainWindow::on_actionHelpContents_triggered() { diff --git a/ui/qt/tcp_stream_dialog.cpp b/ui/qt/tcp_stream_dialog.cpp index dbee330d05..8a6000ea7f 100644 --- a/ui/qt/tcp_stream_dialog.cpp +++ b/ui/qt/tcp_stream_dialog.cpp @@ -31,8 +31,10 @@ #include "wireshark_application.h" #include "tango_colors.h" +#include <QCursor> #include <QDir> #include <QFileDialog> +#include <QIcon> #include <QPushButton> #include <QDebug> @@ -47,11 +49,16 @@ const int moving_avg_period_ = 20; const QRgb graph_color_1 = tango_sky_blue_5; const QRgb graph_color_2 = tango_butter_6; +// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph +// in zoom mode. +const int min_zoom_pixels_ = 20; + const QString average_throughput_label_ = QObject::tr("Avgerage Througput (bits/s)"); const QString round_trip_time_ms_label_ = QObject::tr("Round Trip Time (ms)"); const QString segment_length_label_ = QObject::tr("Segment Length (B)"); -const QString sequence_number_label_ = QObject::tr("Relative Sequence Number (B)"); +const QString sequence_number_label_ = QObject::tr("Sequence Number (B)"); const QString time_s_label_ = QObject::tr("Time (s)"); +const QString window_size_label_ = QObject::tr("Window Size (B)"); Q_DECLARE_METATYPE(tcp_graph_type) @@ -59,7 +66,11 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty QDialog(parent), ui(new Ui::TCPStreamDialog), cap_file_(cf), + ts_origin_conn_(true), + seq_origin_zero_(true), tracer_(NULL), + mouse_drags_(true), + rubber_band_(NULL), num_dsegs_(-1), num_acks_(-1), num_sack_ranges_(-1) @@ -68,7 +79,8 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty ui->setupUi(this); - if (!select_tcpip_session(cap_file_, ¤t)) { + struct tcpheader *header = select_tcpip_session(cap_file_, ¤t); + if (!header) { done(QDialog::Rejected); } @@ -80,15 +92,20 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty ui->graphTypeComboBox->addItem(tr("Time / Sequence (Stevens)"), qVariantFromValue(GRAPH_TSEQ_STEVENS)); ui->graphTypeComboBox->addItem(tr("Throughput"), qVariantFromValue(GRAPH_THROUGHPUT)); ui->graphTypeComboBox->addItem(tr("Round Trip Time"), qVariantFromValue(GRAPH_RTT)); + ui->graphTypeComboBox->addItem(tr("Window Scaling"), qVariantFromValue(GRAPH_WSCALE)); ui->graphTypeComboBox->setCurrentIndex(-1); ui->graphTypeComboBox->setUpdatesEnabled(true); + ui->mouseHorizontalLayout->setContentsMargins(0, 0, 0, 0); + ui->dragToolButton->setChecked(mouse_drags_); + memset (&graph_, 0, sizeof(graph_)); graph_.type = graph_type; COPY_ADDRESS(&graph_.src_address, ¤t.ip_src); graph_.src_port = current.th_sport; COPY_ADDRESS(&graph_.dst_address, ¤t.ip_dst); graph_.dst_port = current.th_dport; + graph_.stream = header->th_stream; QCustomPlot *sp = ui->streamPlot; QCPPlotTitle *file_title = new QCPPlotTitle(sp, cf_get_display_name(cap_file_)); @@ -106,10 +123,6 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty // Fills the graph ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(qVariantFromValue(graph_type))); - sp->setInteractions( - QCP::iRangeDrag | - QCP::iRangeZoom - ); sp->setMouseTracking(true); sp->graph(0)->setPen(QPen(QBrush(graph_color_1), 0.25)); sp->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 5)); @@ -128,6 +141,9 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*))); connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + connect(sp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*))); + connect(sp, SIGNAL(axisClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)), + this, SLOT(axisClicked(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*))); connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(transformYRange(QCPRange))); disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); @@ -192,17 +208,38 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event) toggleTracerStyle(); break; - // Reset case Qt::Key_0: case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards case Qt::Key_R: case Qt::Key_Home: resetAxes(); break; - case Qt::Key_S: + + case Qt::Key_D: on_otherDirectionButton_clicked(); break; - // Alas, there is no Blade Runner-style Qt::Key_Ehance + case Qt::Key_G: + if (tracer_->visible() && cap_file_ && packet_num_ > 0) { + emit goToPacket(packet_num_); + } + break; + case Qt::Key_S: + seq_origin_zero_ = seq_origin_zero_ ? false : true; + fillGraph(); + break; + case Qt::Key_T: + ts_origin_conn_ = ts_origin_conn_ ? false : true; + fillGraph(); + break; + case Qt::Key_Z: + if (mouse_drags_) { + ui->selectToolButton->toggle(); + } else { + ui->dragToolButton->toggle(); + } + break; + + // Alas, there is no Blade Runner-style Qt::Key_Enhance } if (scale_range) { @@ -223,6 +260,51 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event) sp->replot(); } QDialog::keyPressEvent(event); + + // GTK+ Shortcuts: + // Left Mouse Button selects segment under cursor in Wiresharks packet list + // can also drag to zoom in on a rectangular region + // Middle Mouse Button zooms in (towards area under cursor) + // <Shift>-Middle Mouse Button zooms out + + // Right Mouse Button moves the graph (if zoomed in) + // <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified + + // 1 display Round Trip Time Graph + // 2 display Throughput Graph + // 3 display Time/Sequence Graph (Stevens) + // 4 display Time/Sequence Graph (tcptrace) + // 5 display Window Scaling Graph + + // <Space bar> toggles crosshairs on/off + + // i or + zoom in (towards area under mouse pointer) + // o or - zoom out + // r or <Home> restore graph to initial state (zoom out max) + // s toggles relative/absolute sequence numbers + // t toggles time origin + // g go to frame under cursor in Wiresharks packet list (if possible) + + // <Left> move view left by 100 pixels (if zoomed in) + // <Right> move view right 100 pixels (if zoomed in) + // <Up> move view up by 100 pixels (if zoomed in) + // <Down> move view down by 100 pixels (if zoomed in) + + // <Shift><Left> move view left by 10 pixels (if zoomed in) + // <Shift><Right> move view right 10 pixels (if zoomed in) + // <Shift><Up> move view up by 10 pixels (if zoomed in) + // <Shift><Down> move view down by 10 pixels (if zoomed in) + + // <Ctrl><Left> move view left by 1 pixel (if zoomed in) + // <Ctrl><Right> move view right 1 pixel (if zoomed in) + // <Ctrl><Up> move view up by 1 pixel (if zoomed in) + // <Ctrl><Down> move view down by 1 pixel (if zoomed in) + +} + +void TCPStreamDialog::mouseReleaseEvent(QMouseEvent *event) +{ + mouseReleased(event); } void TCPStreamDialog::fillGraph() @@ -231,7 +313,7 @@ void TCPStreamDialog::fillGraph() if (sp->graphCount() < 1) return; - rel_time_map_.clear(); + time_stamp_map_.clear(); sequence_num_map_.clear(); graph_segment_list_free(&graph_); tracer_->setGraph(NULL); @@ -244,6 +326,8 @@ void TCPStreamDialog::fillGraph() sp->xAxis->setLabel(time_s_label_); sp->xAxis->setNumberFormat("gb"); sp->xAxis->setNumberPrecision(6); + sp->yAxis->setNumberFormat("f"); + sp->yAxis->setNumberPrecision(0); sp->yAxis2->setVisible(false); sp->yAxis2->setLabel(QString()); @@ -261,24 +345,35 @@ void TCPStreamDialog::fillGraph() // graphs. If the throughput list used the same list we could call this // above in our ctor. graph_segment_list_get(cap_file_, &graph_, TRUE); + ts_offset_ = 0; + seq_offset_ = 0; + bool first = true; for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { if (!compareHeaders(seg)) { continue; } - double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0; - rel_time_map_.insertMulti(rt_val, seg); + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + if (first) { + if (ts_origin_conn_) ts_offset_ = ts; + if (seq_origin_zero_) seq_offset_ = seg->th_seq; + first = false; + } + time_stamp_map_.insertMulti(ts - ts_offset_, seg); } switch (graph_.type) { case GRAPH_TSEQ_STEVENS: - initializeStevens(); + fillStevens(); break; case GRAPH_THROUGHPUT: - initializeThroughput(); + fillThroughput(); break; case GRAPH_RTT: - initializeRoundTripTime(); + fillRoundTripTime(); + break; + case GRAPH_WSCALE: + fillWindowScale(); break; default: break; @@ -319,13 +414,15 @@ void TCPStreamDialog::resetAxes() sp->replot(); } -void TCPStreamDialog::initializeStevens() +void TCPStreamDialog::fillStevens() { QString dlg_title = QString(tr("Sequence Numbers")) + streamDescription(); setWindowTitle(dlg_title); title_->setText(dlg_title); QCustomPlot *sp = ui->streamPlot; + sp->yAxis->setLabel(sequence_number_label_); + // True Stevens-style graphs don't have lines but I like them - gcc sp->graph(0)->setLineStyle(QCPGraph::lsStepLeft); @@ -335,15 +432,14 @@ void TCPStreamDialog::initializeStevens() continue; } - double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0; - rel_time.append(rt_val); - seq.append(seg->th_seq); + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + rel_time.append(ts - ts_offset_); + seq.append(seg->th_seq - seq_offset_); } sp->graph(0)->setData(rel_time, seq); - sp->yAxis->setLabel(sequence_number_label_); } -void TCPStreamDialog::initializeThroughput() +void TCPStreamDialog::fillThroughput() { QString dlg_title = QString(tr("Throughput")) + streamDescription(); #ifdef MA_1_SECOND @@ -355,6 +451,12 @@ void TCPStreamDialog::initializeThroughput() title_->setText(dlg_title); QCustomPlot *sp = ui->streamPlot; + sp->yAxis->setLabel(segment_length_label_); + sp->yAxis2->setLabel(average_throughput_label_); + sp->yAxis2->setLabelColor(QColor(graph_color_2)); + sp->yAxis2->setTickLabelColor(QColor(graph_color_2)); + sp->yAxis2->setVisible(true); + sp->graph(0)->setLineStyle(QCPGraph::lsNone); sp->graph(1)->setVisible(true); sp->graph(1)->setPen(QPen(QBrush(graph_color_2), 0.5)); @@ -376,10 +478,10 @@ void TCPStreamDialog::initializeThroughput() // For now use not-really-correct initial values just to keep our vector // lengths the same. for (struct segment *seg = graph_.segments->next; seg != NULL; seg = seg->next) { - double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0; + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; #ifdef MA_1_SECOND - while (rt_val - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0) > 1.0) { + while (ts - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0) > 1.0) { oldest_seg = oldest_seg->next; sum -= oldest_seg->th_seglen; } @@ -391,7 +493,7 @@ void TCPStreamDialog::initializeThroughput() i++; #endif - double dtime = rt_val - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0); + double dtime = ts - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0); double av_tput; sum += seg->th_seglen; if (dtime > 0.0) { @@ -400,37 +502,35 @@ void TCPStreamDialog::initializeThroughput() av_tput = 0.0; } - rel_time.append(rt_val); + rel_time.append(ts - ts_offset_); seg_len.append(seg->th_seglen); // Add a data point only if our time window has advanced. Otherwise // update the most recent point. (We might want to show a warning // for out-of-order packets.) - if (tput_time.size() > 0 && rt_val <= tput_time.last()) { + if (tput_time.size() > 0 && ts <= tput_time.last()) { tput[tput.size() - 1] = av_tput; } else { tput.append(av_tput); - tput_time.append(rt_val); + tput_time.append(ts); } } sp->graph(0)->setData(rel_time, seg_len); sp->graph(1)->setData(tput_time, tput); - - sp->yAxis->setLabel(segment_length_label_); - - sp->yAxis2->setLabel(average_throughput_label_); - sp->yAxis2->setLabelColor(QColor(graph_color_2)); - sp->yAxis2->setTickLabelColor(QColor(graph_color_2)); - sp->yAxis2->setVisible(true); } -void TCPStreamDialog::initializeRoundTripTime() +void TCPStreamDialog::fillRoundTripTime() { QString dlg_title = QString(tr("Round Trip Time")) + streamDescription(); setWindowTitle(dlg_title); title_->setText(dlg_title); QCustomPlot *sp = ui->streamPlot; + sp->xAxis->setLabel(sequence_number_label_); + sp->xAxis->setNumberFormat("f"); + sp->xAxis->setNumberPrecision(0); + sp->yAxis->setLabel(round_trip_time_ms_label_); + sp->graph(0)->setLineStyle(QCPGraph::lsLine); QVector<double> seq_no, rtt; @@ -452,7 +552,7 @@ void TCPStreamDialog::initializeRoundTripTime() for (u = unack; u; u = v) { if (ack_no > u->seqno) { - seq_no.append(u->seqno); + seq_no.append(u->seqno - seq_offset_); rtt.append((rt_val - u->time) * 1000.0); sequence_num_map_.insert(u->seqno, seg); rtt_delete_unack_from_list(&unack, u); @@ -462,10 +562,33 @@ void TCPStreamDialog::initializeRoundTripTime() } } sp->graph(0)->setData(seq_no, rtt); - sp->xAxis->setLabel(sequence_number_label_); - sp->xAxis->setNumberFormat("f"); - sp->xAxis->setNumberPrecision(0); - sp->yAxis->setLabel(round_trip_time_ms_label_); +} + +void TCPStreamDialog::fillWindowScale() +{ + QString dlg_title = QString(tr("Window Scaling")) + streamDescription(); + setWindowTitle(dlg_title); + title_->setText(dlg_title); + + QCustomPlot *sp = ui->streamPlot; + sp->graph(0)->setLineStyle(QCPGraph::lsLine); + + QVector<double> rel_time, win_size; + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + if (!compareHeaders(seg)) { + continue; + } + + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + guint16 flags = seg->th_flags; + + if ( (flags & (TH_SYN|TH_RST)) == 0 ) { + rel_time.append(ts - ts_offset_); + win_size.append(seg->th_win); + } + } + sp->graph(0)->setData(rel_time, win_size); + sp->yAxis->setLabel(window_size_label_); } QString TCPStreamDialog::streamDescription() @@ -511,58 +634,163 @@ void TCPStreamDialog::toggleTracerStyle(bool force_default) ui->streamPlot->replot(); } +QRectF TCPStreamDialog::getZoomRanges(QRect zoom_rect) +{ + QRectF zoom_ranges = QRectF(); + + if (zoom_rect.width() < min_zoom_pixels_ && zoom_rect.height() < min_zoom_pixels_) { + return zoom_ranges; + } + + QCustomPlot *sp = ui->streamPlot; + QRect zr = zoom_rect.normalized(); + QRect ar = sp->axisRect()->rect(); + if (ar.intersects(zr)) { + QRect zsr = ar.intersected(zr); + zoom_ranges.setX(sp->xAxis->range().lower + + sp->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width()); + zoom_ranges.setWidth(sp->xAxis->range().size() * zsr.width() / ar.width()); + + // QRects grow down + zoom_ranges.setY(sp->yAxis->range().lower + + sp->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height()); + zoom_ranges.setHeight(sp->yAxis->range().size() * zsr.height() / ar.height()); + } + return zoom_ranges; +} + void TCPStreamDialog::graphClicked(QMouseEvent *event) { Q_UNUSED(event) -// QRect spr = ui->streamPlot->axisRect()->rect(); - if (tracer_->visible() && cap_file_ && packet_num_ > 0) { - emit goToPacket(packet_num_); + if (mouse_drags_) { + if (tracer_->visible() && cap_file_ && packet_num_ > 0) { + emit goToPacket(packet_num_); + } + } else { + if (!rubber_band_) { + rubber_band_ = new QRubberBand(QRubberBand::Rectangle, ui->streamPlot); + } + rb_origin_ = event->pos(); + rubber_band_->setGeometry(QRect(rb_origin_, QSize())); + rubber_band_->show(); } } -// Setting mouseTracking on our streamPlot may not be as reliable -// as we need. If it's not we might want to poll the mouse position -// using a QTimer instead. -void TCPStreamDialog::mouseMoved(QMouseEvent *event) +void TCPStreamDialog::axisClicked(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event) { - double tr_key = tracer_->position->key(); - struct segment *packet_seg = NULL; - packet_num_ = 0; + Q_UNUSED(part) + Q_UNUSED(event) + QCustomPlot *sp = ui->streamPlot; - if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) { + if (axis == sp->xAxis) { switch (graph_.type) { - case GRAPH_TSEQ_STEVENS: case GRAPH_THROUGHPUT: - packet_seg = rel_time_map_.value(tr_key, NULL); + case GRAPH_TSEQ_STEVENS: + case GRAPH_TSEQ_TCPTRACE: + case GRAPH_WSCALE: + ts_origin_conn_ = ts_origin_conn_ ? false : true; + fillGraph(); break; case GRAPH_RTT: - packet_seg = sequence_num_map_.value(tr_key, NULL); + seq_origin_zero_ = seq_origin_zero_ ? false : true; + fillGraph(); + break; + default: + break; + } + } else if (axis == sp->yAxis) { + switch (graph_.type) { + case GRAPH_TSEQ_STEVENS: + case GRAPH_TSEQ_TCPTRACE: + seq_origin_zero_ = seq_origin_zero_ ? false : true; + fillGraph(); + break; default: break; } } +} - if (!packet_seg) { - tracer_->setVisible(false); - ui->hintLabel->setText(tr("<small><i>Hover over the graph for details.</i></small>")); +// Setting mouseTracking on our streamPlot may not be as reliable +// as we need. If it's not we might want to poll the mouse position +// using a QTimer instead. +void TCPStreamDialog::mouseMoved(QMouseEvent *event) +{ + if (mouse_drags_) { + double tr_key = tracer_->position->key(); + struct segment *packet_seg = NULL; + packet_num_ = 0; + + if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) { + switch (graph_.type) { + case GRAPH_TSEQ_STEVENS: + case GRAPH_THROUGHPUT: + case GRAPH_WSCALE: + packet_seg = time_stamp_map_.value(tr_key, NULL); + break; + case GRAPH_RTT: + packet_seg = sequence_num_map_.value(tr_key, NULL); + default: + break; + } + } + + if (!packet_seg) { + tracer_->setVisible(false); + ui->hintLabel->setText(tr("<small><i>Hover over the graph for details.</i></small>")); + ui->streamPlot->replot(); + return; + } + + tracer_->setVisible(true); + packet_num_ = packet_seg->num; + QString hint = QString(tr("<small><i>%1 %2 (%3s len %4 seq %5 ack %6 win %7)</i></small>")) + .arg(cap_file_ ? tr("Click to select packet") : tr("Packet")) + .arg(packet_num_) + .arg(QString::number(packet_seg->rel_secs + packet_seg->rel_usecs / 1000000.0, 'g', 4)) + .arg(packet_seg->th_seglen) + .arg(packet_seg->th_seq) + .arg(packet_seg->th_ack) + .arg(packet_seg->th_win); + ui->hintLabel->setText(hint); + tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x())); ui->streamPlot->replot(); - return; + } else { + QString hint = QString(tr("<small>Click to select a portion of the graph</small>")); + if (rubber_band_) { + rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized()); + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + hint = QString(tr("<small>Release to zoom, x = %1 to %2, y = %3 to %4</small>")) + .arg(zoom_ranges.x()) + .arg(zoom_ranges.x() + zoom_ranges.width()) + .arg(zoom_ranges.y()) + .arg(zoom_ranges.y() + zoom_ranges.height()); + } else { + hint = QString(tr("<small>Unable to select range</small>")); + } + } + ui->hintLabel->setText(hint); } +} - tracer_->setVisible(true); - packet_num_ = packet_seg->num; - QString hint = QString(tr("<small><i>%1 %2 (%3s len %4 seq %5 ack %6 win %7)</i></small>")) - .arg(cap_file_ ? tr("Click to select packet") : tr("Packet")) - .arg(packet_num_) - .arg(QString::number(packet_seg->rel_secs + packet_seg->rel_usecs / 1000000.0, 'g', 4)) - .arg(packet_seg->th_seglen) - .arg(packet_seg->th_seq) - .arg(packet_seg->th_ack) - .arg(packet_seg->th_win); - ui->hintLabel->setText(hint); - tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x())); - ui->streamPlot->replot(); +void TCPStreamDialog::mouseReleased(QMouseEvent *event) +{ + if (rubber_band_) { + rubber_band_->hide(); + if (!mouse_drags_) { + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + QCustomPlot *sp = ui->streamPlot; + sp->xAxis->setRangeLower(zoom_ranges.x()); + sp->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width()); + sp->yAxis->setRangeLower(zoom_ranges.y()); + sp->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height()); + sp->replot(); + } + } + } } void TCPStreamDialog::transformYRange(const QCPRange &y_range1) @@ -648,6 +876,23 @@ void TCPStreamDialog::on_otherDirectionButton_clicked() fillGraph(); } +void TCPStreamDialog::on_dragToolButton_toggled(bool checked) +{ + if (checked) mouse_drags_ = true; + ui->streamPlot->setInteractions( + QCP::iRangeDrag | + QCP::iRangeZoom + ); + ui->streamPlot->setCursor(QCursor(Qt::OpenHandCursor)); +} + +void TCPStreamDialog::on_selectToolButton_toggled(bool checked) +{ + if (checked) mouse_drags_ = false; + ui->streamPlot->setInteractions(0); + ui->streamPlot->setCursor(QCursor(Qt::CrossCursor)); +} + /* * Editor modelines * diff --git a/ui/qt/tcp_stream_dialog.h b/ui/qt/tcp_stream_dialog.h index 087aba9d42..481bb2bb20 100644 --- a/ui/qt/tcp_stream_dialog.h +++ b/ui/qt/tcp_stream_dialog.h @@ -36,6 +36,7 @@ #include "qcustomplot.h" #include <QDialog> +#include <QRubberBand> namespace Ui { class TCPStreamDialog; @@ -58,17 +59,26 @@ public slots: protected: void showEvent(QShowEvent *event); void keyPressEvent(QKeyEvent *event); + void mouseReleaseEvent(QMouseEvent *event); private: Ui::TCPStreamDialog *ui; capture_file *cap_file_; - QMap<double, struct segment *> rel_time_map_; + QMap<double, struct segment *> time_stamp_map_; + double ts_offset_; + bool ts_origin_conn_; QMap<double, struct segment *> sequence_num_map_; + double seq_offset_; + bool seq_origin_zero_; struct tcp_graph graph_; QCPPlotTitle *title_; QCPItemTracer *tracer_; + QRectF axis_bounds_; guint32 packet_num_; QTransform y_axis_xfrm_; + bool mouse_drags_; + QRubberBand *rubber_band_; + QPoint rb_origin_; int num_dsegs_; int num_acks_; @@ -76,21 +86,27 @@ private: void fillGraph(); void resetAxes(); - void initializeStevens(); - void initializeThroughput(); - void initializeRoundTripTime(); + void fillStevens(); + void fillThroughput(); + void fillRoundTripTime(); + void fillWindowScale(); QString streamDescription(); bool compareHeaders(struct segment *seg); void toggleTracerStyle(bool force_default = false); + QRectF getZoomRanges(QRect zoom_rect); private slots: void graphClicked(QMouseEvent *event); + void axisClicked(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event); void mouseMoved(QMouseEvent *event); + void mouseReleased(QMouseEvent *event); void transformYRange(const QCPRange &y_range1); void on_buttonBox_accepted(); void on_graphTypeComboBox_currentIndexChanged(int index); void on_resetButton_clicked(); void on_otherDirectionButton_clicked(); + void on_dragToolButton_toggled(bool checked); + void on_selectToolButton_toggled(bool checked); }; #endif // TCP_STREAM_DIALOG_H diff --git a/ui/qt/tcp_stream_dialog.ui b/ui/qt/tcp_stream_dialog.ui index d19d5ed04d..b9e139129e 100644 --- a/ui/qt/tcp_stream_dialog.ui +++ b/ui/qt/tcp_stream_dialog.ui @@ -45,7 +45,11 @@ <tr><th><i>Shift+</i>↑</th><td>Move up 10%</td></th> <tr><th><i>Shift+</i>↓</th><td>Move down 10%</td></th> <tr><th>0</th><td>Reset graph to its initial state</td></th> -<tr><th>s</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<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>s</th><td>Toggle relative / absolute sequence numbers</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> @@ -56,7 +60,7 @@ </widget> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QLabel" name="label"> <property name="text"> @@ -81,6 +85,44 @@ </spacer> </item> <item> + <layout class="QHBoxLayout" name="mouseHorizontalLayout"> + <item> + <widget class="QToolButton" name="dragToolButton"> + <property name="toolTip"> + <string>Drag using the mouse button.</string> + </property> + <property name="icon"> + <iconset resource="../../image/toolbar.qrc"> + <normaloff>:/graph/openhand-16.png</normaloff>:/graph/openhand-16.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <attribute name="buttonGroup"> + <string notr="true">mouseButtonGroup</string> + </attribute> + </widget> + </item> + <item> + <widget class="QToolButton" name="selectToolButton"> + <property name="toolTip"> + <string>Select using the mouse button.</string> + </property> + <property name="icon"> + <iconset resource="../../image/toolbar.qrc"> + <normaloff>:/graph/rubberband-16.png</normaloff>:/graph/rubberband-16.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <attribute name="buttonGroup"> + <string notr="true">mouseButtonGroup</string> + </attribute> + </widget> + </item> + </layout> + </item> + <item> <widget class="QPushButton" name="resetButton"> <property name="toolTip"> <string><html><head/><body><p>Reset the graph to its initial state.</p></body></html></string> @@ -127,7 +169,9 @@ <header>elided_label.h</header> </customwidget> </customwidgets> - <resources/> + <resources> + <include location="../../image/toolbar.qrc"/> + </resources> <connections> <connection> <sender>buttonBox</sender> @@ -162,4 +206,7 @@ </hints> </connection> </connections> + <buttongroups> + <buttongroup name="mouseButtonGroup"/> + </buttongroups> </ui> |