// Once channel details are known, complete the graph with details that depend upon the channel.
void LteRlcGraphDialog::completeGraph(bool may_be_empty)
{
    QCustomPlot *rp = ui->rlcPlot;

    // If no channel chosen already, try to use currently selected frame.
    findChannel(may_be_empty);

    // Set window title here.
    if (graph_.channelSet) {
        QString dlg_title = tr("LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5)")
                                 .arg(graph_.ueid)
                                 .arg((graph_.channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB")
                                 .arg(graph_.channelId)
                                 .arg((graph_.direction == DIRECTION_UPLINK) ? "UL" : "DL")
                                 .arg((graph_.rlcMode == RLC_UM_MODE) ? "UM" : "AM");
        setWindowTitle(dlg_title);
    }
    else {
        setWindowTitle(tr("LTE RLC Graph - no channel selected"));
    }

    // Set colours/styles for each of the traces on the graph.
    QCustomPlot *sp = ui->rlcPlot;
    base_graph_ = sp->addGraph(); // All: Selectable segments
    base_graph_->setPen(QPen(QBrush(Qt::black), 0.25));

    reseg_graph_ = sp->addGraph();
    reseg_graph_->setPen(QPen(QBrush(Qt::lightGray), 0.25));

    acks_graph_ = sp->addGraph();
    acks_graph_->setPen(QPen(QBrush(graph_color_ack), 1.0));

    nacks_graph_ = sp->addGraph();
    nacks_graph_->setPen(QPen(QBrush(graph_color_nack), 0.25));

    // Create tracer
    tracer_ = new QCPItemTracer(sp);
    sp->addItem(tracer_);
    tracer_->setVisible(false);
    toggleTracerStyle(true);

    // Change label on save/export button.
    QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save);
    save_bt->setText(tr("Save As" UTF8_HORIZONTAL_ELLIPSIS));

    connect(rp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*)));
    connect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
    connect(rp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*)));
    disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    this->setResult(QDialog::Accepted);

    // Extract the data that the graph can use.
    fillGraph();
}
void LteRlcGraphDialog::on_actionCrosshairs_triggered()
{
    toggleTracerStyle(false);
}
// Respond to a key press.
void LteRlcGraphDialog::keyPressEvent(QKeyEvent *event)
{
    int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10;

    switch(event->key()) {
    case Qt::Key_Minus:
    case Qt::Key_Underscore:    // Shifted minus on U.S. keyboards
    case Qt::Key_O:             // GTK+
    case Qt::Key_R:
        zoomAxes(false);
        break;
    case Qt::Key_Plus:
    case Qt::Key_Equal:         // Unshifted plus on U.S. keyboards
    case Qt::Key_I:             // GTK+
        zoomAxes(true);
        break;

    case Qt::Key_X:             // Zoom X axis only
        if(event->modifiers() & Qt::ShiftModifier){
            zoomXAxis(false);   // upper case X -> Zoom out
        } else {
            zoomXAxis(true);    // lower case x -> Zoom in
        }
        break;
    case Qt::Key_Y:             // Zoom Y axis only
        if(event->modifiers() & Qt::ShiftModifier){
            zoomYAxis(false);   // upper case Y -> Zoom out
        } else {
            zoomYAxis(true);    // lower case y -> Zoom in
        }
        break;

    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, pan_pixels);
        break;
    case Qt::Key_Down:
    case Qt::Key_J:
        panAxes(0, -1 * pan_pixels);
        break;

    case Qt::Key_PageUp:
        panAxes(0, 20 * pan_pixels);
        break;
    case Qt::Key_PageDown:
        panAxes(0, -20 * pan_pixels);
        break;

    case Qt::Key_Space:
        toggleTracerStyle(false);
        break;

    case Qt::Key_0:
    case Qt::Key_ParenRight:    // Shifted 0 on U.S. keyboards
    case Qt::Key_Home:
        resetAxes();
        break;

    case Qt::Key_G:
        on_actionGoToPacket_triggered();
        break;
    case Qt::Key_T:
//        on_actionToggleTimeOrigin_triggered();
        break;
    case Qt::Key_Z:
        on_actionDragZoom_triggered();
        break;
    }

    WiresharkDialog::keyPressEvent(event);
}
TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_type graph_type) :
    QDialog(NULL, Qt::Window),
    ui(new Ui::TCPStreamDialog),
    cap_file_(cf),
    ts_offset_(0),
    ts_origin_conn_(true),
    seq_offset_(0),
    seq_origin_zero_(true),
    title_(NULL),
    base_graph_(NULL),
    tput_graph_(NULL),
    seg_graph_(NULL),
    ack_graph_(NULL),
    rwin_graph_(NULL),
    tracer_(NULL),
    packet_num_(0),
    mouse_drags_(true),
    rubber_band_(NULL),
    num_dsegs_(-1),
    num_acks_(-1),
    num_sack_ranges_(-1)
{
    struct segment current;
    int graph_idx = -1;

    ui->setupUi(this);
    setAttribute(Qt::WA_DeleteOnClose, true);

    graph_.type = GRAPH_UNDEFINED;
    set_address(&graph_.src_address, AT_NONE, 0, NULL);
    graph_.src_port = 0;
    set_address(&graph_.dst_address, AT_NONE, 0, NULL);
    graph_.dst_port = 0;
    graph_.stream = 0;
    graph_.segments = NULL;

    struct tcpheader *header = select_tcpip_session(cap_file_, &current);
    if (!header) {
        done(QDialog::Rejected);
        return;
    }

//#ifdef Q_OS_MAC
//    ui->hintLabel->setAttribute(Qt::WA_MacSmallSize, true);
//#endif

    QComboBox *gtcb = ui->graphTypeComboBox;
    gtcb->setUpdatesEnabled(false);
    gtcb->addItem(ui->actionRoundTripTime->text(), GRAPH_RTT);
    if (graph_type == GRAPH_RTT) graph_idx = gtcb->count() - 1;
    gtcb->addItem(ui->actionThroughput->text(), GRAPH_THROUGHPUT);
    if (graph_type == GRAPH_THROUGHPUT) graph_idx = gtcb->count() - 1;
    gtcb->addItem(ui->actionStevens->text(), GRAPH_TSEQ_STEVENS);
    if (graph_type == GRAPH_TSEQ_STEVENS) graph_idx = gtcb->count() - 1;
    gtcb->addItem(ui->actionTcptrace->text(), GRAPH_TSEQ_TCPTRACE);
    if (graph_type == GRAPH_TSEQ_TCPTRACE) graph_idx = gtcb->count() - 1;
    gtcb->addItem(ui->actionWindowScaling->text(), GRAPH_WSCALE);
    if (graph_type == GRAPH_WSCALE) graph_idx = gtcb->count() - 1;
    gtcb->setUpdatesEnabled(true);

    ui->dragRadioButton->setChecked(mouse_drags_);

    ctx_menu_.addAction(ui->actionZoomIn);
    ctx_menu_.addAction(ui->actionZoomInX);
    ctx_menu_.addAction(ui->actionZoomInY);
    ctx_menu_.addAction(ui->actionZoomOut);
    ctx_menu_.addAction(ui->actionZoomOutX);
    ctx_menu_.addAction(ui->actionZoomOutY);
    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->actionNextStream);
    ctx_menu_.addAction(ui->actionPreviousStream);
    ctx_menu_.addAction(ui->actionSwitchDirection);
    ctx_menu_.addAction(ui->actionGoToPacket);
    ctx_menu_.addSeparator();
    ctx_menu_.addAction(ui->actionDragZoom);
    ctx_menu_.addAction(ui->actionToggleSequenceNumbers);
    ctx_menu_.addAction(ui->actionToggleTimeOrigin);
    ctx_menu_.addAction(ui->actionCrosshairs);
    ctx_menu_.addSeparator();
    ctx_menu_.addAction(ui->actionRoundTripTime);
    ctx_menu_.addAction(ui->actionThroughput);
    ctx_menu_.addAction(ui->actionStevens);
    ctx_menu_.addAction(ui->actionTcptrace);
    ctx_menu_.addAction(ui->actionWindowScaling);

    memset (&graph_, 0, sizeof(graph_));
    graph_.type = graph_type;
    copy_address(&graph_.src_address, &current.ip_src);
    graph_.src_port = current.th_sport;
    copy_address(&graph_.dst_address, &current.ip_dst);
    graph_.dst_port = current.th_dport;
    graph_.stream = header->th_stream;
    findStream();

    ui->streamNumberSpinBox->blockSignals(true);
    ui->streamNumberSpinBox->setMaximum(get_tcp_stream_count() - 1);
    ui->streamNumberSpinBox->setValue(graph_.stream);
    ui->streamNumberSpinBox->blockSignals(false);

    QCustomPlot *sp = ui->streamPlot;
    QCPPlotTitle *file_title = new QCPPlotTitle(sp, cf_get_display_name(cap_file_));
    file_title->setFont(sp->xAxis->labelFont());
    title_ = new QCPPlotTitle(sp);
    sp->plotLayout()->insertRow(0);
    sp->plotLayout()->addElement(0, 0, file_title);
    sp->plotLayout()->insertRow(0);
    sp->plotLayout()->addElement(0, 0, title_);

    base_graph_ = sp->addGraph(); // All: Selectable segments
    base_graph_->setPen(QPen(QBrush(graph_color_1), 0.25));
    tput_graph_ = sp->addGraph(sp->xAxis, sp->yAxis2); // Throughput: Moving average
    tput_graph_->setPen(QPen(QBrush(graph_color_2), 0.5));
    tput_graph_->setLineStyle(QCPGraph::lsLine);
    seg_graph_ = sp->addGraph(); // tcptrace: fwd segments
    seg_graph_->setErrorType(QCPGraph::etValue);
    seg_graph_->setLineStyle(QCPGraph::lsNone);
    seg_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot, Qt::transparent, 0));
    seg_graph_->setErrorPen(QPen(QBrush(graph_color_1), 0.5));
    seg_graph_->setErrorBarSize(pkt_point_size_);
    ack_graph_ = sp->addGraph(); // tcptrace: rev ACKs
    ack_graph_->setPen(QPen(QBrush(graph_color_2), 0.5));
    ack_graph_->setLineStyle(QCPGraph::lsStepLeft);
    rwin_graph_ = sp->addGraph(); // tcptrace: rev RWIN
    rwin_graph_->setPen(QPen(QBrush(graph_color_3), 0.5));
    rwin_graph_->setLineStyle(QCPGraph::lsStepLeft);

    tracer_ = new QCPItemTracer(sp);
    sp->addItem(tracer_);

    // Triggers fillGraph().
    ui->graphTypeComboBox->setCurrentIndex(graph_idx);

    sp->setMouseTracking(true);

    sp->yAxis->setLabelColor(QColor(graph_color_1));
    sp->yAxis->setTickLabelColor(QColor(graph_color_1));

    tracer_->setVisible(false);
    toggleTracerStyle(true);

    QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save);
    save_bt->setText(tr("Save As" UTF8_HORIZONTAL_ELLIPSIS));

    QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close);
    if (close_bt) {
        close_bt->setDefault(true);
    }

    ProgressFrame::addToButtonBox(ui->buttonBox, parent);

    connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*)));
    connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
    connect(sp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*)));
    connect(sp, SIGNAL(axisClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)),
            this, SLOT(axisClicked(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)));
    connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(transformYRange(QCPRange)));
    disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    this->setResult(QDialog::Accepted);
}
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:
    case Qt::Key_Underscore:    // Shifted minus on U.S. keyboards
    case Qt::Key_O:             // GTK+
        zoomAxes(false);
        break;
    case Qt::Key_Plus:
    case Qt::Key_Equal:         // Unshifted plus on U.S. keyboards
    case Qt::Key_I:             // GTK+
        zoomAxes(true);
        break;
    case Qt::Key_X:             // Zoom X axis only
        if(event->modifiers() & Qt::ShiftModifier){
            zoomXAxis(false);   // upper case X -> Zoom out
        } else {
            zoomXAxis(true);    // lower case x -> Zoom in
        }
        break;
    case Qt::Key_Y:             // Zoom Y axis only
        if(event->modifiers() & Qt::ShiftModifier){
            zoomYAxis(false);   // upper case Y -> Zoom out
        } else {
            zoomYAxis(true);    // lower case y -> Zoom in
        }
        break;
    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, pan_pixels);
        break;
    case Qt::Key_Down:
    case Qt::Key_J:
        panAxes(0, -1 * pan_pixels);
        break;

    case Qt::Key_Space:
        toggleTracerStyle();
        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_PageUp:
        on_actionNextStream_triggered();
        break;
    case Qt::Key_PageDown:
        on_actionPreviousStream_triggered();
        break;

    case Qt::Key_D:
        on_actionSwitchDirection_triggered();
        break;
    case Qt::Key_G:
        on_actionGoToPacket_triggered();
        break;
    case Qt::Key_S:
        on_actionToggleSequenceNumbers_triggered();
        break;
    case Qt::Key_T:
        on_actionToggleTimeOrigin_triggered();
        break;
    case Qt::Key_Z:
        on_actionDragZoom_triggered();
        break;

    case Qt::Key_1:
        on_actionRoundTripTime_triggered();
        break;
    case Qt::Key_2:
        on_actionThroughput_triggered();
        break;
    case Qt::Key_3:
        on_actionStevens_triggered();
        break;
    case Qt::Key_4:
        on_actionTcptrace_triggered();
        break;
    case Qt::Key_5:
        on_actionWindowScaling_triggered();
        break;
        // Alas, there is no Blade Runner-style Qt::Key_Enhance
    }

    QDialog::keyPressEvent(event);
}