File indexing completed on 2025-04-20 09:32:21
0001 /* 0002 SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "skyobjectuserdata.h" 0008 #ifdef _WIN32 0009 #include <windows.h> 0010 #endif 0011 0012 #include "skymap.h" 0013 0014 #include "ksasteroid.h" 0015 #include "kstars_debug.h" 0016 #include "fov.h" 0017 #include "imageviewer.h" 0018 #include "xplanetimageviewer.h" 0019 #include "ksdssdownloader.h" 0020 #include "kspaths.h" 0021 #include "kspopupmenu.h" 0022 #include "kstars.h" 0023 #include "ksutils.h" 0024 #include "Options.h" 0025 #include "skymapcomposite.h" 0026 #ifdef HAVE_OPENGL 0027 #include "skymapgldraw.h" 0028 #endif 0029 #include "skymapqdraw.h" 0030 #include "starhopperdialog.h" 0031 #include "starobject.h" 0032 #include "texturemanager.h" 0033 #include "dialogs/detaildialog.h" 0034 #include "printing/printingwizard.h" 0035 #include "skycomponents/flagcomponent.h" 0036 #include "skyobjects/ksplanetbase.h" 0037 #include "skyobjects/satellite.h" 0038 #include "tools/flagmanager.h" 0039 #include "widgets/infoboxwidget.h" 0040 #include "projections/azimuthalequidistantprojector.h" 0041 #include "projections/equirectangularprojector.h" 0042 #include "projections/lambertprojector.h" 0043 #include "projections/gnomonicprojector.h" 0044 #include "projections/orthographicprojector.h" 0045 #include "projections/stereographicprojector.h" 0046 #include "catalogobject.h" 0047 #include "catalogsdb.h" 0048 #include "catalogscomponent.h" 0049 0050 #include <KActionCollection> 0051 #include <KToolBar> 0052 0053 #include <QBitmap> 0054 #include <QPainterPath> 0055 #include <QToolTip> 0056 #include <QClipboard> 0057 #include <QInputDialog> 0058 #include <QDesktopServices> 0059 0060 #include <QProcess> 0061 #include <QFileDialog> 0062 0063 #include <cmath> 0064 0065 namespace 0066 { 0067 // Draw bitmap for zoom cursor. Width is size of pen to draw with. 0068 QBitmap zoomCursorBitmap(int width) 0069 { 0070 QBitmap b(32, 32); 0071 b.fill(Qt::color0); 0072 int mx = 16, my = 16; 0073 // Begin drawing 0074 QPainter p; 0075 p.begin(&b); 0076 p.setPen(QPen(Qt::color1, width)); 0077 p.drawEllipse(mx - 7, my - 7, 14, 14); 0078 p.drawLine(mx + 5, my + 5, mx + 11, my + 11); 0079 p.end(); 0080 return b; 0081 } 0082 0083 // Draw bitmap for rotation cursor 0084 QBitmap rotationCursorBitmap(int width) 0085 { 0086 constexpr int size = 32; 0087 constexpr int mx = size / 2, my = size / 2; 0088 QBitmap b(size, size); 0089 b.fill(Qt::color0); 0090 const int pad = 4; 0091 0092 QPainter p; 0093 p.begin(&b); 0094 p.setPen(QPen(Qt::color1, width)); 0095 0096 QPainterPath arc1; 0097 arc1.moveTo(mx, pad); 0098 arc1.arcTo(QRect(pad, pad, size - 2 * pad, size - 2 * pad), 90, 90); 0099 auto arcEnd1 = arc1.currentPosition(); 0100 arc1.lineTo(arcEnd1.x() - pad / 2, arcEnd1.y() - pad); 0101 p.drawPath(arc1); 0102 0103 QPainterPath arc2; 0104 arc2.moveTo(mx, size - pad); 0105 arc2.arcTo(QRect(pad, pad, size - 2 * pad, size - 2 * pad), 270, 90); 0106 auto arcEnd2 = arc2.currentPosition(); 0107 arc2.lineTo(arcEnd2.x() + pad / 2, arcEnd2.y() + pad); 0108 p.drawPath(arc2); 0109 0110 p.end(); 0111 return b; 0112 } 0113 0114 // Draw bitmap for default cursor. Width is size of pen to draw with. 0115 QBitmap defaultCursorBitmap(int width) 0116 { 0117 QBitmap b(32, 32); 0118 b.fill(Qt::color0); 0119 int mx = 16, my = 16; 0120 // Begin drawing 0121 QPainter p; 0122 p.begin(&b); 0123 p.setPen(QPen(Qt::color1, width)); 0124 // 1. diagonal 0125 p.drawLine(mx - 2, my - 2, mx - 8, mx - 8); 0126 p.drawLine(mx + 2, my + 2, mx + 8, mx + 8); 0127 // 2. diagonal 0128 p.drawLine(mx - 2, my + 2, mx - 8, mx + 8); 0129 p.drawLine(mx + 2, my - 2, mx + 8, mx - 8); 0130 p.end(); 0131 return b; 0132 } 0133 0134 QBitmap circleCursorBitmap(int width) 0135 { 0136 QBitmap b(32, 32); 0137 b.fill(Qt::color0); 0138 int mx = 16, my = 16; 0139 // Begin drawing 0140 QPainter p; 0141 p.begin(&b); 0142 p.setPen(QPen(Qt::color1, width)); 0143 0144 // Circle 0145 p.drawEllipse(mx - 8, my - 8, mx, my); 0146 // 1. diagonal 0147 p.drawLine(mx - 8, my - 8, 0, 0); 0148 p.drawLine(mx + 8, my - 8, 32, 0); 0149 // 2. diagonal 0150 p.drawLine(mx - 8, my + 8, 0, 32); 0151 p.drawLine(mx + 8, my + 8, 32, 32); 0152 0153 p.end(); 0154 return b; 0155 } 0156 0157 } // namespace 0158 0159 SkyMap *SkyMap::pinstance = nullptr; 0160 0161 SkyMap *SkyMap::Create() 0162 { 0163 delete pinstance; 0164 pinstance = new SkyMap(); 0165 return pinstance; 0166 } 0167 0168 SkyMap *SkyMap::Instance() 0169 { 0170 return pinstance; 0171 } 0172 0173 SkyMap::SkyMap() 0174 : QGraphicsView(KStars::Instance()), computeSkymap(true), rulerMode(false), data(KStarsData::Instance()), pmenu(nullptr), 0175 ClickedObject(nullptr), FocusObject(nullptr), m_proj(nullptr), m_previewLegend(false), m_objPointingMode(false) 0176 { 0177 #if !defined(KSTARS_LITE) 0178 grabGesture(Qt::PinchGesture); 0179 grabGesture(Qt::TapAndHoldGesture); 0180 #endif 0181 m_Scale = 1.0; 0182 0183 ZoomRect = QRect(); 0184 0185 // set the default cursor 0186 setMouseCursorShape(static_cast<Cursor>(Options::defaultCursor())); 0187 0188 QPalette p = palette(); 0189 p.setColor(QPalette::Window, QColor(data->colorScheme()->colorNamed("SkyColor"))); 0190 setPalette(p); 0191 0192 setFocusPolicy(Qt::StrongFocus); 0193 setMinimumSize(380, 250); 0194 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); 0195 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0196 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0197 setStyleSheet("QGraphicsView { border-style: none; }"); 0198 0199 setMouseTracking(true); //Generate MouseMove events! 0200 midMouseButtonDown = false; 0201 mouseButtonDown = false; 0202 slewing = false; 0203 clockSlewing = false; 0204 0205 ClickedObject = nullptr; 0206 FocusObject = nullptr; 0207 0208 m_SkyMapDraw = nullptr; 0209 0210 pmenu = new KSPopupMenu(); 0211 0212 setupProjector(); 0213 0214 //Initialize Transient label stuff 0215 m_HoverTimer.setSingleShot(true); // using this timer as a single shot timer 0216 0217 connect(&m_HoverTimer, SIGNAL(timeout()), this, SLOT(slotTransientLabel())); 0218 connect(this, SIGNAL(destinationChanged()), this, SLOT(slewFocus())); 0219 connect(KStarsData::Instance(), SIGNAL(skyUpdate(bool)), this, SLOT(slotUpdateSky(bool))); 0220 0221 // Time infobox 0222 m_timeBox = new InfoBoxWidget(Options::shadeTimeBox(), Options::positionTimeBox(), Options::stickyTimeBox(), 0223 QStringList(), this); 0224 m_timeBox->setVisible(Options::showTimeBox()); 0225 connect(data->clock(), SIGNAL(timeChanged()), m_timeBox, SLOT(slotTimeChanged())); 0226 connect(data->clock(), SIGNAL(timeAdvanced()), m_timeBox, SLOT(slotTimeChanged())); 0227 0228 // Geo infobox 0229 m_geoBox = new InfoBoxWidget(Options::shadeGeoBox(), Options::positionGeoBox(), Options::stickyGeoBox(), 0230 QStringList(), this); 0231 m_geoBox->setVisible(Options::showGeoBox()); 0232 connect(data, SIGNAL(geoChanged()), m_geoBox, SLOT(slotGeoChanged())); 0233 0234 // Object infobox 0235 m_objBox = new InfoBoxWidget(Options::shadeFocusBox(), Options::positionFocusBox(), Options::stickyFocusBox(), 0236 QStringList(), this); 0237 m_objBox->setVisible(Options::showFocusBox()); 0238 connect(this, &SkyMap::objectChanged, m_objBox, &InfoBoxWidget::slotObjectChanged); 0239 connect(this, &SkyMap::positionChanged, m_objBox, &InfoBoxWidget::slotPointChanged); 0240 0241 m_SkyMapDraw = new SkyMapQDraw(this); 0242 m_SkyMapDraw->setMouseTracking(true); 0243 0244 m_SkyMapDraw->setParent(this->viewport()); 0245 m_SkyMapDraw->show(); 0246 0247 m_iboxes = new InfoBoxes(m_SkyMapDraw); 0248 0249 m_iboxes->setVisible(Options::showInfoBoxes()); 0250 m_iboxes->addInfoBox(m_timeBox); 0251 m_iboxes->addInfoBox(m_geoBox); 0252 m_iboxes->addInfoBox(m_objBox); 0253 } 0254 0255 void SkyMap::slotToggleGeoBox(bool flag) 0256 { 0257 m_geoBox->setVisible(flag); 0258 } 0259 0260 void SkyMap::slotToggleFocusBox(bool flag) 0261 { 0262 m_objBox->setVisible(flag); 0263 } 0264 0265 void SkyMap::slotToggleTimeBox(bool flag) 0266 { 0267 m_timeBox->setVisible(flag); 0268 } 0269 0270 void SkyMap::slotToggleInfoboxes(bool flag) 0271 { 0272 m_iboxes->setVisible(flag); 0273 Options::setShowInfoBoxes(flag); 0274 } 0275 0276 SkyMap::~SkyMap() 0277 { 0278 /* == Save infoxes status into Options == */ 0279 //Options::setShowInfoBoxes(m_iboxes->isVisibleTo(parentWidget())); 0280 // Time box 0281 Options::setPositionTimeBox(m_timeBox->pos()); 0282 Options::setShadeTimeBox(m_timeBox->shaded()); 0283 Options::setStickyTimeBox(m_timeBox->sticky()); 0284 Options::setShowTimeBox(m_timeBox->isVisibleTo(m_iboxes)); 0285 // Geo box 0286 Options::setPositionGeoBox(m_geoBox->pos()); 0287 Options::setShadeGeoBox(m_geoBox->shaded()); 0288 Options::setStickyGeoBox(m_geoBox->sticky()); 0289 Options::setShowGeoBox(m_geoBox->isVisibleTo(m_iboxes)); 0290 // Obj box 0291 Options::setPositionFocusBox(m_objBox->pos()); 0292 Options::setShadeFocusBox(m_objBox->shaded()); 0293 Options::setStickyFocusBox(m_objBox->sticky()); 0294 Options::setShowFocusBox(m_objBox->isVisibleTo(m_iboxes)); 0295 0296 //store focus values in Options 0297 //If not tracking and using Alt/Az coords, stor the Alt/Az coordinates 0298 if (Options::useAltAz() && !Options::isTracking()) 0299 { 0300 Options::setFocusRA(focus()->az().Degrees()); 0301 Options::setFocusDec(focus()->alt().Degrees()); 0302 } 0303 else 0304 { 0305 Options::setFocusRA(focus()->ra().Hours()); 0306 Options::setFocusDec(focus()->dec().Degrees()); 0307 } 0308 0309 #ifdef HAVE_OPENGL 0310 delete m_SkyMapGLDraw; 0311 delete m_SkyMapQDraw; 0312 m_SkyMapDraw = 0; // Just a formality 0313 #else 0314 delete m_SkyMapDraw; 0315 #endif 0316 0317 delete pmenu; 0318 0319 delete m_proj; 0320 0321 pinstance = nullptr; 0322 } 0323 0324 void SkyMap::showFocusCoords() 0325 { 0326 if (focusObject() && Options::isTracking()) 0327 emit objectChanged(focusObject()); 0328 else 0329 emit positionChanged(focus()); 0330 } 0331 0332 void SkyMap::updateInfoBoxes() 0333 { 0334 if (focusObject() && Options::isTracking()) 0335 m_objBox->slotObjectChanged(focusObject()); 0336 else 0337 m_objBox->slotPointChanged(focus()); 0338 } 0339 0340 void SkyMap::slotTransientLabel() 0341 { 0342 //This function is only called if the HoverTimer manages to timeout. 0343 //(HoverTimer is restarted with every mouseMoveEvent; so if it times 0344 //out, that means there was no mouse movement for HOVER_INTERVAL msec.) 0345 if (hasFocus() && !slewing && 0346 !(Options::useAltAz() && Options::showGround() && m_MousePoint.altRefracted().Degrees() < 0.0)) 0347 { 0348 double maxrad = 1000.0 / Options::zoomFactor(); 0349 SkyObject *so = data->skyComposite()->objectNearest(&m_MousePoint, maxrad); 0350 0351 if (so && !isObjectLabeled(so)) 0352 { 0353 QString name = so->translatedLongName(); 0354 if (!std::isnan(so->mag())) 0355 name += QString(": %1<sup>m</sup>").arg(QString::number(so->mag(), 'f', 1)); 0356 QToolTip::showText(QCursor::pos(), name, this); 0357 } 0358 } 0359 } 0360 0361 //Slots 0362 0363 void SkyMap::setClickedObject(SkyObject *o) 0364 { 0365 ClickedObject = o; 0366 } 0367 0368 void SkyMap::setFocusObject(SkyObject *o) 0369 { 0370 FocusObject = o; 0371 if (FocusObject) 0372 Options::setFocusObject(FocusObject->name()); 0373 else 0374 Options::setFocusObject(i18n("nothing")); 0375 } 0376 0377 void SkyMap::slotCenter() 0378 { 0379 KStars *kstars = KStars::Instance(); 0380 TrailObject *trailObj = dynamic_cast<TrailObject *>(focusObject()); 0381 0382 SkyPoint *foc; 0383 if(ClickedObject != nullptr) 0384 foc = ClickedObject; 0385 else 0386 foc = &ClickedPoint; 0387 0388 //clear the planet trail of old focusObject, if it was temporary 0389 if (trailObj && data->temporaryTrail) 0390 { 0391 trailObj->clearTrail(); 0392 data->temporaryTrail = false; 0393 } 0394 0395 //If the requested object is below the opaque horizon, issue a warning message 0396 //(unless user is already pointed below the horizon) 0397 if (Options::useAltAz() && Options::showGround() && 0398 focus()->alt().Degrees() > SkyPoint::altCrit && 0399 foc->alt().Degrees() <= SkyPoint::altCrit) 0400 { 0401 QString caption = i18n("Requested Position Below Horizon"); 0402 QString message = i18n("The requested position is below the horizon.\nWould you like to go there anyway?"); 0403 if (KMessageBox::warningYesNo(this, message, caption, KGuiItem(i18n("Go Anyway")), 0404 KGuiItem(i18n("Keep Position")), "dag_focus_below_horiz") == KMessageBox::No) 0405 { 0406 setClickedObject(nullptr); 0407 setFocusObject(nullptr); 0408 Options::setIsTracking(false); 0409 0410 return; 0411 } 0412 } 0413 0414 //set FocusObject before slewing. Otherwise, KStarsData::updateTime() can reset 0415 //destination to previous object... 0416 setFocusObject(ClickedObject); 0417 if(ClickedObject == nullptr) 0418 setFocusPoint(&ClickedPoint); 0419 0420 Options::setIsTracking(true); 0421 0422 if (kstars) 0423 { 0424 kstars->actionCollection() 0425 ->action("track_object") 0426 ->setIcon(QIcon::fromTheme("document-encrypt")); 0427 kstars->actionCollection()->action("track_object")->setText(i18n("Stop &Tracking")); 0428 } 0429 0430 //If focusObject is a SS body and doesn't already have a trail, set the temporaryTrail 0431 0432 if (Options::useAutoTrail() && trailObj && trailObj->hasTrail()) 0433 { 0434 trailObj->addToTrail(); 0435 data->temporaryTrail = true; 0436 } 0437 0438 //update the destination to the selected coordinates 0439 if (Options::useAltAz()) 0440 { 0441 setDestinationAltAz(foc->alt(), foc->az(), false); 0442 } 0443 else 0444 { 0445 setDestination(*foc); 0446 } 0447 0448 foc->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0449 0450 //display coordinates in statusBar 0451 emit mousePointChanged(foc); 0452 showFocusCoords(); //update FocusBox 0453 } 0454 0455 void SkyMap::slotUpdateSky(bool now) 0456 { 0457 // Code moved from KStarsData::updateTime() 0458 //Update focus 0459 updateFocus(); 0460 0461 if (now) 0462 QTimer::singleShot( 0463 0, this, 0464 SLOT(forceUpdateNow())); // Why is it done this way rather than just calling forceUpdateNow()? -- asimha // --> Opening a neww thread? -- Valentin 0465 else 0466 forceUpdate(); 0467 } 0468 0469 void SkyMap::slotDSS() 0470 { 0471 dms ra(0.0), dec(0.0); 0472 QString urlstring; 0473 0474 //ra and dec must be the coordinates at J2000. If we clicked on an object, just use the object's ra0, dec0 coords 0475 //if we clicked on empty sky, we need to precess to J2000. 0476 if (clickedObject()) 0477 { 0478 urlstring = KSDssDownloader::getDSSURL(clickedObject()); 0479 } 0480 else 0481 { 0482 //SkyPoint deprecessedPoint = clickedPoint()->deprecess(data->updateNum()); 0483 SkyPoint deprecessedPoint = clickedPoint()->catalogueCoord(data->updateNum()->julianDay()); 0484 ra = deprecessedPoint.ra(); 0485 dec = deprecessedPoint.dec(); 0486 urlstring = KSDssDownloader::getDSSURL(ra, dec); // Use default size for non-objects 0487 } 0488 0489 QUrl url(urlstring); 0490 0491 KStars *kstars = KStars::Instance(); 0492 if (kstars) 0493 { 0494 new ImageViewer( 0495 url, i18n("Digitized Sky Survey image provided by the Space Telescope Science Institute [free for non-commercial use]."), 0496 this); 0497 //iv->show(); 0498 } 0499 } 0500 0501 void SkyMap::slotCopyCoordinates() 0502 { 0503 dms J2000RA(0.0), J2000DE(0.0), JNowRA(0.0), JNowDE(0.0), Az, Alt; 0504 if (clickedObject()) 0505 { 0506 J2000RA = clickedObject()->ra0(); 0507 J2000DE = clickedObject()->dec0(); 0508 JNowRA = clickedObject()->ra(); 0509 JNowDE = clickedObject()->dec(); 0510 Az = clickedObject()->az(); 0511 Alt = clickedObject()->alt(); 0512 } 0513 else 0514 { 0515 // Empty point only have valid JNow RA/DE, not J2000 information. 0516 SkyPoint emptyPoint = *clickedPoint(); 0517 // Now get J2000 from JNow but de-aberrating, de-nutating, de-preccessing 0518 // This modifies emptyPoint, but the RA/DE are now missing and need 0519 // to be repopulated. 0520 emptyPoint.catalogueCoord(data->updateNum()->julianDay()); 0521 emptyPoint.setRA(clickedPoint()->ra()); 0522 emptyPoint.setDec(clickedPoint()->dec()); 0523 emptyPoint.EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0524 0525 J2000RA = emptyPoint.ra0(); 0526 J2000DE = emptyPoint.dec0(); 0527 JNowRA = emptyPoint.ra(); 0528 JNowDE = emptyPoint.dec(); 0529 Az = emptyPoint.az(); 0530 Alt = emptyPoint.alt(); 0531 } 0532 0533 QApplication::clipboard()->setText(i18nc("Equatorial & Horizontal Coordinates", 0534 "JNow:\t%1\t%2\nJ2000:\t%3\t%4\nAzAlt:\t%5\t%6", 0535 JNowRA.toHMSString(), 0536 JNowDE.toDMSString(), 0537 J2000RA.toHMSString(), 0538 J2000DE.toDMSString(), 0539 Az.toDMSString(), 0540 Alt.toDMSString())); 0541 } 0542 0543 0544 void SkyMap::slotCopyTLE() 0545 { 0546 0547 QString tle = ""; 0548 if (clickedObject()->type() == SkyObject::SATELLITE) 0549 { 0550 Satellite *sat = (Satellite *) clickedObject(); 0551 tle = sat->tle(); 0552 } 0553 else 0554 { 0555 tle = "NO TLE FOR OBJECT"; 0556 } 0557 0558 0559 QApplication::clipboard()->setText(tle); 0560 } 0561 0562 void SkyMap::slotSDSS() 0563 { 0564 // TODO: Remove code duplication -- we have the same stuff 0565 // implemented in ObservingList::setCurrentImage() etc. in 0566 // tools/observinglist.cpp; must try to de-duplicate as much as 0567 // possible. 0568 QString URLprefix("http://skyserver.sdss.org/dr16/SkyServerWS/ImgCutout/getjpeg?"); 0569 QString URLsuffix("&scale=1.0&width=600&height=600"); 0570 dms ra(0.0), dec(0.0); 0571 QString RAString, DecString; 0572 0573 //ra and dec must be the coordinates at J2000. If we clicked on an object, just use the object's ra0, dec0 coords 0574 //if we clicked on empty sky, we need to precess to J2000. 0575 if (clickedObject()) 0576 { 0577 ra = clickedObject()->ra0(); 0578 dec = clickedObject()->dec0(); 0579 } 0580 else 0581 { 0582 //SkyPoint deprecessedPoint = clickedPoint()->deprecess(data->updateNum()); 0583 SkyPoint deprecessedPoint = clickedPoint()->catalogueCoord(data->updateNum()->julianDay()); 0584 deprecessedPoint.catalogueCoord(data->updateNum()->julianDay()); 0585 ra = deprecessedPoint.ra(); 0586 dec = deprecessedPoint.dec(); 0587 } 0588 0589 RAString = QString::asprintf("ra=%f", ra.Degrees()); 0590 DecString = QString::asprintf("&dec=%f", dec.Degrees()); 0591 0592 //concat all the segments into the kview command line: 0593 QUrl url(URLprefix + RAString + DecString + URLsuffix); 0594 0595 KStars *kstars = KStars::Instance(); 0596 if (kstars) 0597 { 0598 new ImageViewer(url, 0599 i18n("Sloan Digital Sky Survey image provided by the Astrophysical Research Consortium [free " 0600 "for non-commercial use]."), 0601 this); 0602 //iv->show(); 0603 } 0604 } 0605 0606 void SkyMap::slotEyepieceView() 0607 { 0608 KStars::Instance()->slotEyepieceView((clickedObject() ? clickedObject() : clickedPoint())); 0609 } 0610 void SkyMap::slotBeginAngularDistance() 0611 { 0612 beginRulerMode(false); 0613 } 0614 0615 void SkyMap::slotBeginStarHop() 0616 { 0617 beginRulerMode(true); 0618 } 0619 0620 void SkyMap::beginRulerMode(bool starHopRuler) 0621 { 0622 rulerMode = true; 0623 starHopDefineMode = starHopRuler; 0624 AngularRuler.clear(); 0625 0626 //If the cursor is near a SkyObject, reset the AngularRuler's 0627 //start point to the position of the SkyObject 0628 double maxrad = 1000.0 / Options::zoomFactor(); 0629 SkyObject *so = data->skyComposite()->objectNearest(clickedPoint(), maxrad); 0630 if (so) 0631 { 0632 AngularRuler.append(so); 0633 AngularRuler.append(so); 0634 m_rulerStartPoint = so; 0635 } 0636 else 0637 { 0638 AngularRuler.append(clickedPoint()); 0639 AngularRuler.append(clickedPoint()); 0640 m_rulerStartPoint = clickedPoint(); 0641 } 0642 0643 AngularRuler.update(data); 0644 } 0645 0646 void SkyMap::slotEndRulerMode() 0647 { 0648 if (!rulerMode) 0649 return; 0650 if (!starHopDefineMode) // Angular Ruler 0651 { 0652 QString sbMessage; 0653 0654 //If the cursor is near a SkyObject, reset the AngularRuler's 0655 //end point to the position of the SkyObject 0656 double maxrad = 1000.0 / Options::zoomFactor(); 0657 SkyPoint *rulerEndPoint; 0658 SkyObject *so = data->skyComposite()->objectNearest(clickedPoint(), maxrad); 0659 if (so) 0660 { 0661 AngularRuler.setPoint(1, so); 0662 sbMessage = so->translatedLongName() + " "; 0663 rulerEndPoint = so; 0664 } 0665 else 0666 { 0667 AngularRuler.setPoint(1, clickedPoint()); 0668 rulerEndPoint = clickedPoint(); 0669 } 0670 0671 rulerMode = false; 0672 AngularRuler.update(data); 0673 dms angularDistance = AngularRuler.angularSize(); 0674 0675 sbMessage += i18n("Angular distance: %1", angularDistance.toDMSString()); 0676 0677 const StarObject *p1 = dynamic_cast<const StarObject *>(m_rulerStartPoint); 0678 const StarObject *p2 = dynamic_cast<const StarObject *>(rulerEndPoint); 0679 0680 qCDebug(KSTARS) << "Starobjects? " << p1 << p2; 0681 if (p1 && p2) 0682 qCDebug(KSTARS) << "Distances: " << p1->distance() << "pc; " << p2->distance() << "pc"; 0683 if (p1 && p2 && std::isfinite(p1->distance()) && std::isfinite(p2->distance()) && p1->distance() > 0 && 0684 p2->distance() > 0) 0685 { 0686 double dist = sqrt(p1->distance() * p1->distance() + p2->distance() * p2->distance() - 0687 2 * p1->distance() * p2->distance() * cos(angularDistance.radians())); 0688 qCDebug(KSTARS) << "Could calculate physical distance: " << dist << " pc"; 0689 sbMessage += i18n("; Physical distance: %1 pc", QString::number(dist)); 0690 } 0691 0692 AngularRuler.clear(); 0693 0694 // Create unobsructive message box with suicidal tendencies 0695 // to display result. 0696 InfoBoxWidget *box = new InfoBoxWidget(true, mapFromGlobal(QCursor::pos()), 0, QStringList(sbMessage), this); 0697 connect(box, SIGNAL(clicked()), box, SLOT(deleteLater())); 0698 QTimer::singleShot(5000, box, SLOT(deleteLater())); 0699 box->adjust(); 0700 box->show(); 0701 } 0702 else // Star Hop 0703 { 0704 StarHopperDialog *shd = new StarHopperDialog(this); 0705 const SkyPoint &startHop = *AngularRuler.point(0); 0706 const SkyPoint &stopHop = *clickedPoint(); 0707 double fov; // Field of view in arcminutes 0708 bool ok; // true if user did not cancel the operation 0709 if (data->getAvailableFOVs().size() == 1) 0710 { 0711 // Exactly 1 FOV symbol visible, so use that. Also assume a circular FOV of size min{sizeX, sizeY} 0712 FOV *f = data->getAvailableFOVs().first(); 0713 fov = ((f->sizeX() >= f->sizeY() && f->sizeY() != 0) ? f->sizeY() : f->sizeX()); 0714 ok = true; 0715 } 0716 else if (!data->getAvailableFOVs().isEmpty()) 0717 { 0718 // Ask the user to choose from a list of available FOVs. 0719 FOV const *f; 0720 QMap<QString, double> nameToFovMap; 0721 foreach (f, data->getAvailableFOVs()) 0722 { 0723 nameToFovMap.insert(f->name(), 0724 ((f->sizeX() >= f->sizeY() && f->sizeY() != 0) ? f->sizeY() : f->sizeX())); 0725 } 0726 fov = nameToFovMap[QInputDialog::getItem(this, i18n("Star Hopper: Choose a field-of-view"), 0727 i18n("FOV to use for star hopping:"), nameToFovMap.keys(), 0, 0728 false, &ok)]; 0729 } 0730 else 0731 { 0732 // Ask the user to enter a field of view 0733 fov = 0734 QInputDialog::getDouble(this, i18n("Star Hopper: Enter field-of-view to use"), 0735 i18n("FOV to use for star hopping (in arcminutes):"), 60.0, 1.0, 600.0, 1, &ok); 0736 } 0737 0738 Q_ASSERT(fov > 0.0); 0739 0740 if (ok) 0741 { 0742 qCDebug(KSTARS) << "fov = " << fov; 0743 0744 shd->starHop(startHop, stopHop, fov / 60.0, 9.0); //FIXME: Hardcoded maglimit value 0745 shd->show(); 0746 } 0747 0748 rulerMode = false; 0749 } 0750 } 0751 0752 void SkyMap::slotCancelRulerMode(void) 0753 { 0754 rulerMode = false; 0755 AngularRuler.clear(); 0756 } 0757 0758 void SkyMap::slotAddFlag() 0759 { 0760 KStars *ks = KStars::Instance(); 0761 0762 // popup FlagManager window and update coordinates 0763 ks->slotFlagManager(); 0764 ks->flagManager()->clearFields(); 0765 0766 //ra and dec must be the coordinates at J2000. If we clicked on an object, just use the object's ra0, dec0 coords 0767 //if we clicked on empty sky, we need to precess to J2000. 0768 0769 dms J2000RA, J2000DE; 0770 0771 if (clickedObject()) 0772 { 0773 J2000RA = clickedObject()->ra0(); 0774 J2000DE = clickedObject()->dec0(); 0775 } 0776 else 0777 { 0778 //SkyPoint deprecessedPoint = clickedPoint()->deprecess(data->updateNum()); 0779 SkyPoint deprecessedPoint = clickedPoint()->catalogueCoord(data->updateNum()->julianDay()); 0780 deprecessedPoint.catalogueCoord(data->updateNum()->julianDay()); 0781 J2000RA = deprecessedPoint.ra(); 0782 J2000DE = deprecessedPoint.dec(); 0783 } 0784 0785 ks->flagManager()->setRaDec(J2000RA, J2000DE); 0786 } 0787 0788 void SkyMap::slotEditFlag(int flagIdx) 0789 { 0790 KStars *ks = KStars::Instance(); 0791 0792 // popup FlagManager window and switch to selected flag 0793 ks->slotFlagManager(); 0794 ks->flagManager()->showFlag(flagIdx); 0795 } 0796 0797 void SkyMap::slotDeleteFlag(int flagIdx) 0798 { 0799 KStars *ks = KStars::Instance(); 0800 0801 ks->data()->skyComposite()->flags()->remove(flagIdx); 0802 ks->data()->skyComposite()->flags()->saveToFile(); 0803 0804 // if there is FlagManager created, update its flag model 0805 if (ks->flagManager()) 0806 { 0807 ks->flagManager()->deleteFlagItem(flagIdx); 0808 } 0809 } 0810 0811 void SkyMap::slotImage() 0812 { 0813 const auto *action = qobject_cast<QAction *>(sender()); 0814 const auto url = action->data().toUrl(); 0815 const QString message{ action->text().remove('&') }; 0816 0817 if (!url.isEmpty()) 0818 new ImageViewer(url, clickedObject()->messageFromTitle(message), this); 0819 } 0820 0821 void SkyMap::slotInfo() 0822 { 0823 const auto *action = qobject_cast<QAction *>(sender()); 0824 const auto url = action->data().toUrl(); 0825 0826 if (!url.isEmpty()) 0827 QDesktopServices::openUrl(url); 0828 } 0829 0830 bool SkyMap::isObjectLabeled(SkyObject *object) 0831 { 0832 return data->skyComposite()->labelObjects().contains(object); 0833 } 0834 0835 SkyPoint SkyMap::getCenterPoint() 0836 { 0837 SkyPoint retVal; 0838 // FIXME: subtraction of these 0.00001 is a simple workaround, because wrong 0839 // SkyPoint is returned when _exact_ center of SkyMap is passed to the projector. 0840 retVal = projector()->fromScreen(QPointF((qreal)width() / 2 - 0.00001, (qreal)height() / 2 - 0.00001), data->lst(), 0841 data->geo()->lat()); 0842 return retVal; 0843 } 0844 0845 void SkyMap::slotRemoveObjectLabel() 0846 { 0847 data->skyComposite()->removeNameLabel(clickedObject()); 0848 forceUpdate(); 0849 } 0850 0851 void SkyMap::slotRemoveCustomObject() 0852 { 0853 auto *object = dynamic_cast<CatalogObject *>(clickedObject()); 0854 if (!object) 0855 return; 0856 0857 const auto &cat = object->getCatalog(); 0858 if (!cat.mut) 0859 return; 0860 0861 CatalogsDB::DBManager manager{ CatalogsDB::dso_db_path() }; 0862 manager.remove_object(cat.id, object->getObjectId()); 0863 0864 emit removeSkyObject(object); 0865 data->skyComposite()->removeFromNames(object); 0866 data->skyComposite()->removeFromLists(object); 0867 data->skyComposite()->reloadDeepSky(); 0868 KStars::Instance()->updateTime(); 0869 } 0870 0871 void SkyMap::slotAddObjectLabel() 0872 { 0873 data->skyComposite()->addNameLabel(clickedObject()); 0874 forceUpdate(); 0875 } 0876 0877 void SkyMap::slotRemovePlanetTrail() 0878 { 0879 TrailObject *tobj = dynamic_cast<TrailObject *>(clickedObject()); 0880 if (tobj) 0881 { 0882 tobj->clearTrail(); 0883 forceUpdate(); 0884 } 0885 } 0886 0887 void SkyMap::slotAddPlanetTrail() 0888 { 0889 TrailObject *tobj = dynamic_cast<TrailObject *>(clickedObject()); 0890 if (tobj) 0891 { 0892 tobj->addToTrail(); 0893 forceUpdate(); 0894 } 0895 } 0896 0897 void SkyMap::slotDetail() 0898 { 0899 // check if object is selected 0900 if (!clickedObject()) 0901 { 0902 KMessageBox::sorry(this, i18n("No object selected."), i18n("Object Details")); 0903 return; 0904 } 0905 DetailDialog *detail = new DetailDialog(clickedObject(), data->ut(), data->geo(), KStars::Instance()); 0906 detail->setAttribute(Qt::WA_DeleteOnClose); 0907 detail->show(); 0908 } 0909 0910 void SkyMap::slotObjectSelected() 0911 { 0912 if (m_objPointingMode && KStars::Instance()->printingWizard()) 0913 { 0914 KStars::Instance()->printingWizard()->pointingDone(clickedObject()); 0915 m_objPointingMode = false; 0916 } 0917 } 0918 0919 void SkyMap::slotCancelLegendPreviewMode() 0920 { 0921 m_previewLegend = false; 0922 forceUpdate(true); 0923 KStars::Instance()->showImgExportDialog(); 0924 } 0925 0926 void SkyMap::slotFinishFovCaptureMode() 0927 { 0928 if (m_fovCaptureMode && KStars::Instance()->printingWizard()) 0929 { 0930 KStars::Instance()->printingWizard()->fovCaptureDone(); 0931 m_fovCaptureMode = false; 0932 } 0933 } 0934 0935 void SkyMap::slotCaptureFov() 0936 { 0937 if (KStars::Instance()->printingWizard()) 0938 { 0939 KStars::Instance()->printingWizard()->captureFov(); 0940 } 0941 } 0942 0943 void SkyMap::slotClockSlewing() 0944 { 0945 //If the current timescale exceeds slewTimeScale, set clockSlewing=true, and stop the clock. 0946 if ((fabs(data->clock()->scale()) > Options::slewTimeScale()) ^ clockSlewing) 0947 { 0948 data->clock()->setManualMode(!clockSlewing); 0949 clockSlewing = !clockSlewing; 0950 // don't change automatically the DST status 0951 KStars *kstars = KStars::Instance(); 0952 if (kstars) 0953 kstars->updateTime(false); 0954 } 0955 } 0956 0957 void SkyMap::setFocus(SkyPoint *p) 0958 { 0959 setFocus(p->ra(), p->dec()); 0960 } 0961 0962 void SkyMap::setFocus(const dms &ra, const dms &dec) 0963 { 0964 Options::setFocusRA(ra.Hours()); 0965 Options::setFocusDec(dec.Degrees()); 0966 0967 focus()->set(ra, dec); 0968 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0969 } 0970 0971 void SkyMap::setFocusAltAz(const dms &alt, const dms &az) 0972 { 0973 Options::setFocusRA(focus()->ra().Hours()); 0974 Options::setFocusDec(focus()->dec().Degrees()); 0975 focus()->setAlt(alt); 0976 focus()->setAz(az); 0977 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 0978 0979 slewing = false; 0980 forceUpdate(); //need a total update, or slewing with the arrow keys doesn't work. 0981 } 0982 0983 void SkyMap::setDestination(const SkyPoint &p) 0984 { 0985 setDestination(p.ra(), p.dec()); 0986 } 0987 0988 void SkyMap::setDestination(const dms &ra, const dms &dec) 0989 { 0990 destination()->set(ra, dec); 0991 destination()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0992 emit destinationChanged(); 0993 } 0994 0995 void SkyMap::setDestinationAltAz(const dms &alt, const dms &az, bool altIsRefracted) 0996 { 0997 if (altIsRefracted) 0998 { 0999 // The alt in the SkyPoint is always actual, not apparent 1000 destination()->setAlt(SkyPoint::unrefract(alt)); 1001 } 1002 else 1003 { 1004 destination()->setAlt(alt); 1005 } 1006 destination()->setAz(az); 1007 destination()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 1008 emit destinationChanged(); 1009 } 1010 1011 void SkyMap::setClickedPoint(const SkyPoint *f) 1012 { 1013 ClickedPoint = *f; 1014 } 1015 1016 void SkyMap::updateFocus() 1017 { 1018 if (slewing) 1019 return; 1020 1021 //Tracking on an object 1022 if (Options::isTracking()) 1023 { 1024 if (focusObject()) 1025 { 1026 focusObject()->updateCoordsNow(data->updateNum()); 1027 setDestination(*focusObject()); 1028 } 1029 else 1030 setDestination(*focusPoint()); 1031 } 1032 else 1033 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 1034 } 1035 1036 void SkyMap::slewFocus() 1037 { 1038 //Don't slew if the mouse button is pressed 1039 //Also, no animated slews if the Manual Clock is active 1040 //08/2002: added possibility for one-time skipping of slew with snapNextFocus 1041 if (!mouseButtonDown) 1042 { 1043 bool goSlew = (Options::useAnimatedSlewing() && !data->snapNextFocus()) && 1044 !(data->clock()->isManualMode() && data->clock()->isActive()); 1045 if (goSlew) 1046 { 1047 double dX, dY; 1048 double maxstep = 10.0; 1049 if (Options::useAltAz()) 1050 { 1051 dX = destination()->az().Degrees() - focus()->az().Degrees(); 1052 dY = destination()->alt().Degrees() - focus()->alt().Degrees(); 1053 } 1054 else 1055 { 1056 dX = destination()->ra().Degrees() - focus()->ra().Degrees(); 1057 dY = destination()->dec().Degrees() - focus()->dec().Degrees(); 1058 } 1059 1060 //switch directions to go the short way around the celestial sphere, if necessary. 1061 dX = KSUtils::reduceAngle(dX, -180.0, 180.0); 1062 1063 double r0 = sqrt(dX * dX + dY * dY); 1064 if (r0 < 20.0) //smaller slews have smaller maxstep 1065 { 1066 maxstep *= (10.0 + 0.5 * r0) / 20.0; 1067 } 1068 double step = 0.5; 1069 double r = r0; 1070 while (r > step) 1071 { 1072 //DEBUG 1073 //qDebug() << Q_FUNC_INFO << step << ": " << r << ": " << r0; 1074 double fX = dX / r; 1075 double fY = dY / r; 1076 1077 if (Options::useAltAz()) 1078 { 1079 focus()->setAlt(focus()->alt().Degrees() + fY * step); 1080 focus()->setAz(dms(focus()->az().Degrees() + fX * step).reduce()); 1081 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 1082 } 1083 else 1084 { 1085 fX = fX / 15.; //convert RA degrees to hours 1086 SkyPoint newFocus(focus()->ra().Hours() + fX * step, focus()->dec().Degrees() + fY * step); 1087 setFocus(&newFocus); 1088 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 1089 } 1090 1091 slewing = true; 1092 1093 forceUpdate(); 1094 qApp->processEvents(); //keep up with other stuff 1095 1096 if (Options::useAltAz()) 1097 { 1098 dX = destination()->az().Degrees() - focus()->az().Degrees(); 1099 dY = destination()->alt().Degrees() - focus()->alt().Degrees(); 1100 } 1101 else 1102 { 1103 dX = destination()->ra().Degrees() - focus()->ra().Degrees(); 1104 dY = destination()->dec().Degrees() - focus()->dec().Degrees(); 1105 } 1106 1107 //switch directions to go the short way around the celestial sphere, if necessary. 1108 dX = KSUtils::reduceAngle(dX, -180.0, 180.0); 1109 r = sqrt(dX * dX + dY * dY); 1110 1111 //Modify step according to a cosine-shaped profile 1112 //centered on the midpoint of the slew 1113 //NOTE: don't allow the full range from -PI/2 to PI/2 1114 //because the slew will never reach the destination as 1115 //the speed approaches zero at the end! 1116 double t = dms::PI * (r - 0.5 * r0) / (1.05 * r0); 1117 step = cos(t) * maxstep; 1118 } 1119 } 1120 1121 //Either useAnimatedSlewing==false, or we have slewed, and are within one step of destination 1122 //set focus=destination. 1123 if (Options::useAltAz()) 1124 { 1125 setFocusAltAz(destination()->alt(), destination()->az()); 1126 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 1127 } 1128 else 1129 { 1130 setFocus(destination()); 1131 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 1132 } 1133 1134 slewing = false; 1135 1136 //Turn off snapNextFocus, we only want it to happen once 1137 if (data->snapNextFocus()) 1138 { 1139 data->setSnapNextFocus(false); 1140 } 1141 1142 //Start the HoverTimer. if the user leaves the mouse in place after a slew, 1143 //we want to attach a label to the nearest object. 1144 if (Options::useHoverLabel()) 1145 m_HoverTimer.start(HOVER_INTERVAL); 1146 1147 forceUpdate(); 1148 } 1149 } 1150 1151 void SkyMap::slotZoomIn() 1152 { 1153 setZoomFactor(Options::zoomFactor() * DZOOM); 1154 } 1155 1156 void SkyMap::slotZoomOut() 1157 { 1158 setZoomFactor(Options::zoomFactor() / DZOOM); 1159 } 1160 1161 void SkyMap::slotZoomDefault() 1162 { 1163 setZoomFactor(DEFAULTZOOM); 1164 } 1165 1166 void SkyMap::setZoomFactor(double factor) 1167 { 1168 Options::setZoomFactor(KSUtils::clamp(factor, MINZOOM, MAXZOOM)); 1169 forceUpdate(); 1170 emit zoomChanged(); 1171 } 1172 1173 // force a new calculation of the skymap (used instead of update(), which may skip the redraw) 1174 // if now=true, SkyMap::paintEvent() is run immediately, rather than being added to the event queue 1175 // also, determine new coordinates of mouse cursor. 1176 void SkyMap::forceUpdate(bool now) 1177 { 1178 QPoint mp(mapFromGlobal(QCursor::pos())); 1179 if (!projector()->unusablePoint(mp)) 1180 { 1181 //determine RA, Dec of mouse pointer 1182 m_MousePoint = projector()->fromScreen(mp, data->lst(), data->geo()->lat()); 1183 } 1184 1185 computeSkymap = true; 1186 1187 // Ensure that stars are recomputed 1188 data->incUpdateID(); 1189 1190 if (now) 1191 m_SkyMapDraw->repaint(); 1192 else 1193 m_SkyMapDraw->update(); 1194 } 1195 1196 float SkyMap::fov() 1197 { 1198 float diagonalPixels = sqrt(static_cast<double>(width() * width() + height() * height())); 1199 return diagonalPixels / (2 * Options::zoomFactor() * dms::DegToRad); 1200 } 1201 1202 dms SkyMap::determineSkyRotation() 1203 { 1204 // Note: The erect observer correction accounts for the fact that 1205 // an observer remains erect despite the tube of an 1206 // Altazmith-mounted Newtonian moving up and down, so an 1207 // additional rotation of altitude applies to match the 1208 // orientation of the field. This would not apply to a CCD camera 1209 // plugged into the same telescope, since the CCD would rotate as 1210 // seen from the ground when the telescope moves in altitude. 1211 return dms(Options::skyRotation() - ( 1212 (Options::erectObserverCorrection() && Options::useAltAz()) ? focus()->alt().Degrees() : 0.0)); 1213 } 1214 1215 void SkyMap::slotSetSkyRotation(double angle) 1216 { 1217 angle = dms(angle).reduce().Degrees(); 1218 Options::setSkyRotation(angle); 1219 KStars *kstars = KStars::Instance(); 1220 if (kstars) 1221 { 1222 if (angle == 0.) 1223 { 1224 kstars->actionCollection()->action("up_orientation")->setChecked(true); 1225 } 1226 else if (angle == 180.) 1227 { 1228 kstars->actionCollection()->action("down_orientation")->setChecked(true); 1229 } 1230 else 1231 { 1232 kstars->actionCollection()->action("arbitrary_orientation")->setChecked(true); 1233 } 1234 } 1235 forceUpdate(); 1236 } 1237 1238 void SkyMap::setupProjector() 1239 { 1240 //Update View Parameters for projection 1241 ViewParams p; 1242 p.focus = focus(); 1243 p.height = height(); 1244 p.width = width(); 1245 p.useAltAz = Options::useAltAz(); 1246 p.useRefraction = Options::useRefraction(); 1247 p.zoomFactor = Options::zoomFactor(); 1248 p.rotationAngle = determineSkyRotation(); 1249 p.fillGround = Options::showGround(); 1250 //Check if we need a new projector 1251 if (m_proj && Options::projection() == m_proj->type()) 1252 m_proj->setViewParams(p); 1253 else 1254 { 1255 delete m_proj; 1256 switch (Options::projection()) 1257 { 1258 case Gnomonic: 1259 m_proj = new GnomonicProjector(p); 1260 break; 1261 case Stereographic: 1262 m_proj = new StereographicProjector(p); 1263 break; 1264 case Orthographic: 1265 m_proj = new OrthographicProjector(p); 1266 break; 1267 case AzimuthalEquidistant: 1268 m_proj = new AzimuthalEquidistantProjector(p); 1269 break; 1270 case Equirectangular: 1271 m_proj = new EquirectangularProjector(p); 1272 break; 1273 case Lambert: 1274 default: 1275 //TODO: implement other projection classes 1276 m_proj = new LambertProjector(p); 1277 break; 1278 } 1279 } 1280 } 1281 1282 void SkyMap::setZoomMouseCursor() 1283 { 1284 mouseMoveCursor = false; // no mousemove cursor 1285 mouseDragCursor = false; 1286 QBitmap cursor = zoomCursorBitmap(2); 1287 QBitmap mask = zoomCursorBitmap(4); 1288 setCursor(QCursor(cursor, mask)); 1289 } 1290 1291 void SkyMap::setRotationMouseCursor() 1292 { 1293 mouseMoveCursor = false; 1294 mouseDragCursor = false; 1295 QBitmap cursor = rotationCursorBitmap(2); 1296 QBitmap mask = rotationCursorBitmap(4); 1297 setCursor(QCursor(cursor, mask)); 1298 } 1299 1300 void SkyMap::setMouseCursorShape(Cursor type) 1301 { 1302 // no mousemove cursor 1303 mouseMoveCursor = false; 1304 mouseDragCursor = false; 1305 1306 switch (type) 1307 { 1308 case Cross: 1309 { 1310 QBitmap cursor = defaultCursorBitmap(2); 1311 QBitmap mask = defaultCursorBitmap(3); 1312 setCursor(QCursor(cursor, mask)); 1313 } 1314 break; 1315 1316 case Circle: 1317 { 1318 QBitmap cursor = circleCursorBitmap(2); 1319 QBitmap mask = circleCursorBitmap(3); 1320 setCursor(QCursor(cursor, mask)); 1321 } 1322 break; 1323 1324 case NoCursor: 1325 setCursor(Qt::ArrowCursor); 1326 break; 1327 } 1328 } 1329 1330 void SkyMap::setMouseMoveCursor() 1331 { 1332 if (mouseButtonDown) 1333 { 1334 setCursor(Qt::SizeAllCursor); // cursor shape defined in qt 1335 mouseMoveCursor = true; 1336 } 1337 } 1338 1339 void SkyMap::setMouseDragCursor() 1340 { 1341 if (mouseButtonDown) 1342 { 1343 setCursor(Qt::OpenHandCursor); // cursor shape defined in qt 1344 mouseDragCursor = true; 1345 } 1346 } 1347 1348 void SkyMap::updateAngleRuler() 1349 { 1350 if (rulerMode && (!pmenu || !pmenu->isVisible())) 1351 AngularRuler.setPoint(1, &m_MousePoint); 1352 AngularRuler.update(data); 1353 } 1354 1355 bool SkyMap::isSlewing() const 1356 { 1357 return (slewing || (clockSlewing && data->clock()->isActive())); 1358 } 1359 1360 void SkyMap::slotStartXplanetViewer() 1361 { 1362 if(clickedObject()) 1363 new XPlanetImageViewer(clickedObject()->name(), this); 1364 else 1365 new XPlanetImageViewer(i18n("Saturn"), this); 1366 } 1367 1368