Sequence / flow diagram updates.

Add "Save As..." Add a context menu and keyboard shortcuts. Add a
tooltip.

svn path=/trunk/; revision=53263
This commit is contained in:
Gerald Combs 2013-11-12 01:08:08 +00:00
parent edbe198322
commit fc93c3a7c0
8 changed files with 340 additions and 42 deletions

View File

@ -28,18 +28,25 @@
#include "wsutil/nstime.h"
#include "wireshark_application.h"
#include <QDir>
#include <QFileDialog>
#include <QFontMetrics>
#include <QPoint>
#include <QDebug>
// To do:
// - Save as
// - Add UTF8 to text dump
// - Context menu
// - Add UTF8 to text dump
// - Selection highlighting
// - Keyboard shortcuts
// - ...
// - Save to XMI? http://www.umlgraph.org/
// - Time: abs vs delta
// - Hide nodes
// - Clickable time + comments?
// - Incorporate packet comments?
// - Change line_style to seq_type (i.e. draw ACKs dashed)
// - Create WSGraph subclasses with common behavior.
SequenceDialog::SequenceDialog(QWidget *parent, capture_file *cf, SequenceType type) :
QDialog(parent),
@ -72,15 +79,33 @@ SequenceDialog::SequenceDialog(QWidget *parent, capture_file *cf, SequenceType t
ui->gridLayout->setSpacing(0);
connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), sp->yAxis2, SLOT(setRange(QCPRange)));
ctx_menu_.addAction(ui->actionReset);
ctx_menu_.addSeparator();
ctx_menu_.addAction(ui->actionMoveRight10);
ctx_menu_.addAction(ui->actionMoveLeft10);
ctx_menu_.addAction(ui->actionMoveUp10);
ctx_menu_.addAction(ui->actionMoveDown10);
ctx_menu_.addAction(ui->actionMoveRight1);
ctx_menu_.addAction(ui->actionMoveLeft1);
ctx_menu_.addAction(ui->actionMoveUp1);
ctx_menu_.addAction(ui->actionMoveDown1);
ctx_menu_.addSeparator();
ctx_menu_.addAction(ui->actionGoToPacket);
memset (&seq_analysis_, 0, sizeof(seq_analysis_));
ui->showComboBox->blockSignals(true);
ui->showComboBox->setCurrentIndex(0);
ui->showComboBox->blockSignals(false);
ui->addressComboBox->blockSignals(true);
ui->addressComboBox->setCurrentIndex(0);
ui->addressComboBox->blockSignals(false);
QComboBox *fcb = ui->flowComboBox;
fcb->addItem(ui->actionFlowAny->text(), SEQ_ANALYSIS_ANY);
fcb->addItem(ui->actionFlowTcp->text(), SEQ_ANALYSIS_TCP);
ui->flowComboBox->blockSignals(true);
switch (type) {
case any:
seq_analysis_.type = SEQ_ANALYSIS_ANY;
@ -96,8 +121,12 @@ SequenceDialog::SequenceDialog(QWidget *parent, capture_file *cf, SequenceType t
ui->flowLabel->hide();
break;
}
ui->flowComboBox->blockSignals(false);
seq_analysis_.all_packets = TRUE;
QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save);
save_bt->setText(tr("Save As..."));
if (parent) {
resize(parent->width(), parent->height() * 4 / 5);
}
@ -109,6 +138,7 @@ SequenceDialog::SequenceDialog(QWidget *parent, capture_file *cf, SequenceType t
connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(diagramClicked(QMouseEvent*)));
connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
connect(sp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*)));
disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
fillDiagram();
}
@ -137,6 +167,44 @@ void SequenceDialog::resizeEvent(QResizeEvent *event)
resetAxes(true);
}
void SequenceDialog::keyPressEvent(QKeyEvent *event)
{
int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10;
// XXX - Copy some shortcuts from tcp_stream_dialog.cpp
switch(event->key()) {
case Qt::Key_Right:
case Qt::Key_L:
panAxes(pan_pixels, 0);
break;
case Qt::Key_Left:
case Qt::Key_H:
panAxes(-1 * pan_pixels, 0);
break;
case Qt::Key_Up:
case Qt::Key_K:
panAxes(0, -1 * pan_pixels);
break;
case Qt::Key_Down:
case Qt::Key_J:
panAxes(0, pan_pixels);
break;
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_G:
on_actionGoToPacket_triggered();
break;
}
QDialog::keyPressEvent(event);
}
void SequenceDialog::mouseReleaseEvent(QMouseEvent *event)
{
mouseReleased(event);
@ -174,23 +242,14 @@ void SequenceDialog::diagramClicked(QMouseEvent *event)
{
QCustomPlot *sp = ui->sequencePlot;
// if (event->button() == Qt::RightButton) {
// // XXX We should find some way to get streamPlot to handle a
// // contextMenuEvent instead.
// ctx_menu_.exec(event->globalPos());
// } else if (mouse_drags_) {
if (sp->axisRect()->rect().contains(event->pos())) {
sp->setCursor(QCursor(Qt::ClosedHandCursor));
}
on_actionGoToPacket_triggered();
// } 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();
// }
if (event->button() == Qt::RightButton) {
// XXX We should find some way to get sequenceDiagram to handle a
// contextMenuEvent instead.
ctx_menu_.exec(event->globalPos());
} else if (sp->axisRect()->rect().contains(event->pos())) {
sp->setCursor(QCursor(Qt::ClosedHandCursor));
}
on_actionGoToPacket_triggered();
}
void SequenceDialog::mouseMoved(QMouseEvent *event)
@ -235,6 +294,50 @@ void SequenceDialog::mouseReleased(QMouseEvent *event)
}
}
void SequenceDialog::on_buttonBox_accepted()
{
QString file_name, extension;
QDir path(wsApp->lastOpenDir());
QString pdf_filter = tr("Portable Document Format (*.pdf)");
QString png_filter = tr("Portable Network Graphics (*.png)");
QString bmp_filter = tr("Windows Bitmap (*.bmp)");
// Gaze upon my beautiful graph with lossy artifacts!
QString jpeg_filter = tr("JPEG File Interchange Format (*.jpeg *.jpg)");
QString ascii_filter = tr("ASCII (*.txt)");
QString filter = QString("%1;;%2;;%3;;%4")
.arg(pdf_filter)
.arg(png_filter)
.arg(bmp_filter)
.arg(jpeg_filter);
if (cap_file_) {
filter.append(QString(";;%5").arg(ascii_filter));
}
file_name = QFileDialog::getSaveFileName(this, tr("Wireshark: Save Graph As..."),
path.canonicalPath(), filter, &extension);
if (file_name.length() > 0) {
bool save_ok = false;
if (extension.compare(pdf_filter) == 0) {
save_ok = ui->sequencePlot->savePdf(file_name);
} else if (extension.compare(png_filter) == 0) {
save_ok = ui->sequencePlot->savePng(file_name);
} else if (extension.compare(bmp_filter) == 0) {
save_ok = ui->sequencePlot->saveBmp(file_name);
} else if (extension.compare(jpeg_filter) == 0) {
save_ok = ui->sequencePlot->saveJpg(file_name);
} else if (extension.compare(ascii_filter) == 0 && cap_file_) {
save_ok = sequence_analysis_dump_to_file(file_name.toUtf8().constData(), &seq_analysis_, cap_file_, 0);
}
// else error dialog?
if (save_ok) {
path = QDir(file_name);
wsApp->setLastOpenDir(path.canonicalPath().toUtf8().constData());
}
}
}
void SequenceDialog::fillDiagram()
{
QCustomPlot *sp = ui->sequencePlot;
@ -262,6 +365,25 @@ void SequenceDialog::fillDiagram()
sp->setFocus();
}
void SequenceDialog::panAxes(int x_pixels, int y_pixels)
{
QCustomPlot *sp = ui->sequencePlot;
double h_pan = 0.0;
double v_pan = 0.0;
h_pan = sp->xAxis2->range().size() * x_pixels / sp->xAxis2->axisRect()->width();
v_pan = sp->yAxis->range().size() * y_pixels / sp->yAxis->axisRect()->height();
// The GTK+ version won't pan unless we're zoomed. Should we do the same here?
if (h_pan) {
sp->xAxis2->moveRange(h_pan);
sp->replot();
}
if (v_pan) {
sp->yAxis->moveRange(v_pan);
sp->replot();
}
}
void SequenceDialog::resetAxes(bool keep_lower)
{
QCustomPlot *sp = ui->sequencePlot;
@ -309,7 +431,7 @@ void SequenceDialog::on_showComboBox_currentIndexChanged(int index)
seq_analysis_.all_packets = FALSE;
}
if (isVisible()) fillDiagram();
// if (isVisible()) fillDiagram();
}
void SequenceDialog::on_flowComboBox_currentIndexChanged(int index)
@ -317,7 +439,7 @@ void SequenceDialog::on_flowComboBox_currentIndexChanged(int index)
if (index < 0) return;
seq_analysis_.type = static_cast<seq_analysis_type>(ui->flowComboBox->itemData(index).toInt());
if (isVisible()) fillDiagram();
// if (isVisible()) fillDiagram();
}
void SequenceDialog::on_addressComboBox_currentIndexChanged(int index)
@ -328,5 +450,50 @@ void SequenceDialog::on_addressComboBox_currentIndexChanged(int index)
seq_analysis_.any_addr = FALSE;
}
if (isVisible()) fillDiagram();
// if (isVisible()) fillDiagram();
}
void SequenceDialog::on_actionReset_triggered()
{
on_resetButton_clicked();
}
void SequenceDialog::on_actionMoveRight10_triggered()
{
panAxes(10, 0);
}
void SequenceDialog::on_actionMoveLeft10_triggered()
{
panAxes(-10, 0);
}
void SequenceDialog::on_actionMoveUp10_triggered()
{
panAxes(0, -10);
}
void SequenceDialog::on_actionMoveDown10_triggered()
{
panAxes(0, 10);
}
void SequenceDialog::on_actionMoveRight1_triggered()
{
panAxes(1, 0);
}
void SequenceDialog::on_actionMoveLeft1_triggered()
{
panAxes(-1, 0);
}
void SequenceDialog::on_actionMoveUp1_triggered()
{
panAxes(0, -1);
}
void SequenceDialog::on_actionMoveDown1_triggered()
{
panAxes(0, 1);
}

View File

@ -35,6 +35,7 @@
#include "sequence_diagram.h"
#include <QDialog>
#include <QMenu>
namespace Ui {
class SequenceDialog;
@ -59,6 +60,7 @@ public slots:
protected:
void showEvent(QShowEvent *event);
void resizeEvent(QResizeEvent *event);
void keyPressEvent(QKeyEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private slots:
@ -70,14 +72,21 @@ private slots:
void mouseMoved(QMouseEvent *event);
void mouseReleased(QMouseEvent *event);
void on_buttonBox_accepted();
void on_resetButton_clicked();
void on_actionGoToPacket_triggered();
void on_showComboBox_currentIndexChanged(int index);
void on_flowComboBox_currentIndexChanged(int index);
void on_addressComboBox_currentIndexChanged(int index);
void on_actionReset_triggered();
void on_actionMoveRight10_triggered();
void on_actionMoveLeft10_triggered();
void on_actionMoveUp10_triggered();
void on_actionMoveDown10_triggered();
void on_actionMoveRight1_triggered();
void on_actionMoveLeft1_triggered();
void on_actionMoveUp1_triggered();
void on_actionMoveDown1_triggered();
private:
Ui::SequenceDialog *ui;
@ -88,8 +97,10 @@ private:
guint32 packet_num_;
double one_em_;
int node_label_w_;
QMenu ctx_menu_;
void fillDiagram();
void panAxes(int x_pixels, int y_pixels);
void resetAxes(bool keep_lower = false);
};

View File

@ -47,6 +47,28 @@
</item>
<item>
<widget class="QLabel" name="hintLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
&lt;h3&gt;Valuable and amazing time-saving keyboard shortcuts&lt;/h3&gt;
&lt;table&gt;&lt;tbody&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;→&lt;/th&gt;&lt;td&gt;Move right 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;←&lt;/th&gt;&lt;td&gt;Move left 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;↑&lt;/th&gt;&lt;td&gt;Move up 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;↓&lt;/th&gt;&lt;td&gt;Move down 10 pixels&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 right 1 pixel&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 left 1 pixel&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 up 1 pixel&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 1 pixel&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;g&lt;/th&gt;&lt;td&gt;Go to packet under cursor&lt;/td&gt;&lt;/th&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>&lt;small&gt;&lt;i&gt;A hint&lt;/i&gt;&lt;/small&gt;</string>
</property>
@ -182,6 +204,105 @@
</widget>
</item>
</layout>
<action name="actionReset">
<property name="text">
<string>Reset Diagram</string>
</property>
<property name="toolTip">
<string>Reset the diagram to its initial state.</string>
</property>
<property name="shortcut">
<string>0</string>
</property>
</action>
<action name="actionMoveUp10">
<property name="text">
<string>Move Up 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move up 10 pixels</string>
</property>
<property name="shortcut">
<string>Up</string>
</property>
</action>
<action name="actionMoveLeft10">
<property name="text">
<string>Move Left 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move left 10 pixels</string>
</property>
<property name="shortcut">
<string>Left</string>
</property>
</action>
<action name="actionMoveRight10">
<property name="text">
<string>Move Right 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move right 10 pixels</string>
</property>
<property name="shortcut">
<string>Right</string>
</property>
</action>
<action name="actionMoveDown10">
<property name="text">
<string>Move Down 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move down 10 pixels</string>
</property>
<property name="shortcut">
<string>Down</string>
</property>
</action>
<action name="actionMoveUp1">
<property name="text">
<string>Move Up 1 Pixel</string>
</property>
<property name="toolTip">
<string>Move up 1 pixel</string>
</property>
<property name="shortcut">
<string>Shift+Up</string>
</property>
</action>
<action name="actionMoveLeft1">
<property name="text">
<string>Move Left 1 Pixel</string>
</property>
<property name="toolTip">
<string>Move left 1 pixel</string>
</property>
<property name="shortcut">
<string>Shift+Left</string>
</property>
</action>
<action name="actionMoveRight1">
<property name="text">
<string>Move Right 1 Pixel</string>
</property>
<property name="toolTip">
<string>Move right 1 pixel</string>
</property>
<property name="shortcut">
<string>Shift+Right</string>
</property>
</action>
<action name="actionMoveDown1">
<property name="text">
<string>Move Down 1 Pixel</string>
</property>
<property name="toolTip">
<string>Move down 1 pixel</string>
</property>
<property name="shortcut">
<string>Shift+Down</string>
</property>
</action>
<action name="actionGoToPacket">
<property name="text">
<string>Go To Packet Under Cursor</string>

View File

@ -234,7 +234,6 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
{
int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10;
// XXX - This differs from the main window but matches other applications (e.g. Mozilla and Safari)
switch(event->key()) {
case Qt::Key_Minus:
@ -258,11 +257,11 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
break;
case Qt::Key_Up:
case Qt::Key_K:
panAxes(0, pan_pixels);
panAxes(0, -1 * pan_pixels);
break;
case Qt::Key_Down:
case Qt::Key_J:
panAxes(0, -1 * pan_pixels);
panAxes(0, pan_pixels);
break;
case Qt::Key_Space:
@ -1054,7 +1053,7 @@ void TCPStreamDialog::on_dragRadioButton_toggled(bool checked)
);
}
void TCPStreamDialog::on_selectRadioButton_toggled(bool checked)
void TCPStreamDialog::on_zoomRadioButton_toggled(bool checked)
{
if (checked) mouse_drags_ = false;
ui->streamPlot->setInteractions(0);
@ -1087,12 +1086,12 @@ void TCPStreamDialog::on_actionMoveLeft10_triggered()
void TCPStreamDialog::on_actionMoveUp10_triggered()
{
panAxes(0, 10);
panAxes(0, -10);
}
void TCPStreamDialog::on_actionMoveDown10_triggered()
{
panAxes(0, -10);
panAxes(0, 10);
}
void TCPStreamDialog::on_actionMoveRight1_triggered()
@ -1107,12 +1106,12 @@ void TCPStreamDialog::on_actionMoveLeft1_triggered()
void TCPStreamDialog::on_actionMoveUp1_triggered()
{
panAxes(0, 1);
panAxes(0, -1);
}
void TCPStreamDialog::on_actionMoveDown1_triggered()
{
panAxes(0, -1);
panAxes(0, 1);
}
void TCPStreamDialog::on_actionNextStream_triggered()
@ -1154,7 +1153,7 @@ void TCPStreamDialog::on_actionGoToPacket_triggered()
void TCPStreamDialog::on_actionDragZoom_triggered()
{
if (mouse_drags_) {
ui->selectRadioButton->toggle();
ui->zoomRadioButton->toggle();
} else {
ui->dragRadioButton->toggle();
}

View File

@ -119,7 +119,7 @@ private slots:
void on_streamNumberSpinBox_valueChanged(int new_stream);
void on_otherDirectionButton_clicked();
void on_dragRadioButton_toggled(bool checked);
void on_selectRadioButton_toggled(bool checked);
void on_zoomRadioButton_toggled(bool checked);
void on_actionZoomIn_triggered();
void on_actionZoomOut_triggered();
void on_actionReset_triggered();

View File

@ -145,12 +145,12 @@
</widget>
</item>
<item>
<widget class="QRadioButton" name="selectRadioButton">
<widget class="QRadioButton" name="zoomRadioButton">
<property name="toolTip">
<string>Select using the mouse button.</string>
</property>
<property name="text">
<string>selects</string>
<string>zooms</string>
</property>
<property name="checkable">
<bool>true</bool>

View File

@ -391,7 +391,7 @@ sequence_analysis_get_nodes(seq_analysis_info_t *sainfo)
/****************************************************************************/
gboolean
sequence_analysis_dump_to_file(char *pathname, seq_analysis_info_t *sainfo, capture_file *cf, unsigned int first_node)
sequence_analysis_dump_to_file(const char *pathname, seq_analysis_info_t *sainfo, capture_file *cf, unsigned int first_node)
{
guint32 i, display_items, display_nodes;
guint32 start_position, end_position, item_width, header_length;

View File

@ -107,7 +107,7 @@ int sequence_analysis_get_nodes(seq_analysis_info_t *sainfo);
* @param first_node Start drawing at this node.
* @return TRUE on success, FALSE on failure.
*/
gboolean sequence_analysis_dump_to_file(char *pathname, seq_analysis_info_t *sainfo, capture_file *cf, unsigned int first_node);
gboolean sequence_analysis_dump_to_file(const char *pathname, seq_analysis_info_t *sainfo, capture_file *cf, unsigned int first_node);
#ifdef __cplusplus
}