diff options
-rw-r--r-- | epan/to_str.h | 8 | ||||
-rw-r--r-- | ui/gtk/tcp_graph.c | 7 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 2 | ||||
-rw-r--r-- | ui/qt/tcp_stream_dialog.cpp | 198 | ||||
-rw-r--r-- | ui/qt/tcp_stream_dialog.h | 15 | ||||
-rw-r--r-- | ui/qt/tcp_stream_dialog.ui | 1 | ||||
-rw-r--r-- | ui/tap-tcp-stream.h | 1 |
7 files changed, 186 insertions, 46 deletions
diff --git a/epan/to_str.h b/epan/to_str.h index 3e2f137ab9..f9a49a7096 100644 --- a/epan/to_str.h +++ b/epan/to_str.h @@ -39,6 +39,10 @@ #define VINES_ADDR_LEN 6 #define EUI64_STR_LEN 24 +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + /* * These are utility functions which convert various types to strings, * but for which no more specific module applies. @@ -103,4 +107,8 @@ WS_DLL_PUBLIC const char *decode_numeric_bitfield(const guint32 val, const guint WS_DLL_PUBLIC const gchar* port_type_to_str (port_type type); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + #endif /* __TO_STR_H__ */ diff --git a/ui/gtk/tcp_graph.c b/ui/gtk/tcp_graph.c index 4e98935754..a085a12c57 100644 --- a/ui/gtk/tcp_graph.c +++ b/ui/gtk/tcp_graph.c @@ -47,6 +47,7 @@ #include "../../stat_menu.h" #include "ui/tap-tcp-stream.h" +#include "ui/utf8_entities.h" #include "ui/gtk/gui_utils.h" #include "ui/gtk/dlg_utils.h" @@ -700,7 +701,7 @@ static void create_drawing_area(struct gtk_graph *g) /* Set title of window with file + conversation details */ display_name = cf_get_display_name(&cfile); - g_snprintf(window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d -> %s:%d", + g_snprintf(window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d " UTF8_RIGHTWARDS_ARROW " %s:%d", refnum, display_name, ep_address_to_str(&g->tg.src_address), @@ -2058,7 +2059,7 @@ static void draw_element_ellipse(struct gtk_graph *g, struct element *e, cairo_t gdouble x = e->p.ellipse.dim.x + g->geom.x - g->wp.x; gdouble y = g->geom.height-1 - e->p.ellipse.dim.y + g->geom.y - g->wp.y; - debug(DBS_GRAPH_DRAWING) printf("ellipse: (x, y) -> (w, h): (%f, %f) -> (%f, %f)\n", x, y, w, h); + debug(DBS_GRAPH_DRAWING) printf("ellipse: (x, y) " UTF8_RIGHTWARDS_ARROW " (w, h): (%f, %f) " UTF8_RIGHTWARDS_ARROW " (%f, %f)\n", x, y, w, h); cairo_save(cr); cairo_set_source_rgb(cr, 0, 0, 0); @@ -2383,7 +2384,7 @@ static void axis_compute_ticks(struct axis *axis, double x0, double xmax, int di axis->major = steps[j] * pow(10, i); - debug(DBS_AXES_TICKS) printf("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->" + debug(DBS_AXES_TICKS) printf("zoom=%.1f, x=%f " UTF8_RIGHTWARDS_ARROW " i=%d " UTF8_RIGHTWARDS_ARROW " ms=%d " UTF8_RIGHTWARDS_ARROW " j=%d ->" " axis->major=%f\n", zoom, x, i, ms, j, axis->major); /* let's compute minor ticks */ diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 10fb4574bd..1e2750c635 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -1600,6 +1600,8 @@ void MainWindow::on_actionAnalyzePAFOrNotSelected_triggered() void MainWindow::on_actionStatisticsTcpStreamStevens_triggered() { TCPStreamDialog stream_dialog(this, cap_file_, GRAPH_TSEQ_STEVENS); + connect(&stream_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); stream_dialog.exec(); } diff --git a/ui/qt/tcp_stream_dialog.cpp b/ui/qt/tcp_stream_dialog.cpp index 45d8a2462d..04a45a6cd5 100644 --- a/ui/qt/tcp_stream_dialog.cpp +++ b/ui/qt/tcp_stream_dialog.cpp @@ -24,6 +24,10 @@ #include "tcp_stream_dialog.h" #include "ui_tcp_stream_dialog.h" +#include "epan/to_str.h" + +#include "ui/utf8_entities.h" + #include "tango_colors.h" #include <QDebug> @@ -31,7 +35,8 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_type graph_type) : QDialog(parent), ui(new Ui::TCPStreamDialog), - cap_file_(cf) + cap_file_(cf), + tracer_(NULL) { struct segment current; @@ -41,57 +46,81 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty done(QDialog::Rejected); } +//#ifdef Q_OS_MAC +// ui->hintLabel->setAttribute(Qt::WA_MacSmallSize, true); +//#endif + memset (&graph_, 0, sizeof(graph_)); graph_.type = graph_type; graph_segment_list_get(cap_file_, &graph_, FALSE); - ui->streamPlot->setInteractions( - QCP::iRangeDrag | - QCP::iRangeZoom | - QCP::iSelectPlottables - ); + + QString dlg_title = QString(tr("TCP Graph %1 %2:%3 %4 %5:%6")) + .arg(cf_get_display_name(cap_file_)) + .arg(ep_address_to_str(&graph_.src_address)) + .arg(graph_.src_port) + .arg(UTF8_RIGHTWARDS_ARROW) + .arg(ep_address_to_str(&graph_.dst_address)) + .arg(graph_.dst_port); + setWindowTitle(dlg_title); QVector<double> rel_time, seq; double rel_time_min = QCPRange::maxRange, rel_time_max = QCPRange::minRange; double seq_min = QCPRange::maxRange, seq_max = QCPRange::minRange; - for (struct segment *cur = graph_.segments; cur != NULL; cur = cur->next) { - if (!compare_headers(&graph_.src_address, &graph_.dst_address, - graph_.src_port, graph_.dst_port, - &cur->ip_src, &cur->ip_dst, - cur->th_sport, cur->th_dport, - COMPARE_CURR_DIR)) { + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + if (!compareHeaders(seg)) { continue; } - double rt_val = cur->rel_secs + cur->rel_usecs / 1000000.0; + double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0; rel_time.append(rt_val); if (rel_time_min > rt_val) rel_time_min = rt_val; if (rel_time_max < rt_val) rel_time_max = rt_val; - seq.append(cur->th_seq); - if (seq_min > cur->th_seq) seq_min = cur->th_seq; - if (seq_max < cur->th_seq) seq_max = cur->th_seq; + seq.append(seg->th_seq); + if (seq_min > seg->th_seq) seq_min = seg->th_seq; + if (seq_max < seg->th_seq) seq_max = seg->th_seq; + + segment_map_.insertMulti(rt_val, seg); } - ui->streamPlot->addGraph(); - ui->streamPlot->graph(0)->setData(rel_time, seq); + QCustomPlot *sp = ui->streamPlot; + sp->addGraph(); + sp->graph(0)->setData(rel_time, seq); + sp->setInteractions( + QCP::iRangeDrag | + QCP::iRangeZoom + ); + sp->setMouseTracking(true); // True Stevens-style graphs don't have lines but I like them - gcc - ui->streamPlot->graph(0)->setPen(QPen(QBrush(tango_sky_blue_5), 0.25)); - ui->streamPlot->graph(0)->setLineStyle(QCPGraph::lsStepLeft); - ui->streamPlot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 5)); + sp->graph(0)->setPen(QPen(QBrush(tango_sky_blue_5), 0.25)); + sp->graph(0)->setLineStyle(QCPGraph::lsStepLeft); + sp->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 5)); - ui->streamPlot->xAxis->setLabel(tr("Time (s)")); + sp->xAxis->setLabel(tr("Time (s)")); double range_pad = (rel_time_max - rel_time_min) * 0.05; data_range_.setLeft(rel_time_min - range_pad); data_range_.setRight(rel_time_max + range_pad); - ui->streamPlot->xAxis->setRange(data_range_.left(), data_range_.right()); - ui->streamPlot->yAxis->setLabel(tr("Sequence number (B)")); + sp->xAxis->setRange(data_range_.left(), data_range_.right()); + sp->yAxis->setLabel(tr("Sequence number (B)")); range_pad = (seq_max - seq_min) * 0.05; data_range_.setBottom(seq_min - range_pad); data_range_.setTop(seq_max + range_pad); - ui->streamPlot->yAxis->setRange(data_range_.bottom(), data_range_.top()); + sp->yAxis->setRange(data_range_.bottom(), data_range_.top()); + + tracer_ = new QCPItemTracer(sp); + tracer_->setVisible(false); + tracer_->setGraph(sp->graph(0)); + tracer_->setInterpolating(false); + sp->addItem(tracer_); + toggleTracerStyle(true); + // XXX - QCustomPlot doesn't seem to draw any sort of focus indicator. - ui->streamPlot->setFocus(); + sp->setFocus(); + + connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*))); + connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); +// connect(sp, SIGNAL(mou)) } TCPStreamDialog::~TCPStreamDialog() @@ -101,8 +130,9 @@ TCPStreamDialog::~TCPStreamDialog() void TCPStreamDialog::keyPressEvent(QKeyEvent *event) { - double h_factor = ui->streamPlot->axisRect()->rangeZoomFactor(Qt::Horizontal); - double v_factor = ui->streamPlot->axisRect()->rangeZoomFactor(Qt::Vertical); + QCustomPlot *sp = ui->streamPlot; + double h_factor = sp->axisRect()->rangeZoomFactor(Qt::Horizontal); + double v_factor = sp->axisRect()->rangeZoomFactor(Qt::Vertical); bool scale_range = false; double h_pan = 0.0; @@ -125,53 +155,135 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event) case Qt::Key_Right: case Qt::Key_L: - h_pan = ui->streamPlot->xAxis->range().size() * 0.1; + h_pan = sp->xAxis->range().size() * 0.1; break; case Qt::Key_Left: case Qt::Key_H: - h_pan = ui->streamPlot->xAxis->range().size() * -0.1; + h_pan = sp->xAxis->range().size() * -0.1; break; case Qt::Key_Up: case Qt::Key_K: - v_pan = ui->streamPlot->yAxis->range().size() * 0.1; + v_pan = sp->yAxis->range().size() * 0.1; break; case Qt::Key_Down: case Qt::Key_J: - v_pan = ui->streamPlot->yAxis->range().size() * -0.1; + v_pan = sp->yAxis->range().size() * -0.1; + break; + + case Qt::Key_Space: + toggleTracerStyle(); break; - // Reset + // Reset case Qt::Key_0: case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards case Qt::Key_R: case Qt::Key_Home: - ui->streamPlot->xAxis->setRange(data_range_.left(), data_range_.right()); - ui->streamPlot->yAxis->setRange(data_range_.bottom(), data_range_.top()); - ui->streamPlot->replot(); + sp->xAxis->setRange(data_range_.left(), data_range_.right()); + sp->yAxis->setRange(data_range_.bottom(), data_range_.top()); + sp->replot(); break; // Alas, there is no Blade Runner-style Qt::Key_Ehance } if (scale_range) { - ui->streamPlot->xAxis->scaleRange(h_factor, ui->streamPlot->xAxis->range().center()); - ui->streamPlot->yAxis->scaleRange(v_factor, ui->streamPlot->yAxis->range().center()); - ui->streamPlot->replot(); + sp->xAxis->scaleRange(h_factor, sp->xAxis->range().center()); + sp->yAxis->scaleRange(v_factor, sp->yAxis->range().center()); + sp->replot(); } double pan_mul = event->modifiers() & Qt::ShiftModifier ? 0.1 : 1.0; // The GTK+ version won't pan unless we're zoomed. Should we do the same here? if (h_pan) { - ui->streamPlot->xAxis->moveRange(h_pan * pan_mul); - ui->streamPlot->replot(); + sp->xAxis->moveRange(h_pan * pan_mul); + sp->replot(); } if (v_pan) { - ui->streamPlot->yAxis->moveRange(v_pan * pan_mul); - ui->streamPlot->replot(); + sp->yAxis->moveRange(v_pan * pan_mul); + sp->replot(); } QDialog::keyPressEvent(event); } +bool TCPStreamDialog::compareHeaders(segment *seg) +{ + return (compare_headers(&graph_.src_address, &graph_.dst_address, + graph_.src_port, graph_.dst_port, + &seg->ip_src, &seg->ip_dst, + seg->th_sport, seg->th_dport, + COMPARE_CURR_DIR)); +} + +void TCPStreamDialog::toggleTracerStyle(bool force_default) +{ + if (!tracer_->visible() && !force_default) return; + + QPen sp_pen = ui->streamPlot->graph(0)->pen(); + QCPItemTracer::TracerStyle tstyle = QCPItemTracer::tsCrosshair; + QPen tr_pen = QPen(tracer_->pen()); + QColor tr_color = sp_pen.color(); + + if (force_default || tracer_->style() != QCPItemTracer::tsCircle) { + tstyle = QCPItemTracer::tsCircle; + tr_color.setAlphaF(1.0); + tr_pen.setWidthF(1.5); + tr_pen.color().setAlphaF(1.0); + } else { + tr_color.setAlphaF(0.5); + tr_pen.setWidthF(1.0); + } + + tracer_->setStyle(tstyle); + tr_pen.setColor(tr_color); + tracer_->setPen(tr_pen); + ui->streamPlot->replot(); +} + +void TCPStreamDialog::graphClicked(QMouseEvent *event) +{ + Q_UNUSED(event) +// QRect spr = ui->streamPlot->axisRect()->rect(); + + if (tracer_->visible() && packet_num_ > 0) { + emit goToPacket(packet_num_); + } +} + +// 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) +{ + QRect spr = ui->streamPlot->axisRect()->rect(); + struct segment *packet_seg = NULL; + packet_num_ = 0; + + if (spr.contains(event->pos())) { + double ts = tracer_->position->key(); + packet_seg = segment_map_.value(ts, NULL); + } + + 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>Click to select packet %1 (len %2 seq %3 ack %4 win %5)</i></small>")) + .arg(packet_num_) + .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(); +} + /* * Editor modelines * diff --git a/ui/qt/tcp_stream_dialog.h b/ui/qt/tcp_stream_dialog.h index da85a730a9..5f411d8cf6 100644 --- a/ui/qt/tcp_stream_dialog.h +++ b/ui/qt/tcp_stream_dialog.h @@ -34,6 +34,7 @@ #include "ui/tap-tcp-stream.h" +#include <qcustomplot.h> #include <QDialog> namespace Ui { @@ -48,14 +49,28 @@ public: explicit TCPStreamDialog(QWidget *parent = 0, capture_file *cf = NULL, tcp_graph_type graph_type = GRAPH_TSEQ_STEVENS); ~TCPStreamDialog(); +signals: + void goToPacket(int packet_num); + protected: void keyPressEvent(QKeyEvent *event); private: Ui::TCPStreamDialog *ui; capture_file *cap_file_; + QMap<double, struct segment *> segment_map_; struct tcp_graph graph_; QRectF data_range_; + QCPItemTracer *tracer_; + guint32 packet_num_; + + + bool compareHeaders(struct segment *seg); + void toggleTracerStyle(bool force_default = false); + +private slots: + void graphClicked(QMouseEvent *event); + void mouseMoved(QMouseEvent *event); }; #endif // TCP_STREAM_DIALOG_H diff --git a/ui/qt/tcp_stream_dialog.ui b/ui/qt/tcp_stream_dialog.ui index 6d6c62cb75..12a09c5982 100644 --- a/ui/qt/tcp_stream_dialog.ui +++ b/ui/qt/tcp_stream_dialog.ui @@ -45,6 +45,7 @@ <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>Space</th><td>Toggle crosshairs</td></th> </tbody></table> </body></html></string> </property> diff --git a/ui/tap-tcp-stream.h b/ui/tap-tcp-stream.h index a502354daa..71f9a5c0f6 100644 --- a/ui/tap-tcp-stream.h +++ b/ui/tap-tcp-stream.h @@ -70,6 +70,7 @@ struct tcp_graph { guint16 src_port; address dst_address; guint16 dst_port; + /* Should this be a map or tree instead? */ struct segment *segments; }; |