File indexing completed on 2024-12-01 09:41:29

0001 /*
0002     SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 //This file contains Event handlers for the SkyMap class.
0008 
0009 #include "skymap.h"
0010 
0011 #include "ksplanetbase.h"
0012 #include "kspopupmenu.h"
0013 #include "kstars.h"
0014 #include "observinglist.h"
0015 #include "Options.h"
0016 #include "skyglpainter.h"
0017 #include "skyqpainter.h"
0018 #include "printing/simplefovexporter.h"
0019 #include "skycomponents/skylabeler.h"
0020 #include "skycomponents/skymapcomposite.h"
0021 #include "skycomponents/starcomponent.h"
0022 #include "skycomponents/mosaiccomponent.h"
0023 #ifdef HAVE_INDI
0024 #include "skyobjects/mosaictiles.h"
0025 #endif
0026 #include "widgets/infoboxwidget.h"
0027 
0028 #include <QGestureEvent>
0029 #include <QStatusBar>
0030 #include <QToolTip>
0031 
0032 void SkyMap::resizeEvent(QResizeEvent *)
0033 {
0034     computeSkymap = true; // skymap must be new computed
0035 
0036     //FIXME: No equivalent for this line in Qt4 ??
0037     //  if ( testWState( Qt::WState_AutoMask ) ) updateMask();
0038 
0039     // Resize the widget that draws the sky map.
0040     // FIXME: The resize event doesn't pass to children. Any better way of doing this?
0041     m_SkyMapDraw->resize(size());
0042 
0043     // Resize infoboxes container.
0044     // FIXME: this is not really pretty. Maybe there are some better way to this???
0045     m_iboxes->resize(size());
0046 }
0047 
0048 void SkyMap::keyPressEvent(QKeyEvent *e)
0049 {
0050     bool arrowKeyPressed(false);
0051     bool shiftPressed(false);
0052     float step = 1.0;
0053     if (e->modifiers() & Qt::ShiftModifier)
0054     {
0055         step         = 10.0;
0056         shiftPressed = true;
0057     }
0058 
0059     //If the DBus resume key is not empty, then DBus processing is
0060     //paused while we wait for a keypress
0061     if (!data->resumeKey.isEmpty() && QKeySequence(e->key()) == data->resumeKey)
0062     {
0063         //The resumeKey was pressed.  Signal that it was pressed by
0064         //resetting it to empty; this will break the loop in
0065         //KStars::waitForKey()
0066         data->resumeKey = QKeySequence();
0067         return;
0068     }
0069 
0070     if (m_previewLegend)
0071     {
0072         slotCancelLegendPreviewMode();
0073     }
0074 
0075     switch (e->key())
0076     {
0077         case Qt::Key_Left:
0078             if (Options::useAltAz())
0079             {
0080                 focus()->setAz(dms(focus()->az().Degrees() - step * MINZOOM / Options::zoomFactor()).reduce());
0081                 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
0082             }
0083             else
0084             {
0085                 focus()->setRA(focus()->ra().Hours() + 0.05 * step * MINZOOM / Options::zoomFactor());
0086                 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0087             }
0088 
0089             arrowKeyPressed = true;
0090             slewing         = true;
0091             break;
0092 
0093         case Qt::Key_Right:
0094             if (Options::useAltAz())
0095             {
0096                 focus()->setAz(dms(focus()->az().Degrees() + step * MINZOOM / Options::zoomFactor()).reduce());
0097                 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
0098             }
0099             else
0100             {
0101                 focus()->setRA(focus()->ra().Hours() - 0.05 * step * MINZOOM / Options::zoomFactor());
0102                 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0103             }
0104 
0105             arrowKeyPressed = true;
0106             slewing         = true;
0107             break;
0108 
0109         case Qt::Key_Up:
0110             if (Options::useAltAz())
0111             {
0112                 focus()->setAltRefracted(focus()->altRefracted().Degrees() + step * MINZOOM / Options::zoomFactor());
0113                 if (focus()->alt().Degrees() > 90.0)
0114                     focus()->setAlt(90.0);
0115                 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
0116             }
0117             else
0118             {
0119                 focus()->setDec(focus()->dec().Degrees() + step * MINZOOM / Options::zoomFactor());
0120                 if (focus()->dec().Degrees() > 90.0)
0121                     focus()->setDec(90.0);
0122                 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0123             }
0124 
0125             arrowKeyPressed = true;
0126             slewing         = true;
0127             break;
0128 
0129         case Qt::Key_Down:
0130             if (Options::useAltAz())
0131             {
0132                 focus()->setAltRefracted(focus()->altRefracted().Degrees() - step * MINZOOM / Options::zoomFactor());
0133                 if (focus()->alt().Degrees() < -90.0)
0134                     focus()->setAlt(-90.0);
0135                 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
0136             }
0137             else
0138             {
0139                 focus()->setDec(focus()->dec().Degrees() - step * MINZOOM / Options::zoomFactor());
0140                 if (focus()->dec().Degrees() < -90.0)
0141                     focus()->setDec(-90.0);
0142                 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0143             }
0144 
0145             arrowKeyPressed = true;
0146             slewing         = true;
0147             break;
0148 
0149         case Qt::Key_Plus: //Zoom in
0150         case Qt::Key_Equal:
0151             zoomInOrMagStep(e->modifiers());
0152             break;
0153 
0154         case Qt::Key_Minus: //Zoom out
0155         case Qt::Key_Underscore:
0156             zoomOutOrMagStep(e->modifiers());
0157             break;
0158 
0159         case Qt::Key_0: //center on Sun
0160             setClickedObject(data->skyComposite()->planet(KSPlanetBase::SUN));
0161             setClickedPoint(clickedObject());
0162             slotCenter();
0163             break;
0164 
0165         case Qt::Key_1: //center on Mercury
0166             setClickedObject(data->skyComposite()->planet(KSPlanetBase::MERCURY));
0167             setClickedPoint(clickedObject());
0168             slotCenter();
0169             break;
0170 
0171         case Qt::Key_2: //center on Venus
0172             setClickedObject(data->skyComposite()->planet(KSPlanetBase::VENUS));
0173             setClickedPoint(clickedObject());
0174             slotCenter();
0175             break;
0176 
0177         case Qt::Key_3: //center on Moon
0178             setClickedObject(data->skyComposite()->planet(KSPlanetBase::MOON));
0179             setClickedPoint(clickedObject());
0180             slotCenter();
0181             break;
0182 
0183         case Qt::Key_4: //center on Mars
0184             setClickedObject(data->skyComposite()->planet(KSPlanetBase::MARS));
0185             setClickedPoint(clickedObject());
0186             slotCenter();
0187             break;
0188 
0189         case Qt::Key_5: //center on Jupiter
0190             setClickedObject(data->skyComposite()->planet(KSPlanetBase::JUPITER));
0191             setClickedPoint(clickedObject());
0192             slotCenter();
0193             break;
0194 
0195         case Qt::Key_6: //center on Saturn
0196             setClickedObject(data->skyComposite()->planet(KSPlanetBase::SATURN));
0197             setClickedPoint(clickedObject());
0198             slotCenter();
0199             break;
0200 
0201         case Qt::Key_7: //center on Uranus
0202             setClickedObject(data->skyComposite()->planet(KSPlanetBase::URANUS));
0203             setClickedPoint(clickedObject());
0204             slotCenter();
0205             break;
0206 
0207         case Qt::Key_8: //center on Neptune
0208             setClickedObject(data->skyComposite()->planet(KSPlanetBase::NEPTUNE));
0209             setClickedPoint(clickedObject());
0210             slotCenter();
0211             break;
0212 
0213         /*case Qt::Key_9: //center on Pluto
0214             setClickedObject( data->skyComposite()->planet( KSPlanetBase::PLUTO ) );
0215             setClickedPoint( clickedObject() );
0216             slotCenter();
0217             break;*/
0218 
0219         case Qt::Key_BracketLeft: // Begin measuring angular distance
0220             if (!rulerMode)
0221                 slotBeginAngularDistance();
0222             break;
0223         case Qt::Key_Escape: // Cancel angular distance measurement
0224         {
0225             if (rulerMode)
0226                 slotCancelRulerMode();
0227 
0228             if (m_fovCaptureMode)
0229                 slotFinishFovCaptureMode();
0230             break;
0231         }
0232 
0233         case Qt::Key_C: //Center clicked object
0234             if (clickedObject())
0235                 slotCenter();
0236             break;
0237 
0238         case Qt::Key_D: //Details window for Clicked/Centered object
0239         {
0240             SkyObject *orig = nullptr;
0241             if (shiftPressed)
0242             {
0243                 orig = clickedObject();
0244                 setClickedObject(focusObject());
0245             }
0246 
0247             if (clickedObject())
0248             {
0249                 slotDetail();
0250             }
0251 
0252             if (orig)
0253             {
0254                 setClickedObject(orig);
0255             }
0256             break;
0257         }
0258 
0259         case Qt::Key_P: //Show Popup menu for Clicked/Centered object
0260             if (shiftPressed)
0261             {
0262                 if (focusObject())
0263                     focusObject()->showPopupMenu(pmenu, QCursor::pos());
0264             }
0265             else
0266             {
0267                 if (clickedObject())
0268                     clickedObject()->showPopupMenu(pmenu, QCursor::pos());
0269             }
0270             break;
0271 
0272         case Qt::Key_O: //Add object to Observing List
0273         {
0274             SkyObject *orig = nullptr;
0275             if (shiftPressed)
0276             {
0277                 orig = clickedObject();
0278                 setClickedObject(focusObject());
0279             }
0280 
0281             if (clickedObject())
0282             {
0283                 data->observingList()->slotAddObject();
0284             }
0285 
0286             if (orig)
0287             {
0288                 setClickedObject(orig);
0289             }
0290             break;
0291         }
0292 
0293         case Qt::Key_L: //Toggle User label on Clicked/Centered object
0294         {
0295             SkyObject *orig = nullptr;
0296             if (shiftPressed)
0297             {
0298                 orig = clickedObject();
0299                 setClickedObject(focusObject());
0300             }
0301 
0302             if (clickedObject())
0303             {
0304                 if (isObjectLabeled(clickedObject()))
0305                     slotRemoveObjectLabel();
0306                 else
0307                     slotAddObjectLabel();
0308             }
0309 
0310             if (orig)
0311             {
0312                 setClickedObject(orig);
0313             }
0314             break;
0315         }
0316 
0317         case Qt::Key_T: //Toggle planet trail on Clicked/Centered object (if solsys)
0318         {
0319             SkyObject *orig = nullptr;
0320             if (shiftPressed)
0321             {
0322                 orig = clickedObject();
0323                 setClickedObject(focusObject());
0324             }
0325 
0326             KSPlanetBase *planet = dynamic_cast<KSPlanetBase *>(clickedObject());
0327             if (planet)
0328             {
0329                 if (planet->hasTrail())
0330                     slotRemovePlanetTrail();
0331                 else
0332                     slotAddPlanetTrail();
0333             }
0334 
0335             if (orig)
0336             {
0337                 setClickedObject(orig);
0338             }
0339             break;
0340         }
0341 
0342         case Qt::Key_R:
0343         {
0344             // Toggle relativistic corrections
0345             Options::setUseRelativistic(!Options::useRelativistic());
0346             qDebug() << Q_FUNC_INFO << "Relativistic corrections: " << Options::useRelativistic();
0347             forceUpdate();
0348             break;
0349         }
0350 
0351         case Qt::Key_A:
0352             Options::setUseAntialias(!Options::useAntialias());
0353             qDebug() << Q_FUNC_INFO << "Use Antialiasing: " << Options::useAntialias();
0354             forceUpdate();
0355             break;
0356 
0357         case Qt::Key_K:
0358         {
0359             if (m_fovCaptureMode)
0360                 slotCaptureFov();
0361             break;
0362         }
0363 
0364         case Qt::Key_PageUp:
0365         {
0366             KStars::Instance()->selectPreviousFov();
0367             break;
0368         }
0369 
0370         case Qt::Key_PageDown:
0371         {
0372             KStars::Instance()->selectNextFov();
0373             break;
0374         }
0375 
0376         default:
0377             // We don't want to do anything in this case. Key is unknown
0378             return;
0379     }
0380 
0381     if (arrowKeyPressed)
0382     {
0383         stopTracking();
0384         setDestination(*focus());
0385     }
0386 
0387     forceUpdate(); //need a total update, or slewing with the arrow keys doesn't work.
0388 }
0389 
0390 void SkyMap::stopTracking()
0391 {
0392     KStars *kstars = KStars::Instance();
0393 
0394     emit positionChanged(focus());
0395     if (kstars && Options::isTracking())
0396         kstars->slotTrack();
0397 }
0398 
0399 bool SkyMap::event(QEvent *event)
0400 {
0401 #if !defined(KSTARS_LITE)
0402     if (event->type() == QEvent::TouchBegin)
0403     {
0404         m_touchMode = true;
0405         m_pinchScale = -1;
0406     }
0407 
0408     if (event->type() == QEvent::Gesture && m_touchMode)
0409     {
0410         QGestureEvent* gestureEvent = static_cast<QGestureEvent*>(event);
0411 
0412         if (QPinchGesture *pinch = static_cast<QPinchGesture*>(gestureEvent->gesture(Qt::PinchGesture)))
0413         {
0414             QPinchGesture::ChangeFlags changeFlags = pinch->changeFlags();
0415 
0416             m_pinchMode = true;
0417             if (changeFlags & QPinchGesture::ScaleFactorChanged)
0418             {
0419                 if (m_pinchScale == -1)
0420                 {
0421                     m_pinchScale = pinch->totalScaleFactor();
0422                     return true;
0423                 }
0424                 if (pinch->totalScaleFactor() - m_pinchScale > 0.1)
0425                 {
0426                     m_pinchScale = pinch->totalScaleFactor();
0427                     zoomInOrMagStep(0);
0428                     return true;
0429                 }
0430                 if (pinch->totalScaleFactor() - m_pinchScale < -0.1)
0431                 {
0432                     m_pinchScale = pinch->totalScaleFactor();
0433                     zoomOutOrMagStep(0);
0434                     return true;
0435                 }
0436             }
0437         }
0438         if (QTapAndHoldGesture *tapAndHold = static_cast<QTapAndHoldGesture*>(gestureEvent->gesture(Qt::TapAndHoldGesture)))
0439         {
0440             m_tapAndHoldMode = true;
0441             if (tapAndHold->state() == Qt::GestureFinished)
0442             {
0443                 if (clickedObject())
0444                 {
0445                     clickedObject()->showPopupMenu(pmenu, tapAndHold->position().toPoint());
0446                 }
0447                 else
0448                 {
0449                     pmenu->createEmptyMenu(clickedPoint());
0450                     pmenu->popup(tapAndHold->position().toPoint());
0451                 }
0452                 m_touchMode = false;
0453                 m_pinchMode = false;
0454                 m_tapAndHoldMode = false;
0455             }
0456         }
0457         return true;
0458     }
0459 #endif
0460     return QGraphicsView::event(event);
0461 }
0462 
0463 void SkyMap::keyReleaseEvent(QKeyEvent *e)
0464 {
0465     switch (e->key())
0466     {
0467         case Qt::Key_Plus: //Zoom in
0468         case Qt::Key_Equal:
0469         case Qt::Key_Minus: //Zoom out
0470         case Qt::Key_Underscore:
0471 
0472         case Qt::Key_Left:  //no break; continue to Qt::Key_Down
0473         case Qt::Key_Right: //no break; continue to Qt::Key_Down
0474         case Qt::Key_Up:    //no break; continue to Qt::Key_Down
0475         case Qt::Key_Down:
0476             slewing = false;
0477 
0478             if (Options::useAltAz())
0479                 setDestinationAltAz(focus()->alt(), focus()->az(), false);
0480             else
0481                 setDestination(*focus());
0482 
0483             showFocusCoords();
0484             forceUpdate(); // Need a full update to draw faint objects that are not drawn while slewing.
0485             break;
0486     }
0487 }
0488 
0489 void SkyMap::mouseMoveEvent(QMouseEvent *e)
0490 {
0491 #if !defined(KSTARS_LITE)
0492     // Skip touch points
0493     if (m_pinchMode || m_tapAndHoldMode || (m_touchMode && e->globalX() == 0 && e->globalY() == 0))
0494         return;
0495 #endif
0496 
0497     if (Options::useHoverLabel())
0498     {
0499         //Start a single-shot timer to monitor whether we are currently hovering.
0500         //The idea is that whenever a moveEvent occurs, the timer is reset.  It
0501         //will only timeout if there are no move events for HOVER_INTERVAL ms
0502         m_HoverTimer.start(HOVER_INTERVAL);
0503         QToolTip::hideText();
0504     }
0505 
0506     //Are we defining a ZoomRect?
0507     if (ZoomRect.center().x() > 0 && ZoomRect.center().y() > 0)
0508     {
0509         //cancel operation if the user let go of CTRL
0510         if (!(e->modifiers() & Qt::ControlModifier))
0511         {
0512             ZoomRect = QRect(); //invalidate ZoomRect
0513             update();
0514         }
0515         else
0516         {
0517             //Resize the rectangle so that it passes through the cursor position
0518             QPoint pcenter = ZoomRect.center();
0519             int dx         = abs(e->x() - pcenter.x());
0520             int dy         = abs(e->y() - pcenter.y());
0521             if (dx == 0 || float(dy) / float(dx) > float(height()) / float(width()))
0522             {
0523                 //Size rect by height
0524                 ZoomRect.setHeight(2 * dy);
0525                 ZoomRect.setWidth(2 * dy * width() / height());
0526             }
0527             else
0528             {
0529                 //Size rect by height
0530                 ZoomRect.setWidth(2 * dx);
0531                 ZoomRect.setHeight(2 * dx * height() / width());
0532             }
0533             ZoomRect.moveCenter(pcenter); //reset center
0534 
0535             update();
0536             return;
0537         }
0538     }
0539 
0540     // Are we setting the skymap rotation?
0541     if (rotationStart.x() > 0 && rotationStart.y() > 0)
0542     {
0543         // stop the operation if the user let go of SHIFT
0544         if (!(e->modifiers() & Qt::ShiftModifier))
0545         {
0546             rotationStart = QPoint(); // invalidate
0547             rotationStartAngle = dms(); // NaN
0548             slewing = false;
0549             forceUpdate();
0550             return;
0551         }
0552         else
0553         {
0554             // Compute the rotation
0555             const float start_x = rotationStart.x() - width() / 2.0f;
0556             const float start_y = height() / 2.0f - rotationStart.y();
0557 
0558             const float curr_x = e->pos().x() - width() / 2.0f;
0559             const float curr_y = height() / 2.0f - e->pos().y();
0560 
0561             const dms angle {(std::atan2(curr_y, curr_x) - std::atan2(start_y, start_x)) / dms::DegToRad };
0562             slotSetSkyRotation((rotationStartAngle - angle).Degrees());
0563             return;
0564         }
0565     }
0566 
0567     if (projector()->unusablePoint(e->pos()))
0568         return; // break if point is unusable
0569 
0570     //determine RA, Dec of mouse pointer
0571     m_MousePoint = projector()->fromScreen(e->pos(), data->lst(), data->geo()->lat());
0572 
0573     double dyPix = 0.5 * height() - e->y();
0574     if (midMouseButtonDown) //zoom according to y-offset
0575     {
0576         float yoff = dyPix - y0;
0577         if (yoff > 10)
0578         {
0579             y0 = dyPix;
0580             slotZoomIn();
0581         }
0582         if (yoff < -10)
0583         {
0584             y0 = dyPix;
0585             slotZoomOut();
0586         }
0587     }
0588 
0589     if (mouseButtonDown)
0590     {
0591 #ifdef HAVE_INDI
0592         if (Options::showMosaicPanel())
0593         {
0594             auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
0595 
0596             if (tiles->operationMode() == MosaicTiles::MODE_PLANNING)
0597             {
0598                 // Check if mouse point within Mosaic FOV bounds.
0599                 auto mosaicFOV = tiles->mosaicFOV();
0600                 auto upperRA = tiles->ra0().Degrees() + mosaicFOV.width() / 60;
0601                 auto lowerRA = tiles->ra0().Degrees() - mosaicFOV.width() / 60;
0602                 auto upperDE = tiles->dec0().Degrees() + mosaicFOV.height() / 60;
0603                 auto lowerDE = tiles->dec0().Degrees() - mosaicFOV.height() / 60;
0604 
0605                 auto mouseRA = m_MousePoint.ra().Degrees();
0606                 auto mouseDE = m_MousePoint.dec().Degrees();
0607 
0608                 // If mouse point is within, then behave like drag and drop
0609                 if (mouseRA > lowerRA && mouseRA < upperRA && mouseDE > lowerDE && mouseDE < upperDE)
0610                 {
0611                     if (!mouseDragCursor)
0612                         setMouseDragCursor();
0613 
0614                     dms dRA  = m_MousePoint.ra() - clickedPoint()->ra();
0615                     dms dDec = m_MousePoint.dec() - clickedPoint()->dec();
0616 
0617                     // Emit difference between mouse point and clicked point.
0618                     emit mosaicCenterChanged(dRA, dDec);
0619 
0620                     // Update mouse and clicked points.
0621                     m_MousePoint = projector()->fromScreen(e->pos(), data->lst(), data->geo()->lat());
0622                     setClickedPoint(&m_MousePoint);
0623                     update();
0624                     return;
0625                 }
0626             }
0627         }
0628 #endif
0629 
0630         // set the mouseMoveCursor and set slewing=true, if they are not set yet
0631         if (!mouseMoveCursor)
0632             setMouseMoveCursor();
0633         if (!slewing)
0634         {
0635             slewing = true;
0636             stopTracking(); //toggle tracking off
0637         }
0638 
0639         //Update focus such that the sky coords at mouse cursor remain approximately constant
0640         if (Options::useAltAz())
0641         {
0642             m_MousePoint.EquatorialToHorizontal(data->lst(), data->geo()->lat());
0643             clickedPoint()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0644             dms dAz  = m_MousePoint.az() - clickedPoint()->az();
0645             dms dAlt = m_MousePoint.altRefracted() - clickedPoint()->altRefracted();
0646             focus()->setAz(focus()->az().Degrees() - dAz.Degrees()); //move focus in opposite direction
0647             focus()->setAz(focus()->az().reduce());
0648             focus()->setAltRefracted(KSUtils::clamp(focus()->altRefracted().Degrees() - dAlt.Degrees(), -90.0, 90.0));
0649             focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat());
0650         }
0651         else
0652         {
0653             dms dRA  = m_MousePoint.ra() - clickedPoint()->ra();
0654             dms dDec = m_MousePoint.dec() - clickedPoint()->dec();
0655             focus()->setRA(focus()->ra().Hours() - dRA.Hours()); //move focus in opposite direction
0656             focus()->setRA(focus()->ra().reduce());
0657             focus()->setDec(KSUtils::clamp(focus()->dec().Degrees() - dDec.Degrees(), -90.0, 90.0));
0658             focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0659         }
0660         showFocusCoords();
0661 
0662         //redetermine RA, Dec of mouse pointer, using new focus
0663         m_MousePoint = projector()->fromScreen(e->pos(), data->lst(), data->geo()->lat());
0664         setClickedPoint(&m_MousePoint);
0665         forceUpdate(); // must be new computed
0666     }
0667     else //mouse button not down
0668     {
0669         if (Options::useAltAz())
0670             m_MousePoint.EquatorialToHorizontal(data->lst(), data->geo()->lat());
0671         emit mousePointChanged(&m_MousePoint);
0672     }
0673 }
0674 
0675 void SkyMap::wheelEvent(QWheelEvent *e)
0676 {
0677     if (e->angleDelta().y() > 0)
0678         zoomInOrMagStep(e->modifiers());
0679     else if (e->angleDelta().y() < 0)
0680         zoomOutOrMagStep(e->modifiers());
0681 }
0682 
0683 void SkyMap::mouseReleaseEvent(QMouseEvent *e)
0684 {
0685 #if !defined(KSTARS_LITE)
0686     if (m_touchMode)
0687     {
0688         m_touchMode = false;
0689         m_pinchMode = false;
0690         m_tapAndHoldMode = false;
0691     }
0692 #endif
0693 
0694     if (ZoomRect.isValid())
0695     {
0696         stopTracking();
0697         SkyPoint newcenter = projector()->fromScreen(ZoomRect.center(), data->lst(), data->geo()->lat());
0698         setFocus(&newcenter);
0699         setDestination(newcenter);
0700 
0701         //Zoom in on center of Zoom Circle, by a factor equal to the ratio
0702         //of the sky pixmap's width to the Zoom Circle's diameter
0703         float factor = float(width()) / float(ZoomRect.width());
0704         setZoomFactor(Options::zoomFactor() * factor);
0705     }
0706 
0707     setMouseCursorShape(static_cast<Cursor>(Options::defaultCursor()));
0708 
0709     ZoomRect = QRect(); //invalidate ZoomRect
0710 
0711     if (m_previewLegend)
0712     {
0713         slotCancelLegendPreviewMode();
0714     }
0715 
0716     // Are we setting the skymap rotation?
0717     if (rotationStart.x() > 0 && rotationStart.y() > 0)
0718     {
0719         rotationStart = QPoint(); // invalidate
0720         rotationStartAngle = dms(); // NaN
0721         slewing = false;
0722         forceUpdateNow();
0723         return;
0724     }
0725 
0726     //false if double-clicked, because it's unset there.
0727     if (mouseButtonDown)
0728     {
0729         mouseButtonDown = false;
0730         if (slewing)
0731         {
0732             slewing = false;
0733             if (Options::useAltAz())
0734                 setDestinationAltAz(focus()->alt(), focus()->az(), false);
0735             else
0736                 setDestination(*focus());
0737         }
0738         else if (Options::leftClickSelectsObject())
0739             mouseDoubleClickEvent(e);
0740         forceUpdate(); // is needed because after moving the sky not all stars are shown
0741     }
0742     // if middle button was pressed unset here
0743     midMouseButtonDown = false;
0744 }
0745 
0746 void SkyMap::mousePressEvent(QMouseEvent *e)
0747 {
0748     KStars *kstars = KStars::Instance();
0749 
0750     if ((e->modifiers() & Qt::ControlModifier) && (e->button() == Qt::LeftButton))
0751     {
0752         ZoomRect.moveCenter(e->pos());
0753         setZoomMouseCursor();
0754         update(); //refresh without redrawing skymap
0755         return;
0756     }
0757 
0758     if ((e->modifiers() & Qt::ShiftModifier) && (e->button() == Qt::LeftButton))
0759     {
0760         // Skymap rotation mode
0761         rotationStart = e->pos();
0762         rotationStartAngle = dms(Options::skyRotation());
0763         slewing = true;
0764         setRotationMouseCursor();
0765         forceUpdate();
0766         return;
0767     }
0768 
0769     // if button is down and cursor is not moved set the move cursor after 500 ms
0770     //QTimer::singleShot(500, this, SLOT(setMouseMoveCursor()));
0771 
0772     // break if point is unusable
0773     if (projector()->unusablePoint(e->pos()))
0774         return;
0775 
0776     if (!midMouseButtonDown && e->button() == Qt::MidButton)
0777     {
0778         y0                 = 0.5 * height() - e->y(); //record y pixel coordinate for middle-button zooming
0779         midMouseButtonDown = true;
0780     }
0781 
0782     if (!mouseButtonDown)
0783     {
0784         if (e->button() == Qt::LeftButton)
0785         {
0786             mouseButtonDown = true;
0787         }
0788 
0789         //determine RA, Dec of mouse pointer
0790         m_MousePoint = projector()->fromScreen(e->pos(), data->lst(), data->geo()->lat());
0791         setClickedPoint(&m_MousePoint);
0792 
0793         //Find object nearest to clickedPoint()
0794         double maxrad  = 5000.0 / Options::zoomFactor();
0795         SkyObject *obj = data->skyComposite()->objectNearest(clickedPoint(), maxrad);
0796         setClickedObject(obj);
0797         if (obj)
0798             setClickedPoint(obj);
0799 
0800         switch (e->button())
0801         {
0802             case Qt::LeftButton:
0803             {
0804                 QString name;
0805                 if (clickedObject())
0806                 {
0807                     name = clickedObject()->translatedLongName();
0808                     emit objectClicked(clickedObject());
0809                 }
0810                 else
0811                     name = i18n("Empty sky");
0812                 //kstars->statusBar()->changeItem(name, 0 );
0813                 kstars->statusBar()->showMessage(name, 0);
0814 
0815                 emit positionClicked(&m_MousePoint);
0816             }
0817 
0818             break;
0819             case Qt::RightButton:
0820                 if (rulerMode)
0821                 {
0822                     // Compute angular distance.
0823                     slotEndRulerMode();
0824                 }
0825                 else
0826                 {
0827                     // Show popup menu
0828                     if (clickedObject())
0829                     {
0830                         clickedObject()->showPopupMenu(pmenu, QCursor::pos());
0831                     }
0832                     else
0833                     {
0834                         pmenu->createEmptyMenu(clickedPoint());
0835                         pmenu->popup(QCursor::pos());
0836                     }
0837                 }
0838                 break;
0839             default:
0840                 ;
0841         }
0842     }
0843 }
0844 
0845 void SkyMap::mouseDoubleClickEvent(QMouseEvent *e)
0846 {
0847     if (e->button() == Qt::LeftButton && !projector()->unusablePoint(e->pos()))
0848     {
0849         mouseButtonDown = false;
0850         if (e->x() != width() / 2 || e->y() != height() / 2)
0851             slotCenter();
0852     }
0853 }
0854 
0855 double SkyMap::zoomFactor(const int modifier)
0856 {
0857     double factor = (modifier & Qt::ControlModifier) ? DZOOM : (Options::zoomScrollFactor() + 1);
0858     if (modifier & Qt::ShiftModifier)
0859         factor = sqrt(factor);
0860     return factor;
0861 }
0862 
0863 void SkyMap::zoomInOrMagStep(const int modifier)
0864 {
0865     if (modifier & Qt::AltModifier)
0866         incMagLimit(modifier);
0867     else
0868         setZoomFactor(Options::zoomFactor() * zoomFactor(modifier));
0869 }
0870 
0871 void SkyMap::zoomOutOrMagStep(const int modifier)
0872 {
0873     if (modifier & Qt::AltModifier)
0874         decMagLimit(modifier);
0875     else
0876         setZoomFactor(Options::zoomFactor() / zoomFactor(modifier));
0877 }
0878 
0879 double SkyMap::magFactor(const int modifier)
0880 {
0881     double factor = (modifier & Qt::ControlModifier) ? 0.1 : 0.5;
0882     if (modifier & Qt::ShiftModifier)
0883         factor *= 2.0;
0884     return factor;
0885 }
0886 
0887 void SkyMap::incMagLimit(const int modifier)
0888 {
0889     double limit = 2.222 * log10(static_cast<double>(Options::starDensity())) + 0.35;
0890     limit += magFactor(modifier);
0891     if (limit > 5.75954)
0892         limit = 5.75954;
0893     Options::setStarDensity(pow(10, (limit - 0.35) / 2.222));
0894     //printf("maglim set to %3.1f\n", limit);
0895     forceUpdate();
0896 }
0897 
0898 void SkyMap::decMagLimit(const int modifier)
0899 {
0900     double limit = 2.222 * log10(static_cast<double>(Options::starDensity())) + 0.35;
0901     limit -= magFactor(modifier);
0902     if (limit < 1.18778)
0903         limit = 1.18778;
0904     Options::setStarDensity(pow(10, (limit - 0.35) / 2.222));
0905     //printf("maglim set to %3.1f\n", limit);
0906     forceUpdate();
0907 }