5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "sequence_dialog.h"
25 #include "ui_sequence_dialog.h"
27 #include "epan/addr_resolv.h"
29 #include "wsutil/nstime.h"
31 #include "wireshark_application.h"
34 #include <QFileDialog>
35 #include <QFontMetrics>
41 // - Add UTF8 to text dump
42 // - Save to XMI? http://www.umlgraph.org/
43 // - Time: abs vs delta
45 // - Clickable time + comments?
46 // - Incorporate packet comments?
47 // - Change line_style to seq_type (i.e. draw ACKs dashed)
48 // - Create WSGraph subclasses with common behavior.
49 // - Help button and text
51 SequenceDialog::SequenceDialog(QWidget
*parent
, capture_file
*cf
, SequenceType type
) :
53 ui(new Ui::SequenceDialog
),
60 QCustomPlot
*sp
= ui
->sequencePlot
;
62 seq_diagram_
= new SequenceDiagram(sp
->yAxis
, sp
->xAxis2
, sp
->yAxis2
);
63 sp
->addPlottable(seq_diagram_
);
64 sp
->axisRect()->setRangeDragAxes(sp
->xAxis2
, sp
->yAxis
);
66 sp
->xAxis
->setVisible(false);
67 sp
->xAxis
->setPadding(0);
68 sp
->xAxis
->setLabelPadding(0);
69 sp
->xAxis
->setTickLabelPadding(0);
70 sp
->xAxis2
->setVisible(true);
71 sp
->yAxis2
->setVisible(true);
73 one_em_
= QFontMetrics(sp
->yAxis
->labelFont()).height();
74 ui
->horizontalScrollBar
->setSingleStep(100 / one_em_
);
75 ui
->verticalScrollBar
->setSingleStep(100 / one_em_
);
77 sp
->setInteractions(QCP::iRangeDrag
);
79 ui
->gridLayout
->setSpacing(0);
80 connect(sp
->yAxis
, SIGNAL(rangeChanged(QCPRange
)), sp
->yAxis2
, SLOT(setRange(QCPRange
)));
82 ctx_menu_
.addAction(ui
->actionReset
);
83 ctx_menu_
.addSeparator();
84 ctx_menu_
.addAction(ui
->actionMoveRight10
);
85 ctx_menu_
.addAction(ui
->actionMoveLeft10
);
86 ctx_menu_
.addAction(ui
->actionMoveUp10
);
87 ctx_menu_
.addAction(ui
->actionMoveDown10
);
88 ctx_menu_
.addAction(ui
->actionMoveRight1
);
89 ctx_menu_
.addAction(ui
->actionMoveLeft1
);
90 ctx_menu_
.addAction(ui
->actionMoveUp1
);
91 ctx_menu_
.addAction(ui
->actionMoveDown1
);
92 ctx_menu_
.addSeparator();
93 ctx_menu_
.addAction(ui
->actionGoToPacket
);
95 memset (&seq_analysis_
, 0, sizeof(seq_analysis_
));
97 ui
->showComboBox
->blockSignals(true);
98 ui
->showComboBox
->setCurrentIndex(0);
99 ui
->showComboBox
->blockSignals(false);
100 ui
->addressComboBox
->blockSignals(true);
101 ui
->addressComboBox
->setCurrentIndex(0);
102 ui
->addressComboBox
->blockSignals(false);
104 QComboBox
*fcb
= ui
->flowComboBox
;
105 fcb
->addItem(ui
->actionFlowAny
->text(), SEQ_ANALYSIS_ANY
);
106 fcb
->addItem(ui
->actionFlowTcp
->text(), SEQ_ANALYSIS_TCP
);
108 ui
->flowComboBox
->blockSignals(true);
111 seq_analysis_
.type
= SEQ_ANALYSIS_ANY
;
112 ui
->flowComboBox
->setCurrentIndex(SEQ_ANALYSIS_ANY
);
115 seq_analysis_
.type
= SEQ_ANALYSIS_TCP
;
116 ui
->flowComboBox
->setCurrentIndex(SEQ_ANALYSIS_TCP
);
119 seq_analysis_
.type
= SEQ_ANALYSIS_VOIP
;
120 ui
->flowComboBox
->hide();
121 ui
->flowLabel
->hide();
124 ui
->flowComboBox
->blockSignals(false);
125 seq_analysis_
.all_packets
= TRUE
;
127 QPushButton
*save_bt
= ui
->buttonBox
->button(QDialogButtonBox::Save
);
128 save_bt
->setText(tr("Save As..."));
131 resize(parent
->width(), parent
->height() * 4 / 5);
134 connect(ui
->horizontalScrollBar
, SIGNAL(valueChanged(int)), this, SLOT(hScrollBarChanged(int)));
135 connect(ui
->verticalScrollBar
, SIGNAL(valueChanged(int)), this, SLOT(vScrollBarChanged(int)));
136 connect(sp
->xAxis2
, SIGNAL(rangeChanged(QCPRange
)), this, SLOT(xAxisChanged(QCPRange
)));
137 connect(sp
->yAxis
, SIGNAL(rangeChanged(QCPRange
)), this, SLOT(yAxisChanged(QCPRange
)));
138 connect(sp
, SIGNAL(mousePress(QMouseEvent
*)), this, SLOT(diagramClicked(QMouseEvent
*)));
139 connect(sp
, SIGNAL(mouseMove(QMouseEvent
*)), this, SLOT(mouseMoved(QMouseEvent
*)));
140 connect(sp
, SIGNAL(mouseRelease(QMouseEvent
*)), this, SLOT(mouseReleased(QMouseEvent
*)));
141 connect(this, SIGNAL(goToPacket(int)), seq_diagram_
, SLOT(setSelectedPacket(int)));
143 disconnect(ui
->buttonBox
, SIGNAL(accepted()), this, SLOT(accept()));
148 SequenceDialog::~SequenceDialog()
153 void SequenceDialog::setCaptureFile(capture_file
*cf
)
155 if (!cf
) { // We only want to know when the file closes.
160 void SequenceDialog::showEvent(QShowEvent
*event
)
166 void SequenceDialog::resizeEvent(QResizeEvent
*event
)
172 void SequenceDialog::keyPressEvent(QKeyEvent
*event
)
174 int pan_pixels
= event
->modifiers() & Qt::ShiftModifier
? 1 : 10;
176 // XXX - Copy some shortcuts from tcp_stream_dialog.cpp
177 switch(event
->key()) {
180 panAxes(pan_pixels
, 0);
184 panAxes(-1 * pan_pixels
, 0);
188 panAxes(0, -1 * pan_pixels
);
192 panAxes(0, pan_pixels
);
196 case Qt::Key_ParenRight
: // Shifted 0 on U.S. keyboards
203 on_actionGoToPacket_triggered();
207 QDialog::keyPressEvent(event
);
210 void SequenceDialog::mouseReleaseEvent(QMouseEvent
*event
)
212 mouseReleased(event
);
215 void SequenceDialog::hScrollBarChanged(int value
)
217 if (qAbs(ui
->sequencePlot
->xAxis2
->range().center()-value
/100.0) > 0.01) {
218 ui
->sequencePlot
->xAxis2
->setRange(value
/100.0, ui
->sequencePlot
->xAxis2
->range().size(), Qt::AlignCenter
);
219 ui
->sequencePlot
->replot();
223 void SequenceDialog::vScrollBarChanged(int value
)
225 if (qAbs(ui
->sequencePlot
->yAxis
->range().center()-value
/100.0) > 0.01) {
226 ui
->sequencePlot
->yAxis
->setRange(value
/100.0, ui
->sequencePlot
->yAxis
->range().size(), Qt::AlignCenter
);
227 ui
->sequencePlot
->replot();
231 void SequenceDialog::xAxisChanged(QCPRange range
)
233 ui
->horizontalScrollBar
->setValue(qRound(range
.center()*100.0));
234 ui
->horizontalScrollBar
->setPageStep(qRound(range
.size()*100.0));
237 void SequenceDialog::yAxisChanged(QCPRange range
)
239 ui
->verticalScrollBar
->setValue(qRound(range
.center()*100.0));
240 ui
->verticalScrollBar
->setPageStep(qRound(range
.size()*100.0));
243 void SequenceDialog::diagramClicked(QMouseEvent
*event
)
245 QCustomPlot
*sp
= ui
->sequencePlot
;
247 if (event
->button() == Qt::RightButton
) {
248 // XXX We should find some way to get sequenceDiagram to handle a
249 // contextMenuEvent instead.
250 ctx_menu_
.exec(event
->globalPos());
251 } else if (sp
->axisRect()->rect().contains(event
->pos())) {
252 sp
->setCursor(QCursor(Qt::ClosedHandCursor
));
254 on_actionGoToPacket_triggered();
257 void SequenceDialog::mouseMoved(QMouseEvent
*event
)
259 QCustomPlot
*sp
= ui
->sequencePlot
;
260 Qt::CursorShape shape
= Qt::ArrowCursor
;
262 if (event
->buttons().testFlag(Qt::LeftButton
)) {
263 shape
= Qt::ClosedHandCursor
;
265 if (sp
->axisRect()->rect().contains(event
->pos())) {
266 shape
= Qt::OpenHandCursor
;
270 sp
->setCursor(QCursor(shape
));
275 seq_analysis_item_t
*sai
= seq_diagram_
->itemForPosY(event
->pos().y());
277 packet_num_
= sai
->fd
->num
;
278 hint
= QString("Packet %1: %2").arg(packet_num_
).arg(sai
->comment
);
282 if (hint
.isEmpty()) {
283 hint
+= QString("%1 nodes, %2 items").arg(seq_analysis_
.num_nodes
).arg(num_items_
);
286 hint
.prepend("<small><i>");
287 hint
.append("</i></small>");
288 ui
->hintLabel
->setText(hint
);
291 void SequenceDialog::mouseReleased(QMouseEvent
*event
)
294 if (ui
->sequencePlot
->cursor().shape() == Qt::ClosedHandCursor
) {
295 ui
->sequencePlot
->setCursor(QCursor(Qt::OpenHandCursor
));
299 void SequenceDialog::on_buttonBox_accepted()
301 QString file_name
, extension
;
302 QDir
path(wsApp
->lastOpenDir());
303 QString pdf_filter
= tr("Portable Document Format (*.pdf)");
304 QString png_filter
= tr("Portable Network Graphics (*.png)");
305 QString bmp_filter
= tr("Windows Bitmap (*.bmp)");
306 // Gaze upon my beautiful graph with lossy artifacts!
307 QString jpeg_filter
= tr("JPEG File Interchange Format (*.jpeg *.jpg)");
308 QString ascii_filter
= tr("ASCII (*.txt)");
310 QString filter
= QString("%1;;%2;;%3;;%4")
316 filter
.append(QString(";;%5").arg(ascii_filter
));
319 file_name
= QFileDialog::getSaveFileName(this, tr("Wireshark: Save Graph As..."),
320 path
.canonicalPath(), filter
, &extension
);
322 if (file_name
.length() > 0) {
323 bool save_ok
= false;
324 if (extension
.compare(pdf_filter
) == 0) {
325 save_ok
= ui
->sequencePlot
->savePdf(file_name
);
326 } else if (extension
.compare(png_filter
) == 0) {
327 save_ok
= ui
->sequencePlot
->savePng(file_name
);
328 } else if (extension
.compare(bmp_filter
) == 0) {
329 save_ok
= ui
->sequencePlot
->saveBmp(file_name
);
330 } else if (extension
.compare(jpeg_filter
) == 0) {
331 save_ok
= ui
->sequencePlot
->saveJpg(file_name
);
332 } else if (extension
.compare(ascii_filter
) == 0 && cap_file_
) {
333 save_ok
= sequence_analysis_dump_to_file(file_name
.toUtf8().constData(), &seq_analysis_
, cap_file_
, 0);
335 // else error dialog?
337 path
= QDir(file_name
);
338 wsApp
->setLastOpenDir(path
.canonicalPath().toUtf8().constData());
343 void SequenceDialog::fillDiagram()
345 QCustomPlot
*sp
= ui
->sequencePlot
;
347 sequence_analysis_list_free(&seq_analysis_
);
348 sequence_analysis_list_get(cap_file_
, &seq_analysis_
);
349 num_items_
= sequence_analysis_get_nodes(&seq_analysis_
);
351 seq_diagram_
->setData(&seq_analysis_
);
353 QFontMetrics vfm
= QFontMetrics(sp
->xAxis2
->labelFont());
355 for (guint i
= 0; i
< seq_analysis_
.num_nodes
; i
++) {
356 int label_w
= vfm
.width(get_addr_name(&(seq_analysis_
.nodes
[i
])));
357 if (node_label_w_
< label_w
) {
358 node_label_w_
= label_w
;
361 node_label_w_
= (node_label_w_
* 3 / 4) + one_em_
;
366 // XXX QCustomPlot doesn't seem to draw any sort of focus indicator.
370 void SequenceDialog::panAxes(int x_pixels
, int y_pixels
)
372 QCustomPlot
*sp
= ui
->sequencePlot
;
376 h_pan
= sp
->xAxis2
->range().size() * x_pixels
/ sp
->xAxis2
->axisRect()->width();
377 v_pan
= sp
->yAxis
->range().size() * y_pixels
/ sp
->yAxis
->axisRect()->height();
378 // The GTK+ version won't pan unless we're zoomed. Should we do the same here?
380 sp
->xAxis2
->moveRange(h_pan
);
384 sp
->yAxis
->moveRange(v_pan
);
389 void SequenceDialog::resetAxes(bool keep_lower
)
391 QCustomPlot
*sp
= ui
->sequencePlot
;
392 // Allow space for labels on the top and port numbers on the left.
393 double top_pos
= -1.0, left_pos
= -0.5;
395 top_pos
= sp
->yAxis
->range().lower
;
396 left_pos
= sp
->xAxis2
->range().lower
;
399 double range_ratio
= sp
->xAxis2
->axisRect()->width() / node_label_w_
;
400 sp
->xAxis2
->setRange(left_pos
, range_ratio
+ left_pos
);
402 range_ratio
= sp
->yAxis
->axisRect()->height() / (one_em_
* 1.5);
403 sp
->yAxis
->setRange(top_pos
, range_ratio
+ top_pos
);
405 double rmin
= sp
->xAxis2
->range().size() / 2;
406 ui
->horizontalScrollBar
->setRange((rmin
- 0.5) * 100, (seq_analysis_
.num_nodes
- 0.5 - rmin
) * 100);
407 xAxisChanged(sp
->xAxis2
->range());
409 rmin
= (sp
->yAxis
->range().size() / 2);
410 ui
->verticalScrollBar
->setRange((rmin
- 1.0) * 100, (num_items_
- 0.5 - rmin
) * 100);
411 yAxisChanged(sp
->yAxis
->range());
416 void SequenceDialog::on_resetButton_clicked()
421 void SequenceDialog::on_actionGoToPacket_triggered()
423 if (cap_file_
&& packet_num_
> 0) {
424 emit
goToPacket(packet_num_
);
428 void SequenceDialog::on_showComboBox_currentIndexChanged(int index
)
431 seq_analysis_
.all_packets
= TRUE
;
433 seq_analysis_
.all_packets
= FALSE
;
438 void SequenceDialog::on_flowComboBox_currentIndexChanged(int index
)
440 if (index
< 0) return;
441 seq_analysis_
.type
= static_cast<seq_analysis_type
>(ui
->flowComboBox
->itemData(index
).toInt());
445 void SequenceDialog::on_addressComboBox_currentIndexChanged(int index
)
448 seq_analysis_
.any_addr
= TRUE
;
450 seq_analysis_
.any_addr
= FALSE
;
455 void SequenceDialog::on_actionReset_triggered()
457 on_resetButton_clicked();
460 void SequenceDialog::on_actionMoveRight10_triggered()
465 void SequenceDialog::on_actionMoveLeft10_triggered()
470 void SequenceDialog::on_actionMoveUp10_triggered()
475 void SequenceDialog::on_actionMoveDown10_triggered()
480 void SequenceDialog::on_actionMoveRight1_triggered()
485 void SequenceDialog::on_actionMoveLeft1_triggered()
490 void SequenceDialog::on_actionMoveUp1_triggered()
495 void SequenceDialog::on_actionMoveDown1_triggered()