Qt: Add next / previous sequence shortcuts.
Add next (N) and previous (P) keyboard shortcuts. The GTK+ UI uses the down and up keys, but we're already using those for panning the Y axis. Add a scroll margin when using N and P. Add mouse wheel and trackpad support. Disable mouse dragging for now until we figure out how to limit it to our axis boundaries. Ping-Bug: 12419 Change-Id: I292319928db365206277bf2bb3e42e14ef811ff0 Reviewed-on: https://code.wireshark.org/review/15559 Reviewed-by: Gerald Combs <gerald@wireshark.org> Petri-Dish: Gerald Combs <gerald@wireshark.org> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
parent
aaa5551346
commit
33103f3fb2
|
@ -58,7 +58,8 @@ SequenceDiagram::SequenceDiagram(QCPAxis *keyAxis, QCPAxis *valueAxis, QCPAxis *
|
|||
comment_axis_(commentAxis),
|
||||
data_(NULL),
|
||||
sainfo_(NULL),
|
||||
selected_packet_(0)
|
||||
selected_packet_(0),
|
||||
selected_key_(-1.0)
|
||||
{
|
||||
data_ = new WSCPSeqDataMap();
|
||||
// xaxis (value): Address
|
||||
|
@ -103,6 +104,46 @@ SequenceDiagram::~SequenceDiagram()
|
|||
delete data_;
|
||||
}
|
||||
|
||||
int SequenceDiagram::adjacentPacket(bool next)
|
||||
{
|
||||
int adjacent_packet = -1;
|
||||
WSCPSeqDataMap::const_iterator it;
|
||||
|
||||
if (data_->size() < 1) return adjacent_packet;
|
||||
|
||||
if (selected_packet_ < 1) {
|
||||
WSCPSeqData &data = next ? data_->first() : data_->last();
|
||||
return data.value->frame_number;
|
||||
}
|
||||
|
||||
if (next) {
|
||||
for (it = data_->constBegin(); it != data_->constEnd(); ++it) {
|
||||
if (it.value().value->frame_number == selected_packet_) {
|
||||
++it;
|
||||
if (it != data_->constEnd()) {
|
||||
adjacent_packet = it.value().value->frame_number;
|
||||
selected_key_ = it.value().key;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
it = data_->constEnd();
|
||||
--it;
|
||||
while (it != data_->constBegin()) {
|
||||
guint32 prev_frame = it.value().value->frame_number;
|
||||
--it;
|
||||
if (prev_frame == selected_packet_) {
|
||||
adjacent_packet = it.value().value->frame_number;
|
||||
selected_key_ = it.value().key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return adjacent_packet;
|
||||
}
|
||||
|
||||
void SequenceDiagram::setData(_seq_analysis_info *sainfo)
|
||||
{
|
||||
data_->clear();
|
||||
|
@ -154,6 +195,7 @@ void SequenceDiagram::setData(_seq_analysis_info *sainfo)
|
|||
|
||||
void SequenceDiagram::setSelectedPacket(int selected_packet)
|
||||
{
|
||||
selected_key_ = -1;
|
||||
if (selected_packet > 0) {
|
||||
selected_packet_ = selected_packet;
|
||||
} else {
|
||||
|
@ -215,6 +257,7 @@ void SequenceDiagram::draw(QCPPainter *painter)
|
|||
QPalette sel_pal;
|
||||
fg_pen.setColor(sel_pal.color(QPalette::HighlightedText));
|
||||
bg_color = sel_pal.color(QPalette::Highlight);
|
||||
selected_key_ = cur_key;
|
||||
} else {
|
||||
fg_pen.setColor(Qt::black);
|
||||
bg_color = ColorUtils::sequenceColor(sai->conv_num);
|
||||
|
@ -339,7 +382,7 @@ QCPRange SequenceDiagram::getValueRange(bool &validRange, QCPAbstractPlottable::
|
|||
|
||||
if (sainfo_) {
|
||||
range.lower = 0;
|
||||
range.upper = sainfo_->num_nodes;
|
||||
range.upper = data_->size();
|
||||
valid = true;
|
||||
}
|
||||
validRange = valid;
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
struct _seq_analysis_info;
|
||||
struct _seq_analysis_item;
|
||||
|
||||
// Most of this is probably unnecessary
|
||||
// Some of this is probably unnecessary
|
||||
class WSCPSeqData
|
||||
{
|
||||
public:
|
||||
|
@ -44,11 +44,8 @@ public:
|
|||
double key;
|
||||
struct _seq_analysis_item *value;
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(WSCPSeqData, Q_MOVABLE_TYPE);
|
||||
|
||||
typedef QMap<double, WSCPSeqData> WSCPSeqDataMap;
|
||||
typedef QMapIterator<double, WSCPSeqData> WSCPSeqDataMapIterator;
|
||||
typedef QMutableMapIterator<double, WSCPSeqData> WSCPSeqDataMutableMapIterator;
|
||||
|
||||
class SequenceDiagram : public QCPAbstractPlottable
|
||||
{
|
||||
|
@ -58,6 +55,10 @@ public:
|
|||
virtual ~SequenceDiagram();
|
||||
|
||||
// getters:
|
||||
// Next / previous packet.
|
||||
int adjacentPacket(bool next);
|
||||
|
||||
double selectedKey() { return selected_key_; }
|
||||
|
||||
// setters:
|
||||
void setData(struct _seq_analysis_info *sainfo);
|
||||
|
@ -85,6 +86,7 @@ private:
|
|||
WSCPSeqDataMap *data_;
|
||||
struct _seq_analysis_info *sainfo_;
|
||||
guint32 selected_packet_;
|
||||
double selected_key_;
|
||||
};
|
||||
|
||||
#endif // SEQUENCE_DIAGRAM_H
|
||||
|
|
|
@ -39,8 +39,7 @@
|
|||
|
||||
// To do:
|
||||
// - Add zoom controls.
|
||||
// - Limit dragging to valid ranges.
|
||||
// - Handle trackpad and mouse wheel scrolling.
|
||||
// - Show + hide the Time and Comment axes.
|
||||
// - Add UTF8 to text dump
|
||||
// - Save to XMI? http://www.umlgraph.org/
|
||||
// - Time: abs vs delta
|
||||
|
@ -51,6 +50,9 @@
|
|||
// - Create WSGraph subclasses with common behavior.
|
||||
// - Help button and text
|
||||
|
||||
static const double min_top_ = -1.0;
|
||||
static const double min_left_ = -0.5;
|
||||
|
||||
SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *info) :
|
||||
WiresharkDialog(parent, cf),
|
||||
ui(new Ui::SequenceDialog),
|
||||
|
@ -76,7 +78,11 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
|
|||
|
||||
seq_diagram_ = new SequenceDiagram(sp->yAxis, sp->xAxis2, sp->yAxis2);
|
||||
sp->addPlottable(seq_diagram_);
|
||||
sp->axisRect()->setRangeDragAxes(sp->xAxis2, sp->yAxis);
|
||||
|
||||
// When dragging is enabled it's easy to drag past the lower and upper
|
||||
// bounds of each axis. Disable it for now.
|
||||
//sp->axisRect()->setRangeDragAxes(sp->xAxis2, sp->yAxis);
|
||||
//sp->setInteractions(QCP::iRangeDrag);
|
||||
|
||||
sp->xAxis->setVisible(false);
|
||||
sp->xAxis->setPadding(0);
|
||||
|
@ -96,7 +102,7 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
|
|||
key_text_->setText(tr("Time"));
|
||||
sp->addItem(key_text_);
|
||||
|
||||
key_text_->setPositionAlignment(Qt::AlignRight | Qt::AlignBottom);
|
||||
key_text_->setPositionAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
key_text_->position->setType(QCPItemPosition::ptAbsolute);
|
||||
key_text_->setClipToAxisRect(false);
|
||||
|
||||
|
@ -104,7 +110,7 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
|
|||
comment_text_->setText(tr("Comment"));
|
||||
sp->addItem(comment_text_);
|
||||
|
||||
comment_text_->setPositionAlignment(Qt::AlignLeft | Qt::AlignBottom);
|
||||
comment_text_->setPositionAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
comment_text_->position->setType(QCPItemPosition::ptAbsolute);
|
||||
comment_text_->setClipToAxisRect(false);
|
||||
|
||||
|
@ -112,8 +118,6 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
|
|||
ui->horizontalScrollBar->setSingleStep(100 / one_em_);
|
||||
ui->verticalScrollBar->setSingleStep(100 / one_em_);
|
||||
|
||||
sp->setInteractions(QCP::iRangeDrag);
|
||||
|
||||
ui->gridLayout->setSpacing(0);
|
||||
connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), sp->yAxis2, SLOT(setRange(QCPRange)));
|
||||
|
||||
|
@ -129,6 +133,8 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
|
|||
ctx_menu_.addAction(ui->actionMoveDown1);
|
||||
ctx_menu_.addSeparator();
|
||||
ctx_menu_.addAction(ui->actionGoToPacket);
|
||||
ctx_menu_.addAction(ui->actionGoToNextPacket);
|
||||
ctx_menu_.addAction(ui->actionGoToPreviousPacket);
|
||||
|
||||
ui->showComboBox->setCurrentIndex(0);
|
||||
ui->addressComboBox->setCurrentIndex(0);
|
||||
|
@ -155,7 +161,7 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
|
|||
connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(yAxisChanged(QCPRange)));
|
||||
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*)));
|
||||
connect(sp, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheeled(QWheelEvent*)));
|
||||
connect(this, SIGNAL(goToPacket(int)), seq_diagram_, SLOT(setSelectedPacket(int)));
|
||||
|
||||
disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
|
@ -215,16 +221,17 @@ void SequenceDialog::keyPressEvent(QKeyEvent *event)
|
|||
case Qt::Key_G:
|
||||
on_actionGoToPacket_triggered();
|
||||
break;
|
||||
case Qt::Key_N:
|
||||
on_actionGoToNextPacket_triggered();
|
||||
break;
|
||||
case Qt::Key_P:
|
||||
on_actionGoToPreviousPacket_triggered();
|
||||
break;
|
||||
}
|
||||
|
||||
QDialog::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void SequenceDialog::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
mouseReleased(event);
|
||||
}
|
||||
|
||||
void SequenceDialog::hScrollBarChanged(int value)
|
||||
{
|
||||
if (qAbs(ui->sequencePlot->xAxis2->range().center()-value/100.0) > 0.01) {
|
||||
|
@ -255,33 +262,22 @@ void SequenceDialog::yAxisChanged(QCPRange range)
|
|||
|
||||
void SequenceDialog::diagramClicked(QMouseEvent *event)
|
||||
{
|
||||
QCustomPlot *sp = ui->sequencePlot;
|
||||
|
||||
if (event->button() == Qt::RightButton) {
|
||||
switch (event->button()) {
|
||||
case Qt::LeftButton:
|
||||
on_actionGoToPacket_triggered();
|
||||
break;
|
||||
case 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));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
on_actionGoToPacket_triggered();
|
||||
}
|
||||
|
||||
void SequenceDialog::mouseMoved(QMouseEvent *event)
|
||||
{
|
||||
QCustomPlot *sp = ui->sequencePlot;
|
||||
Qt::CursorShape shape = Qt::ArrowCursor;
|
||||
if (event) {
|
||||
if (event->buttons().testFlag(Qt::LeftButton)) {
|
||||
shape = Qt::ClosedHandCursor;
|
||||
} else {
|
||||
if (sp->axisRect()->rect().contains(event->pos())) {
|
||||
shape = Qt::OpenHandCursor;
|
||||
}
|
||||
}
|
||||
}
|
||||
sp->setCursor(QCursor(shape));
|
||||
|
||||
packet_num_ = 0;
|
||||
QString hint;
|
||||
if (event) {
|
||||
|
@ -307,11 +303,17 @@ void SequenceDialog::mouseMoved(QMouseEvent *event)
|
|||
ui->hintLabel->setText(hint);
|
||||
}
|
||||
|
||||
void SequenceDialog::mouseReleased(QMouseEvent *)
|
||||
void SequenceDialog::mouseWheeled(QWheelEvent *event)
|
||||
{
|
||||
if (ui->sequencePlot->cursor().shape() == Qt::ClosedHandCursor) {
|
||||
ui->sequencePlot->setCursor(QCursor(Qt::OpenHandCursor));
|
||||
int scroll_delta = event->delta() * -1 / 15;
|
||||
if (event->orientation() == Qt::Vertical) {
|
||||
scroll_delta *= ui->verticalScrollBar->singleStep();
|
||||
ui->verticalScrollBar->setValue(ui->verticalScrollBar->value() + scroll_delta);
|
||||
} else {
|
||||
scroll_delta *= ui->horizontalScrollBar->singleStep();
|
||||
ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->value() + scroll_delta);
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void SequenceDialog::on_buttonBox_accepted()
|
||||
|
@ -374,8 +376,7 @@ void SequenceDialog::fillDiagram()
|
|||
seq_diagram_->setData(info_->sainfo());
|
||||
}
|
||||
|
||||
QFontMetrics fm = QFontMetrics(sp->xAxis2->labelFont());
|
||||
sequence_w_ = fm.height() * 15 ; // Arbitrary
|
||||
sequence_w_ = one_em_ * 15 ; // Arbitrary
|
||||
|
||||
mouseMoved(NULL);
|
||||
resetAxes();
|
||||
|
@ -386,18 +387,32 @@ void SequenceDialog::fillDiagram()
|
|||
|
||||
void SequenceDialog::panAxes(int x_pixels, int y_pixels)
|
||||
{
|
||||
// We could simplify this quite a bit if we set the scroll bar values instead.
|
||||
if (!info_->sainfo()) return;
|
||||
|
||||
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();
|
||||
if (h_pan < 0) {
|
||||
h_pan = qMax(h_pan, min_left_ - sp->xAxis2->range().lower);
|
||||
} else {
|
||||
h_pan = qMin(h_pan, info_->sainfo()->num_nodes - sp->xAxis2->range().upper);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (v_pan < 0) {
|
||||
v_pan = qMax(v_pan, min_top_ - sp->yAxis->range().lower);
|
||||
} else {
|
||||
v_pan = qMin(v_pan, num_items_ - sp->yAxis->range().upper);
|
||||
}
|
||||
|
||||
if (h_pan && !(sp->xAxis2->range().contains(min_left_) && sp->xAxis2->range().contains(info_->sainfo()->num_nodes))) {
|
||||
sp->xAxis2->moveRange(h_pan);
|
||||
sp->replot();
|
||||
}
|
||||
if (v_pan) {
|
||||
if (v_pan && !(sp->yAxis->range().contains(min_top_) && sp->yAxis->range().contains(num_items_))) {
|
||||
sp->yAxis->moveRange(v_pan);
|
||||
sp->replot();
|
||||
}
|
||||
|
@ -410,7 +425,7 @@ void SequenceDialog::resetAxes(bool keep_lower)
|
|||
QCustomPlot *sp = ui->sequencePlot;
|
||||
|
||||
// Allow space for labels on the top and port numbers on the left.
|
||||
double top_pos = -1.0, left_pos = -0.5;
|
||||
double top_pos = min_top_, left_pos = min_left_;
|
||||
if (keep_lower) {
|
||||
top_pos = sp->yAxis->range().lower;
|
||||
left_pos = sp->xAxis2->range().lower;
|
||||
|
@ -440,16 +455,17 @@ void SequenceDialog::resetAxes(bool keep_lower)
|
|||
sp->replot(QCustomPlot::rpQueued);
|
||||
|
||||
QRect axis_rect = sp->axisRect()->rect();
|
||||
|
||||
key_text_->position->setCoords(axis_rect.left()
|
||||
- sp->yAxis->padding()
|
||||
- sp->yAxis->tickLabelPadding()
|
||||
- sp->yAxis->offset(),
|
||||
axis_rect.top());
|
||||
axis_rect.top() / 2);
|
||||
comment_text_->position->setCoords(axis_rect.right()
|
||||
+ sp->yAxis2->padding()
|
||||
+ sp->yAxis2->tickLabelPadding()
|
||||
+ sp->yAxis2->offset(),
|
||||
axis_rect.top());
|
||||
axis_rect.top() / 2);
|
||||
|
||||
sp->replot(QCustomPlot::rpHint);
|
||||
}
|
||||
|
@ -466,6 +482,44 @@ void SequenceDialog::on_actionGoToPacket_triggered()
|
|||
}
|
||||
}
|
||||
|
||||
void SequenceDialog::goToAdjacentPacket(bool next)
|
||||
{
|
||||
if (file_closed_) return;
|
||||
int old_key = seq_diagram_->selectedKey();
|
||||
int adjacent_packet = seq_diagram_->adjacentPacket(next);
|
||||
int new_key = seq_diagram_->selectedKey();
|
||||
|
||||
if (adjacent_packet > 0) {
|
||||
if (old_key >= 0 && new_key >= 0) {
|
||||
// Scroll if we're at our scroll margin and we haven't reached
|
||||
// the end of our range.
|
||||
// XXX We should probably ensure the selected packet is visible as well.
|
||||
QCustomPlot *sp = ui->sequencePlot;
|
||||
double range_offset = new_key - old_key;
|
||||
double scroll_margin = 3.0; // Lines
|
||||
if (next) {
|
||||
if (new_key + scroll_margin < sp->yAxis->range().upper) {
|
||||
range_offset = 0.0;
|
||||
}
|
||||
while (range_offset > 0 && range_offset + sp->yAxis->range().upper > num_items_) {
|
||||
range_offset--;
|
||||
}
|
||||
} else {
|
||||
if (new_key - scroll_margin > sp->yAxis->range().lower) {
|
||||
range_offset = 0.0;
|
||||
}
|
||||
|
||||
double slop_top = min_top_ * 1.1;
|
||||
while (range_offset < 0 && range_offset + sp->yAxis->range().lower < slop_top) {
|
||||
range_offset++;
|
||||
}
|
||||
}
|
||||
sp->yAxis->moveRange(range_offset);
|
||||
}
|
||||
emit goToPacket(adjacent_packet);
|
||||
}
|
||||
}
|
||||
|
||||
void SequenceDialog::on_showComboBox_activated(int index)
|
||||
{
|
||||
if (!info_->sainfo()) return;
|
||||
|
|
|
@ -71,7 +71,6 @@ protected:
|
|||
void showEvent(QShowEvent *event);
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
|
||||
private slots:
|
||||
void updateWidgets();
|
||||
|
@ -81,13 +80,15 @@ private slots:
|
|||
void yAxisChanged(QCPRange range);
|
||||
void diagramClicked(QMouseEvent *event);
|
||||
void mouseMoved(QMouseEvent *event);
|
||||
void mouseReleased(QMouseEvent *event);
|
||||
void mouseWheeled(QWheelEvent *event);
|
||||
|
||||
void fillDiagram();
|
||||
|
||||
void on_buttonBox_accepted();
|
||||
void on_resetButton_clicked();
|
||||
void on_actionGoToPacket_triggered();
|
||||
void on_actionGoToNextPacket_triggered() { goToAdjacentPacket(true); }
|
||||
void on_actionGoToPreviousPacket_triggered() { goToAdjacentPacket(false); }
|
||||
void on_showComboBox_activated(int index);
|
||||
void on_flowComboBox_activated(int index);
|
||||
void on_addressComboBox_activated(int index);
|
||||
|
@ -115,7 +116,7 @@ private:
|
|||
|
||||
void panAxes(int x_pixels, int y_pixels);
|
||||
void resetAxes(bool keep_lower = false);
|
||||
|
||||
void goToAdjacentPacket(bool next);
|
||||
};
|
||||
|
||||
#endif // SEQUENCE_DIALOG_H
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th>
|
||||
|
||||
<tr><th>g</th><td>Go to packet under cursor</td></th>
|
||||
<tr><th>n</th><td>Go to the next packet</td></th>
|
||||
<tr><th>p</th><td>Go to the previous packet</td></th>
|
||||
|
||||
</tbody></table>
|
||||
</body></html></string>
|
||||
|
@ -347,6 +349,28 @@
|
|||
<string>1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGoToNextPacket">
|
||||
<property name="text">
|
||||
<string>Go To Next Packet</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Go to the next packet</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGoToPreviousPacket">
|
||||
<property name="text">
|
||||
<string>Go To Previous Packet</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Go to the previous packet</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>P</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
|
Loading…
Reference in New Issue