void SingleCellViewGraphPanelPlotWidget::mouseReleaseEvent(QMouseEvent *pEvent)
{
    // Default handling of the event

    QwtPlot::mouseReleaseEvent(pEvent);

    // Check that interaction is allowed

    if (!mInteractive)
        return;

    // Check whether we need to carry out an action

    if (mAction == None)
        return;

    // Keep track of the action to carry out
    // Note: this is so that we can reset mAction while still being able to
    //       finish carrying out the action...

    Action action = mAction;

    // We are done carrying out an action, so...

    mAction = None;

    // Finish carrying out the action

    switch (action) {
    case ShowCoordinates:
        // Remove the coordinates by replotting ourselves

        replotNow();

        break;
    case ZoomRegion: {
        // Zoom our region

        QRectF zoomRegionRect = zoomRegion();

        setLocalAxes(zoomRegionRect.left(), zoomRegionRect.right(),
                     zoomRegionRect.bottom(), zoomRegionRect.top());

        break;
    }
    default:
        // Another action which needs nothing more to be done

        ;
    }
}
bool SingleCellViewGraphPanelPlotWidget::setAxes(double pMinX, double pMaxX,
                                                 double pMinY,double pMaxY,
                                                 const bool &pCanReplot)
{
    // Keep track of our axes' old values

    double oldMinX = minX();
    double oldMaxX = maxX();
    double oldMinY = minY();
    double oldMaxY = maxY();

    // Make sure that the given axes' values are fine

    checkAxesValues(pMinX, pMaxX, pMinY, pMaxY);

    // Update our axes' values, if needed

    bool axesValuesChanged = false;

    if ((pMinX != oldMinX) || (pMaxX != oldMaxX)) {
        setAxis(QwtPlot::xBottom, pMinX, pMaxX);

        axesValuesChanged = true;
    }

    if ((pMinY != oldMinY) || (pMaxY != oldMaxY)) {
        setAxis(QwtPlot::yLeft, pMinY, pMaxY);

        axesValuesChanged = true;
    }

    // Update our actions in case the axes' values have changed

    if (axesValuesChanged)
        updateActions();

    // Replot ourselves, if needed and allowed

    if (axesValuesChanged && pCanReplot) {
        replotNow();

        return true;
    } else {
        return false;
    }
}
void SingleCellViewGraphPanelPlotWidget::mousePressEvent(QMouseEvent *pEvent)
{
    // Default handling of the event

    QwtPlot::mousePressEvent(pEvent);

    // Check that interaction is allowed

    if (!mInteractive)
        return;

    // Check that the position of the mouse is over our canvas

    if (!plotLayout()->canvasRect().contains(pEvent->pos()))
        return;

    // Check which action we can carry out

    if (   (pEvent->button() == Qt::LeftButton)
        && (pEvent->modifiers() == Qt::NoModifier)) {
        // We want to pan

        mAction = Pan;
    } else if (   (pEvent->button() == Qt::LeftButton)
               && (pEvent->modifiers() == Qt::ShiftModifier)) {
        // We want to show the coordinates

        mAction = ShowCoordinates;
    } else if (   (pEvent->button() == Qt::RightButton)
               && (pEvent->modifiers() == Qt::NoModifier)) {
        // We want to zoom in/out

        mAction = Zoom;
    } else if (   (pEvent->button() == Qt::RightButton)
               && (pEvent->modifiers() == Qt::ControlModifier)) {
        // We want to zoom a region, but we can only do this if we are not
        // already fully zoomed in

        if ((mZoomFactorX < MaxZoomFactor) || (mZoomFactorY < MaxZoomFactor)) {
            mAction = ZoomRegion;
        } else {
            // We are already fully zoomed in, so...

            mAction = None;

            return;
        }
    } else {
        // We cannot carry out any action, so check whether we need to replot
        // ourselves (in case we were in the middle of carrying out a visual
        // action), make sure that we have no action to carry out, replot
        // ourselves if needed, and then leave

        bool needReplotNow;

        switch (mAction) {
        case ShowCoordinates:
        case ZoomRegion:
            needReplotNow = true;

            break;
        default:
            needReplotNow = false;
        }

        mAction = None;

        if (needReplotNow)
            replotNow();

        return;
    }

    // Retrieve a pixmap version of our canvas, if needed

    if ((mAction == ShowCoordinates) || (mAction == ZoomRegion))
        mCanvasPixmap = grab(plotLayout()->canvasRect().toRect());

    // Keep track of the mouse position

    mOriginPoint = mousePositionWithinCanvas(pEvent->pos());
}
void SingleCellViewGraphPanelPlotWidget::mouseMoveEvent(QMouseEvent *pEvent)
{
    // Default handling of the event

    QwtPlot::mouseMoveEvent(pEvent);

    // Check that interaction is allowed

    if (!mInteractive)
        return;

    // Retrieve the current point

    QPointF currentPoint = mousePositionWithinCanvas(pEvent->pos());

    // Carry out the action

    switch (mAction) {
    case Pan: {
        // Determine the X/Y shifts for the panning

        double shiftX = currentPoint.x()-mOriginPoint.x();
        double shiftY = currentPoint.y()-mOriginPoint.y();

        // Determine our new local minimum/maximum values for our axes

        double newLocalMinX = localMinX()-shiftX;
        double newLocalMaxX = localMaxX()-shiftX;
        double newLocalMinY = localMinY()-shiftY;
        double newLocalMaxY = localMaxY()-shiftY;

        // Make sure that our new local minimum/maximum values for our axes
        // are within our local minimum/maximum values

        if (newLocalMinX < minX()) {
            newLocalMinX = minX();
            newLocalMaxX = newLocalMinX+localMaxX()-localMinX();
        } else if (newLocalMaxX > maxX()) {
            newLocalMaxX = maxX();
            newLocalMinX = newLocalMaxX-localMaxX()+localMinX();
        }

        if (newLocalMinY < minY()) {
            newLocalMinY = minY();
            newLocalMaxY = newLocalMinY+localMaxY()-localMinY();
        } else if (newLocalMaxY > maxY()) {
            newLocalMaxY = maxY();
            newLocalMinY = newLocalMaxY-localMaxY()+localMinY();
        }

        // Set our new local minimum/maximum values for our local axes which
        // will replot ourselves as a result

        setLocalAxes(newLocalMinX, newLocalMaxX, newLocalMinY, newLocalMaxY);

        break;
    }
    case ShowCoordinates:
        // Show the coordinates by simply replotting ourselves

        replotNow();

        break;
    case Zoom: {
        // Rescale ourselves (which will replot ourselves as a result)

        double deltaX = currentPoint.x()-mOriginPoint.x();
        double deltaY = currentPoint.y()-mOriginPoint.y();

        scaleLocalAxes(deltaX?
                           (deltaX > 0)?
                               ScalingInFactor:
                               ScalingOutFactor:
                           NoScalingFactor,
                       deltaY?
                           (deltaY < 0)?
                               ScalingInFactor:
                               ScalingOutFactor:
                           NoScalingFactor);

        break;
    }
    case ZoomRegion:
        // Draw our zoom region by updating our end point and then replotting
        // ourselves

        mEndPoint = mousePositionWithinCanvas(pEvent->pos());

        replotNow();

        break;
    default:
        // None

        ;
    }

    // Reset our point of origin, but only if we are doing something and it's
    // not zooming a region

    if ((mAction != None) && (mAction != ZoomRegion))
        mOriginPoint = mousePositionWithinCanvas(pEvent->pos());
}
void SingleCellViewGraphPanelPlotWidget::setLocalAxes(const double &pLocalMinX,
                                                      const double &pLocalMaxX,
                                                      const double &pLocalMinY,
                                                      const double &pLocalMaxY,
                                                      const bool &pCanReplot,
                                                      const bool &pForceMinMaxValues,
                                                      const bool &pUpdateMinMaxValues,
                                                      const bool &pResetMinMaxValues)
{
    // Update our axes

    double oldLocalMinX = localMinX();
    double oldLocalMaxX = localMaxX();
    double oldLocalMinY = localMinY();
    double oldLocalMaxY = localMaxY();

    double newLocalMinX = pLocalMinX;
    double newLocalMaxX = pLocalMaxX;
    double newLocalMinY = pLocalMinY;
    double newLocalMaxY = pLocalMaxY;

    // Retrieve the bounding rectangle for all our curves (but only for those
    // that have some data)

    QRectF boundingRect = QRectF();

    foreach (SingleCellViewGraphPanelPlotCurve *curve, mCurves)
        if (curve->dataSize())
            boundingRect |= curve->boundingRect();

    // Update the minimum/maximum values of our axes, should we have retrieved a
    // valid bounding rectangle

    if (boundingRect != QRectF()) {
        // Optimise our bounding rectangle by first retrieving the
        // minimum/maximum values of our axes

        double xMin = boundingRect.left();
        double xMax = boundingRect.right();
        double yMin = boundingRect.top();
        double yMax = boundingRect.bottom();

        // Make sure that the minimum/maximum values of our axes have finite
        // values

        checkAnyAxesValues(xMin, xMax, yMin, yMax);

        // Optimise the minimum/maximum values of our axes by rounding them
        // down/up, respectively

        double powerX = qPow(10.0, qMax(xMin?qFloor(log10(qAbs(xMin))):0, xMax?qFloor(log10(qAbs(xMax))):0));
        double powerY = qPow(10.0, qMax(yMin?qFloor(log10(qAbs(yMin))):0, yMax?qFloor(log10(qAbs(yMax))):0));

        xMin = powerX*qFloor(xMin/powerX);
        xMax = powerX*qCeil(xMax/powerX);
        yMin = powerY*qFloor(yMin/powerY);
        yMax = powerY*qCeil(yMax/powerY);

        // Make sure that the optimised minimum/maximum values of our axes have
        // finite values

        checkAnyAxesValues(xMin, xMax, yMin, yMax);

        // Update the minimum/maximum values of our axes, if required

        if (!mFixedAxisX) {
            if (pResetMinMaxValues)
                setMinMaxX(xMin, xMax);
            else if (pUpdateMinMaxValues)
                setMinMaxX(qMin(mMinX, xMin), qMax(mMaxX, xMax));
        }

        if (!mFixedAxisY) {
            if (pResetMinMaxValues)
                setMinMaxY(yMin, yMax);
            else if (pUpdateMinMaxValues)
                setMinMaxY(qMin(mMinY, yMin), qMax(mMaxY, yMax));
        }
    }

    // Make sure that the new minimum/maximum values of our local axes fit
    // within the minimum/maximum values of our axes

    if (pForceMinMaxValues) {
        newLocalMinX = mMinX;
        newLocalMaxX = mMaxX;
        newLocalMinY = mMinY;
        newLocalMaxY = mMaxY;
    } else {
        newLocalMinX = qMax(newLocalMinX, mMinX);
        newLocalMaxX = qMin(newLocalMaxX, mMaxX);
        newLocalMinY = qMax(newLocalMinY, mMinY);
        newLocalMaxY = qMin(newLocalMaxY, mMaxY);
    }

    // Make sure that the new minimum/maximum values of our local axes have
    // finite values

    checkAnyAxesValues(newLocalMinX, newLocalMaxX, newLocalMinY, newLocalMaxY);

    // Make sure that the new minimum/maximum values of our local axes have a
    // valid zoom factor

    checkLocalAxisValues(QwtPlot::xBottom, newLocalMinX, newLocalMaxX);
    checkLocalAxisValues(QwtPlot::yLeft, newLocalMinY, newLocalMaxY);

    // Update the minimum/maximum values of our local axes, if needed

    bool needReplot = false;

    if ((newLocalMinX != oldLocalMinX) || (newLocalMaxX != oldLocalMaxX)) {
        setLocalMinMaxX(newLocalMinX, newLocalMaxX);

        needReplot = true;
    }

    if ((newLocalMinY != oldLocalMinY) || (newLocalMaxY != oldLocalMaxY)) {
        setLocalMinMaxY(newLocalMinY, newLocalMaxY);

        needReplot = true;
    }

    // Replot ourselves, if needed and allowed

    if (needReplot && pCanReplot)
        replotNow();
}
void SingleCellViewGraphPanelPlotWidget::drawCurveSegment
(
 QSharedPointer<SingleCellViewGraphPanelPlotCurve> pCurve,
 const qulonglong &pFrom,
 const qulonglong &pTo)
{
    // Make sure that we have a curve segment to draw

    if (pFrom == pTo)
        return;

    // Reset our local axes and replot ourselves, if it is our first curve
    // segment, or carry on as normal

    if (!pFrom) {
        // It is our first curve segment, so check our local axes
        // Note: we always want to replot, hence our passing false as an
        //       argument to resetLocalAxes()...

        resetLocalAxes(false);
        replotNow();
    } else {
        // It's not our first curve segment, so determine the minimum/maximum
        // X/Y values of our new data

        double xMin = 0.0;
        double xMax = 0.0;
        double yMin = 0.0;
        double yMax = 0.0;

        for (qulonglong i = pFrom; i <= pTo; ++i)
            if (i == pFrom) {
                xMin = xMax = pCurve->data()->sample(i).x();
                yMin = yMax = pCurve->data()->sample(i).y();
            } else {
                double xVal = pCurve->data()->sample(i).x();
                double yVal = pCurve->data()->sample(i).y();

                xMin = qMin(xMin, xVal);
                xMax = qMax(xMax, xVal);

                yMin = qMin(yMin, yVal);
                yMax = qMax(yMax, yVal);
            }

        // Check whether our X/Y axis can handle the minimum/maximum X/Y values
        // of our new data

        if (   (xMin < minX()) || (xMax > maxX())
            || (yMin < minY()) || (yMax > maxY()))
            // Our X/Y axis cannot handle the minimum/maximum X/Y values of our
            // new data, so check our local axes

            checkLocalAxes(true, true, true);
        else
            // Our X/Y axis can handle the X/Y min/max of our new data, so just
            // draw our new curve segment

            mDirectPainter->drawSeries(pCurve.data(), pFrom, pTo);
    }
}