summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2013-08-30 21:15:24 +0000
committerGerald Combs <gerald@wireshark.org>2013-08-30 21:15:24 +0000
commit1d27c70838a8dd04dc302dc6c6527bb852b58cc5 (patch)
tree3940900b523269602e861776613d3568bb59f188
parent8445c81d50c9c49b61298eed974dbe9f0ed1826c (diff)
downloadwireshark-1d27c70838a8dd04dc302dc6c6527bb852b58cc5.tar.gz
Add an item tracer to the TCP stream graph. Enable packet selection.
QCustomPlot data sets are made up of keys (x axis by default) and values (y axis). It looks like we can have multiple values for a given key (i.e. we can show multiple sequence numbers for a given timestamp) but QCPItemTracers can only be positioned by a key and not a key+value pair. This makes our graph selection behavior a bit different compared to the GTK+ version. We can only select one segment for a give timestamp but our selection targets are much larger (the height of the graph). Add a map for segment data so we don't have to iterate to find them. Use UTF8_RIGHTWARDS_ARROW where appropriate. Set a window title. Tell the user what will happen if he or she clicks. Disable graph selection. svn path=/trunk/; revision=51604
-rw-r--r--epan/to_str.h8
-rw-r--r--ui/gtk/tcp_graph.c7
-rw-r--r--ui/qt/main_window_slots.cpp2
-rw-r--r--ui/qt/tcp_stream_dialog.cpp198
-rw-r--r--ui/qt/tcp_stream_dialog.h15
-rw-r--r--ui/qt/tcp_stream_dialog.ui1
-rw-r--r--ui/tap-tcp-stream.h1
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 @@
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↑&lt;/th&gt;&lt;td&gt;Move up 10%&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↓&lt;/th&gt;&lt;td&gt;Move down 10%&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;0&lt;/th&gt;&lt;td&gt;Reset graph to its initial state&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;Space&lt;/th&gt;&lt;td&gt;Toggle crosshairs&lt;/td&gt;&lt;/th&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;&lt;/html&gt;</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;
};