/*
    SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

//This file contains Event handlers for the SkyMap class.

#include "skymap.h"
#include "qtcompat.h"

#include "ksplanetbase.h"
#include "kspopupmenu.h"
#include "kstars.h"
#include "observinglist.h"
#include "Options.h"
#include "skyglpainter.h"
#include "skyqpainter.h"
#include "printing/simplefovexporter.h"
#include "skycomponents/skylabeler.h"
#include "skycomponents/skymapcomposite.h"
#include "skycomponents/starcomponent.h"
#include "skycomponents/mosaiccomponent.h"
#ifdef HAVE_INDI
#include "skyobjects/mosaictiles.h"
#endif
#include "widgets/infoboxwidget.h"

#include <QGestureEvent>
#include <QStatusBar>
#include <QToolTip>

void SkyMap::resizeEvent(QResizeEvent *)
{
    computeSkymap = true; // skymap must be new computed

    //FIXME: No equivalent for this line in Qt4 ??
    //	if ( testWState( Qt::WState_AutoMask ) ) updateMask();

    // Resize the widget that draws the sky map.
    // FIXME: The resize event doesn't pass to children. Any better way of doing this?
    m_SkyMapDraw->resize(size());

    // Resize infoboxes container.
    // FIXME: this is not really pretty. Maybe there are some better way to this???
    m_iboxes->resize(size());
}

void SkyMap::keyPressEvent(QKeyEvent *e)
{
    bool arrowKeyPressed(false);
    bool shiftPressed(false);
    float step = 1.0;
    if (e->modifiers() & Qt::ShiftModifier)
    {
        step         = 10.0;
        shiftPressed = true;
    }

    //If the DBus resume key is not empty, then DBus processing is
    //paused while we wait for a keypress
    if (!data->resumeKey.isEmpty() && QKeySequence(e->key()) == data->resumeKey)
    {
        //The resumeKey was pressed.  Signal that it was pressed by
        //resetting it to empty; this will break the loop in
        //KStars::waitForKey()
        data->resumeKey = QKeySequence();
        return;
    }

    if (m_previewLegend)
    {
        slotCancelLegendPreviewMode();
    }

    switch (e->key())
    {
        case Qt::Key_Left:
            if (Options::useAltAz())
            {
                focus()->setAz(dms(focus()->az().Degrees() - step * MINZOOM / Options::zoomFactor()).reduce());
                focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
            }
            else
            {
                focus()->setRA(focus()->ra().Hours() + 0.05 * step * MINZOOM / Options::zoomFactor());
                focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
            }

            arrowKeyPressed = true;
            slewing         = true;
            break;

        case Qt::Key_Right:
            if (Options::useAltAz())
            {
                focus()->setAz(dms(focus()->az().Degrees() + step * MINZOOM / Options::zoomFactor()).reduce());
                focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
            }
            else
            {
                focus()->setRA(focus()->ra().Hours() - 0.05 * step * MINZOOM / Options::zoomFactor());
                focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
            }

            arrowKeyPressed = true;
            slewing         = true;
            break;

        case Qt::Key_Up:
            if (Options::useAltAz())
            {
                focus()->setAltRefracted(focus()->altRefracted().Degrees() + step * MINZOOM / Options::zoomFactor());
                if (focus()->alt().Degrees() > 90.0)
                    focus()->setAlt(90.0);
                focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
            }
            else
            {
                focus()->setDec(focus()->dec().Degrees() + step * MINZOOM / Options::zoomFactor());
                if (focus()->dec().Degrees() > 90.0)
                    focus()->setDec(90.0);
                focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
            }

            arrowKeyPressed = true;
            slewing         = true;
            break;

        case Qt::Key_Down:
            if (Options::useAltAz())
            {
                focus()->setAltRefracted(focus()->altRefracted().Degrees() - step * MINZOOM / Options::zoomFactor());
                if (focus()->alt().Degrees() < -90.0)
                    focus()->setAlt(-90.0);
                focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
            }
            else
            {
                focus()->setDec(focus()->dec().Degrees() - step * MINZOOM / Options::zoomFactor());
                if (focus()->dec().Degrees() < -90.0)
                    focus()->setDec(-90.0);
                focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
            }

            arrowKeyPressed = true;
            slewing         = true;
            break;

        case Qt::Key_Plus: //Zoom in
        case Qt::Key_Equal:
            zoomInOrMagStep(e->modifiers());
            break;

        case Qt::Key_Minus: //Zoom out
        case Qt::Key_Underscore:
            zoomOutOrMagStep(e->modifiers());
            break;

        case Qt::Key_0: //center on Sun
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::SUN));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        case Qt::Key_1: //center on Mercury
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::MERCURY));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        case Qt::Key_2: //center on Venus
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::VENUS));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        case Qt::Key_3: //center on Moon
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::MOON));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        case Qt::Key_4: //center on Mars
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::MARS));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        case Qt::Key_5: //center on Jupiter
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::JUPITER));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        case Qt::Key_6: //center on Saturn
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::SATURN));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        case Qt::Key_7: //center on Uranus
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::URANUS));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        case Qt::Key_8: //center on Neptune
            setClickedObject(data->skyComposite()->planet(KSPlanetBase::NEPTUNE));
            setClickedPoint(clickedObject());
            slotCenter();
            break;

        /*case Qt::Key_9: //center on Pluto
            setClickedObject( data->skyComposite()->planet( KSPlanetBase::PLUTO ) );
            setClickedPoint( clickedObject() );
            slotCenter();
            break;*/

        case Qt::Key_BracketLeft: // Begin measuring angular distance
            if (!rulerMode)
                slotBeginAngularDistance();
            break;
        case Qt::Key_Escape: // Cancel angular distance measurement
        {
            if (rulerMode)
                slotCancelRulerMode();

            if (m_fovCaptureMode)
                slotFinishFovCaptureMode();
            break;
        }

        case Qt::Key_C: //Center clicked object
            if (clickedObject())
                slotCenter();
            break;

        case Qt::Key_D: //Details window for Clicked/Centered object
        {
            SkyObject *orig = nullptr;
            if (shiftPressed)
            {
                orig = clickedObject();
                setClickedObject(focusObject());
            }

            if (clickedObject())
            {
                slotDetail();
            }

            if (orig)
            {
                setClickedObject(orig);
            }
            break;
        }

        case Qt::Key_P: //Show Popup menu for Clicked/Centered object
            if (shiftPressed)
            {
                if (focusObject())
                    focusObject()->showPopupMenu(pmenu, QCursor::pos());
            }
            else
            {
                if (clickedObject())
                    clickedObject()->showPopupMenu(pmenu, QCursor::pos());
            }
            break;

        case Qt::Key_O: //Add object to Observing List
        {
            SkyObject *orig = nullptr;
            if (shiftPressed)
            {
                orig = clickedObject();
                setClickedObject(focusObject());
            }

            if (clickedObject())
            {
                data->observingList()->slotAddObject();
            }

            if (orig)
            {
                setClickedObject(orig);
            }
            break;
        }

        case Qt::Key_L: //Toggle User label on Clicked/Centered object
        {
            SkyObject *orig = nullptr;
            if (shiftPressed)
            {
                orig = clickedObject();
                setClickedObject(focusObject());
            }

            if (clickedObject())
            {
                if (isObjectLabeled(clickedObject()))
                    slotRemoveObjectLabel();
                else
                    slotAddObjectLabel();
            }

            if (orig)
            {
                setClickedObject(orig);
            }
            break;
        }

        case Qt::Key_T: //Toggle planet trail on Clicked/Centered object (if solsys)
        {
            SkyObject *orig = nullptr;
            if (shiftPressed)
            {
                orig = clickedObject();
                setClickedObject(focusObject());
            }

            KSPlanetBase *planet = dynamic_cast<KSPlanetBase *>(clickedObject());
            if (planet)
            {
                if (planet->hasTrail())
                    slotRemovePlanetTrail();
                else
                    slotAddPlanetTrail();
            }

            if (orig)
            {
                setClickedObject(orig);
            }
            break;
        }

        case Qt::Key_R:
        {
            // Toggle relativistic corrections
            Options::setUseRelativistic(!Options::useRelativistic());
            qDebug() << Q_FUNC_INFO << "Relativistic corrections: " << Options::useRelativistic();
            forceUpdate();
            break;
        }

        case Qt::Key_A:
            Options::setUseAntialias(!Options::useAntialias());
            qDebug() << Q_FUNC_INFO << "Use Antialiasing: " << Options::useAntialias();
            forceUpdate();
            break;

        case Qt::Key_K:
        {
            if (m_fovCaptureMode)
                slotCaptureFov();
            break;
        }

        case Qt::Key_PageUp:
        {
            if (shiftPressed)
            {
                KStars::Instance()->selectPreviousView();
            }
            else
            {
                KStars::Instance()->selectPreviousFov();
            }
            break;
        }

        case Qt::Key_PageDown:
        {
            if (shiftPressed)
            {
                KStars::Instance()->selectNextView();
            }
            else
            {
                KStars::Instance()->selectNextFov();
            }
            break;
        }

        default:
            // We don't want to do anything in this case. Key is unknown
            return;
    }

    if (arrowKeyPressed)
    {
        stopTracking();
        setDestination(*focus());
    }

    forceUpdate(); //need a total update, or slewing with the arrow keys doesn't work.
}

void SkyMap::stopTracking()
{
    KStars *kstars = KStars::Instance();

    Q_EMIT positionChanged(focus());
    if (kstars && Options::isTracking())
        kstars->slotTrack();
}

bool SkyMap::event(QEvent *event)
{
#if !defined(KSTARS_LITE)
    if (event->type() == QEvent::TouchBegin)
    {
        m_touchMode = true;
        m_pinchScale = -1;
    }

    if (event->type() == QEvent::Gesture && m_touchMode)
    {
        QGestureEvent* gestureEvent = static_cast<QGestureEvent*>(event);

        if (QPinchGesture *pinch = static_cast<QPinchGesture * >(gestureEvent->gesture(Qt::PinchGesture)))
        {
            QPinchGesture::ChangeFlags changeFlags = pinch->changeFlags();

            m_pinchMode = true;
            if (changeFlags & QPinchGesture::ScaleFactorChanged)
            {
                if (m_pinchScale == -1)
                {
                    m_pinchScale = pinch->totalScaleFactor();
                    return true;
                }
                if (pinch->totalScaleFactor() - m_pinchScale > 0.1)
                {
                    m_pinchScale = pinch->totalScaleFactor();
                    zoomInOrMagStep(0);
                    return true;
                }
                if (pinch->totalScaleFactor() - m_pinchScale < -0.1)
                {
                    m_pinchScale = pinch->totalScaleFactor();
                    zoomOutOrMagStep(0);
                    return true;
                }
            }
        }
        if (QTapAndHoldGesture *tapAndHold = static_cast<QTapAndHoldGesture * >(gestureEvent->gesture(Qt::TapAndHoldGesture)))
        {
            m_tapAndHoldMode = true;
            if (tapAndHold->state() == Qt::GestureFinished)
            {
                if (clickedObject())
                {
                    clickedObject()->showPopupMenu(pmenu, tapAndHold->position().toPoint());
                }
                else
                {
                    pmenu->createEmptyMenu(clickedPoint());
                    pmenu->popup(tapAndHold->position().toPoint());
                }
                m_touchMode = false;
                m_pinchMode = false;
                m_tapAndHoldMode = false;
            }
        }
        return true;
    }
#endif
    return QGraphicsView::event(event);
}

void SkyMap::keyReleaseEvent(QKeyEvent *e)
{
    switch (e->key())
    {
        case Qt::Key_Plus: //Zoom in
        case Qt::Key_Equal:
        case Qt::Key_Minus: //Zoom out
        case Qt::Key_Underscore:

        case Qt::Key_Left:  //no break; continue to Qt::Key_Down
        case Qt::Key_Right: //no break; continue to Qt::Key_Down
        case Qt::Key_Up:    //no break; continue to Qt::Key_Down
        case Qt::Key_Down:
            slewing = false;

            if (Options::useAltAz())
                setDestinationAltAz(focus()->alt(), focus()->az(), false);
            else
                setDestination(*focus());

            showFocusCoords();
            forceUpdate(); // Need a full update to draw faint objects that are not drawn while slewing.
            break;
    }
}

void SkyMap::mouseMoveEvent(QMouseEvent *e)
{
#if !defined(KSTARS_LITE)
    // Skip touch points
    if (m_pinchMode || m_tapAndHoldMode || (m_touchMode && QtCompat::mouseGlobalX(e) == 0 && QtCompat::mouseGlobalY(e) == 0))
        return;
#endif

    if (Options::useHoverLabel())
    {
        //Start a single-shot timer to monitor whether we are currently hovering.
        //The idea is that whenever a moveEvent occurs, the timer is reset.  It
        //will only timeout if there are no move events for HOVER_INTERVAL ms
        m_HoverTimer.start(HOVER_INTERVAL);
        QToolTip::hideText();
    }

    //Are we defining a ZoomRect?
    if (ZoomRect.center().x() > 0 && ZoomRect.center().y() > 0)
    {
        //cancel operation if the user let go of CTRL
        if (!(e->modifiers() & Qt::ControlModifier))
        {
            ZoomRect = QRect(); //invalidate ZoomRect
            update();
        }
        else
        {
            //Resize the rectangle so that it passes through the cursor position
            QPoint pcenter = ZoomRect.center();
            int dx         = abs(QtCompat::mouseX(e) - pcenter.x());
            int dy         = abs(QtCompat::mouseY(e) - pcenter.y());
            if (dx == 0 || float(dy) / float(dx) > float(height()) / float(width()))
            {
                //Size rect by height
                ZoomRect.setHeight(2 * dy);
                ZoomRect.setWidth(2 * dy * width() / height());
            }
            else
            {
                //Size rect by height
                ZoomRect.setWidth(2 * dx);
                ZoomRect.setHeight(2 * dx * height() / width());
            }
            ZoomRect.moveCenter(pcenter); //reset center

            update();
            return;
        }
    }

    // Are we setting the skymap rotation?
    if (rotationStart.x() > 0 && rotationStart.y() > 0)
    {
        // stop the operation if the user let go of SHIFT
        if (!(e->modifiers() & Qt::ShiftModifier))
        {
            rotationStart = QPoint(); // invalidate
            rotationStartAngle = dms(); // NaN
            slewing = false;
            forceUpdate();
            return;
        }
        else
        {
            // Compute the rotation
            const float start_x = rotationStart.x() - width() / 2.0f;
            const float start_y = height() / 2.0f - rotationStart.y();

            const float curr_x = QtCompat::mouseX(e) - width() / 2.0f;
            const float curr_y = height() / 2.0f - QtCompat::mouseY(e);

            const dms angle {(std::atan2(curr_y, curr_x) - std::atan2(start_y, start_x)) / dms::DegToRad };
            slotSetSkyRotation((rotationStartAngle - angle).Degrees());
            return;
        }
    }

    // Are we setting the fov rotation?
    if (fovRotationStart.x() > 0 && fovRotationStart.y() > 0)
    {
        // stop the operation if the user let go of SHIFT or ALT or CONTROL
        if (!((e->modifiers() & Qt::ShiftModifier) && (e->modifiers() & Qt::ControlModifier) && (e->modifiers() & Qt::AltModifier)))
        {
            fovRotationStart = QPoint(); // invalidate
            fovRotationStartAngle = dms(); // NaN
            slewing = false;
            forceUpdate();
            return;
        }
        else
        {
            // Compute the rotation
            const float start_x = fovRotationStart.x() - width() / 2.0f;
            const float start_y = height() / 2.0f - fovRotationStart.y();

            const float curr_x = QtCompat::mouseX(e) - width() / 2.0f;
            const float curr_y = height() / 2.0f - QtCompat::mouseY(e);

            const dms angle {(std::atan2(curr_y, curr_x) - std::atan2(start_y, start_x)) / dms::DegToRad };
            setExtraFovRotation((fovRotationStartAngle - angle).Degrees());
            return;
        }
    }

    if (projector()->unusablePoint(QtCompat::mousePos(e).toPoint()))
        return; // break if point is unusable

    //determine RA, Dec of mouse pointer
    m_MousePoint = projector()->fromScreen(QtCompat::mousePos(e).toPoint(), data);

    double dyPix = 0.5 * height() - QtCompat::mouseY(e);
    if (midMouseButtonDown) //zoom according to y-offset
    {
        float yoff = dyPix - y0;
        if (yoff > 10)
        {
            y0 = dyPix;
            slotZoomIn();
        }
        if (yoff < -10)
        {
            y0 = dyPix;
            slotZoomOut();
        }
    }

    if (mouseButtonDown)
    {
#ifdef HAVE_INDI
        if (Options::showMosaicPanel())
        {
            auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();

            if (tiles->operationMode() == MosaicTiles::MODE_PLANNING)
            {
                // Check if mouse point within Mosaic FOV bounds.
                auto mosaicFOV = tiles->mosaicFOV();
                auto upperRA = tiles->ra0().Degrees() + mosaicFOV.width() / 60;
                auto lowerRA = tiles->ra0().Degrees() - mosaicFOV.width() / 60;
                auto upperDE = tiles->dec0().Degrees() + mosaicFOV.height() / 60;
                auto lowerDE = tiles->dec0().Degrees() - mosaicFOV.height() / 60;

                auto mouseRA = m_MousePoint.ra().Degrees();
                auto mouseDE = m_MousePoint.dec().Degrees();

                // If mouse point is within, then behave like drag and drop
                if (mouseRA > lowerRA && mouseRA < upperRA && mouseDE > lowerDE && mouseDE < upperDE)
                {
                    if (!mouseDragCursor)
                        setMouseDragCursor();

                    dms dRA  = m_MousePoint.ra() - clickedPoint()->ra();
                    dms dDec = m_MousePoint.dec() - clickedPoint()->dec();

                    // Emit difference between mouse point and clicked point.
                    Q_EMIT mosaicCenterChanged(dRA, dDec);

                    // Update mouse and clicked points.
                    m_MousePoint = projector()->fromScreen(QtCompat::mousePos(e).toPoint(), data);
                    setClickedPoint(&m_MousePoint);
                    update();
                    return;
                }
            }
        }
#endif

        // set the mouseMoveCursor and set slewing=true, if they are not set yet
        if (!mouseMoveCursor)
            setMouseMoveCursor();
        if (!slewing)
        {
            slewing = true;
            stopTracking(); //toggle tracking off
        }

        //Update focus such that the sky coords at mouse cursor remain approximately constant
        if (Options::useAltAz())
        {
            m_MousePoint.EquatorialToHorizontal(data->lst(), data->geo()->lat());
            clickedPoint()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
            dms dAz  = m_MousePoint.az() - clickedPoint()->az();
            dms dAlt = m_MousePoint.altRefracted() - clickedPoint()->altRefracted();
            focus()->setAz(focus()->az().Degrees() - dAz.Degrees()); //move focus in opposite direction
            focus()->setAz(focus()->az().reduce());
            focus()->setAltRefracted(KSUtils::clamp(focus()->altRefracted().Degrees() - dAlt.Degrees(), -90.0, 90.0));
            focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
        }
        else
        {
            dms dRA  = m_MousePoint.ra() - clickedPoint()->ra();
            dms dDec = m_MousePoint.dec() - clickedPoint()->dec();
            focus()->setRA(focus()->ra().Hours() - dRA.Hours()); //move focus in opposite direction
            focus()->setRA(focus()->ra().reduce());
            focus()->setDec(KSUtils::clamp(focus()->dec().Degrees() - dDec.Degrees(), -90.0, 90.0));
            focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
        }
        showFocusCoords();

        //redetermine RA, Dec of mouse pointer, using new focus
        m_MousePoint = projector()->fromScreen(QtCompat::mousePos(e).toPoint(), data);
        setClickedPoint(&m_MousePoint);
        forceUpdate(); // must be new computed
    }
    else //mouse button not down
    {
        if (Options::useAltAz())
            m_MousePoint.EquatorialToHorizontal(data->lst(), data->geo()->lat());
        Q_EMIT mousePointChanged(&m_MousePoint);
    }
}

void SkyMap::wheelEvent(QWheelEvent *e)
{
    if (e->angleDelta().y() > 0)
        zoomInOrMagStep(e->modifiers());
    else if (e->angleDelta().y() < 0)
        zoomOutOrMagStep(e->modifiers());
}

void SkyMap::mouseReleaseEvent(QMouseEvent *e)
{
#if !defined(KSTARS_LITE)
    if (m_touchMode)
    {
        m_touchMode = false;
        m_pinchMode = false;
        m_tapAndHoldMode = false;
    }
#endif

    if (ZoomRect.isValid())
    {
        stopTracking();
        SkyPoint newcenter = projector()->fromScreen(ZoomRect.center(), data);
        setFocus(&newcenter);
        setDestination(newcenter);

        //Zoom in on center of Zoom Circle, by a factor equal to the ratio
        //of the sky pixmap's width to the Zoom Circle's diameter
        float factor = float(width()) / float(ZoomRect.width());
        setZoomFactor(Options::zoomFactor() * factor);
    }

    setMouseCursorShape(static_cast<Cursor>(Options::defaultCursor()));

    ZoomRect = QRect(); //invalidate ZoomRect

    if (m_previewLegend)
    {
        slotCancelLegendPreviewMode();
    }

    // Are we setting the skymap rotation?
    if (rotationStart.x() > 0 && rotationStart.y() > 0)
    {
        rotationStart = QPoint(); // invalidate
        rotationStartAngle = dms(); // NaN
        slewing = false;
        forceUpdateNow();
        return;
    }

    // Are we setting the fov rotation?
    if (fovRotationStart.x() > 0 && fovRotationStart.y() > 0)
    {
        fovRotationStart = QPoint(); // invalidate
        fovRotationStartAngle = dms(); // NaN
        slewing = false;
        forceUpdateNow();
        return;
    }

    //false if double-clicked, because it's unset there.
    if (mouseButtonDown)
    {
        mouseButtonDown = false;
        if (slewing)
        {
            slewing = false;
            if (Options::useAltAz())
                setDestinationAltAz(focus()->alt(), focus()->az(), false);
            else
                setDestination(*focus());
        }
        else if (Options::leftClickSelectsObject())
            mouseDoubleClickEvent(e);
        forceUpdate(); // is needed because after moving the sky not all stars are shown
    }
    // if middle button was pressed unset here
    midMouseButtonDown = false;
}

void SkyMap::mousePressEvent(QMouseEvent *e)
{
    if ((e->modifiers() & Qt::ControlModifier) && (e->modifiers() & Qt::ShiftModifier) && (e->modifiers() & Qt::AltModifier)
            && (e->button() == Qt::LeftButton))
    {
        // FOV rotation mode
        fovRotationStart = QtCompat::mousePos(e).toPoint();
        fovRotationStartAngle = dms(Options::skyRotation());
        slewing = true;
        setFovRotationMouseCursor();
        forceUpdate();
        return;
    }

    m_MousePointPressed = m_MousePoint;

    KStars *kstars = KStars::Instance();

    if ((e->modifiers() & Qt::ControlModifier) && (e->button() == Qt::LeftButton))
    {
        ZoomRect.moveCenter(QtCompat::mousePos(e).toPoint());
        setZoomMouseCursor();
        update(); //refresh without redrawing skymap
        return;
    }

    if ((e->modifiers() & Qt::ShiftModifier) && (e->button() == Qt::LeftButton))
    {
        // Skymap rotation mode
        rotationStart = QtCompat::mousePos(e).toPoint();
        rotationStartAngle = dms(Options::skyRotation());
        slewing = true;
        setRotationMouseCursor();
        forceUpdate();
        return;
    }

    // if button is down and cursor is not moved set the move cursor after 500 ms
    //QTimer::singleShot(500, this, SLOT(setMouseMoveCursor()));

    // break if point is unusable
    if (projector()->unusablePoint(QtCompat::mousePos(e).toPoint()))
        return;

    if (!midMouseButtonDown && e->button() == Qt::MiddleButton)
    {
        y0                 = 0.5 * height() - QtCompat::mouseY(e); //record y pixel coordinate for middle-button zooming
        midMouseButtonDown = true;
    }

    if (!mouseButtonDown)
    {
        if (e->button() == Qt::LeftButton)
        {
            mouseButtonDown = true;
        }

        //determine RA, Dec of mouse pointer
        m_MousePoint = projector()->fromScreen(QtCompat::mousePos(e).toPoint(), data);
        setClickedPoint(&m_MousePoint);

        //Find object nearest to clickedPoint()
        double maxrad  = 5000.0 / Options::zoomFactor();
        SkyObject *obj = data->skyComposite()->objectNearest(clickedPoint(), maxrad);
        setClickedObject(obj);
        if (obj)
            setClickedPoint(obj);

        switch (e->button())
        {
            case Qt::LeftButton:
            {
                QString name;
                if (clickedObject())
                {
                    name = clickedObject()->translatedLongName();
                    Q_EMIT objectClicked(clickedObject());
                }
                else
                    name = i18n("Empty sky");
                //kstars->statusBar()->changeItem(name, 0 );
                kstars->statusBar()->showMessage(name, 0);

                Q_EMIT positionClicked(&m_MousePoint);
            }

            break;
            case Qt::RightButton:
                if (rulerMode)
                {
                    // Compute angular distance.
                    slotEndRulerMode();
                }
                else
                {
                    // Show popup menu
                    if (clickedObject())
                    {
                        clickedObject()->showPopupMenu(pmenu, QCursor::pos());
                    }
                    else
                    {
                        pmenu->createEmptyMenu(clickedPoint());
                        pmenu->popup(QCursor::pos());
                    }
                }
                break;
            default:
                ;
        }
    }
}

void SkyMap::mouseDoubleClickEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton && !projector()->unusablePoint(QtCompat::mousePos(e).toPoint()))
    {
        mouseButtonDown = false;
        if (QtCompat::mouseX(e) != width() / 2 || QtCompat::mouseY(e) != height() / 2)
            slotCenter();
    }
}

double SkyMap::zoomFactor(const int modifier)
{
    double factor = (modifier & Qt::ControlModifier) ? DZOOM : (Options::zoomScrollFactor() + 1);
    if (modifier & Qt::ShiftModifier)
        factor = sqrt(factor);
    return factor;
}

void SkyMap::zoomInOrMagStep(const int modifier)
{
    if (modifier & Qt::AltModifier)
        incMagLimit(modifier);
    else
        setZoomFactor(Options::zoomFactor() * zoomFactor(modifier));
}

void SkyMap::zoomOutOrMagStep(const int modifier)
{
    if (modifier & Qt::AltModifier)
        decMagLimit(modifier);
    else
        setZoomFactor(Options::zoomFactor() / zoomFactor(modifier));
}

double SkyMap::magFactor(const int modifier)
{
    double factor = (modifier & Qt::ControlModifier) ? 0.1 : 0.5;
    if (modifier & Qt::ShiftModifier)
        factor *= 2.0;
    return factor;
}

void SkyMap::incMagLimit(const int modifier)
{
    double limit = 2.222 * log10(static_cast<double>(Options::starDensity())) + 0.35;
    limit += magFactor(modifier);
    if (limit > 5.75954)
        limit = 5.75954;
    Options::setStarDensity(pow(10, (limit - 0.35) / 2.222));
    //printf("maglim set to %3.1f\n", limit);
    forceUpdate();
}

void SkyMap::decMagLimit(const int modifier)
{
    double limit = 2.222 * log10(static_cast<double>(Options::starDensity())) + 0.35;
    limit -= magFactor(modifier);
    if (limit < 1.18778)
        limit = 1.18778;
    Options::setStarDensity(pow(10, (limit - 0.35) / 2.222));
    //printf("maglim set to %3.1f\n", limit);
    forceUpdate();
}
